aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTomi Valkeinen <tomi.valkeinen@ti.com>2013-06-06 06:08:35 -0400
committerTomi Valkeinen <tomi.valkeinen@ti.com>2013-06-17 07:00:54 -0400
commitddb1d5ca99f515442592b487171481dbf3a309d5 (patch)
tree1609a35b8e43ef3fc631880bd895505c3af53a59
parente9f322b4913e5d3e5c5d21dc462ca6f8a86e1df1 (diff)
OMAPDSS: HDMI: clean up PHY power handling
The TRM tells to set PHY to TXON only after getting LINK_CONNECT, and to set PHY to OFF or LDOON after getting LINK_DISCONNECT, in order to avoid damage to the PHY. We don't currently do it quite like that. Instead of using the HDMI interrupts, we use HPD signal. This works, but is not actually quite correct, as HPD comes at a different time than LINK_CONNECT and LINK_DISCONNECT interrupts. Also, the HPD GPIO is a property of the TPD level shifter, not HDMI IP, so handling the GPIO in the HDMI driver is wrong. This patch implements the PHY power handling correctly, using the interrupts. There is a corner case that causes some additional difficulties: we may get both LINK_CONNECT and LINK_DISCONNECT interrupts at the same time. This is handled in the code by retrying: turning off the PHY, clearing the interrupt status, and re-enabling the PHY. This causes a new LINK_CONNECT interrupt to happen if a cable is connected. Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
-rw-r--r--drivers/video/omap2/dss/hdmi.c6
-rw-r--r--drivers/video/omap2/dss/ti_hdmi.h1
-rw-r--r--drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c81
-rw-r--r--drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h1
4 files changed, 51 insertions, 38 deletions
diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c
index 0f93903d4409..0fb3662e28b6 100644
--- a/drivers/video/omap2/dss/hdmi.c
+++ b/drivers/video/omap2/dss/hdmi.c
@@ -1072,6 +1072,12 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev)
1072 if (IS_ERR(hdmi.ip_data.base_wp)) 1072 if (IS_ERR(hdmi.ip_data.base_wp))
1073 return PTR_ERR(hdmi.ip_data.base_wp); 1073 return PTR_ERR(hdmi.ip_data.base_wp);
1074 1074
1075 hdmi.ip_data.irq = platform_get_irq(pdev, 0);
1076 if (hdmi.ip_data.irq < 0) {
1077 DSSERR("platform_get_irq failed\n");
1078 return -ENODEV;
1079 }
1080
1075 r = hdmi_get_clocks(pdev); 1081 r = hdmi_get_clocks(pdev);
1076 if (r) { 1082 if (r) {
1077 DSSERR("can't get clocks\n"); 1083 DSSERR("can't get clocks\n");
diff --git a/drivers/video/omap2/dss/ti_hdmi.h b/drivers/video/omap2/dss/ti_hdmi.h
index 216aa704f9d7..2f7fbc894578 100644
--- a/drivers/video/omap2/dss/ti_hdmi.h
+++ b/drivers/video/omap2/dss/ti_hdmi.h
@@ -155,6 +155,7 @@ struct hdmi_ip_data {
155 unsigned long core_av_offset; 155 unsigned long core_av_offset;
156 unsigned long pll_offset; 156 unsigned long pll_offset;
157 unsigned long phy_offset; 157 unsigned long phy_offset;
158 int irq;
158 const struct ti_hdmi_ip_ops *ops; 159 const struct ti_hdmi_ip_ops *ops;
159 struct hdmi_config cfg; 160 struct hdmi_config cfg;
160 struct hdmi_pll_info pll_data; 161 struct hdmi_pll_info pll_data;
diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
index e18b222ed739..052f2db35d62 100644
--- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
+++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
@@ -38,6 +38,9 @@
38#include "dss.h" 38#include "dss.h"
39#include "dss_features.h" 39#include "dss_features.h"
40 40
41#define HDMI_IRQ_LINK_CONNECT (1 << 25)
42#define HDMI_IRQ_LINK_DISCONNECT (1 << 26)
43
41static inline void hdmi_write_reg(void __iomem *base_addr, 44static inline void hdmi_write_reg(void __iomem *base_addr,
42 const u16 idx, u32 val) 45 const u16 idx, u32 val)
43{ 46{
@@ -233,37 +236,39 @@ void ti_hdmi_4xxx_pll_disable(struct hdmi_ip_data *ip_data)
233 hdmi_set_pll_pwr(ip_data, HDMI_PLLPWRCMD_ALLOFF); 236 hdmi_set_pll_pwr(ip_data, HDMI_PLLPWRCMD_ALLOFF);
234} 237}
235 238
236static int hdmi_check_hpd_state(struct hdmi_ip_data *ip_data) 239static irqreturn_t hdmi_irq_handler(int irq, void *data)
237{ 240{
238 bool hpd; 241 struct hdmi_ip_data *ip_data = data;
239 int r; 242 void __iomem *wp_base = hdmi_wp_base(ip_data);
240 243 u32 irqstatus;
241 mutex_lock(&ip_data->lock); 244
242 245 irqstatus = hdmi_read_reg(wp_base, HDMI_WP_IRQSTATUS);
243 hpd = gpio_get_value(ip_data->hpd_gpio); 246 hdmi_write_reg(wp_base, HDMI_WP_IRQSTATUS, irqstatus);
247 /* flush posted write */
248 hdmi_read_reg(wp_base, HDMI_WP_IRQSTATUS);
249
250 if ((irqstatus & HDMI_IRQ_LINK_CONNECT) &&
251 irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
252 /*
253 * If we get both connect and disconnect interrupts at the same
254 * time, turn off the PHY, clear interrupts, and restart, which
255 * raises connect interrupt if a cable is connected, or nothing
256 * if cable is not connected.
257 */
258 hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF);
244 259
245 if (hpd) 260 hdmi_write_reg(wp_base, HDMI_WP_IRQSTATUS,
246 r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_TXON); 261 HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT);
247 else 262 /* flush posted write */
248 r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_LDOON); 263 hdmi_read_reg(wp_base, HDMI_WP_IRQSTATUS);
249 264
250 if (r) { 265 hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_LDOON);
251 DSSERR("Failed to %s PHY TX power\n", 266 } else if (irqstatus & HDMI_IRQ_LINK_CONNECT) {
252 hpd ? "enable" : "disable"); 267 hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_TXON);
253 goto err; 268 } else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
269 hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_LDOON);
254 } 270 }
255 271
256err:
257 mutex_unlock(&ip_data->lock);
258 return r;
259}
260
261static irqreturn_t hpd_irq_handler(int irq, void *data)
262{
263 struct hdmi_ip_data *ip_data = data;
264
265 hdmi_check_hpd_state(ip_data);
266
267 return IRQ_HANDLED; 272 return IRQ_HANDLED;
268} 273}
269 274
@@ -272,6 +277,12 @@ int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data)
272 u16 r = 0; 277 u16 r = 0;
273 void __iomem *phy_base = hdmi_phy_base(ip_data); 278 void __iomem *phy_base = hdmi_phy_base(ip_data);
274 279
280 hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_IRQENABLE_CLR,
281 0xffffffff);
282
283 hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_IRQSTATUS,
284 HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT);
285
275 r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_LDOON); 286 r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_LDOON);
276 if (r) 287 if (r)
277 return r; 288 return r;
@@ -297,29 +308,23 @@ int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data)
297 /* Write to phy address 3 to change the polarity control */ 308 /* Write to phy address 3 to change the polarity control */
298 REG_FLD_MOD(phy_base, HDMI_TXPHY_PAD_CFG_CTRL, 0x1, 27, 27); 309 REG_FLD_MOD(phy_base, HDMI_TXPHY_PAD_CFG_CTRL, 0x1, 27, 27);
299 310
300 r = request_threaded_irq(gpio_to_irq(ip_data->hpd_gpio), 311 r = request_threaded_irq(ip_data->irq, NULL, hdmi_irq_handler,
301 NULL, hpd_irq_handler, 312 IRQF_ONESHOT, "OMAP HDMI", ip_data);
302 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
303 IRQF_ONESHOT, "hpd", ip_data);
304 if (r) { 313 if (r) {
305 DSSERR("HPD IRQ request failed\n"); 314 DSSERR("HDMI IRQ request failed\n");
306 hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF); 315 hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF);
307 return r; 316 return r;
308 } 317 }
309 318
310 r = hdmi_check_hpd_state(ip_data); 319 hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_IRQENABLE_SET,
311 if (r) { 320 HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT);
312 free_irq(gpio_to_irq(ip_data->hpd_gpio), ip_data);
313 hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF);
314 return r;
315 }
316 321
317 return 0; 322 return 0;
318} 323}
319 324
320void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data) 325void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data)
321{ 326{
322 free_irq(gpio_to_irq(ip_data->hpd_gpio), ip_data); 327 free_irq(ip_data->irq, ip_data);
323 328
324 hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF); 329 hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF);
325} 330}
diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h
index 8366ae19e82e..6ef2f929a76d 100644
--- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h
+++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h
@@ -33,6 +33,7 @@
33#define HDMI_WP_IRQSTATUS 0x28 33#define HDMI_WP_IRQSTATUS 0x28
34#define HDMI_WP_PWR_CTRL 0x40 34#define HDMI_WP_PWR_CTRL 0x40
35#define HDMI_WP_IRQENABLE_SET 0x2C 35#define HDMI_WP_IRQENABLE_SET 0x2C
36#define HDMI_WP_IRQENABLE_CLR 0x30
36#define HDMI_WP_VIDEO_CFG 0x50 37#define HDMI_WP_VIDEO_CFG 0x50
37#define HDMI_WP_VIDEO_SIZE 0x60 38#define HDMI_WP_VIDEO_SIZE 0x60
38#define HDMI_WP_VIDEO_TIMING_H 0x68 39#define HDMI_WP_VIDEO_TIMING_H 0x68