diff options
author | Archit Taneja <archit@ti.com> | 2013-10-08 11:37:00 +0400 |
---|---|---|
committer | Tomi Valkeinen <tomi.valkeinen@ti.com> | 2013-10-09 13:42:11 +0400 |
commit | 5cac5aee18568579931267d3211371a176efa476 (patch) | |
tree | c35391f24c28b11c30de9ae8f684240fce6db415 /drivers | |
parent | c1577c1ea01732fea5bc18f05a9e005c0dce6261 (diff) | |
download | linux-5cac5aee18568579931267d3211371a176efa476.tar.xz |
omapdss: HDMI: create a PHY library
HDMI PHY is a block common to DSS in OMAP4, OMAP5 and DRA7x. Move the
existing functions from ti_hdmi_4xxx_ip.c to a separate file. These funcs are
called directly from the hdmi driver rather than hdmi_ip_ops function pointer
calls.
Add the PHY library function declarations to ti_hdmi.h. These will be shared
amongst the omap4/5 hdmi platform drivers. Remove the PHY function pointer ops
from the ti_hdmi_ip_ops struct.
The DT/hwmod information for hdmi doesn't split the address space according to
the required sub blocks. Keep the address offset and size information in the
driver for now. This will be removed when the driver gets the information
correctly from DT/hwmod.
Signed-off-by: Archit Taneja <archit@ti.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/video/omap2/dss/Makefile | 3 | ||||
-rw-r--r-- | drivers/video/omap2/dss/dss_features.c | 3 | ||||
-rw-r--r-- | drivers/video/omap2/dss/hdmi.c | 19 | ||||
-rw-r--r-- | drivers/video/omap2/dss/hdmi_phy.c | 194 | ||||
-rw-r--r-- | drivers/video/omap2/dss/ti_hdmi.h | 26 | ||||
-rw-r--r-- | drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c | 109 |
6 files changed, 219 insertions, 135 deletions
diff --git a/drivers/video/omap2/dss/Makefile b/drivers/video/omap2/dss/Makefile index 5ea65d327cfb..d88e93870e15 100644 --- a/drivers/video/omap2/dss/Makefile +++ b/drivers/video/omap2/dss/Makefile @@ -10,5 +10,6 @@ omapdss-$(CONFIG_OMAP2_DSS_RFBI) += rfbi.o omapdss-$(CONFIG_OMAP2_DSS_VENC) += venc.o omapdss-$(CONFIG_OMAP2_DSS_SDI) += sdi.o omapdss-$(CONFIG_OMAP2_DSS_DSI) += dsi.o -omapdss-$(CONFIG_OMAP4_DSS_HDMI) += hdmi.o hdmi_wp.o hdmi_pll.o ti_hdmi_4xxx_ip.o +omapdss-$(CONFIG_OMAP4_DSS_HDMI) += hdmi.o hdmi_wp.o hdmi_pll.o hdmi_phy.o \ + ti_hdmi_4xxx_ip.o ccflags-$(CONFIG_OMAP2_DSS_DEBUG) += -DDEBUG diff --git a/drivers/video/omap2/dss/dss_features.c b/drivers/video/omap2/dss/dss_features.c index 9ee92e90caff..2777eb6d603b 100644 --- a/drivers/video/omap2/dss/dss_features.c +++ b/drivers/video/omap2/dss/dss_features.c @@ -794,11 +794,8 @@ static const struct omap_dss_features omap5_dss_features = { static const struct ti_hdmi_ip_ops omap4_hdmi_functions = { .video_configure = ti_hdmi_4xxx_basic_configure, - .phy_enable = ti_hdmi_4xxx_phy_enable, - .phy_disable = ti_hdmi_4xxx_phy_disable, .read_edid = ti_hdmi_4xxx_read_edid, .dump_core = ti_hdmi_4xxx_core_dump, - .dump_phy = ti_hdmi_4xxx_phy_dump, #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) .audio_start = ti_hdmi_4xxx_audio_start, .audio_stop = ti_hdmi_4xxx_audio_stop, diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c index f6a2eba244e7..f7e2ac6861b6 100644 --- a/drivers/video/omap2/dss/hdmi.c +++ b/drivers/video/omap2/dss/hdmi.c @@ -42,7 +42,6 @@ #define HDMI_CORE_SYS 0x400 #define HDMI_CORE_AV 0x900 -#define HDMI_PHY 0x300 /* HDMI EDID Length move this */ #define HDMI_EDID_MAX_LENGTH 256 @@ -487,7 +486,8 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev) goto err_pll_enable; } - r = hdmi.ip_data.ops->phy_enable(&hdmi.ip_data); + r = hdmi_phy_enable(&hdmi.ip_data.phy, &hdmi.ip_data.wp, + &hdmi.ip_data.cfg); if (r) { DSSDBG("Failed to start PHY\n"); goto err_phy_enable; @@ -514,7 +514,7 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev) err_mgr_enable: hdmi_wp_video_stop(&hdmi.ip_data.wp); err_vid_enable: - hdmi.ip_data.ops->phy_disable(&hdmi.ip_data); + hdmi_phy_disable(&hdmi.ip_data.phy, &hdmi.ip_data.wp); err_phy_enable: hdmi_pll_disable(&hdmi.ip_data.pll, &hdmi.ip_data.wp); err_pll_enable: @@ -529,7 +529,7 @@ static void hdmi_power_off_full(struct omap_dss_device *dssdev) dss_mgr_disable(mgr); hdmi_wp_video_stop(&hdmi.ip_data.wp); - hdmi.ip_data.ops->phy_disable(&hdmi.ip_data); + hdmi_phy_disable(&hdmi.ip_data.phy, &hdmi.ip_data.wp); hdmi_pll_disable(&hdmi.ip_data.pll, &hdmi.ip_data.wp); hdmi_power_off_core(dssdev); @@ -593,7 +593,7 @@ static void hdmi_dump_regs(struct seq_file *s) hdmi_wp_dump(&hdmi.ip_data.wp, s); hdmi_pll_dump(&hdmi.ip_data.pll, s); - hdmi.ip_data.ops->dump_phy(&hdmi.ip_data, s); + hdmi_phy_dump(&hdmi.ip_data.phy, s); hdmi.ip_data.ops->dump_core(&hdmi.ip_data, s); hdmi_runtime_put(); @@ -1049,11 +1049,9 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev) if (r) return r; - hdmi.ip_data.irq = platform_get_irq(pdev, 0); - if (hdmi.ip_data.irq < 0) { - DSSERR("platform_get_irq failed\n"); - return -ENODEV; - } + r = hdmi_phy_init(pdev, &hdmi.ip_data.phy); + if (r) + return r; r = hdmi_get_clocks(pdev); if (r) { @@ -1065,7 +1063,6 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev) hdmi.ip_data.core_sys_offset = HDMI_CORE_SYS; hdmi.ip_data.core_av_offset = HDMI_CORE_AV; - hdmi.ip_data.phy_offset = HDMI_PHY; hdmi_init_output(pdev); diff --git a/drivers/video/omap2/dss/hdmi_phy.c b/drivers/video/omap2/dss/hdmi_phy.c new file mode 100644 index 000000000000..48bdba8d7031 --- /dev/null +++ b/drivers/video/omap2/dss/hdmi_phy.c @@ -0,0 +1,194 @@ +/* + * HDMI PHY + * + * Copyright (C) 2013 Texas Instruments Incorporated + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <video/omapdss.h> + +#include "dss.h" +#include "ti_hdmi.h" +#include "ti_hdmi_4xxx_ip.h" + +#define HDMI_IRQ_LINK_CONNECT (1 << 25) +#define HDMI_IRQ_LINK_DISCONNECT (1 << 26) + +static inline void hdmi_write_reg(void __iomem *base_addr, const u16 idx, + u32 val) +{ + __raw_writel(val, base_addr + idx); +} + +static inline u32 hdmi_read_reg(void __iomem *base_addr, const u16 idx) +{ + return __raw_readl(base_addr + idx); +} + +#define REG_FLD_MOD(base, idx, val, start, end) \ + hdmi_write_reg(base, idx, FLD_MOD(hdmi_read_reg(base, idx),\ + val, start, end)) +#define REG_GET(base, idx, start, end) \ + FLD_GET(hdmi_read_reg(base, idx), start, end) + +static inline int hdmi_wait_for_bit_change(void __iomem *base_addr, + const u16 idx, int b2, int b1, u32 val) +{ + u32 t = 0; + while (val != REG_GET(base_addr, idx, b2, b1)) { + udelay(1); + if (t++ > 10000) + return !val; + } + return val; +} + +void hdmi_phy_dump(struct hdmi_phy_data *phy, struct seq_file *s) +{ +#define DUMPPHY(r) seq_printf(s, "%-35s %08x\n", #r,\ + hdmi_read_reg(phy->base, r)) + + DUMPPHY(HDMI_TXPHY_TX_CTRL); + DUMPPHY(HDMI_TXPHY_DIGITAL_CTRL); + DUMPPHY(HDMI_TXPHY_POWER_CTRL); + DUMPPHY(HDMI_TXPHY_PAD_CFG_CTRL); +} + +static irqreturn_t hdmi_irq_handler(int irq, void *data) +{ + struct hdmi_wp_data *wp = data; + u32 irqstatus; + + irqstatus = hdmi_wp_get_irqstatus(wp); + hdmi_wp_set_irqstatus(wp, irqstatus); + + if ((irqstatus & HDMI_IRQ_LINK_CONNECT) && + irqstatus & HDMI_IRQ_LINK_DISCONNECT) { + /* + * If we get both connect and disconnect interrupts at the same + * time, turn off the PHY, clear interrupts, and restart, which + * raises connect interrupt if a cable is connected, or nothing + * if cable is not connected. + */ + hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF); + + hdmi_wp_set_irqstatus(wp, HDMI_IRQ_LINK_CONNECT | + HDMI_IRQ_LINK_DISCONNECT); + + hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON); + } else if (irqstatus & HDMI_IRQ_LINK_CONNECT) { + hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_TXON); + } else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) { + hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON); + } + + return IRQ_HANDLED; +} + +int hdmi_phy_enable(struct hdmi_phy_data *phy, struct hdmi_wp_data *wp, + struct hdmi_config *cfg) +{ + u16 r = 0; + u32 irqstatus; + + hdmi_wp_clear_irqenable(wp, 0xffffffff); + + irqstatus = hdmi_wp_get_irqstatus(wp); + hdmi_wp_set_irqstatus(wp, irqstatus); + + r = hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON); + if (r) + return r; + + /* + * Read address 0 in order to get the SCP reset done completed + * Dummy access performed to make sure reset is done + */ + hdmi_read_reg(phy->base, HDMI_TXPHY_TX_CTRL); + + /* + * Write to phy address 0 to configure the clock + * use HFBITCLK write HDMI_TXPHY_TX_CONTROL_FREQOUT field + */ + REG_FLD_MOD(phy->base, HDMI_TXPHY_TX_CTRL, 0x1, 31, 30); + + /* Write to phy address 1 to start HDMI line (TXVALID and TMDSCLKEN) */ + hdmi_write_reg(phy->base, HDMI_TXPHY_DIGITAL_CTRL, 0xF0000000); + + /* Setup max LDO voltage */ + REG_FLD_MOD(phy->base, HDMI_TXPHY_POWER_CTRL, 0xB, 3, 0); + + /* Write to phy address 3 to change the polarity control */ + REG_FLD_MOD(phy->base, HDMI_TXPHY_PAD_CFG_CTRL, 0x1, 27, 27); + + r = request_threaded_irq(phy->irq, NULL, hdmi_irq_handler, + IRQF_ONESHOT, "OMAP HDMI", wp); + if (r) { + DSSERR("HDMI IRQ request failed\n"); + hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF); + return r; + } + + hdmi_wp_set_irqenable(wp, + HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT); + + return 0; +} + +void hdmi_phy_disable(struct hdmi_phy_data *phy, struct hdmi_wp_data *wp) +{ + free_irq(phy->irq, wp); + + hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF); +} + +#define PHY_OFFSET 0x300 +#define PHY_SIZE 0x100 + +int hdmi_phy_init(struct platform_device *pdev, struct hdmi_phy_data *phy) +{ + struct resource *res; + struct resource temp_res; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hdmi_txphy"); + if (!res) { + DSSDBG("can't get PHY mem resource by name\n"); + /* + * if hwmod/DT doesn't have the memory resource information + * split into HDMI sub blocks by name, we try again by getting + * the platform's first resource. this code will be removed when + * the driver can get the mem resources by name + */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + DSSERR("can't get PHY mem resource\n"); + return -EINVAL; + } + + temp_res.start = res->start + PHY_OFFSET; + temp_res.end = temp_res.start + PHY_SIZE - 1; + res = &temp_res; + } + + phy->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!phy->base) { + DSSERR("can't ioremap TX PHY\n"); + return -ENOMEM; + } + + phy->irq = platform_get_irq(pdev, 0); + if (phy->irq < 0) { + DSSERR("platform_get_irq failed\n"); + return -ENODEV; + } + + return 0; +} diff --git a/drivers/video/omap2/dss/ti_hdmi.h b/drivers/video/omap2/dss/ti_hdmi.h index 62a83c79628e..716bac450ef0 100644 --- a/drivers/video/omap2/dss/ti_hdmi.h +++ b/drivers/video/omap2/dss/ti_hdmi.h @@ -149,16 +149,10 @@ struct ti_hdmi_ip_ops { void (*video_configure)(struct hdmi_ip_data *ip_data); - int (*phy_enable)(struct hdmi_ip_data *ip_data); - - void (*phy_disable)(struct hdmi_ip_data *ip_data); - int (*read_edid)(struct hdmi_ip_data *ip_data, u8 *edid, int len); void (*dump_core)(struct hdmi_ip_data *ip_data, struct seq_file *s); - void (*dump_phy)(struct hdmi_ip_data *ip_data, struct seq_file *s); - #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) int (*audio_start)(struct hdmi_ip_data *ip_data); @@ -223,14 +217,20 @@ struct hdmi_pll_data { struct hdmi_pll_info info; }; +struct hdmi_phy_data { + void __iomem *base; + + int irq; +}; + struct hdmi_ip_data { struct hdmi_wp_data wp; struct hdmi_pll_data pll; + struct hdmi_phy_data phy; unsigned long core_sys_offset; unsigned long core_av_offset; - unsigned long phy_offset; - int irq; + const struct ti_hdmi_ip_ops *ops; struct hdmi_config cfg; struct hdmi_core_infoframe_avi avi_cfg; @@ -266,12 +266,16 @@ void hdmi_pll_dump(struct hdmi_pll_data *pll, struct seq_file *s); void hdmi_pll_compute(struct hdmi_pll_data *pll, unsigned long clkin, int phy); int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll); -int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data); -void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data); +/* HDMI PHY funcs */ +int hdmi_phy_enable(struct hdmi_phy_data *phy, struct hdmi_wp_data *wp, + struct hdmi_config *cfg); +void hdmi_phy_disable(struct hdmi_phy_data *phy, struct hdmi_wp_data *wp); +void hdmi_phy_dump(struct hdmi_phy_data *phy, struct seq_file *s); +int hdmi_phy_init(struct platform_device *pdev, struct hdmi_phy_data *phy); + int ti_hdmi_4xxx_read_edid(struct hdmi_ip_data *ip_data, u8 *edid, int len); void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data); void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s); -void ti_hdmi_4xxx_phy_dump(struct hdmi_ip_data *ip_data, struct seq_file *s); #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) int hdmi_compute_acr(u32 sample_freq, u32 *n, u32 *cts); int hdmi_wp_audio_enable(struct hdmi_wp_data *wp, bool enable); diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c index 8cfb54b39688..c705aa113aac 100644 --- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c @@ -37,9 +37,6 @@ #include "dss.h" #include "dss_features.h" -#define HDMI_IRQ_LINK_CONNECT (1 << 25) -#define HDMI_IRQ_LINK_DISCONNECT (1 << 26) - static inline void hdmi_write_reg(void __iomem *base_addr, const u16 idx, u32 val) { @@ -70,11 +67,6 @@ static inline int hdmi_wait_for_bit_change(void __iomem *base_addr, return val; } -static inline void __iomem *hdmi_phy_base(struct hdmi_ip_data *ip_data) -{ - return ip_data->wp.base + ip_data->phy_offset; -} - static inline void __iomem *hdmi_av_base(struct hdmi_ip_data *ip_data) { return ip_data->wp.base + ip_data->core_av_offset; @@ -85,94 +77,6 @@ static inline void __iomem *hdmi_core_sys_base(struct hdmi_ip_data *ip_data) return ip_data->wp.base + ip_data->core_sys_offset; } -static irqreturn_t hdmi_irq_handler(int irq, void *data) -{ - struct hdmi_ip_data *ip_data = data; - u32 irqstatus; - - irqstatus = hdmi_wp_get_irqstatus(&ip_data->wp); - hdmi_wp_set_irqstatus(&ip_data->wp, irqstatus); - - if ((irqstatus & HDMI_IRQ_LINK_CONNECT) && - irqstatus & HDMI_IRQ_LINK_DISCONNECT) { - /* - * If we get both connect and disconnect interrupts at the same - * time, turn off the PHY, clear interrupts, and restart, which - * raises connect interrupt if a cable is connected, or nothing - * if cable is not connected. - */ - hdmi_wp_set_phy_pwr(&ip_data->wp, HDMI_PHYPWRCMD_OFF); - - hdmi_wp_set_irqstatus(&ip_data->wp, HDMI_IRQ_LINK_CONNECT | - HDMI_IRQ_LINK_DISCONNECT); - - hdmi_wp_set_phy_pwr(&ip_data->wp, HDMI_PHYPWRCMD_LDOON); - } else if (irqstatus & HDMI_IRQ_LINK_CONNECT) { - hdmi_wp_set_phy_pwr(&ip_data->wp, HDMI_PHYPWRCMD_TXON); - } else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) { - hdmi_wp_set_phy_pwr(&ip_data->wp, HDMI_PHYPWRCMD_LDOON); - } - - return IRQ_HANDLED; -} - -int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data) -{ - u16 r = 0; - u32 irqstatus; - void __iomem *phy_base = hdmi_phy_base(ip_data); - - hdmi_wp_clear_irqenable(&ip_data->wp, 0xffffffff); - - irqstatus = hdmi_wp_get_irqstatus(&ip_data->wp); - hdmi_wp_set_irqstatus(&ip_data->wp, irqstatus); - - r = hdmi_wp_set_phy_pwr(&ip_data->wp, HDMI_PHYPWRCMD_LDOON); - if (r) - return r; - - /* - * Read address 0 in order to get the SCP reset done completed - * Dummy access performed to make sure reset is done - */ - hdmi_read_reg(phy_base, HDMI_TXPHY_TX_CTRL); - - /* - * Write to phy address 0 to configure the clock - * use HFBITCLK write HDMI_TXPHY_TX_CONTROL_FREQOUT field - */ - REG_FLD_MOD(phy_base, HDMI_TXPHY_TX_CTRL, 0x1, 31, 30); - - /* Write to phy address 1 to start HDMI line (TXVALID and TMDSCLKEN) */ - hdmi_write_reg(phy_base, HDMI_TXPHY_DIGITAL_CTRL, 0xF0000000); - - /* Setup max LDO voltage */ - REG_FLD_MOD(phy_base, HDMI_TXPHY_POWER_CTRL, 0xB, 3, 0); - - /* Write to phy address 3 to change the polarity control */ - REG_FLD_MOD(phy_base, HDMI_TXPHY_PAD_CFG_CTRL, 0x1, 27, 27); - - r = request_threaded_irq(ip_data->irq, NULL, hdmi_irq_handler, - IRQF_ONESHOT, "OMAP HDMI", ip_data); - if (r) { - DSSERR("HDMI IRQ request failed\n"); - hdmi_wp_set_phy_pwr(&ip_data->wp, HDMI_PHYPWRCMD_OFF); - return r; - } - - hdmi_wp_set_irqenable(&ip_data->wp, - HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT); - - return 0; -} - -void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data) -{ - free_irq(ip_data->irq, ip_data); - - hdmi_wp_set_phy_pwr(&ip_data->wp, HDMI_PHYPWRCMD_OFF); -} - static int hdmi_core_ddc_init(struct hdmi_ip_data *ip_data) { void __iomem *base = hdmi_core_sys_base(ip_data); @@ -524,8 +428,6 @@ static void hdmi_core_av_packet_config(struct hdmi_ip_data *ip_data, (repeat_cfg.generic_pkt_repeat)); } - - void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data) { /* HDMI */ @@ -769,17 +671,6 @@ void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s) DUMPCOREAV(HDMI_CORE_AV_CEC_ADDR_ID); } -void ti_hdmi_4xxx_phy_dump(struct hdmi_ip_data *ip_data, struct seq_file *s) -{ -#define DUMPPHY(r) seq_printf(s, "%-35s %08x\n", #r,\ - hdmi_read_reg(hdmi_phy_base(ip_data), r)) - - DUMPPHY(HDMI_TXPHY_TX_CTRL); - DUMPPHY(HDMI_TXPHY_DIGITAL_CTRL); - DUMPPHY(HDMI_TXPHY_POWER_CTRL); - DUMPPHY(HDMI_TXPHY_PAD_CFG_CTRL); -} - #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) static void ti_hdmi_4xxx_core_audio_config(struct hdmi_ip_data *ip_data, struct hdmi_core_audio_config *cfg) |