From patchwork Thu Oct 28 12:34:56 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Guennadi Liakhovetski X-Patchwork-Id: 287512 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id o9SCYgAd021656 for ; Thu, 28 Oct 2010 12:34:47 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1758677Ab0J1Meq (ORCPT ); Thu, 28 Oct 2010 08:34:46 -0400 Received: from mailout-de.gmx.net ([213.165.64.23]:36697 "HELO mail.gmx.net" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with SMTP id S1758659Ab0J1Mep (ORCPT ); Thu, 28 Oct 2010 08:34:45 -0400 Received: (qmail invoked by alias); 28 Oct 2010 12:34:43 -0000 Received: from p50898A9A.dip0.t-ipconnect.de (EHLO axis700.grange) [80.137.138.154] by mail.gmx.net (mp071) with SMTP; 28 Oct 2010 14:34:43 +0200 X-Authenticated: #20450766 X-Provags-ID: V01U2FsdGVkX1/46RgdXo34Be3a+mEc2RigzGmh5RJlq7zmnSzUff aykTQFMXn/3Z4e Received: from lyakh (helo=localhost) by axis700.grange with local-esmtp (Exim 4.63) (envelope-from ) id 1PBRh2-0000Kl-EW; Thu, 28 Oct 2010 14:34:56 +0200 Date: Thu, 28 Oct 2010 14:34:56 +0200 (CEST) From: Guennadi Liakhovetski To: linux-fbdev@vger.kernel.org cc: linux-sh@vger.kernel.org, Erik Gilling , Andrew Morton , Dave Airlie , Geert Uytterhoeven Subject: [PATCH 4/4] fbdev: sh_mobile_hdmi: add support for E-EDID parsing In-Reply-To: Message-ID: References: MIME-Version: 1.0 X-Y-GMX-Trusted: 0 Sender: linux-sh-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sh@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter1.kernel.org [140.211.167.41]); Thu, 28 Oct 2010 12:34:47 +0000 (UTC) diff --git a/drivers/video/sh_mobile_hdmi.c b/drivers/video/sh_mobile_hdmi.c index b1c6aed..df6a01b 100644 --- a/drivers/video/sh_mobile_hdmi.c +++ b/drivers/video/sh_mobile_hdmi.c @@ -208,6 +208,9 @@ struct sh_hdmi { void __iomem *base; enum hotplug_state hp_state; /* hot-plug status */ bool preprogrammed_mode; /* use a pre-programmed VIC or the external mode */ + u8 edid_block_addr; + u8 edid_segment_nr; + u8 edid_blocks; struct clk *hdmi_clk; struct device *dev; struct fb_info *info; @@ -677,7 +680,38 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate, printk(KERN_CONT "\n"); #endif - fb_edid_to_monspecs(edid, &hdmi->monspec); + if (!hdmi->edid_blocks) { + fb_edid_to_monspecs(edid, &hdmi->monspec); + hdmi->edid_blocks = edid[126] + 1; + + dev_dbg(hdmi->dev, "%d main modes, %d extension blocks\n", + hdmi->monspec.modedb_len, hdmi->edid_blocks - 1); + } else { + dev_dbg(hdmi->dev, "Extension %u detected, DTD start %u\n", + edid[0], edid[2]); + fb_edid_add_monspecs(edid, &hdmi->monspec); + } + + if (hdmi->edid_blocks > hdmi->edid_segment_nr * 2 + + (hdmi->edid_block_addr >> 7) + 1) { + /* More blocks to read */ + if (hdmi->edid_block_addr) { + hdmi->edid_block_addr = 0; + hdmi->edid_segment_nr++; + } else { + hdmi->edid_block_addr = 0x80; + } + /* Set EDID word address */ + hdmi_write(hdmi, hdmi->edid_block_addr, HDMI_EDID_WORD_ADDRESS); + /* Enable EDID interrupt */ + hdmi_write(hdmi, 0xC6, HDMI_INTERRUPT_MASK_1); + /* Set EDID segment pointer - starts reading EDID */ + hdmi_write(hdmi, hdmi->edid_segment_nr, HDMI_EDID_SEGMENT_POINTER); + return -EAGAIN; + } + + /* All E-EDID blocks ready */ + dev_dbg(hdmi->dev, "%d main and extended modes\n", hdmi->monspec.modedb_len); fb_get_options("sh_mobile_lcdc", &forced); if (forced && *forced) { @@ -816,32 +850,34 @@ static irqreturn_t sh_hdmi_hotplug(int irq, void *dev_id) /* Check, if hot plug & MSENS pin status are both high */ if ((msens & 0xC0) == 0xC0) { /* Display plug in */ + hdmi->edid_segment_nr = 0; + hdmi->edid_block_addr = 0; + hdmi->edid_blocks = 0; hdmi->hp_state = HDMI_HOTPLUG_CONNECTED; /* Set EDID word address */ hdmi_write(hdmi, 0x00, HDMI_EDID_WORD_ADDRESS); - /* Set EDID segment pointer */ - hdmi_write(hdmi, 0x00, HDMI_EDID_SEGMENT_POINTER); /* Enable EDID interrupt */ hdmi_write(hdmi, 0xC6, HDMI_INTERRUPT_MASK_1); + /* Set EDID segment pointer - starts reading EDID */ + hdmi_write(hdmi, 0x00, HDMI_EDID_SEGMENT_POINTER); } else if (!(status1 & 0x80)) { /* Display unplug, beware multiple interrupts */ - if (hdmi->hp_state != HDMI_HOTPLUG_DISCONNECTED) + if (hdmi->hp_state != HDMI_HOTPLUG_DISCONNECTED) { + hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED; schedule_delayed_work(&hdmi->edid_work, 0); - - hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED; + } /* display_off will switch back to mode_a */ } } else if (status1 & 2) { /* EDID error interrupt: retry */ /* Set EDID word address */ - hdmi_write(hdmi, 0x00, HDMI_EDID_WORD_ADDRESS); + hdmi_write(hdmi, hdmi->edid_block_addr, HDMI_EDID_WORD_ADDRESS); /* Set EDID segment pointer */ - hdmi_write(hdmi, 0x00, HDMI_EDID_SEGMENT_POINTER); + hdmi_write(hdmi, hdmi->edid_segment_nr, HDMI_EDID_SEGMENT_POINTER); } else if (status1 & 4) { /* Disable EDID interrupt */ hdmi_write(hdmi, 0xC0, HDMI_INTERRUPT_MASK_1); - hdmi->hp_state = HDMI_HOTPLUG_EDID_DONE; schedule_delayed_work(&hdmi->edid_work, msecs_to_jiffies(10)); } @@ -970,7 +1006,7 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work) mutex_lock(&hdmi->mutex); - if (hdmi->hp_state == HDMI_HOTPLUG_EDID_DONE) { + if (hdmi->hp_state == HDMI_HOTPLUG_CONNECTED) { unsigned long parent_rate = 0, hdmi_rate; /* A device has been plugged in */ @@ -980,6 +1016,8 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work) if (ret < 0) goto out; + hdmi->hp_state = HDMI_HOTPLUG_EDID_DONE; + /* Reconfigure the clock */ ret = sh_hdmi_clk_configure(hdmi, hdmi_rate, parent_rate); if (ret < 0) @@ -1030,7 +1068,7 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work) } out: - if (ret < 0) + if (ret < 0 && ret != -EAGAIN) hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED; mutex_unlock(&hdmi->mutex);