summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/mfd/mxc-hdmi-core.c42
-rw-r--r--drivers/video/mxc/mxc_edid.c38
-rw-r--r--drivers/video/mxc_hdmi.c3
-rw-r--r--include/linux/mfd/mxc-hdmi-core.h7
-rw-r--r--sound/soc/codecs/mxc_hdmi.c143
-rw-r--r--sound/soc/imx/imx-hdmi-dma.c14
-rw-r--r--sound/soc/imx/imx-hdmi.h10
7 files changed, 213 insertions, 44 deletions
diff --git a/drivers/mfd/mxc-hdmi-core.c b/drivers/mfd/mxc-hdmi-core.c
index 5f86788750a1..1aa92a01c40c 100644
--- a/drivers/mfd/mxc-hdmi-core.c
+++ b/drivers/mfd/mxc-hdmi-core.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 Freescale Semiconductor, Inc.
+ * Copyright (C) 2011-2012 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -36,6 +36,7 @@
#include <mach/clock.h>
#include <mach/mxc_hdmi.h>
#include <mach/ipu-v3.h>
+#include <mach/mxc_edid.h>
#include "../mxc/ipu3/ipu_prv.h"
#include <linux/mfd/mxc-hdmi-core.h>
#include <linux/fsl_devices.h>
@@ -48,18 +49,20 @@ struct mxc_hdmi_data {
};
static unsigned long hdmi_base;
-struct clk *isfr_clk;
-struct clk *iahb_clk;
+static struct clk *isfr_clk;
+static struct clk *iahb_clk;
static unsigned int irq_enable_cnt;
-spinlock_t irq_spinlock;
-bool irq_initialized;
-bool irq_enabled;
-unsigned int sample_rate;
-unsigned long pixel_clk_rate;
-struct clk *pixel_clk;
-int hdmi_ratio;
+static spinlock_t irq_spinlock;
+static spinlock_t edid_spinlock;
+static bool irq_initialized;
+static bool irq_enabled;
+static unsigned int sample_rate;
+static unsigned long pixel_clk_rate;
+static struct clk *pixel_clk;
+static int hdmi_ratio;
int mxc_hdmi_ipu_id;
int mxc_hdmi_disp_id;
+static struct mxc_edid_cfg hdmi_core_edid_cfg;
u8 hdmi_readb(unsigned int reg)
{
@@ -466,6 +469,24 @@ void hdmi_set_sample_rate(unsigned int rate)
hdmi_set_clk_regenerator();
}
+void hdmi_set_edid_cfg(struct mxc_edid_cfg *cfg)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&edid_spinlock, flags);
+ memcpy(&hdmi_core_edid_cfg, cfg, sizeof(struct mxc_edid_cfg));
+ spin_unlock_irqrestore(&edid_spinlock, flags);
+}
+
+void hdmi_get_edid_cfg(struct mxc_edid_cfg *cfg)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&edid_spinlock, flags);
+ memcpy(cfg, &hdmi_core_edid_cfg, sizeof(struct mxc_edid_cfg));
+ spin_unlock_irqrestore(&edid_spinlock, flags);
+}
+
static int mxc_hdmi_core_probe(struct platform_device *pdev)
{
struct fsl_mxc_hdmi_core_platform_data *pdata = pdev->dev.platform_data;
@@ -497,6 +518,7 @@ static int mxc_hdmi_core_probe(struct platform_device *pdev)
irq_initialized = false;
irq_enabled = true;
spin_lock_init(&irq_spinlock);
+ spin_lock_init(&edid_spinlock);
isfr_clk = clk_get(&hdmi_data->pdev->dev, "hdmi_isfr_clk");
if (IS_ERR(isfr_clk)) {
diff --git a/drivers/video/mxc/mxc_edid.c b/drivers/video/mxc/mxc_edid.c
index 2fa655d345c5..e8a2b9761b65 100644
--- a/drivers/video/mxc/mxc_edid.c
+++ b/drivers/video/mxc/mxc_edid.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2009-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2009-2012 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
@@ -313,6 +313,42 @@ int mxc_edid_parse_ext_blk(unsigned char *edid,
break;
}
case 0x1: /*Audio data block*/
+ {
+ u8 audio_format, max_ch, byte1, byte2, byte3;
+
+ i = 0;
+ cfg->max_channels = 0;
+ cfg->sample_rates = 0;
+ cfg->sample_sizes = 0;
+
+ while (i < blklen) {
+ byte1 = edid[index + 1];
+ byte2 = edid[index + 2];
+ byte3 = edid[index + 3];
+ index += 3;
+ i += 3;
+
+ audio_format = byte1 >> 3;
+ max_ch = (byte1 & 0x07) + 1;
+
+ DPRINTK("Audio Format Descriptor : %2d\n", audio_format);
+ DPRINTK("Max Number of Channels : %2d\n", max_ch);
+ DPRINTK("Sample Rates : %02x\n", byte2);
+
+ /* ALSA can't specify specific compressed
+ * formats, so only care about PCM for now. */
+ if (audio_format == AUDIO_CODING_TYPE_LPCM) {
+ if (max_ch > cfg->max_channels)
+ cfg->max_channels = max_ch;
+
+ cfg->sample_rates |= byte2;
+ cfg->sample_sizes |= byte3 & 0x7;
+ DPRINTK("Sample Sizes : %02x\n",
+ byte3 & 0x7);
+ }
+ }
+ break;
+ }
case 0x4: /*Speaker allocation block*/
case 0x7: /*User extended block*/
default:
diff --git a/drivers/video/mxc_hdmi.c b/drivers/video/mxc_hdmi.c
index 35df3d94deb6..0462f09a8fee 100644
--- a/drivers/video/mxc_hdmi.c
+++ b/drivers/video/mxc_hdmi.c
@@ -1364,6 +1364,9 @@ static int mxc_hdmi_read_edid(struct mxc_hdmi *hdmi)
if (ret < 0)
return HDMI_EDID_FAIL;
+ /* Save edid cfg for audio driver */
+ hdmi_set_edid_cfg(&hdmi->edid_cfg);
+
if (!memcmp(edid_old, hdmi->edid, HDMI_EDID_LEN)) {
dev_info(&hdmi->pdev->dev, "same edid\n");
return HDMI_EDID_SAME;
diff --git a/include/linux/mfd/mxc-hdmi-core.h b/include/linux/mfd/mxc-hdmi-core.h
index 8c6ce456ddad..b607feec147a 100644
--- a/include/linux/mfd/mxc-hdmi-core.h
+++ b/include/linux/mfd/mxc-hdmi-core.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (C) 2011-2012 Freescale Semiconductor, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -19,6 +19,8 @@
#ifndef __LINUX_MXC_HDMI_CORE_H_
#define __LINUX_MXC_HDMI_CORE_H_
+#include <mach/mxc_edid.h>
+
#define IRQ_DISABLE_SUCCEED 0
#define IRQ_DISABLE_FAIL 1
@@ -38,6 +40,9 @@ void hdmi_set_sample_rate(unsigned int rate);
void hdmi_init_clk_regenerator(void);
void hdmi_clk_regenerator_update_pixel_clock(void);
+void hdmi_set_edid_cfg(struct mxc_edid_cfg *cfg);
+void hdmi_get_edid_cfg(struct mxc_edid_cfg *cfg);
+
extern int mxc_hdmi_ipu_id;
extern int mxc_hdmi_disp_id;
diff --git a/sound/soc/codecs/mxc_hdmi.c b/sound/soc/codecs/mxc_hdmi.c
index c6a6bcbdbfd9..70c9b8c9bfe0 100644
--- a/sound/soc/codecs/mxc_hdmi.c
+++ b/sound/soc/codecs/mxc_hdmi.c
@@ -1,7 +1,7 @@
/*
* MXC HDMI ALSA Soc Codec Driver
*
- * Copyright (C) 2011 Freescale Semiconductor, Inc.
+ * Copyright (C) 2011-2012 Freescale Semiconductor, Inc.
*/
/*
@@ -42,6 +42,7 @@
#include <mach/hardware.h>
#include <linux/mfd/mxc-hdmi-core.h>
+#include <mach/mxc_edid.h>
#include <mach/mxc_hdmi.h>
#include "../imx/imx-hdmi.h"
@@ -53,6 +54,16 @@ struct mxc_hdmi_priv {
struct clk *iahb_clk;
};
+static struct mxc_edid_cfg edid_cfg;
+
+static unsigned int playback_rates[HDMI_MAX_RATES];
+static unsigned int playback_sample_size[HDMI_MAX_SAMPLE_SIZE];
+static unsigned int playback_channels[HDMI_MAX_CHANNEL_CONSTRAINTS];
+
+static struct snd_pcm_hw_constraint_list playback_constraint_rates;
+static struct snd_pcm_hw_constraint_list playback_constraint_bits;
+static struct snd_pcm_hw_constraint_list playback_constraint_channels;
+
#ifdef DEBUG
static void dumpregs(void)
{
@@ -100,19 +111,126 @@ static void hdmi_set_audio_infoframe(void)
hdmi_writeb(0x00, HDMI_FC_AUDICONF2);
}
-static unsigned int hdmi_playback_rates[] = { 32000, 44100, 48000, 88200, 96000 };
-
-static struct snd_pcm_hw_constraint_list playback_rate_constraints = {
- .count = ARRAY_SIZE(hdmi_playback_rates),
- .list = hdmi_playback_rates,
- .mask = 0,
+static int cea_audio_rates[HDMI_MAX_RATES] = {
+ 32000,
+ 44100,
+ 48000,
+ 88200,
+ 96000,
+ 176400,
+ 192000,
};
+static void mxc_hdmi_get_playback_rates(void)
+{
+ int i, count = 0;
+ u8 rates;
+
+ /* always assume basic audio support */
+ rates = edid_cfg.sample_rates | 0x7;
+
+ for (i = 0 ; i < HDMI_MAX_RATES ; i++)
+ if ((rates & (1 << i)) != 0)
+ playback_rates[count++] = cea_audio_rates[i];
+
+ playback_constraint_rates.list = playback_rates;
+ playback_constraint_rates.count = count;
+
+#ifdef DEBUG
+ for (i = 0 ; i < playback_constraint_rates.count ; i++)
+ pr_debug("%s: constraint = %d Hz\n", __func__,
+ playback_rates[i]);
+#endif
+}
+
+static void mxc_hdmi_get_playback_sample_size(void)
+{
+ int i = 0;
+
+ /* always assume basic audio support */
+ playback_sample_size[i++] = 16;
+
+ if (edid_cfg.sample_sizes & 0x2)
+ playback_sample_size[i++] = 20;
+
+ if (edid_cfg.sample_sizes & 0x4)
+ playback_sample_size[i++] = 24;
+
+ playback_constraint_bits.list = playback_sample_size;
+ playback_constraint_bits.count = i;
+
+#ifdef DEBUG
+ for (i = 0 ; i < playback_constraint_bits.count ; i++)
+ pr_debug("%s: constraint = %d bits\n", __func__,
+ playback_sample_size[i]);
+#endif
+}
+
+static void mxc_hdmi_get_playback_channels(void)
+{
+ int channels = 2, i = 0;
+
+ /* always assume basic audio support */
+ playback_channels[i++] = channels;
+ channels += 2;
+
+ while ((i < HDMI_MAX_CHANNEL_CONSTRAINTS) &&
+ (channels <= edid_cfg.max_channels)) {
+ playback_channels[i++] = channels;
+ channels += 2;
+ }
+
+ playback_constraint_channels.list = playback_channels;
+ playback_constraint_channels.count = i;
+
+#ifdef DEBUG
+ for (i = 0 ; i < playback_constraint_channels.count ; i++)
+ pr_debug("%s: constraint = %d channels\n", __func__,
+ playback_channels[i]);
+#endif
+}
+
+static int mxc_hdmi_update_constraints(struct mxc_hdmi_priv *priv,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int ret;
+
+ hdmi_get_edid_cfg(&edid_cfg);
+
+ mxc_hdmi_get_playback_rates();
+ ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &playback_constraint_rates);
+ if (ret < 0)
+ return ret;
+
+ mxc_hdmi_get_playback_sample_size();
+ ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ &playback_constraint_bits);
+ if (ret < 0)
+ return ret;
+
+ mxc_hdmi_get_playback_channels();
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ &playback_constraint_channels);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_pcm_hw_constraint_integer(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
static int mxc_hdmi_codec_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct mxc_hdmi_priv *hdmi_priv = snd_soc_dai_get_drvdata(dai);
- struct snd_pcm_runtime *runtime = substream->runtime;
int ret;
clk_enable(hdmi_priv->isfr_clk);
@@ -122,14 +240,7 @@ static int mxc_hdmi_codec_startup(struct snd_pcm_substream *substream,
(int)clk_get_rate(hdmi_priv->isfr_clk),
(int)clk_get_rate(hdmi_priv->iahb_clk));
- ret = snd_pcm_hw_constraint_list(runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- &playback_rate_constraints);
- if (ret < 0)
- return ret;
-
- ret = snd_pcm_hw_constraint_integer(runtime,
- SNDRV_PCM_HW_PARAM_PERIODS);
+ ret = mxc_hdmi_update_constraints(hdmi_priv, substream);
if (ret < 0)
return ret;
diff --git a/sound/soc/imx/imx-hdmi-dma.c b/sound/soc/imx/imx-hdmi-dma.c
index 9ce99efccc1b..d0c4d52e9c56 100644
--- a/sound/soc/imx/imx-hdmi-dma.c
+++ b/sound/soc/imx/imx-hdmi-dma.c
@@ -649,14 +649,6 @@ static struct snd_pcm_hardware snd_imx_hardware = {
.fifo_size = 0,
};
-static unsigned int hw_channels[] = {2, 4, 6, 8};
-
-static struct snd_pcm_hw_constraint_list hw_constraint_channels = {
- .count = ARRAY_SIZE(hw_channels),
- .list = hw_channels,
- .mask = 0,
-};
-
static void hdmi_dma_irq_enable(struct imx_hdmi_dma_runtime_data *rtd)
{
unsigned long flags;
@@ -712,12 +704,6 @@ static int hdmi_dma_open(struct snd_pcm_substream *substream)
if (ret < 0)
return ret;
- ret = snd_pcm_hw_constraint_list(runtime, 0,
- SNDRV_PCM_HW_PARAM_CHANNELS,
- &hw_constraint_channels);
- if (ret < 0)
- return ret;
-
snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware);
hdmi_dma_irq_enable(hdmi_dma_priv);
diff --git a/sound/soc/imx/imx-hdmi.h b/sound/soc/imx/imx-hdmi.h
index 52c58dd516d5..11737579af31 100644
--- a/sound/soc/imx/imx-hdmi.h
+++ b/sound/soc/imx/imx-hdmi.h
@@ -1,7 +1,7 @@
/*
* MXC HDMI ALSA Soc Codec Driver
*
- * Copyright (C) 2011 Freescale Semiconductor, Inc.
+ * Copyright (C) 2011-2012 Freescale Semiconductor, Inc.
*/
/*
@@ -31,11 +31,17 @@ struct imx_hdmi {
struct platform_device *soc_platform_pdev;
};
+#define HDMI_MAX_RATES 7
+#define HDMI_MAX_SAMPLE_SIZE 3
+#define HDMI_MAX_CHANNEL_CONSTRAINTS 4
+
#define MXC_HDMI_RATES_PLAYBACK (SNDRV_PCM_RATE_32000 | \
SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000 | \
SNDRV_PCM_RATE_88200 | \
- SNDRV_PCM_RATE_96000)
+ SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_176400 | \
+ SNDRV_PCM_RATE_192000)
#define MXC_HDMI_FORMATS_PLAYBACK (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE)