aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/omap-hdmi-codec.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs/omap-hdmi-codec.c')
-rw-r--r--sound/soc/codecs/omap-hdmi-codec.c141
1 files changed, 101 insertions, 40 deletions
diff --git a/sound/soc/codecs/omap-hdmi-codec.c b/sound/soc/codecs/omap-hdmi-codec.c
index 8d89eed..c735700 100644
--- a/sound/soc/codecs/omap-hdmi-codec.c
+++ b/sound/soc/codecs/omap-hdmi-codec.c
@@ -48,34 +48,40 @@
#define HDMI_PLLCTRL 0x200
#define HDMI_PHY 0x300
+/* hdmi configuration params */
+struct hdmi_params {
+ int format;
+ int sample_freq;
+ int channels_nr;
+};
+
+
/* codec private data */
-struct hdmi_data {
+struct hdmi_codec_data {
struct hdmi_audio_format audio_fmt;
struct hdmi_audio_dma audio_dma;
struct hdmi_core_audio_config audio_core_cfg;
struct hdmi_core_infoframe_audio aud_if_cfg;
struct hdmi_ip_data ip_data;
struct omap_hwmod *oh;
-};
+ struct omap_dss_device *dssdev;
+ struct notifier_block notifier;
+ struct hdmi_params params;
+ int active;
+} hdmi_data;
-static int hdmi_audio_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
+
+static int hdmi_audio_set_configuration(struct hdmi_codec_data *priv)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
- struct platform_device *pdev = to_platform_device(codec->dev);
- struct hdmi_data *priv = snd_soc_codec_get_drvdata(codec);
struct hdmi_audio_format *audio_format = &priv->audio_fmt;
struct hdmi_audio_dma *audio_dma = &priv->audio_dma;
struct hdmi_core_audio_config *core_cfg = &priv->audio_core_cfg;
struct hdmi_core_infoframe_audio *aud_if_cfg = &priv->aud_if_cfg;
- int err, n, cts, channels_nr, channel_alloc;
+ int err, n, cts, channel_alloc;
enum hdmi_core_audio_sample_freq sample_freq;
u32 pclk = omapdss_hdmi_get_pixel_clock();
-
- switch (params_format(params)) {
+ switch (priv->params.format) {
case SNDRV_PCM_FORMAT_S16_LE:
core_cfg->i2s_cfg.word_max_length =
HDMI_AUDIO_I2S_MAX_WORD_20BITS;
@@ -107,7 +113,7 @@ static int hdmi_audio_hw_params(struct snd_pcm_substream *substream,
}
- switch (params_rate(params)) {
+ switch (priv->params.sample_freq) {
case 32000:
sample_freq = HDMI_AUDIO_FS_32000;
break;
@@ -121,8 +127,8 @@ static int hdmi_audio_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- err = hdmi_ti_4xxx_config_audio_acr(&priv->ip_data, params_rate(params),
- &n, &cts, pclk);
+ err = hdmi_ti_4xxx_config_audio_acr(&priv->ip_data,
+ priv->params.sample_freq, &n, &cts, pclk);
if (err < 0)
return err;
@@ -179,9 +185,8 @@ static int hdmi_audio_hw_params(struct snd_pcm_substream *substream,
core_cfg->en_parallel_aud_input = true;
/* Number of channels */
- channels_nr = params_channels(params);
- switch (channels_nr) {
+ switch (priv->params.channels_nr) {
case 2:
core_cfg->layout = HDMI_AUDIO_LAYOUT_2CH;
channel_alloc = 0x0;
@@ -211,7 +216,7 @@ static int hdmi_audio_hw_params(struct snd_pcm_substream *substream,
HDMI_AUDIO_I2S_SD3_EN;
break;
default:
- dev_err(&pdev->dev, "Unsupported number of channels\n");
+ pr_err("Unsupported number of channels\n");
return -EINVAL;
}
@@ -223,7 +228,7 @@ static int hdmi_audio_hw_params(struct snd_pcm_substream *substream,
* info frame audio see doc CEA861-D page 74
*/
aud_if_cfg->db1_coding_type = HDMI_INFOFRAME_AUDIO_DB1CT_FROM_STREAM;
- aud_if_cfg->db1_channel_count = channels_nr;
+ aud_if_cfg->db1_channel_count = priv->params.channels_nr;
aud_if_cfg->db2_sample_freq = HDMI_INFOFRAME_AUDIO_DB2SF_FROM_STREAM;
aud_if_cfg->db2_sample_size = HDMI_INFOFRAME_AUDIO_DB2SS_FROM_STREAM;
aud_if_cfg->db4_channel_alloc = channel_alloc;
@@ -232,6 +237,42 @@ static int hdmi_audio_hw_params(struct snd_pcm_substream *substream,
hdmi_ti_4xxx_core_audio_infoframe_config(&priv->ip_data, aud_if_cfg);
return 0;
+
+}
+
+int hdmi_audio_notifier_callback(struct notifier_block *nb,
+ unsigned long arg, void *ptr)
+{
+ enum omap_dss_display_state state = arg;
+
+ if (state == OMAP_DSS_DISPLAY_ACTIVE) {
+ /* this happens just after hdmi_power_on */
+ if (hdmi_data.active)
+ hdmi_ti_4xxx_audio_enable(&hdmi_data.ip_data, 0);
+ hdmi_audio_set_configuration(&hdmi_data);
+ if (hdmi_data.active)
+ hdmi_ti_4xxx_audio_enable(&hdmi_data.ip_data, 1);
+ }
+ return 0;
+}
+
+int hdmi_audio_match(struct omap_dss_device *dssdev, void *arg)
+{
+ return sysfs_streq(dssdev->name , "hdmi");
+}
+
+static int hdmi_audio_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec *codec = rtd->codec;
+ struct hdmi_codec_data *priv = snd_soc_codec_get_drvdata(codec);
+
+ priv->params.format = params_format(params);
+ priv->params.sample_freq = params_rate(params);
+ priv->params.channels_nr = params_channels(params);
+ return hdmi_audio_set_configuration(priv);
}
static int hdmi_audio_trigger(struct snd_pcm_substream *substream, int cmd,
@@ -239,7 +280,7 @@ static int hdmi_audio_trigger(struct snd_pcm_substream *substream, int cmd,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec *codec = rtd->codec;
- struct hdmi_data *priv = snd_soc_codec_get_drvdata(codec);
+ struct hdmi_codec_data *priv = snd_soc_codec_get_drvdata(codec);
int err = 0;
switch (cmd) {
@@ -253,11 +294,12 @@ static int hdmi_audio_trigger(struct snd_pcm_substream *substream, int cmd,
omap_hwmod_set_slave_idlemode(priv->oh,
HWMOD_IDLEMODE_NO);
hdmi_ti_4xxx_audio_enable(&priv->ip_data, 1);
+ priv->active = 1;
break;
-
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ priv->active = 0;
hdmi_ti_4xxx_audio_enable(&priv->ip_data, 0);
/*
* switch back to smart-idle & wakeup capable
@@ -283,52 +325,71 @@ static int hdmi_audio_startup(struct snd_pcm_substream *substream,
}
static int hdmi_probe(struct snd_soc_codec *codec)
{
- struct hdmi_data *priv;
struct platform_device *pdev = to_platform_device(codec->dev);
struct resource *hdmi_rsrc;
+ int ret = 0;
- priv = kzalloc(sizeof(struct hdmi_data), GFP_KERNEL);
- if (priv == NULL)
- return -ENOMEM;
-
- snd_soc_codec_set_drvdata(codec, priv);
-
+ snd_soc_codec_set_drvdata(codec, &hdmi_data);
hdmi_rsrc = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!hdmi_rsrc) {
dev_err(&pdev->dev, "Cannot obtain IORESOURCE_MEM HDMI\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto res_err;
}
- priv->oh = omap_hwmod_lookup("dss_hdmi");
+ hdmi_data.oh = omap_hwmod_lookup("dss_hdmi");
- if (!priv->oh) {
+ if (!hdmi_data.oh) {
dev_err(&pdev->dev, "can't find omap_hwmod for hdmi\n");
- return -ENODEV;
+ ret = -ENODEV;
+ goto res_err;
}
/* Base address taken from platform */
- priv->ip_data.base_wp = ioremap(hdmi_rsrc->start,
+ hdmi_data.ip_data.base_wp = ioremap(hdmi_rsrc->start,
resource_size(hdmi_rsrc));
- if (!priv->ip_data.base_wp) {
+ if (!hdmi_data.ip_data.base_wp) {
dev_err(&pdev->dev, "can't ioremap WP\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto res_err;
+ }
+
+ hdmi_data.ip_data.hdmi_core_sys_offset = HDMI_CORE_SYS;
+ hdmi_data.ip_data.hdmi_core_av_offset = HDMI_CORE_AV;
+ hdmi_data.ip_data.hdmi_pll_offset = HDMI_PLLCTRL;
+ hdmi_data.ip_data.hdmi_phy_offset = HDMI_PHY;
+
+ hdmi_data.dssdev = omap_dss_find_device(NULL, hdmi_audio_match);
+
+ if (!hdmi_data.dssdev) {
+ dev_err(&pdev->dev, "can't find HDMI device\n");
+ ret = -ENODEV;
+ goto dssdev_err;
}
- priv->ip_data.hdmi_core_sys_offset = HDMI_CORE_SYS;
- priv->ip_data.hdmi_core_av_offset = HDMI_CORE_AV;
- priv->ip_data.hdmi_pll_offset = HDMI_PLLCTRL;
- priv->ip_data.hdmi_phy_offset = HDMI_PHY;
+ hdmi_data.notifier.notifier_call = hdmi_audio_notifier_callback;
+ blocking_notifier_chain_register(&hdmi_data.dssdev->state_notifiers,
+ &hdmi_data.notifier);
return 0;
+
+dssdev_err:
+ iounmap(hdmi_data.ip_data.base_wp);
+res_err:
+ return ret;
+
}
static int hdmi_remove(struct snd_soc_codec *codec)
{
- struct hdmi_data *priv = snd_soc_codec_get_drvdata(codec);
+ struct hdmi_codec_data *priv = snd_soc_codec_get_drvdata(codec);
+
+ blocking_notifier_chain_unregister(&priv->dssdev->state_notifiers,
+ &priv->notifier);
iounmap(priv->ip_data.base_wp);
kfree(priv);
return 0;