2016-03-31 21:36:00 +08:00
|
|
|
/*
|
|
|
|
* ALSA SoC codec for HDMI encoder drivers
|
|
|
|
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
|
|
|
|
* Author: Jyri Sarha <jsarha@ti.com>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* version 2 as published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* General Public License for more details.
|
|
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/string.h>
|
|
|
|
#include <sound/core.h>
|
|
|
|
#include <sound/pcm.h>
|
|
|
|
#include <sound/pcm_params.h>
|
|
|
|
#include <sound/soc.h>
|
|
|
|
#include <sound/pcm_drm_eld.h>
|
|
|
|
#include <sound/hdmi-codec.h>
|
|
|
|
#include <sound/pcm_iec958.h>
|
|
|
|
|
|
|
|
#include <drm/drm_crtc.h> /* This is only to get MAX_ELD_BYTES */
|
|
|
|
|
ASoC: hdmi-codec: enable multi probe for same device
hdmi-codec driver is common HDMI sound driver,
but it doesn't care about multi sound ports.
For example, hdmi-codec driver is supporting 1 I2S and 1 SPDIF ports,
so, we can't use this driver if HDMI has 2 or more I2S ports.
And we would like to use multi detection.
For example, DesignWare HDMI driver is providing dw_hdmi_bind() to
DRM/KMS driver, and it will setups HDMI video/sound.
Note is that it will be called under for_each loop of ports.
int dw_hdmi_bind(xxx)
{
/* register hdmi-codec driver here */
}
static int xxx_probe(struct platform_device *pdev)
{
for_each_xxx(xx) {
...
dw_hdmi_bind(xxx);
...
}
}
This case, dw_hdmi_bind() would like to use hdmi-codec,
and it will be called multiple times for each ports.
Here, ASoC's CPU/Codec/Card bind will checks each "of_node" on DT,
and hdmi-codec driver is assuming its parent device for it.
But it doesn't care about case.
Thus, ASoC never detect correct sound card in this case.
To solve this issue, this patch checks each parent device,
and names "hdmi-hifi.x" in order to each ports.
And uses struct snd_soc_component_driver :: of_xlate_dai_name
for snd_soc_get_dai_name().
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
2016-08-03 10:08:41 +08:00
|
|
|
struct hdmi_device {
|
|
|
|
struct device *dev;
|
|
|
|
struct list_head list;
|
|
|
|
int cnt;
|
|
|
|
};
|
|
|
|
#define pos_to_hdmi_device(pos) container_of((pos), struct hdmi_device, list)
|
|
|
|
LIST_HEAD(hdmi_device_list);
|
|
|
|
|
|
|
|
#define DAI_NAME_SIZE 16
|
2016-03-31 21:36:00 +08:00
|
|
|
struct hdmi_codec_priv {
|
|
|
|
struct hdmi_codec_pdata hcd;
|
|
|
|
struct snd_soc_dai_driver *daidrv;
|
|
|
|
struct hdmi_codec_daifmt daifmt[2];
|
|
|
|
struct mutex current_stream_lock;
|
|
|
|
struct snd_pcm_substream *current_stream;
|
|
|
|
struct snd_pcm_hw_constraint_list ratec;
|
|
|
|
uint8_t eld[MAX_ELD_BYTES];
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct snd_soc_dapm_widget hdmi_widgets[] = {
|
|
|
|
SND_SOC_DAPM_OUTPUT("TX"),
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct snd_soc_dapm_route hdmi_routes[] = {
|
|
|
|
{ "TX", NULL, "Playback" },
|
|
|
|
};
|
|
|
|
|
|
|
|
enum {
|
|
|
|
DAI_ID_I2S = 0,
|
|
|
|
DAI_ID_SPDIF,
|
|
|
|
};
|
|
|
|
|
2016-04-20 16:59:58 +08:00
|
|
|
static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_info *uinfo)
|
|
|
|
{
|
|
|
|
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
|
|
|
struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
|
|
|
|
|
|
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
|
|
|
|
uinfo->count = sizeof(hcp->eld);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hdmi_eld_ctl_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.bytes.data, hcp->eld, sizeof(hcp->eld));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct snd_kcontrol_new hdmi_controls[] = {
|
|
|
|
{
|
|
|
|
.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,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2016-03-31 21:36:00 +08:00
|
|
|
static int hdmi_codec_new_stream(struct snd_pcm_substream *substream,
|
|
|
|
struct snd_soc_dai *dai)
|
|
|
|
{
|
|
|
|
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
mutex_lock(&hcp->current_stream_lock);
|
|
|
|
if (!hcp->current_stream) {
|
|
|
|
hcp->current_stream = substream;
|
|
|
|
} else if (hcp->current_stream != substream) {
|
|
|
|
dev_err(dai->dev, "Only one simultaneous stream supported!\n");
|
|
|
|
ret = -EINVAL;
|
|
|
|
}
|
|
|
|
mutex_unlock(&hcp->current_stream_lock);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hdmi_codec_startup(struct snd_pcm_substream *substream,
|
|
|
|
struct snd_soc_dai *dai)
|
|
|
|
{
|
|
|
|
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
dev_dbg(dai->dev, "%s()\n", __func__);
|
|
|
|
|
|
|
|
ret = hdmi_codec_new_stream(substream, dai);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (hcp->hcd.ops->audio_startup) {
|
ASoC: hdmi-codec: callback function will be called with private data
Current hdmi-codec driver is assuming that it will be registered
from HDMI driver. Because of this assumption, each callback function
has struct device pointer which is parent device (= HDMI).
Then, it can use dev_get_drvdata() to get private data.
OTOH, on some SoC/HDMI case, SoC has VIDEO/SOUND and HDMI IPs.
This case, it needs SoC VIDEO, SoC SOUND and HDMI video, HDMI codec
driver. In DesignWare HDMI IP case, SoC VIDEO (= DRM/KMS) driver tries
to bind DesignWare HDMI video driver, and HDMI codec driver
(= hdmi-codec). This case, above "parent device" of HDMI codec driver
is DRM/KMS driver and its "device" already has private data.
And, from DT and ASoC CPU/Codec/Card binding point of view, HDMI codec
(= hdmi-codec) needs to have "parent device" (= DRM/KMS), otherwise,
it never detect sound card.
Because of these reasons, some driver can't use dev_get_drvdata() to
get private data on hdmi-codec driver. This patch add new void pointer
on hdmi_codec_pdata for private data, and callback function will be
called with it.
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
2016-06-24 10:47:55 +08:00
|
|
|
ret = hcp->hcd.ops->audio_startup(dai->dev->parent, hcp->hcd.data);
|
2016-03-31 21:36:00 +08:00
|
|
|
if (ret) {
|
|
|
|
mutex_lock(&hcp->current_stream_lock);
|
|
|
|
hcp->current_stream = NULL;
|
|
|
|
mutex_unlock(&hcp->current_stream_lock);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hcp->hcd.ops->get_eld) {
|
ASoC: hdmi-codec: callback function will be called with private data
Current hdmi-codec driver is assuming that it will be registered
from HDMI driver. Because of this assumption, each callback function
has struct device pointer which is parent device (= HDMI).
Then, it can use dev_get_drvdata() to get private data.
OTOH, on some SoC/HDMI case, SoC has VIDEO/SOUND and HDMI IPs.
This case, it needs SoC VIDEO, SoC SOUND and HDMI video, HDMI codec
driver. In DesignWare HDMI IP case, SoC VIDEO (= DRM/KMS) driver tries
to bind DesignWare HDMI video driver, and HDMI codec driver
(= hdmi-codec). This case, above "parent device" of HDMI codec driver
is DRM/KMS driver and its "device" already has private data.
And, from DT and ASoC CPU/Codec/Card binding point of view, HDMI codec
(= hdmi-codec) needs to have "parent device" (= DRM/KMS), otherwise,
it never detect sound card.
Because of these reasons, some driver can't use dev_get_drvdata() to
get private data on hdmi-codec driver. This patch add new void pointer
on hdmi_codec_pdata for private data, and callback function will be
called with it.
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
2016-06-24 10:47:55 +08:00
|
|
|
ret = hcp->hcd.ops->get_eld(dai->dev->parent, hcp->hcd.data,
|
|
|
|
hcp->eld, sizeof(hcp->eld));
|
2016-03-31 21:36:00 +08:00
|
|
|
|
|
|
|
if (!ret) {
|
|
|
|
ret = snd_pcm_hw_constraint_eld(substream->runtime,
|
|
|
|
hcp->eld);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void hdmi_codec_shutdown(struct snd_pcm_substream *substream,
|
|
|
|
struct snd_soc_dai *dai)
|
|
|
|
{
|
|
|
|
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
|
|
|
|
|
|
|
|
dev_dbg(dai->dev, "%s()\n", __func__);
|
|
|
|
|
|
|
|
WARN_ON(hcp->current_stream != substream);
|
|
|
|
|
ASoC: hdmi-codec: callback function will be called with private data
Current hdmi-codec driver is assuming that it will be registered
from HDMI driver. Because of this assumption, each callback function
has struct device pointer which is parent device (= HDMI).
Then, it can use dev_get_drvdata() to get private data.
OTOH, on some SoC/HDMI case, SoC has VIDEO/SOUND and HDMI IPs.
This case, it needs SoC VIDEO, SoC SOUND and HDMI video, HDMI codec
driver. In DesignWare HDMI IP case, SoC VIDEO (= DRM/KMS) driver tries
to bind DesignWare HDMI video driver, and HDMI codec driver
(= hdmi-codec). This case, above "parent device" of HDMI codec driver
is DRM/KMS driver and its "device" already has private data.
And, from DT and ASoC CPU/Codec/Card binding point of view, HDMI codec
(= hdmi-codec) needs to have "parent device" (= DRM/KMS), otherwise,
it never detect sound card.
Because of these reasons, some driver can't use dev_get_drvdata() to
get private data on hdmi-codec driver. This patch add new void pointer
on hdmi_codec_pdata for private data, and callback function will be
called with it.
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
2016-06-24 10:47:55 +08:00
|
|
|
hcp->hcd.ops->audio_shutdown(dai->dev->parent, hcp->hcd.data);
|
2016-03-31 21:36:00 +08:00
|
|
|
|
|
|
|
mutex_lock(&hcp->current_stream_lock);
|
|
|
|
hcp->current_stream = NULL;
|
|
|
|
mutex_unlock(&hcp->current_stream_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
|
|
|
|
struct snd_pcm_hw_params *params,
|
|
|
|
struct snd_soc_dai *dai)
|
|
|
|
{
|
|
|
|
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
|
|
|
|
struct hdmi_codec_params hp = {
|
|
|
|
.iec = {
|
|
|
|
.status = { 0 },
|
|
|
|
.subcode = { 0 },
|
|
|
|
.pad = 0,
|
|
|
|
.dig_subframe = { 0 },
|
|
|
|
}
|
|
|
|
};
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__,
|
|
|
|
params_width(params), params_rate(params),
|
|
|
|
params_channels(params));
|
|
|
|
|
|
|
|
if (params_width(params) > 24)
|
|
|
|
params->msbits = 24;
|
|
|
|
|
|
|
|
ret = snd_pcm_create_iec958_consumer_hw_params(params, hp.iec.status,
|
|
|
|
sizeof(hp.iec.status));
|
|
|
|
if (ret < 0) {
|
|
|
|
dev_err(dai->dev, "Creating IEC958 channel status failed %d\n",
|
|
|
|
ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = hdmi_codec_new_stream(substream, dai);
|
|
|
|
if (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;
|
|
|
|
|
|
|
|
hp.sample_width = params_width(params);
|
|
|
|
hp.sample_rate = params_rate(params);
|
|
|
|
hp.channels = params_channels(params);
|
|
|
|
|
ASoC: hdmi-codec: callback function will be called with private data
Current hdmi-codec driver is assuming that it will be registered
from HDMI driver. Because of this assumption, each callback function
has struct device pointer which is parent device (= HDMI).
Then, it can use dev_get_drvdata() to get private data.
OTOH, on some SoC/HDMI case, SoC has VIDEO/SOUND and HDMI IPs.
This case, it needs SoC VIDEO, SoC SOUND and HDMI video, HDMI codec
driver. In DesignWare HDMI IP case, SoC VIDEO (= DRM/KMS) driver tries
to bind DesignWare HDMI video driver, and HDMI codec driver
(= hdmi-codec). This case, above "parent device" of HDMI codec driver
is DRM/KMS driver and its "device" already has private data.
And, from DT and ASoC CPU/Codec/Card binding point of view, HDMI codec
(= hdmi-codec) needs to have "parent device" (= DRM/KMS), otherwise,
it never detect sound card.
Because of these reasons, some driver can't use dev_get_drvdata() to
get private data on hdmi-codec driver. This patch add new void pointer
on hdmi_codec_pdata for private data, and callback function will be
called with it.
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
2016-06-24 10:47:55 +08:00
|
|
|
return hcp->hcd.ops->hw_params(dai->dev->parent, hcp->hcd.data,
|
|
|
|
&hcp->daifmt[dai->id], &hp);
|
2016-03-31 21:36:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int hdmi_codec_set_fmt(struct snd_soc_dai *dai,
|
|
|
|
unsigned int fmt)
|
|
|
|
{
|
|
|
|
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
|
|
|
|
struct hdmi_codec_daifmt cf = { 0 };
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
dev_dbg(dai->dev, "%s()\n", __func__);
|
|
|
|
|
|
|
|
if (dai->id == DAI_ID_SPDIF) {
|
|
|
|
cf.fmt = HDMI_SPDIF;
|
|
|
|
} else {
|
|
|
|
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
|
|
|
case SND_SOC_DAIFMT_CBM_CFM:
|
|
|
|
cf.bit_clk_master = 1;
|
|
|
|
cf.frame_clk_master = 1;
|
|
|
|
break;
|
|
|
|
case SND_SOC_DAIFMT_CBS_CFM:
|
|
|
|
cf.frame_clk_master = 1;
|
|
|
|
break;
|
|
|
|
case SND_SOC_DAIFMT_CBM_CFS:
|
|
|
|
cf.bit_clk_master = 1;
|
|
|
|
break;
|
|
|
|
case SND_SOC_DAIFMT_CBS_CFS:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
|
|
|
case SND_SOC_DAIFMT_NB_NF:
|
|
|
|
break;
|
|
|
|
case SND_SOC_DAIFMT_NB_IF:
|
|
|
|
cf.frame_clk_inv = 1;
|
|
|
|
break;
|
|
|
|
case SND_SOC_DAIFMT_IB_NF:
|
|
|
|
cf.bit_clk_inv = 1;
|
|
|
|
break;
|
|
|
|
case SND_SOC_DAIFMT_IB_IF:
|
|
|
|
cf.frame_clk_inv = 1;
|
|
|
|
cf.bit_clk_inv = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
|
|
|
case SND_SOC_DAIFMT_I2S:
|
|
|
|
cf.fmt = HDMI_I2S;
|
|
|
|
break;
|
|
|
|
case SND_SOC_DAIFMT_DSP_A:
|
|
|
|
cf.fmt = HDMI_DSP_A;
|
|
|
|
break;
|
|
|
|
case SND_SOC_DAIFMT_DSP_B:
|
|
|
|
cf.fmt = HDMI_DSP_B;
|
|
|
|
break;
|
|
|
|
case SND_SOC_DAIFMT_RIGHT_J:
|
|
|
|
cf.fmt = HDMI_RIGHT_J;
|
|
|
|
break;
|
|
|
|
case SND_SOC_DAIFMT_LEFT_J:
|
|
|
|
cf.fmt = HDMI_LEFT_J;
|
|
|
|
break;
|
|
|
|
case SND_SOC_DAIFMT_AC97:
|
|
|
|
cf.fmt = HDMI_AC97;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
dev_err(dai->dev, "Invalid DAI interface format\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
hcp->daifmt[dai->id] = cf;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hdmi_codec_digital_mute(struct snd_soc_dai *dai, int mute)
|
|
|
|
{
|
|
|
|
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
|
|
|
|
|
|
|
|
dev_dbg(dai->dev, "%s()\n", __func__);
|
|
|
|
|
|
|
|
if (hcp->hcd.ops->digital_mute)
|
ASoC: hdmi-codec: callback function will be called with private data
Current hdmi-codec driver is assuming that it will be registered
from HDMI driver. Because of this assumption, each callback function
has struct device pointer which is parent device (= HDMI).
Then, it can use dev_get_drvdata() to get private data.
OTOH, on some SoC/HDMI case, SoC has VIDEO/SOUND and HDMI IPs.
This case, it needs SoC VIDEO, SoC SOUND and HDMI video, HDMI codec
driver. In DesignWare HDMI IP case, SoC VIDEO (= DRM/KMS) driver tries
to bind DesignWare HDMI video driver, and HDMI codec driver
(= hdmi-codec). This case, above "parent device" of HDMI codec driver
is DRM/KMS driver and its "device" already has private data.
And, from DT and ASoC CPU/Codec/Card binding point of view, HDMI codec
(= hdmi-codec) needs to have "parent device" (= DRM/KMS), otherwise,
it never detect sound card.
Because of these reasons, some driver can't use dev_get_drvdata() to
get private data on hdmi-codec driver. This patch add new void pointer
on hdmi_codec_pdata for private data, and callback function will be
called with it.
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
2016-06-24 10:47:55 +08:00
|
|
|
return hcp->hcd.ops->digital_mute(dai->dev->parent,
|
|
|
|
hcp->hcd.data, mute);
|
2016-03-31 21:36:00 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct snd_soc_dai_ops hdmi_dai_ops = {
|
|
|
|
.startup = hdmi_codec_startup,
|
|
|
|
.shutdown = hdmi_codec_shutdown,
|
|
|
|
.hw_params = hdmi_codec_hw_params,
|
|
|
|
.set_fmt = hdmi_codec_set_fmt,
|
|
|
|
.digital_mute = hdmi_codec_digital_mute,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
#define HDMI_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
|
|
|
|
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
|
|
|
|
SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\
|
|
|
|
SNDRV_PCM_RATE_192000)
|
|
|
|
|
|
|
|
#define SPDIF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\
|
|
|
|
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE |\
|
|
|
|
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |\
|
|
|
|
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This list is only for formats allowed on the I2S bus. So there is
|
|
|
|
* some formats listed that are not supported by HDMI interface. For
|
|
|
|
* instance allowing the 32-bit formats enables 24-precision with CPU
|
|
|
|
* DAIs that do not support 24-bit formats. If the extra formats cause
|
|
|
|
* problems, we should add the video side driver an option to disable
|
|
|
|
* them.
|
|
|
|
*/
|
|
|
|
#define I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\
|
|
|
|
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE |\
|
|
|
|
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |\
|
|
|
|
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |\
|
|
|
|
SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE)
|
|
|
|
|
|
|
|
static struct snd_soc_dai_driver hdmi_i2s_dai = {
|
|
|
|
.id = DAI_ID_I2S,
|
|
|
|
.playback = {
|
|
|
|
.stream_name = "Playback",
|
|
|
|
.channels_min = 2,
|
|
|
|
.channels_max = 8,
|
|
|
|
.rates = HDMI_RATES,
|
|
|
|
.formats = I2S_FORMATS,
|
|
|
|
.sig_bits = 24,
|
|
|
|
},
|
|
|
|
.ops = &hdmi_dai_ops,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct snd_soc_dai_driver hdmi_spdif_dai = {
|
|
|
|
.id = DAI_ID_SPDIF,
|
|
|
|
.playback = {
|
|
|
|
.stream_name = "Playback",
|
|
|
|
.channels_min = 2,
|
|
|
|
.channels_max = 2,
|
|
|
|
.rates = HDMI_RATES,
|
|
|
|
.formats = SPDIF_FORMATS,
|
|
|
|
},
|
|
|
|
.ops = &hdmi_dai_ops,
|
|
|
|
};
|
|
|
|
|
ASoC: hdmi-codec: enable multi probe for same device
hdmi-codec driver is common HDMI sound driver,
but it doesn't care about multi sound ports.
For example, hdmi-codec driver is supporting 1 I2S and 1 SPDIF ports,
so, we can't use this driver if HDMI has 2 or more I2S ports.
And we would like to use multi detection.
For example, DesignWare HDMI driver is providing dw_hdmi_bind() to
DRM/KMS driver, and it will setups HDMI video/sound.
Note is that it will be called under for_each loop of ports.
int dw_hdmi_bind(xxx)
{
/* register hdmi-codec driver here */
}
static int xxx_probe(struct platform_device *pdev)
{
for_each_xxx(xx) {
...
dw_hdmi_bind(xxx);
...
}
}
This case, dw_hdmi_bind() would like to use hdmi-codec,
and it will be called multiple times for each ports.
Here, ASoC's CPU/Codec/Card bind will checks each "of_node" on DT,
and hdmi-codec driver is assuming its parent device for it.
But it doesn't care about case.
Thus, ASoC never detect correct sound card in this case.
To solve this issue, this patch checks each parent device,
and names "hdmi-hifi.x" in order to each ports.
And uses struct snd_soc_component_driver :: of_xlate_dai_name
for snd_soc_get_dai_name().
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
2016-08-03 10:08:41 +08:00
|
|
|
static char hdmi_dai_name[][DAI_NAME_SIZE] = {
|
|
|
|
"hdmi-hifi.0",
|
|
|
|
"hdmi-hifi.1",
|
|
|
|
"hdmi-hifi.2",
|
|
|
|
"hdmi-hifi.3",
|
|
|
|
};
|
|
|
|
|
|
|
|
static int hdmi_of_xlate_dai_name(struct snd_soc_component *component,
|
|
|
|
struct of_phandle_args *args,
|
|
|
|
const char **dai_name)
|
|
|
|
{
|
2016-10-28 16:18:24 +08:00
|
|
|
int id;
|
|
|
|
|
|
|
|
if (args->args_count)
|
|
|
|
id = args->args[0];
|
|
|
|
else
|
|
|
|
id = 0;
|
ASoC: hdmi-codec: enable multi probe for same device
hdmi-codec driver is common HDMI sound driver,
but it doesn't care about multi sound ports.
For example, hdmi-codec driver is supporting 1 I2S and 1 SPDIF ports,
so, we can't use this driver if HDMI has 2 or more I2S ports.
And we would like to use multi detection.
For example, DesignWare HDMI driver is providing dw_hdmi_bind() to
DRM/KMS driver, and it will setups HDMI video/sound.
Note is that it will be called under for_each loop of ports.
int dw_hdmi_bind(xxx)
{
/* register hdmi-codec driver here */
}
static int xxx_probe(struct platform_device *pdev)
{
for_each_xxx(xx) {
...
dw_hdmi_bind(xxx);
...
}
}
This case, dw_hdmi_bind() would like to use hdmi-codec,
and it will be called multiple times for each ports.
Here, ASoC's CPU/Codec/Card bind will checks each "of_node" on DT,
and hdmi-codec driver is assuming its parent device for it.
But it doesn't care about case.
Thus, ASoC never detect correct sound card in this case.
To solve this issue, this patch checks each parent device,
and names "hdmi-hifi.x" in order to each ports.
And uses struct snd_soc_component_driver :: of_xlate_dai_name
for snd_soc_get_dai_name().
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
2016-08-03 10:08:41 +08:00
|
|
|
|
|
|
|
if (id < ARRAY_SIZE(hdmi_dai_name)) {
|
|
|
|
*dai_name = hdmi_dai_name[id];
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -EAGAIN;
|
|
|
|
}
|
|
|
|
|
2016-03-31 21:36:00 +08:00
|
|
|
static struct snd_soc_codec_driver hdmi_codec = {
|
ASoC: hdmi-codec: enable multi probe for same device
hdmi-codec driver is common HDMI sound driver,
but it doesn't care about multi sound ports.
For example, hdmi-codec driver is supporting 1 I2S and 1 SPDIF ports,
so, we can't use this driver if HDMI has 2 or more I2S ports.
And we would like to use multi detection.
For example, DesignWare HDMI driver is providing dw_hdmi_bind() to
DRM/KMS driver, and it will setups HDMI video/sound.
Note is that it will be called under for_each loop of ports.
int dw_hdmi_bind(xxx)
{
/* register hdmi-codec driver here */
}
static int xxx_probe(struct platform_device *pdev)
{
for_each_xxx(xx) {
...
dw_hdmi_bind(xxx);
...
}
}
This case, dw_hdmi_bind() would like to use hdmi-codec,
and it will be called multiple times for each ports.
Here, ASoC's CPU/Codec/Card bind will checks each "of_node" on DT,
and hdmi-codec driver is assuming its parent device for it.
But it doesn't care about case.
Thus, ASoC never detect correct sound card in this case.
To solve this issue, this patch checks each parent device,
and names "hdmi-hifi.x" in order to each ports.
And uses struct snd_soc_component_driver :: of_xlate_dai_name
for snd_soc_get_dai_name().
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
2016-08-03 10:08:41 +08:00
|
|
|
.component_driver = {
|
2016-08-08 17:16:40 +08:00
|
|
|
.controls = hdmi_controls,
|
|
|
|
.num_controls = ARRAY_SIZE(hdmi_controls),
|
|
|
|
.dapm_widgets = hdmi_widgets,
|
|
|
|
.num_dapm_widgets = ARRAY_SIZE(hdmi_widgets),
|
|
|
|
.dapm_routes = hdmi_routes,
|
|
|
|
.num_dapm_routes = ARRAY_SIZE(hdmi_routes),
|
ASoC: hdmi-codec: enable multi probe for same device
hdmi-codec driver is common HDMI sound driver,
but it doesn't care about multi sound ports.
For example, hdmi-codec driver is supporting 1 I2S and 1 SPDIF ports,
so, we can't use this driver if HDMI has 2 or more I2S ports.
And we would like to use multi detection.
For example, DesignWare HDMI driver is providing dw_hdmi_bind() to
DRM/KMS driver, and it will setups HDMI video/sound.
Note is that it will be called under for_each loop of ports.
int dw_hdmi_bind(xxx)
{
/* register hdmi-codec driver here */
}
static int xxx_probe(struct platform_device *pdev)
{
for_each_xxx(xx) {
...
dw_hdmi_bind(xxx);
...
}
}
This case, dw_hdmi_bind() would like to use hdmi-codec,
and it will be called multiple times for each ports.
Here, ASoC's CPU/Codec/Card bind will checks each "of_node" on DT,
and hdmi-codec driver is assuming its parent device for it.
But it doesn't care about case.
Thus, ASoC never detect correct sound card in this case.
To solve this issue, this patch checks each parent device,
and names "hdmi-hifi.x" in order to each ports.
And uses struct snd_soc_component_driver :: of_xlate_dai_name
for snd_soc_get_dai_name().
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
2016-08-03 10:08:41 +08:00
|
|
|
.of_xlate_dai_name = hdmi_of_xlate_dai_name,
|
|
|
|
},
|
2016-03-31 21:36:00 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static int hdmi_codec_probe(struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
struct hdmi_codec_pdata *hcd = pdev->dev.platform_data;
|
|
|
|
struct device *dev = &pdev->dev;
|
|
|
|
struct hdmi_codec_priv *hcp;
|
ASoC: hdmi-codec: enable multi probe for same device
hdmi-codec driver is common HDMI sound driver,
but it doesn't care about multi sound ports.
For example, hdmi-codec driver is supporting 1 I2S and 1 SPDIF ports,
so, we can't use this driver if HDMI has 2 or more I2S ports.
And we would like to use multi detection.
For example, DesignWare HDMI driver is providing dw_hdmi_bind() to
DRM/KMS driver, and it will setups HDMI video/sound.
Note is that it will be called under for_each loop of ports.
int dw_hdmi_bind(xxx)
{
/* register hdmi-codec driver here */
}
static int xxx_probe(struct platform_device *pdev)
{
for_each_xxx(xx) {
...
dw_hdmi_bind(xxx);
...
}
}
This case, dw_hdmi_bind() would like to use hdmi-codec,
and it will be called multiple times for each ports.
Here, ASoC's CPU/Codec/Card bind will checks each "of_node" on DT,
and hdmi-codec driver is assuming its parent device for it.
But it doesn't care about case.
Thus, ASoC never detect correct sound card in this case.
To solve this issue, this patch checks each parent device,
and names "hdmi-hifi.x" in order to each ports.
And uses struct snd_soc_component_driver :: of_xlate_dai_name
for snd_soc_get_dai_name().
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
2016-08-03 10:08:41 +08:00
|
|
|
struct hdmi_device *hd;
|
|
|
|
struct list_head *pos;
|
2016-03-31 21:36:00 +08:00
|
|
|
int dai_count, i = 0;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
dev_dbg(dev, "%s()\n", __func__);
|
|
|
|
|
|
|
|
if (!hcd) {
|
|
|
|
dev_err(dev, "%s: No plalform data\n", __func__);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
dai_count = hcd->i2s + hcd->spdif;
|
|
|
|
if (dai_count < 1 || !hcd->ops || !hcd->ops->hw_params ||
|
|
|
|
!hcd->ops->audio_shutdown) {
|
|
|
|
dev_err(dev, "%s: Invalid parameters\n", __func__);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
hcp = devm_kzalloc(dev, sizeof(*hcp), GFP_KERNEL);
|
|
|
|
if (!hcp)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
ASoC: hdmi-codec: enable multi probe for same device
hdmi-codec driver is common HDMI sound driver,
but it doesn't care about multi sound ports.
For example, hdmi-codec driver is supporting 1 I2S and 1 SPDIF ports,
so, we can't use this driver if HDMI has 2 or more I2S ports.
And we would like to use multi detection.
For example, DesignWare HDMI driver is providing dw_hdmi_bind() to
DRM/KMS driver, and it will setups HDMI video/sound.
Note is that it will be called under for_each loop of ports.
int dw_hdmi_bind(xxx)
{
/* register hdmi-codec driver here */
}
static int xxx_probe(struct platform_device *pdev)
{
for_each_xxx(xx) {
...
dw_hdmi_bind(xxx);
...
}
}
This case, dw_hdmi_bind() would like to use hdmi-codec,
and it will be called multiple times for each ports.
Here, ASoC's CPU/Codec/Card bind will checks each "of_node" on DT,
and hdmi-codec driver is assuming its parent device for it.
But it doesn't care about case.
Thus, ASoC never detect correct sound card in this case.
To solve this issue, this patch checks each parent device,
and names "hdmi-hifi.x" in order to each ports.
And uses struct snd_soc_component_driver :: of_xlate_dai_name
for snd_soc_get_dai_name().
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
2016-08-03 10:08:41 +08:00
|
|
|
hd = NULL;
|
|
|
|
list_for_each(pos, &hdmi_device_list) {
|
|
|
|
struct hdmi_device *tmp = pos_to_hdmi_device(pos);
|
|
|
|
|
|
|
|
if (tmp->dev == dev->parent) {
|
|
|
|
hd = tmp;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!hd) {
|
|
|
|
hd = devm_kzalloc(dev, sizeof(*hd), GFP_KERNEL);
|
|
|
|
if (!hd)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
hd->dev = dev->parent;
|
|
|
|
|
|
|
|
list_add_tail(&hd->list, &hdmi_device_list);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hd->cnt >= ARRAY_SIZE(hdmi_dai_name)) {
|
|
|
|
dev_err(dev, "too many hdmi codec are deteced\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2016-03-31 21:36:00 +08:00
|
|
|
hcp->hcd = *hcd;
|
|
|
|
mutex_init(&hcp->current_stream_lock);
|
|
|
|
|
|
|
|
hcp->daidrv = devm_kzalloc(dev, dai_count * sizeof(*hcp->daidrv),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!hcp->daidrv)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
if (hcd->i2s) {
|
|
|
|
hcp->daidrv[i] = hdmi_i2s_dai;
|
|
|
|
hcp->daidrv[i].playback.channels_max =
|
|
|
|
hcd->max_i2s_channels;
|
ASoC: hdmi-codec: enable multi probe for same device
hdmi-codec driver is common HDMI sound driver,
but it doesn't care about multi sound ports.
For example, hdmi-codec driver is supporting 1 I2S and 1 SPDIF ports,
so, we can't use this driver if HDMI has 2 or more I2S ports.
And we would like to use multi detection.
For example, DesignWare HDMI driver is providing dw_hdmi_bind() to
DRM/KMS driver, and it will setups HDMI video/sound.
Note is that it will be called under for_each loop of ports.
int dw_hdmi_bind(xxx)
{
/* register hdmi-codec driver here */
}
static int xxx_probe(struct platform_device *pdev)
{
for_each_xxx(xx) {
...
dw_hdmi_bind(xxx);
...
}
}
This case, dw_hdmi_bind() would like to use hdmi-codec,
and it will be called multiple times for each ports.
Here, ASoC's CPU/Codec/Card bind will checks each "of_node" on DT,
and hdmi-codec driver is assuming its parent device for it.
But it doesn't care about case.
Thus, ASoC never detect correct sound card in this case.
To solve this issue, this patch checks each parent device,
and names "hdmi-hifi.x" in order to each ports.
And uses struct snd_soc_component_driver :: of_xlate_dai_name
for snd_soc_get_dai_name().
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
2016-08-03 10:08:41 +08:00
|
|
|
hcp->daidrv[i].name = hdmi_dai_name[hd->cnt++];
|
2016-03-31 21:36:00 +08:00
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
ASoC: hdmi-codec: enable multi probe for same device
hdmi-codec driver is common HDMI sound driver,
but it doesn't care about multi sound ports.
For example, hdmi-codec driver is supporting 1 I2S and 1 SPDIF ports,
so, we can't use this driver if HDMI has 2 or more I2S ports.
And we would like to use multi detection.
For example, DesignWare HDMI driver is providing dw_hdmi_bind() to
DRM/KMS driver, and it will setups HDMI video/sound.
Note is that it will be called under for_each loop of ports.
int dw_hdmi_bind(xxx)
{
/* register hdmi-codec driver here */
}
static int xxx_probe(struct platform_device *pdev)
{
for_each_xxx(xx) {
...
dw_hdmi_bind(xxx);
...
}
}
This case, dw_hdmi_bind() would like to use hdmi-codec,
and it will be called multiple times for each ports.
Here, ASoC's CPU/Codec/Card bind will checks each "of_node" on DT,
and hdmi-codec driver is assuming its parent device for it.
But it doesn't care about case.
Thus, ASoC never detect correct sound card in this case.
To solve this issue, this patch checks each parent device,
and names "hdmi-hifi.x" in order to each ports.
And uses struct snd_soc_component_driver :: of_xlate_dai_name
for snd_soc_get_dai_name().
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
2016-08-03 10:08:41 +08:00
|
|
|
if (hcd->spdif) {
|
2016-03-31 21:36:00 +08:00
|
|
|
hcp->daidrv[i] = hdmi_spdif_dai;
|
ASoC: hdmi-codec: enable multi probe for same device
hdmi-codec driver is common HDMI sound driver,
but it doesn't care about multi sound ports.
For example, hdmi-codec driver is supporting 1 I2S and 1 SPDIF ports,
so, we can't use this driver if HDMI has 2 or more I2S ports.
And we would like to use multi detection.
For example, DesignWare HDMI driver is providing dw_hdmi_bind() to
DRM/KMS driver, and it will setups HDMI video/sound.
Note is that it will be called under for_each loop of ports.
int dw_hdmi_bind(xxx)
{
/* register hdmi-codec driver here */
}
static int xxx_probe(struct platform_device *pdev)
{
for_each_xxx(xx) {
...
dw_hdmi_bind(xxx);
...
}
}
This case, dw_hdmi_bind() would like to use hdmi-codec,
and it will be called multiple times for each ports.
Here, ASoC's CPU/Codec/Card bind will checks each "of_node" on DT,
and hdmi-codec driver is assuming its parent device for it.
But it doesn't care about case.
Thus, ASoC never detect correct sound card in this case.
To solve this issue, this patch checks each parent device,
and names "hdmi-hifi.x" in order to each ports.
And uses struct snd_soc_component_driver :: of_xlate_dai_name
for snd_soc_get_dai_name().
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
2016-08-03 10:08:41 +08:00
|
|
|
hcp->daidrv[i].name = hdmi_dai_name[hd->cnt++];
|
|
|
|
}
|
2016-03-31 21:36:00 +08:00
|
|
|
|
|
|
|
ret = snd_soc_register_codec(dev, &hdmi_codec, hcp->daidrv,
|
|
|
|
dai_count);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(dev, "%s: snd_soc_register_codec() failed (%d)\n",
|
|
|
|
__func__, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev_set_drvdata(dev, hcp);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hdmi_codec_remove(struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
snd_soc_unregister_codec(&pdev->dev);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct platform_driver hdmi_codec_driver = {
|
|
|
|
.driver = {
|
|
|
|
.name = HDMI_CODEC_DRV_NAME,
|
|
|
|
},
|
|
|
|
.probe = hdmi_codec_probe,
|
|
|
|
.remove = hdmi_codec_remove,
|
|
|
|
};
|
|
|
|
|
|
|
|
module_platform_driver(hdmi_codec_driver);
|
|
|
|
|
|
|
|
MODULE_AUTHOR("Jyri Sarha <jsarha@ti.com>");
|
|
|
|
MODULE_DESCRIPTION("HDMI Audio Codec Driver");
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_ALIAS("platform:" HDMI_CODEC_DRV_NAME);
|