diff options
author | Danny Nold <dannynold@freescale.com> | 2011-10-14 13:43:32 -0500 |
---|---|---|
committer | Jason Liu <r64343@freescale.com> | 2012-07-20 13:16:28 +0800 |
commit | 47e92cd155b04ad81673f9cd9c3733ffd974265c (patch) | |
tree | 560f6163a9611525d240d2750f6c0309aa74756a /drivers/mfd/mxc-hdmi-core.c | |
parent | 0d8956cab0581293ac404439693fa34376f964cf (diff) |
ENGR00160061 - MXC HDMI: Optimize HDMI clock management
- Ensure HDMI clocks are disabled when leaving HDMI core probe function.
- Create HDMI core api to allow HDMI sub-drivers to init, enable, and
disable the HDMI IRQ. Required to optimally manage HDMI clocks,
allow IAHB to be disabled, and still have video and audio sub-drivers
able to receive interrupts.
- Update code to adjust for decoupled ISFR and IAHB clocks.
- Disable IAHB clocks whenever HDMI not plugged in.
Signed-off-by: Danny Nold <dannynold@freescale.com>
Diffstat (limited to 'drivers/mfd/mxc-hdmi-core.c')
-rw-r--r-- | drivers/mfd/mxc-hdmi-core.c | 87 |
1 files changed, 86 insertions, 1 deletions
diff --git a/drivers/mfd/mxc-hdmi-core.c b/drivers/mfd/mxc-hdmi-core.c index 0e59dafd0a3b..3fe3d1e1f9f0 100644 --- a/drivers/mfd/mxc-hdmi-core.c +++ b/drivers/mfd/mxc-hdmi-core.c @@ -25,11 +25,15 @@ #include <linux/err.h> #include <linux/io.h> #include <linux/clk.h> +#include <linux/spinlock.h> +#include <linux/irq.h> +#include <linux/interrupt.h> #include <linux/platform_device.h> #include <linux/regulator/machine.h> #include <asm/mach-types.h> +#include <mach/clock.h> #include <mach/mxc_hdmi.h> #include <linux/mfd/mxc-hdmi-core.h> @@ -42,7 +46,11 @@ struct mxc_hdmi_data { }; static unsigned long hdmi_base; - +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; @@ -88,6 +96,56 @@ void hdmi_write4(unsigned int value, unsigned int reg) hdmi_writeb((value >> 24) & 0xff, reg + 3); } +void hdmi_irq_init() +{ + /* First time IRQ is initialized, set enable_cnt to 1, + * since IRQ starts out enabled after request_irq */ + if (!irq_initialized) { + irq_enable_cnt = 1; + irq_initialized = true; + irq_enabled = true; + } +} + +void hdmi_irq_enable(int irq) +{ + unsigned long flags; + + spin_lock_irqsave(&irq_spinlock, flags); + + if (!irq_enabled) { + enable_irq(irq); + irq_enabled = true; + } + + irq_enable_cnt++; + + spin_unlock_irqrestore(&irq_spinlock, flags); +} + +unsigned int hdmi_irq_disable(int irq) +{ + unsigned long flags; + + spin_lock_irqsave(&irq_spinlock, flags); + + WARN_ON (irq_enable_cnt == 0); + + irq_enable_cnt--; + + /* Only disable HDMI IRQ if IAHB clk is off */ + if ((irq_enable_cnt == 0) && (clk_get_usecount(iahb_clk) == 0)) { + disable_irq_nosync(irq); + irq_enabled = false; + spin_unlock_irqrestore(&irq_spinlock, flags); + return IRQ_DISABLE_SUCCEED; + } + + spin_unlock_irqrestore(&irq_spinlock, flags); + + return IRQ_DISABLE_FAIL; +} + static void initialize_hdmi_ih_mutes(void) { u8 ih_mute; @@ -143,6 +201,11 @@ static int mxc_hdmi_core_probe(struct platform_device *pdev) } hdmi_data->pdev = pdev; + 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); @@ -160,6 +223,20 @@ static int mxc_hdmi_core_probe(struct platform_device *pdev) pr_debug("%s isfr_clk:%d\n", __func__, (int)clk_get_rate(hdmi_data->isfr_clk)); + iahb_clk = clk_get(&hdmi_data->pdev->dev, "hdmi_iahb_clk"); + if (IS_ERR(iahb_clk)) { + ret = PTR_ERR(iahb_clk); + dev_err(&hdmi_data->pdev->dev, + "Unable to get HDMI iahb clk: %d\n", ret); + goto eclkg2; + } + + ret = clk_enable(iahb_clk); + if (ret < 0) { + dev_err(&pdev->dev, "Cannot enable HDMI clock: %d\n", ret); + goto eclke2; + } + hdmi_data->reg_phys_base = res->start; if (!request_mem_region(res->start, resource_size(res), dev_name(&pdev->dev))) { @@ -180,6 +257,10 @@ static int mxc_hdmi_core_probe(struct platform_device *pdev) initialize_hdmi_ih_mutes(); + /* Disable HDMI clocks until video/audio sub-drivers are initialized */ + clk_disable(hdmi_data->isfr_clk); + clk_disable(iahb_clk); + /* Replace platform data coming in with a local struct */ platform_set_drvdata(pdev, hdmi_data); @@ -188,6 +269,10 @@ static int mxc_hdmi_core_probe(struct platform_device *pdev) eirq: release_mem_region(res->start, resource_size(res)); emem: + clk_disable(iahb_clk); +eclke2: + clk_put(iahb_clk); +eclkg2: clk_disable(hdmi_data->isfr_clk); eclke: clk_put(hdmi_data->isfr_clk); |