aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorAlan Tull <alan.tull@freescale.com>2011-10-25 15:41:55 -0500
committerEric Miao <eric.miao@canonical.com>2011-11-10 07:38:55 +0800
commit7479540cefc0494f46c16f5a1d320c466d1c9e47 (patch)
tree4209ac31a5bbe984f96f670eeb76ab5fef5d76d5 /drivers
parent5ec5f268c99ecee1de7e554f74aba457a42b4810 (diff)
ENGR00160860-2 hdmi audio driver
Audio driver for i.Mx built-in HDMI Transmitter. * Uses HDMI Transmitter's built-in DMA. * Adds IEC958-style digital audio header info to the raw audio. * Gets pixel clock from the IPU driver and calculates clock regenerator values (cts and N). * Move ipu_id, and disp_id from the HDMI's platform data to the HDMI mfd's platform data. Saves them in the hdmi mfd. * Add mfd functionality to update the clock regenerator values when the hdmi changes the pixel clock rate or when requested from the audio driver with a new audio sample rate. Signed-off-by: Alan Tull <alan.tull@freescale.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mfd/mxc-hdmi-core.c244
-rw-r--r--drivers/video/mxc_hdmi.c10
2 files changed, 234 insertions, 20 deletions
diff --git a/drivers/mfd/mxc-hdmi-core.c b/drivers/mfd/mxc-hdmi-core.c
index 3fe3d1e1f9f..48cc57cc1b8 100644
--- a/drivers/mfd/mxc-hdmi-core.c
+++ b/drivers/mfd/mxc-hdmi-core.c
@@ -35,10 +35,12 @@
#include <mach/clock.h>
#include <mach/mxc_hdmi.h>
+#include <mach/ipu-v3.h>
+#include "../mxc/ipu3/ipu_prv.h"
#include <linux/mfd/mxc-hdmi-core.h>
+#include <linux/fsl_devices.h>
struct mxc_hdmi_data {
- struct clk *isfr_clk;
struct platform_device *pdev;
unsigned long __iomem *reg_base;
unsigned long reg_phys_base;
@@ -46,13 +48,18 @@ struct mxc_hdmi_data {
};
static unsigned long hdmi_base;
+struct clk *isfr_clk;
struct clk *iahb_clk;
static unsigned int irq_enable_cnt;
spinlock_t irq_spinlock;
bool irq_initialized;
bool irq_enabled;
-int mxc_hdmi_pixel_clk;
-int mxc_hdmi_ratio;
+unsigned int sample_rate;
+unsigned long pixel_clk_rate;
+struct clk *pixel_clk;
+int hdmi_ratio;
+int mxc_hdmi_ipu_id;
+int mxc_hdmi_disp_id;
u8 hdmi_readb(unsigned int reg)
{
@@ -181,15 +188,216 @@ static void initialize_hdmi_ih_mutes(void)
hdmi_writeb(ih_mute, HDMI_IH_MUTE);
}
+static void hdmi_set_clock_regenerator_n(unsigned int value)
+{
+ hdmi_writeb(value & 0xff, HDMI_AUD_N1);
+ hdmi_writeb((value >> 8) & 0xff, HDMI_AUD_N2);
+ hdmi_writeb((value >> 16) & 0xff, HDMI_AUD_N3);
+}
+
+static void hdmi_set_clock_regenerator_cts(unsigned int cts)
+{
+ hdmi_writeb(cts & 0xff, HDMI_AUD_CTS1);
+ hdmi_writeb((cts >> 8) & 0xff, HDMI_AUD_CTS2);
+ hdmi_writeb(((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) |
+ HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
+}
+
+static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk,
+ unsigned int ratio)
+{
+ unsigned int n = (128 * freq) / 1000;
+
+ switch (freq) {
+ case 32000:
+ if (pixel_clk == 25170000)
+ n = (ratio == 150) ? 9152 : 4576;
+ else if (pixel_clk == 27020000)
+ n = (ratio == 150) ? 8192 : 4096;
+ else if (pixel_clk == 74170000 || pixel_clk == 148350000)
+ n = 11648;
+ else
+ n = 4096;
+ break;
+
+ case 44100:
+ if (pixel_clk == 25170000)
+ n = 7007;
+ else if (pixel_clk == 74170000)
+ n = 17836;
+ else if (pixel_clk == 148350000)
+ n = (ratio == 150) ? 17836 : 8918;
+ else
+ n = 6272;
+ break;
+
+ case 48000:
+ if (pixel_clk == 25170000)
+ n = (ratio == 150) ? 9152 : 6864;
+ else if (pixel_clk == 27020000)
+ n = (ratio == 150) ? 8192 : 6144;
+ else if (pixel_clk == 74170000)
+ n = 11648;
+ else if (pixel_clk == 148350000)
+ n = (ratio == 150) ? 11648 : 5824;
+ else
+ n = 6144;
+ break;
+
+ case 88200:
+ n = hdmi_compute_n(44100, pixel_clk, ratio) * 2;
+ break;
+
+ case 96000:
+ n = hdmi_compute_n(48000, pixel_clk, ratio) * 2;
+ break;
+
+ case 176400:
+ n = hdmi_compute_n(44100, pixel_clk, ratio) * 4;
+ break;
+
+ case 192000:
+ n = hdmi_compute_n(48000, pixel_clk, ratio) * 4;
+ break;
+
+ default:
+ break;
+ }
+
+ return n;
+}
+
+static unsigned int hdmi_compute_cts(unsigned int freq, unsigned long pixel_clk,
+ unsigned int ratio)
+{
+ unsigned int cts = 0;
+ switch (freq) {
+ case 32000:
+ if (pixel_clk == 297000000) {
+ cts = 222750;
+ break;
+ }
+ case 48000:
+ case 96000:
+ case 192000:
+ switch (pixel_clk) {
+ case 25200000:
+ case 27000000:
+ case 54000000:
+ case 74250000:
+ case 148500000:
+ cts = pixel_clk / 1000;
+ break;
+ case 297000000:
+ cts = 247500;
+ break;
+ /*
+ * All other TMDS clocks are not supported by
+ * DWC_hdmi_tx. The TMDS clocks divided or
+ * multiplied by 1,001 coefficients are not
+ * supported.
+ */
+ default:
+ break;
+ }
+ break;
+ case 44100:
+ case 88200:
+ case 176400:
+ switch (pixel_clk) {
+ case 25200000:
+ cts = 28000;
+ break;
+ case 27000000:
+ cts = 30000;
+ break;
+ case 54000000:
+ cts = 60000;
+ break;
+ case 74250000:
+ cts = 82500;
+ break;
+ case 148500000:
+ cts = 165000;
+ break;
+ case 297000000:
+ cts = 247500;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ if (ratio == 100)
+ return cts;
+ else
+ return (cts * ratio) / 100;
+}
+
+static void hdmi_get_pixel_clk(void)
+{
+ struct ipu_soc *ipu;
+
+ if (pixel_clk == NULL) {
+ ipu = ipu_get_soc(mxc_hdmi_ipu_id);
+ pixel_clk = clk_get(ipu->dev, "pixel_clk_0");
+ if (IS_ERR(pixel_clk)) {
+ pr_err("%s could not get pixel_clk_0\n", __func__);
+ return;
+ }
+ }
+
+ pixel_clk_rate = clk_get_rate(pixel_clk);
+}
+
+/*
+ * input: audio sample rate and video pixel rate
+ * output: N and cts written to the HDMI regs.
+ */
+void hdmi_set_clk_regenerator(void)
+{
+ unsigned int clk_n, clk_cts;
+
+ /* Get pixel clock from ipu */
+ hdmi_get_pixel_clk();
+
+ pr_debug("%s: sample rate is %d ; ratio is %d ; pixel clk is %d\n",
+ __func__, sample_rate, hdmi_ratio, (int)pixel_clk_rate);
+
+ clk_n = hdmi_compute_n(sample_rate, pixel_clk_rate, hdmi_ratio);
+ clk_cts = hdmi_compute_cts(sample_rate, pixel_clk_rate, hdmi_ratio);
+
+ if (clk_cts == 0) {
+ pr_err("%s: pixel clock not supported: %d\n",
+ __func__, (int)pixel_clk_rate);
+ return;
+ }
+
+ clk_enable(isfr_clk);
+ clk_enable(iahb_clk);
+
+ hdmi_set_clock_regenerator_n(clk_n);
+ hdmi_set_clock_regenerator_cts(clk_cts);
+
+ clk_disable(iahb_clk);
+ clk_disable(isfr_clk);
+}
+
+void hdmi_set_sample_rate(unsigned int rate)
+{
+ sample_rate = rate;
+ hdmi_set_clk_regenerator();
+}
+
static int mxc_hdmi_core_probe(struct platform_device *pdev)
{
+ struct fsl_mxc_hdmi_core_platform_data *pdata = pdev->dev.platform_data;
struct mxc_hdmi_data *hdmi_data;
struct resource *res;
int ret = 0;
- /* 100% for 8 bit pixels */
- mxc_hdmi_ratio = 100;
-
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENOENT;
@@ -201,27 +409,32 @@ static int mxc_hdmi_core_probe(struct platform_device *pdev)
}
hdmi_data->pdev = pdev;
+ pixel_clk = NULL;
+ sample_rate = 48000;
+ pixel_clk_rate = 74250000;
+ hdmi_ratio = 100;
+
irq_enable_cnt = 0;
irq_initialized = false;
irq_enabled = true;
spin_lock_init(&irq_spinlock);
- hdmi_data->isfr_clk = clk_get(&hdmi_data->pdev->dev, "hdmi_isfr_clk");
- if (IS_ERR(hdmi_data->isfr_clk)) {
- ret = PTR_ERR(hdmi_data->isfr_clk);
+ isfr_clk = clk_get(&hdmi_data->pdev->dev, "hdmi_isfr_clk");
+ if (IS_ERR(isfr_clk)) {
+ ret = PTR_ERR(isfr_clk);
dev_err(&hdmi_data->pdev->dev,
"Unable to get HDMI isfr clk: %d\n", ret);
goto eclkg;
}
- ret = clk_enable(hdmi_data->isfr_clk);
+ ret = clk_enable(isfr_clk);
if (ret < 0) {
dev_err(&pdev->dev, "Cannot enable HDMI clock: %d\n", ret);
goto eclke;
}
pr_debug("%s isfr_clk:%d\n", __func__,
- (int)clk_get_rate(hdmi_data->isfr_clk));
+ (int)clk_get_rate(isfr_clk));
iahb_clk = clk_get(&hdmi_data->pdev->dev, "hdmi_iahb_clk");
if (IS_ERR(iahb_clk)) {
@@ -255,10 +468,13 @@ static int mxc_hdmi_core_probe(struct platform_device *pdev)
pr_debug("\n%s hdmi hw base = 0x%08x\n\n", __func__, (int)res->start);
+ mxc_hdmi_ipu_id = pdata->ipu_id;
+ mxc_hdmi_disp_id = pdata->disp_id;
+
initialize_hdmi_ih_mutes();
/* Disable HDMI clocks until video/audio sub-drivers are initialized */
- clk_disable(hdmi_data->isfr_clk);
+ clk_disable(isfr_clk);
clk_disable(iahb_clk);
/* Replace platform data coming in with a local struct */
@@ -273,9 +489,9 @@ emem:
eclke2:
clk_put(iahb_clk);
eclkg2:
- clk_disable(hdmi_data->isfr_clk);
+ clk_disable(isfr_clk);
eclke:
- clk_put(hdmi_data->isfr_clk);
+ clk_put(isfr_clk);
eclkg:
kfree(hdmi_data);
return ret;
diff --git a/drivers/video/mxc_hdmi.c b/drivers/video/mxc_hdmi.c
index d48170dab4e..95083f2ebd3 100644
--- a/drivers/video/mxc_hdmi.c
+++ b/drivers/video/mxc_hdmi.c
@@ -1137,9 +1137,6 @@ void hdmi_av_composer(struct mxc_hdmi *hdmi)
fb_mode.upper_margin + fb_mode.lower_margin +
fb_mode.vsync_len) * fb_mode.refresh;
- /* Expose pixel clock for audio driver */
- mxc_hdmi_pixel_clk = vmode->mPixelClock;
-
dev_dbg(&hdmi->pdev->dev, "final pixclk = %d\n", vmode->mPixelClock);
/* Set up HDMI_FC_INVIDCONF */
@@ -1553,6 +1550,7 @@ static int mxc_hdmi_setup(struct mxc_hdmi *hdmi)
hdmi_tx_hdcp_config(hdmi);
hdmi_phy_init(hdmi, TRUE);
hdmi_video_force_output(hdmi, FALSE);
+ hdmi_set_clk_regenerator();
return 0;
}
@@ -1599,8 +1597,8 @@ static int mxc_hdmi_disp_init(struct mxc_dispdrv_entry *disp)
if (!plat || irq < 0)
return -ENODEV;
- setting->dev_id = plat->ipu_id;
- setting->disp_id = plat->disp_id;
+ setting->dev_id = mxc_hdmi_ipu_id;
+ setting->disp_id = mxc_hdmi_disp_id;
setting->if_fmt = IPU_PIX_FMT_RGB24;
hdmi->fbi = setting->fbi;
@@ -1614,7 +1612,7 @@ static int mxc_hdmi_disp_init(struct mxc_dispdrv_entry *disp)
/* Initialize HDMI */
if (plat->init)
- plat->init(plat->ipu_id, plat->disp_id);
+ plat->init(mxc_hdmi_ipu_id, mxc_hdmi_disp_id);
hdmi->hdmi_isfr_clk = clk_get(&hdmi->pdev->dev, "hdmi_isfr_clk");
if (IS_ERR(hdmi->hdmi_isfr_clk)) {