aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Miao <eric.miao@linaro.org>2011-12-30 22:38:31 +0800
committerEric Miao <eric.miao@linaro.org>2011-12-30 22:38:31 +0800
commit2d8bcd8e88e6e5a5fadd3367815c2b2da1029ecb (patch)
tree13aa97923c3fd2c3da82e7ac0667fdcf1f21c82f
parentdd6acff5a2c1c55ef1f1fb18e4d1b05215bf3ad3 (diff)
parentaa87db76e6334652c529839df7eb6659e41c156a (diff)
Merge branch 'topic/lt-3.2-imx6-display' into lt-3.2-imx6
* topic/lt-3.2-imx6-display: input: add device tree support for eGalax touch input: add support for egalax touch screen controller imx6q-sabrelite: switch framebuffer order of HDMI and LVDS mxc: hdmi: fix potention deadlock issue ARM: imx6q-sabrelite: set DDC i2c rate to 100k ENGR00170800 mxc hdmi add more video mode to default modelist ENGR00170534 mxc hdmi: hot-plug detect state notify and recording ENGR00169872-2 rework hdmi initialization and hotplug sequence ENGR00169872-1 rework hdmi initialization and hotplug sequence ENGR00170168-2 mxc_vout: add non FB_VMODE_YWRAP support ENGR00170168-1 ipuv3 fb: add non FB_VMODE_YWRAP support ENGR00169657 mxc_edid: no aspect vmode setting for detailed timing block ENGR00163669-1 mxc fb: remove FB_EVENT_PREMODE_CHANGE for mxc fb drivers ENGR00169509-2 ipuv3 fb: change wait for vsync ioctl irq from eof to nfack ENGR00169509-1 ipuv3 fb: change wait for vsync ioctl irq from eof to nfack ENGR00163616 [mx6q]hdmi will hang in daily build image
-rw-r--r--arch/arm/boot/dts/imx6q-sabrelite.dts27
-rw-r--r--arch/arm/mach-imx/mach-imx6q.c3
-rw-r--r--arch/arm/plat-mxc/include/mach/ipu-v3.h4
-rw-r--r--arch/arm/plat-mxc/include/mach/mxc_edid.h1
-rw-r--r--arch/arm/plat-mxc/include/mach/mxc_hdmi.h85
-rw-r--r--drivers/input/touchscreen/Kconfig10
-rw-r--r--drivers/input/touchscreen/Makefile1
-rw-r--r--drivers/input/touchscreen/egalax_ts.c377
-rw-r--r--drivers/media/video/mxc/output/mxc_vout.c2
-rw-r--r--drivers/mfd/mxc-hdmi-core.c109
-rw-r--r--drivers/video/fbmem.c11
-rw-r--r--drivers/video/mxc/ldb.c98
-rw-r--r--drivers/video/mxc/mxc_dispdrv.c94
-rw-r--r--drivers/video/mxc/mxc_dispdrv.h34
-rw-r--r--drivers/video/mxc/mxc_dvi.c9
-rw-r--r--drivers/video/mxc/mxc_edid.c33
-rw-r--r--drivers/video/mxc/mxc_ipuv3_fb.c160
-rw-r--r--drivers/video/mxc/mxc_lcdif.c9
-rw-r--r--drivers/video/mxc/mxcfb_sii902x.c9
-rw-r--r--drivers/video/mxc/tve.c37
-rw-r--r--drivers/video/mxc_hdmi.c1128
-rw-r--r--include/linux/fb.h2
-rw-r--r--include/linux/mfd/mxc-hdmi-core.h7
23 files changed, 1641 insertions, 609 deletions
diff --git a/arch/arm/boot/dts/imx6q-sabrelite.dts b/arch/arm/boot/dts/imx6q-sabrelite.dts
index d9ea379c824..fe2d320275a 100644
--- a/arch/arm/boot/dts/imx6q-sabrelite.dts
+++ b/arch/arm/boot/dts/imx6q-sabrelite.dts
@@ -60,13 +60,26 @@
i2c@021a4000 { /* I2C2 */
status = "okay";
- clock-frequency = <400000>;
+ clock-frequency = <100000>;
ddc: ddc@50 {
compatible = "fsl,imx6q-hdmi-ddc";
reg = <0x50>;
};
};
+
+ i2c@021a8000 { /* I2C3 */
+ status = "okay";
+ clock-frequency = <400000>;
+
+ egalax@04 {
+ compatible = "eeti,egalax";
+ reg = <0x04>;
+ interrupt-parent = <&gpio1>;
+ interrupts = <9>;
+ interrupt-gpio = <&gpio1 9 0>;
+ };
+ };
};
hdmi@0x00120000 { /* HDMI */
@@ -107,18 +120,18 @@
disp1: fb@0 {
compatible = "fsl,mxcfb-ipuv3";
- disp_dev = "hdmi";
- interface_pix_fmt = "RGB24";
- mode_str = "1280x720M@60";
+ disp_dev = "ldb";
+ interface_pix_fmt = "RGB666";
+ mode_str = "LDB-XGA";
internal_clk = "false";
reg = <0x00000000 0x0>; /* reserve fb mem */
};
disp2: fb@1 {
compatible = "fsl,mxcfb-ipuv3";
- disp_dev = "ldb";
- interface_pix_fmt = "RGB666";
- mode_str = "LDB-XGA";
+ disp_dev = "hdmi";
+ interface_pix_fmt = "RGB24";
+ mode_str = "1280x720M@60";
internal_clk = "false";
reg = <0x00000001 0x0>; /* reserve fb mem */
};
diff --git a/arch/arm/mach-imx/mach-imx6q.c b/arch/arm/mach-imx/mach-imx6q.c
index f5b31867953..3f46730ac06 100644
--- a/arch/arm/mach-imx/mach-imx6q.c
+++ b/arch/arm/mach-imx/mach-imx6q.c
@@ -67,6 +67,9 @@ static iomux_v3_cfg_t imx6q_sabrelite_pads[] = {
/* I2C2 */
MX6Q_PAD_KEY_COL3__I2C2_SCL,
MX6Q_PAD_KEY_ROW3__I2C2_SDA,
+ /* I2C3 */
+ MX6Q_PAD_GPIO_5__I2C3_SCL,
+ MX6Q_PAD_GPIO_16__I2C3_SDA,
/* GPIO */
MX6Q_PAD_NANDF_D0__GPIO_2_0,
MX6Q_PAD_EIM_D23__GPIO_3_23,
diff --git a/arch/arm/plat-mxc/include/mach/ipu-v3.h b/arch/arm/plat-mxc/include/mach/ipu-v3.h
index bccb8811673..0405f2f9ed0 100644
--- a/arch/arm/plat-mxc/include/mach/ipu-v3.h
+++ b/arch/arm/plat-mxc/include/mach/ipu-v3.h
@@ -326,6 +326,10 @@ enum ipu_irq_line {
IPU_IRQ_BG_ALPHA_SYNC_EOF = 51,
IPU_IRQ_BG_ALPHA_ASYNC_EOF = 52,
+ IPU_IRQ_BG_SYNC_NFACK = 64 + 23,
+ IPU_IRQ_FG_SYNC_NFACK = 64 + 27,
+ IPU_IRQ_DC_SYNC_NFACK = 64 + 28,
+
IPU_IRQ_DP_SF_START = 448 + 2,
IPU_IRQ_DP_SF_END = 448 + 3,
IPU_IRQ_BG_SF_END = IPU_IRQ_DP_SF_END,
diff --git a/arch/arm/plat-mxc/include/mach/mxc_edid.h b/arch/arm/plat-mxc/include/mach/mxc_edid.h
index 59688f657a2..4cbbb78ad70 100644
--- a/arch/arm/plat-mxc/include/mach/mxc_edid.h
+++ b/arch/arm/plat-mxc/include/mach/mxc_edid.h
@@ -28,6 +28,7 @@
#define FB_VMODE_ASPECT_4_3 0x10
#define FB_VMODE_ASPECT_16_9 0x20
+#define FB_VMODE_ASPECT_MASK (FB_VMODE_ASPECT_4_3 | FB_VMODE_ASPECT_16_9)
struct mxc_edid_cfg {
bool cea_underscan;
diff --git a/arch/arm/plat-mxc/include/mach/mxc_hdmi.h b/arch/arm/plat-mxc/include/mach/mxc_hdmi.h
index a7f22de9b40..56729b22958 100644
--- a/arch/arm/plat-mxc/include/mach/mxc_hdmi.h
+++ b/arch/arm/plat-mxc/include/mach/mxc_hdmi.h
@@ -377,6 +377,7 @@
#define HDMI_AUD_CTS2 0x3204
#define HDMI_AUD_CTS3 0x3205
#define HDMI_AUD_INPUTCLKFS 0x3206
+#define HDMI_AUD_SPDIFINT 0x3302
#define HDMI_AUD_CONF0_HBR 0x3400
#define HDMI_AUD_HBR_STATUS 0x3401
#define HDMI_AUD_HBR_INT 0x3402
@@ -564,6 +565,16 @@
* Register field definitions
*/
enum {
+/* IH_FC_INT2 field values */
+ HDMI_IH_FC_INT2_OVERFLOW_MASK = 0x03,
+ HDMI_IH_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02,
+ HDMI_IH_FC_INT2_HIGH_PRIORITY_OVERFLOW = 0x01,
+
+/* IH_FC_STAT2 field values */
+ HDMI_IH_FC_STAT2_OVERFLOW_MASK = 0x03,
+ HDMI_IH_FC_STAT2_LOW_PRIORITY_OVERFLOW = 0x02,
+ HDMI_IH_FC_STAT2_HIGH_PRIORITY_OVERFLOW = 0x01,
+
/* IH_PHY_STAT0 field values */
HDMI_IH_PHY_STAT0_RX_SENSE3 = 0x20,
HDMI_IH_PHY_STAT0_RX_SENSE2 = 0x10,
@@ -584,6 +595,11 @@ enum {
HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = 0x02,
HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = 0x01,
+/* IH_MUTE_FC_STAT2 field values */
+ HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK = 0x03,
+ HDMI_IH_MUTE_FC_STAT2_LOW_PRIORITY_OVERFLOW = 0x02,
+ HDMI_IH_MUTE_FC_STAT2_HIGH_PRIORITY_OVERFLOW = 0x01,
+
/* IH_MUTE_AHBDMAAUD_STAT0 field values */
HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = 0x20,
HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = 0x10,
@@ -750,6 +766,21 @@ enum {
HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_LAYOUT1 = 0x1,
HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_LAYOUT0 = 0x0,
+/* FC_STAT2 field values */
+ HDMI_FC_STAT2_OVERFLOW_MASK = 0x03,
+ HDMI_FC_STAT2_LOW_PRIORITY_OVERFLOW = 0x02,
+ HDMI_FC_STAT2_HIGH_PRIORITY_OVERFLOW = 0x01,
+
+/* FC_INT2 field values */
+ HDMI_FC_INT2_OVERFLOW_MASK = 0x03,
+ HDMI_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02,
+ HDMI_FC_INT2_HIGH_PRIORITY_OVERFLOW = 0x01,
+
+/* FC_MASK2 field values */
+ HDMI_FC_MASK2_OVERFLOW_MASK = 0x03,
+ HDMI_FC_MASK2_LOW_PRIORITY_OVERFLOW = 0x02,
+ HDMI_FC_MASK2_HIGH_PRIORITY_OVERFLOW = 0x01,
+
/* FC_PRCONF field values */
HDMI_FC_PRCONF_INCOMING_PR_FACTOR_MASK = 0xF0,
HDMI_FC_PRCONF_INCOMING_PR_FACTOR_OFFSET = 4,
@@ -822,24 +853,19 @@ enum {
HDMI_FC_DBGFORCE_FORCEVIDEO = 0x1,
/* PHY_CONF0 field values */
- HDMI_PHY_CONF0_PDZ = 0x80,
HDMI_PHY_CONF0_PDZ_MASK = 0x80,
HDMI_PHY_CONF0_PDZ_OFFSET = 7,
- HDMI_PHY_CONF0_ENTMDS = 0x40,
HDMI_PHY_CONF0_ENTMDS_MASK = 0x40,
HDMI_PHY_CONF0_ENTMDS_OFFSET = 6,
HDMI_PHY_CONF0_SPARECTRL = 0x20,
HDMI_PHY_CONF0_GEN2_PDDQ_MASK = 0x10,
- HDMI_PHY_CONF0_GEN2_PDDQ_ENABLE = 0x10,
- HDMI_PHY_CONF0_GEN2_PDDQ_DISABLE = 0x00,
+ HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET = 4,
HDMI_PHY_CONF0_GEN2_TXPWRON_MASK = 0x8,
- HDMI_PHY_CONF0_GEN2_TXPWRON_POWER_ON = 0x8,
- HDMI_PHY_CONF0_GEN2_TXPWRON_POWER_OFF = 0x0,
- HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE = 0x4,
- HDMI_PHY_CONF0_SELDATAENPOL = 0x2,
+ HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET = 3,
+ HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE_MASK = 0x4,
+ HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE_OFFSET = 2,
HDMI_PHY_CONF0_SELDATAENPOL_MASK = 0x2,
HDMI_PHY_CONF0_SELDATAENPOL_OFFSET = 1,
- HDMI_PHY_CONF0_SELDIPIF = 0x1,
HDMI_PHY_CONF0_SELDIPIF_MASK = 0x1,
HDMI_PHY_CONF0_SELDIPIF_OFFSET = 0,
@@ -867,6 +893,16 @@ enum {
HDMI_PHY_I2CM_OPERATION_ADDR_WRITE = 0x10,
HDMI_PHY_I2CM_OPERATION_ADDR_READ = 0x1,
+/* HDMI_PHY_I2CM_INT_ADDR */
+ HDMI_PHY_I2CM_INT_ADDR_DONE_POL = 0x08,
+ HDMI_PHY_I2CM_INT_ADDR_DONE_MASK = 0x04,
+
+/* HDMI_PHY_I2CM_CTLINT_ADDR */
+ HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL = 0x80,
+ HDMI_PHY_I2CM_CTLINT_ADDR_NAC_MASK = 0x40,
+ HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL = 0x08,
+ HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_MASK = 0x04,
+
/* AUD_CTS3 field values */
HDMI_AUD_CTS3_N_SHIFT_OFFSET = 5,
HDMI_AUD_CTS3_N_SHIFT_MASK = 0xe0,
@@ -916,27 +952,16 @@ enum {
HDMI_AHB_DMA_BUFFSTAT_EMPTY = 0x01,
/* MC_CLKDIS field values */
- HDMI_MC_CLKDIS_HDCPCLK_DISABLE_MASK = 0x40,
- HDMI_MC_CLKDIS_HDCPCLK_DISABLE_DISABLE = 0x40,
- HDMI_MC_CLKDIS_HDCPCLK_DISABLE_ENABLE = 0x00,
- HDMI_MC_CLKDIS_CECCLK_DISABLE_MASK = 0x20,
- HDMI_MC_CLKDIS_CECCLK_DISABLE_DISABLE = 0x20,
- HDMI_MC_CLKDIS_CECCLK_DISABLE_ENABLE = 0x00,
- HDMI_MC_CLKDIS_CSCCLK_DISABLE_MASK = 0x10,
- HDMI_MC_CLKDIS_CSCCLK_DISABLE_DISABLE = 0x10,
- HDMI_MC_CLKDIS_CSCCLK_DISABLE_ENABLE = 0x00,
- HDMI_MC_CLKDIS_AUDCLK_DISABLE_MASK = 0x8,
- HDMI_MC_CLKDIS_AUDCLK_DISABLE_DISABLE = 0x8,
- HDMI_MC_CLKDIS_AUDCLK_DISABLE_ENABLE = 0x0,
- HDMI_MC_CLKDIS_PREPCLK_DISABLE_MASK = 0x4,
- HDMI_MC_CLKDIS_PREPCLK_DISABLE_DISABLE = 0x4,
- HDMI_MC_CLKDIS_PREPCLK_DISABLE_ENABLE = 0x0,
- HDMI_MC_CLKDIS_TMDSCLK_DISABLE_MASK = 0x2,
- HDMI_MC_CLKDIS_TMDSCLK_DISABLE_DISABLE = 0x2,
- HDMI_MC_CLKDIS_TMDSCLK_DISABLE_ENABLE = 0x0,
- HDMI_MC_CLKDIS_PIXELCLK_DISABLE_MASK = 0x1,
- HDMI_MC_CLKDIS_PIXELCLK_DISABLE_DISABLE = 0x1,
- HDMI_MC_CLKDIS_PIXELCLK_DISABLE_ENABLE = 0x0,
+ HDMI_MC_CLKDIS_HDCPCLK_DISABLE = 0x40,
+ HDMI_MC_CLKDIS_CECCLK_DISABLE = 0x20,
+ HDMI_MC_CLKDIS_CSCCLK_DISABLE = 0x10,
+ HDMI_MC_CLKDIS_AUDCLK_DISABLE = 0x8,
+ HDMI_MC_CLKDIS_PREPCLK_DISABLE = 0x4,
+ HDMI_MC_CLKDIS_TMDSCLK_DISABLE = 0x2,
+ HDMI_MC_CLKDIS_PIXELCLK_DISABLE = 0x1,
+
+/* MC_SWRSTZ field values */
+ HDMI_MC_SWRSTZ_TMDSSWRST_REQ = 0x02,
/* MC_FLOWCTRL field values */
HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_MASK = 0x1,
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 0a3433db0c8..12314e2c023 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -177,6 +177,16 @@ config TOUCHSCREEN_EETI
To compile this driver as a module, choose M here: the
module will be called eeti_ts.
+config TOUCHSCREEN_EGALAX
+ tristate "EETI eGalax multi-touchscreen panel support"
+ depends on I2C
+ help
+ Say Y here to enable support for I2C connected EETI
+ eGalax multiple touch panels.
+
+ To compile this driver as a module, choose M here: the
+ module will be called egalax_ts.
+
config TOUCHSCREEN_FUJITSU
tristate "Fujitsu serial touchscreen"
select SERIO
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index dbc9563dadb..71e19bfebfe 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o
obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o
obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o
obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o
+obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o
obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o
obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o
obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o
diff --git a/drivers/input/touchscreen/egalax_ts.c b/drivers/input/touchscreen/egalax_ts.c
new file mode 100644
index 00000000000..58184dad7a2
--- /dev/null
+++ b/drivers/input/touchscreen/egalax_ts.c
@@ -0,0 +1,377 @@
+/*
+ * Driver for EETI eGalax Multiple Touch Controller
+ *
+ * Copyright (C) 2011 Freescale Semiconductor, Inc.
+ *
+ * based on max11801_ts.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/* EETI eGalax serial touch screen controller is a I2C based multiple
+ * touch screen controller, it can supports 5 pointer multiple
+ * touch. */
+
+/* TODO:
+ - auto idle mode support
+ - early suspend support for android
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+
+#define REPORT_MODE_SINGLE 0x1
+#define REPORT_MODE_VENDOR 0x3
+#define REPORT_MODE_MTTOUCH 0x4
+
+#define MAX_SUPPORT_POINTS 5
+
+#define EVENT_MODE 0
+#define EVENT_STATUS 1
+#define EVENT_VALID_OFFSET 7
+#define EVENT_VAILD_MASK (0x1 << EVENT_VALID_OFFSET)
+#define EVENT_ID_OFFSET 2
+#define EVENT_ID_MASK (0xf << EVENT_ID_OFFSET)
+#define EVENT_IN_RANGE (0x1 << 1)
+#define EVENT_DOWN_UP (0X1 << 0)
+
+#define MAX_I2C_DATA_LEN 10
+
+struct egalax_pointer {
+ bool valid;
+ bool status;
+ u16 x;
+ u16 y;
+};
+
+struct egalax_ts {
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ struct egalax_pointer events[MAX_SUPPORT_POINTS];
+ int gpio_irq;
+};
+
+static irqreturn_t egalax_ts_interrupt(int irq, void *dev_id)
+{
+ struct egalax_ts *data = dev_id;
+ struct input_dev *input_dev = data->input_dev;
+ struct i2c_client *client = data->client;
+ struct egalax_pointer *events = data->events;
+ u8 buf[MAX_I2C_DATA_LEN];
+ int i, id, ret, x, y;
+ bool down, valid;
+ u8 state;
+
+retry:
+ ret = i2c_master_recv(client, buf, MAX_I2C_DATA_LEN);
+ if (ret == -EAGAIN)
+ goto retry;
+
+ if (ret < 0)
+ return IRQ_HANDLED;
+
+ dev_dbg(&client->dev, "recv ret:%d", ret);
+ for (i = 0; i < MAX_I2C_DATA_LEN; i++)
+ dev_dbg(&client->dev, " %x ", buf[i]);
+
+ if (buf[0] != REPORT_MODE_VENDOR
+ && buf[0] != REPORT_MODE_SINGLE
+ && buf[0] != REPORT_MODE_MTTOUCH) {
+ /* invalid point */
+ return IRQ_HANDLED;
+ }
+
+ if (buf[0] == REPORT_MODE_VENDOR) {
+ dev_dbg(&client->dev, "vendor message, ignored\n");
+ return IRQ_HANDLED;
+ }
+
+ state = buf[1];
+ x = (buf[3] << 8) | buf[2];
+ y = (buf[5] << 8) | buf[4];
+
+ /* Currently, the panel Freescale using on SMD board _NOT_
+ * support single pointer mode. All event are going to
+ * multiple pointer mode. Add single pointer mode according
+ * to EETI eGalax I2C programming manual.
+ */
+ if (buf[0] == REPORT_MODE_SINGLE) {
+ input_report_abs(input_dev, ABS_X, x);
+ input_report_abs(input_dev, ABS_Y, y);
+ input_report_key(input_dev, BTN_TOUCH, !!state);
+ input_sync(input_dev);
+ return IRQ_HANDLED;
+ }
+
+ /* deal with multiple touch */
+ valid = state & EVENT_VAILD_MASK;
+ id = (state & EVENT_ID_MASK) >> EVENT_ID_OFFSET;
+ down = state & EVENT_DOWN_UP;
+
+ if (!valid || id > MAX_SUPPORT_POINTS) {
+ dev_dbg(&client->dev, "point invalid\n");
+ return IRQ_HANDLED;
+ }
+
+ if (down) {
+ /* should also report old pointers */
+ events[id].valid = valid;
+ events[id].status = down;
+ events[id].x = x;
+ events[id].y = y;
+
+#ifdef FORCE_SINGLE_POINTER_SUPPORT
+ input_report_abs(input_dev, ABS_X, x);
+ input_report_abs(input_dev, ABS_Y, y);
+ input_event(data->input_dev, EV_KEY, BTN_TOUCH, 1);
+ input_report_abs(input_dev, ABS_PRESSURE, 1);
+#else
+ for (i = 0; i < MAX_SUPPORT_POINTS; i++) {
+ if (!events[i].valid)
+ continue;
+ dev_dbg(&client->dev, "report id:%d valid:%d x:%d y:%d",
+ i, valid, x, y);
+
+ input_report_abs(input_dev,
+ ABS_MT_TRACKING_ID, i);
+ input_report_abs(input_dev,
+ ABS_MT_TOUCH_MAJOR, 1);
+ input_report_abs(input_dev,
+ ABS_MT_POSITION_X, events[i].x);
+ input_report_abs(input_dev,
+ ABS_MT_POSITION_Y, events[i].y);
+ input_mt_sync(input_dev);
+ }
+#endif
+ } else {
+ dev_dbg(&client->dev, "release id:%d\n", id);
+ events[id].valid = 0;
+ events[id].status = 0;
+#ifdef FORCE_SINGLE_POINTER_SUPPORT
+ input_report_key(input_dev, BTN_TOUCH, 0);
+ input_report_abs(input_dev, ABS_PRESSURE, 0);
+#else
+ input_report_abs(input_dev, ABS_MT_TRACKING_ID, id);
+ input_event(input_dev, EV_ABS, ABS_MT_TOUCH_MAJOR, 0);
+ input_mt_sync(input_dev);
+#endif
+ }
+
+ input_sync(input_dev);
+ return IRQ_HANDLED;
+}
+
+static void egalax_wake_up_device(struct i2c_client *client)
+{
+ struct egalax_ts *data = i2c_get_clientdata(client);
+ int ret, gpio_irq = data->gpio_irq;
+
+ if (!gpio_is_valid(gpio_irq))
+ return;
+
+ ret = gpio_request_one(gpio_irq, GPIOF_OUT_INIT_HIGH, "egalax_irq");
+ if (ret < 0) {
+ dev_err(&client->dev, "request gpio failed:%d\n", ret);
+ return;
+ }
+ /* wake up controller via an falling edge on IRQ. */
+ gpio_set_value(gpio_irq, 0);
+ /* controller should be waken up, return irq. */
+ gpio_direction_input(gpio_irq);
+ gpio_free(gpio_irq);
+}
+
+static int egalax_7200_firmware_version(struct i2c_client *client)
+{
+ static const u8 cmd[MAX_I2C_DATA_LEN] = { 0x03, 0x03, 0xa, 0x01, 0x41 };
+ int ret;
+ ret = i2c_master_send(client, cmd, MAX_I2C_DATA_LEN);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static int __devinit egalax_ts_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device_node *n = client->dev.of_node;
+ struct egalax_ts *data;
+ struct input_dev *input_dev;
+ int ret;
+
+ data = kzalloc(sizeof(struct egalax_ts), GFP_KERNEL);
+ if (!data) {
+ dev_err(&client->dev, "Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ dev_err(&client->dev, "Failed to allocate memory\n");
+ ret = -ENOMEM;
+ goto err_free_data;
+ }
+
+ data->client = client;
+ data->input_dev = input_dev;
+ data->gpio_irq = of_get_named_gpio(n, "interrupt-gpio", 0);
+ if (!gpio_is_valid(data->gpio_irq))
+ dev_warn(&client->dev, "invalid interrupt GPIO\n");
+
+ i2c_set_clientdata(client, data);
+
+ egalax_wake_up_device(client);
+ ret = egalax_7200_firmware_version(client);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "egalax_ts: failed to read firmware version\n");
+ ret = -EIO;
+ goto err_free_dev;
+ }
+
+ input_dev->name = "eGalax Touch Screen";
+ input_dev->phys = "I2C",
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->id.vendor = 0x0EEF;
+ input_dev->id.product = 0x0020;
+ input_dev->id.version = 0x0001;
+ input_dev->dev.parent = &client->dev;
+
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+ __set_bit(ABS_X, input_dev->absbit);
+ __set_bit(ABS_Y, input_dev->absbit);
+ __set_bit(ABS_PRESSURE, input_dev->absbit);
+ input_set_abs_params(input_dev, ABS_X, 0, 32767, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, 32767, 0, 0);
+
+#ifndef FORCE_SINGLE_POINTER_SUPPORT
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, 32767, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, 32767, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0,
+ MAX_SUPPORT_POINTS, 0, 0);
+#endif
+ input_set_drvdata(input_dev, data);
+
+ ret = request_threaded_irq(client->irq, NULL, egalax_ts_interrupt,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "egalax_ts", data);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to register interrupt\n");
+ goto err_free_dev;
+ }
+
+ return input_register_device(data->input_dev);
+ if (ret < 0)
+ goto err_free_irq;
+ return 0;
+
+err_free_irq:
+ free_irq(client->irq, data);
+err_free_dev:
+ input_free_device(input_dev);
+err_free_data:
+ kfree(data);
+
+ return ret;
+}
+
+static __devexit int egalax_ts_remove(struct i2c_client *client)
+{
+ struct egalax_ts *data = i2c_get_clientdata(client);
+
+ free_irq(client->irq, data);
+ input_unregister_device(data->input_dev);
+ input_free_device(data->input_dev);
+ kfree(data);
+
+ return 0;
+}
+
+static const struct i2c_device_id egalax_ts_id[] = {
+ {"egalax_ts", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, egalax_ts_id);
+
+#ifdef CONFIG_PM_SLEEP
+static int egalax_ts_suspend(struct device *dev)
+{
+ int ret;
+ u8 suspend_cmd[MAX_I2C_DATA_LEN] = {0x3, 0x6, 0xa, 0x3, 0x36,
+ 0x3f, 0x2, 0, 0, 0};
+ struct i2c_client *client = to_i2c_client(dev);
+ ret = i2c_master_send(client, suspend_cmd,
+ MAX_I2C_DATA_LEN);
+ return ret > 0 ? 0 : ret;
+}
+
+static int egalax_ts_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ egalax_wake_up_device(client);
+ return 0;
+}
+#endif
+
+static struct of_device_id egalax_dt_ids[] = {
+ { .compatible = "eeti,egalax" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_uart_dt_ids);
+
+static SIMPLE_DEV_PM_OPS(egalax_ts_pm_ops, egalax_ts_suspend, egalax_ts_resume);
+
+static struct i2c_driver egalax_ts_driver = {
+ .driver = {
+ .name = "egalax_ts",
+ .pm = &egalax_ts_pm_ops,
+ .of_match_table = egalax_dt_ids,
+ },
+ .id_table = egalax_ts_id,
+ .probe = egalax_ts_probe,
+ .remove = __devexit_p(egalax_ts_remove),
+};
+
+static int __init egalax_ts_init(void)
+{
+ return i2c_add_driver(&egalax_ts_driver);
+}
+
+static void __exit egalax_ts_exit(void)
+{
+ i2c_del_driver(&egalax_ts_driver);
+}
+
+module_init(egalax_ts_init);
+module_exit(egalax_ts_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Touchscreen driver for EETI eGalax touch controller");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/output/mxc_vout.c b/drivers/media/video/mxc/output/mxc_vout.c
index 8092e670114..319aea86caa 100644
--- a/drivers/media/video/mxc/output/mxc_vout.c
+++ b/drivers/media/video/mxc/output/mxc_vout.c
@@ -1328,10 +1328,12 @@ static int config_disp_output(struct mxc_vout_output *vout)
else
var.yres_virtual = var.yres;
var.rotate = vout->task.output.rotate;
+ var.vmode |= FB_VMODE_YWRAP;
} else {
fb_num = FB_BUFS;
var.xres_virtual = var.xres;
var.yres_virtual = fb_num * var.yres;
+ var.vmode &= ~FB_VMODE_YWRAP;
}
var.bits_per_pixel = fmt_to_bpp(vout->task.output.format);
var.nonstd = vout->task.output.format;
diff --git a/drivers/mfd/mxc-hdmi-core.c b/drivers/mfd/mxc-hdmi-core.c
index a211cd0f087..24afc502cea 100644
--- a/drivers/mfd/mxc-hdmi-core.c
+++ b/drivers/mfd/mxc-hdmi-core.c
@@ -74,10 +74,40 @@ u8 hdmi_readb(unsigned int reg)
return value;
}
+#ifdef DEBUG
+static bool overflow_lo;
+static bool overflow_hi;
+
+bool hdmi_check_overflow(void)
+{
+ u8 val, lo, hi;
+
+ val = hdmi_readb(HDMI_IH_FC_STAT2);
+ lo = (val & HDMI_IH_FC_STAT2_LOW_PRIORITY_OVERFLOW) != 0;
+ hi = (val & HDMI_IH_FC_STAT2_HIGH_PRIORITY_OVERFLOW) != 0;
+
+ if ((lo != overflow_lo) || (hi != overflow_hi)) {
+ pr_debug("%s LowPriority=%d HighPriority=%d <=======================\n",
+ __func__, lo, hi);
+ overflow_lo = lo;
+ overflow_hi = hi;
+ return true;
+ }
+ return false;
+}
+#else
+bool hdmi_check_overflow(void)
+{
+ return false;
+}
+#endif
+
void hdmi_writeb(u8 value, unsigned int reg)
{
+ hdmi_check_overflow();
pr_debug("hdmi wr: 0x%04x = 0x%02x\n", reg, value);
__raw_writeb(value, hdmi_base + reg);
+ hdmi_check_overflow();
}
void hdmi_mask_writeb(u8 data, unsigned int reg, u8 shift, u8 mask)
@@ -172,6 +202,23 @@ static void initialize_hdmi_ih_mutes(void)
hdmi_writeb(ih_mute, HDMI_IH_MUTE);
+ /* by default mask all interrupts */
+ hdmi_writeb(0xff, HDMI_VP_MASK);
+ hdmi_writeb(0xff, HDMI_FC_MASK0);
+ hdmi_writeb(0xff, HDMI_FC_MASK1);
+ hdmi_writeb(0xff, HDMI_FC_MASK2);
+ hdmi_writeb(0xff, HDMI_PHY_MASK0);
+ hdmi_writeb(0xff, HDMI_PHY_I2CM_INT_ADDR);
+ hdmi_writeb(0xff, HDMI_PHY_I2CM_CTLINT_ADDR);
+ hdmi_writeb(0xff, HDMI_AUD_INT);
+ hdmi_writeb(0xff, HDMI_AUD_SPDIFINT);
+ hdmi_writeb(0xff, HDMI_AUD_HBR_MASK);
+ hdmi_writeb(0xff, HDMI_GP_MASK);
+ hdmi_writeb(0xff, HDMI_A_APIINTMSK);
+ hdmi_writeb(0xff, HDMI_CEC_MASK);
+ hdmi_writeb(0xff, HDMI_I2CM_INT);
+ hdmi_writeb(0xff, HDMI_I2CM_CTLINT);
+
/* Disable interrupts in the IH_MUTE_* registers */
hdmi_writeb(0xff, HDMI_IH_MUTE_FC_STAT0);
hdmi_writeb(0xff, HDMI_IH_MUTE_FC_STAT1);
@@ -192,13 +239,27 @@ static void initialize_hdmi_ih_mutes(void)
static void hdmi_set_clock_regenerator_n(unsigned int value)
{
+ u8 val;
+
hdmi_writeb(value & 0xff, HDMI_AUD_N1);
hdmi_writeb((value >> 8) & 0xff, HDMI_AUD_N2);
- hdmi_writeb((value >> 16) & 0xff, HDMI_AUD_N3);
+ hdmi_writeb((value >> 16) & 0x0f, HDMI_AUD_N3);
+
+ /* nshift factor = 0 */
+ val = hdmi_readb(HDMI_AUD_CTS3);
+ val &= ~HDMI_AUD_CTS3_N_SHIFT_MASK;
+ hdmi_writeb(val, HDMI_AUD_CTS3);
}
static void hdmi_set_clock_regenerator_cts(unsigned int cts)
{
+ u8 val;
+
+ /* Must be set/cleared first */
+ val = hdmi_readb(HDMI_AUD_CTS3);
+ val &= ~HDMI_AUD_CTS3_CTS_MANUAL;
+ hdmi_writeb(val, HDMI_AUD_CTS3);
+
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) |
@@ -341,6 +402,7 @@ static unsigned int hdmi_compute_cts(unsigned int freq, unsigned long pixel_clk,
static void hdmi_get_pixel_clk(void)
{
struct ipu_soc *ipu;
+ unsigned long rate;
if (pixel_clk == NULL) {
ipu = ipu_get_soc(mxc_hdmi_ipu_id);
@@ -351,28 +413,20 @@ static void hdmi_get_pixel_clk(void)
}
}
- pixel_clk_rate = clk_get_rate(pixel_clk);
+ rate = clk_get_rate(pixel_clk);
+ if (rate != 0)
+ pixel_clk_rate = rate;
}
-/*
- * input: audio sample rate and video pixel rate
- * output: N and cts written to the HDMI regs.
- */
-void hdmi_set_clk_regenerator(void)
+static 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",
+ pr_debug("%s: pixel clock not supported: %d\n",
__func__, (int)pixel_clk_rate);
return;
}
@@ -380,6 +434,10 @@ void hdmi_set_clk_regenerator(void)
clk_enable(isfr_clk);
clk_enable(iahb_clk);
+ pr_debug("%s: samplerate=%d ratio=%d pixelclk=%d N=%d cts=%d\n",
+ __func__, sample_rate, hdmi_ratio, (int)pixel_clk_rate,
+ clk_n, clk_cts);
+
hdmi_set_clock_regenerator_n(clk_n);
hdmi_set_clock_regenerator_cts(clk_cts);
@@ -387,6 +445,23 @@ void hdmi_set_clk_regenerator(void)
clk_disable(isfr_clk);
}
+/* Need to run this before phy is enabled the first time to prevent
+ * overflow condition in HDMI_IH_FC_STAT2 */
+void hdmi_init_clk_regenerator(void)
+{
+ if (pixel_clk_rate == 0) {
+ pixel_clk_rate = 74250000;
+ hdmi_set_clk_regenerator();
+ }
+}
+
+void hdmi_clk_regenerator_update_pixel_clock(void)
+{
+ /* Get pixel clock from ipu */
+ hdmi_get_pixel_clk();
+ hdmi_set_clk_regenerator();
+}
+
void hdmi_set_sample_rate(unsigned int rate)
{
sample_rate = rate;
@@ -428,6 +503,10 @@ static int mxc_hdmi_core_probe(struct platform_device *pdev)
int ret = 0;
struct platform_device_info pdevinfo_hdmi_v;
+#ifdef DEBUG
+ overflow_lo = false;
+ overflow_hi = false;
+#endif
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENOENT;
@@ -441,7 +520,7 @@ static int mxc_hdmi_core_probe(struct platform_device *pdev)
pixel_clk = NULL;
sample_rate = 48000;
- pixel_clk_rate = 74250000;
+ pixel_clk_rate = 0;
hdmi_ratio = 100;
irq_enable_cnt = 0;
diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c
index a65f5b57391..ad936295d8f 100644
--- a/drivers/video/fbmem.c
+++ b/drivers/video/fbmem.c
@@ -991,17 +991,6 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var)
old_var = info->var;
info->var = *var;
- /* call pre-mode change */
- if (flags & FBINFO_MISC_USEREVENT) {
- struct fb_event event;
- int evnt = FB_EVENT_PREMODE_CHANGE;
-
- info->flags &= ~FBINFO_MISC_USEREVENT;
- event.info = info;
- event.data = &mode;
- fb_notifier_call_chain(evnt, &event);
- }
-
if (info->fbops->fb_set_par) {
ret = info->fbops->fb_set_par(info);
diff --git a/drivers/video/mxc/ldb.c b/drivers/video/mxc/ldb.c
index 0fbf57444a6..29c90a1fdd0 100644
--- a/drivers/video/mxc/ldb.c
+++ b/drivers/video/mxc/ldb.c
@@ -83,7 +83,7 @@
struct ldb_data {
struct platform_device *pdev;
- struct mxc_dispdrv_entry *disp_ldb;
+ struct mxc_dispdrv_handle *disp_ldb;
uint32_t *reg;
uint32_t *control_reg;
uint32_t *gpr3_reg;
@@ -236,6 +236,55 @@ static int find_ldb_setting(struct ldb_data *ldb, struct fb_info *fbi)
return -EINVAL;
}
+static int ldb_disp_setup(struct mxc_dispdrv_handle *disp, struct fb_info *fbi)
+{
+ uint32_t reg;
+ uint32_t pixel_clk, rounded_pixel_clk;
+ struct clk *ldb_clk_parent;
+ struct ldb_data *ldb = mxc_dispdrv_getdata(disp);
+ int setting_idx, di;
+
+ setting_idx = find_ldb_setting(ldb, fbi);
+ if (setting_idx < 0)
+ return setting_idx;
+
+ di = ldb->setting[setting_idx].di;
+
+ /* vsync setup */
+ reg = readl(ldb->control_reg);
+ if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT) {
+ if (di == 0)
+ reg = (reg & ~LDB_DI0_VS_POL_MASK)
+ | LDB_DI0_VS_POL_ACT_HIGH;
+ else
+ reg = (reg & ~LDB_DI1_VS_POL_MASK)
+ | LDB_DI1_VS_POL_ACT_HIGH;
+ } else {
+ if (di == 0)
+ reg = (reg & ~LDB_DI0_VS_POL_MASK)
+ | LDB_DI0_VS_POL_ACT_LOW;
+ else
+ reg = (reg & ~LDB_DI1_VS_POL_MASK)
+ | LDB_DI1_VS_POL_ACT_LOW;
+ }
+ writel(reg, ldb->control_reg);
+
+ /* clk setup */
+ pixel_clk = (PICOS2KHZ(fbi->var.pixclock)) * 1000UL;
+ ldb_clk_parent = clk_get_parent(ldb->ldb_di_clk[di]);
+ if ((ldb->mode == LDB_SPL_DI0) || (ldb->mode == LDB_SPL_DI1))
+ clk_set_rate(ldb_clk_parent, pixel_clk * 7 / 2);
+ else
+ clk_set_rate(ldb_clk_parent, pixel_clk * 7);
+ rounded_pixel_clk = clk_round_rate(ldb->ldb_di_clk[di],
+ pixel_clk);
+ clk_set_rate(ldb->ldb_di_clk[di], rounded_pixel_clk);
+ clk_enable(ldb->ldb_di_clk[di]);
+ ldb->setting[setting_idx].clk_en = true;
+
+ return 0;
+}
+
int ldb_fb_event(struct notifier_block *nb, unsigned long val, void *v)
{
struct ldb_data *ldb = container_of(nb, struct ldb_data, nb);
@@ -264,45 +313,6 @@ int ldb_fb_event(struct notifier_block *nb, unsigned long val, void *v)
}
switch (val) {
- case FB_EVENT_PREMODE_CHANGE:
- {
- uint32_t reg;
- uint32_t pixel_clk, rounded_pixel_clk;
- struct clk *ldb_clk_parent;
-
- /* vsync setup */
- reg = readl(ldb->control_reg);
- if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT) {
- if (di == 0)
- reg = (reg & ~LDB_DI0_VS_POL_MASK)
- | LDB_DI0_VS_POL_ACT_HIGH;
- else
- reg = (reg & ~LDB_DI1_VS_POL_MASK)
- | LDB_DI1_VS_POL_ACT_HIGH;
- } else {
- if (di == 0)
- reg = (reg & ~LDB_DI0_VS_POL_MASK)
- | LDB_DI0_VS_POL_ACT_LOW;
- else
- reg = (reg & ~LDB_DI1_VS_POL_MASK)
- | LDB_DI1_VS_POL_ACT_LOW;
- }
- writel(reg, ldb->control_reg);
-
- /* clk setup */
- pixel_clk = (PICOS2KHZ(fbi->var.pixclock)) * 1000UL;
- ldb_clk_parent = clk_get_parent(ldb->ldb_di_clk[di]);
- if ((ldb->mode == LDB_SPL_DI0) || (ldb->mode == LDB_SPL_DI1))
- clk_set_rate(ldb_clk_parent, pixel_clk * 7 / 2);
- else
- clk_set_rate(ldb_clk_parent, pixel_clk * 7);
- rounded_pixel_clk = clk_round_rate(ldb->ldb_di_clk[di],
- pixel_clk);
- clk_set_rate(ldb->ldb_di_clk[di], rounded_pixel_clk);
- clk_enable(ldb->ldb_di_clk[di]);
- ldb->setting[setting_idx].clk_en = true;
- break;
- }
case FB_EVENT_BLANK:
{
if (*((int *)event->data) == FB_BLANK_UNBLANK) {
@@ -511,11 +521,11 @@ static int of_get_ldb_data(struct ldb_data *ldb,
return 0;
}
-static int ldb_disp_init(struct mxc_dispdrv_entry *disp)
+static int ldb_disp_init(struct mxc_dispdrv_handle *disp,
+ struct mxc_dispdrv_setting *setting)
{
int ret = 0, i;
struct ldb_data *ldb = mxc_dispdrv_getdata(disp);
- struct mxc_dispdrv_setting *setting = mxc_dispdrv_getsetting(disp);
struct fsl_mxc_ldb_platform_data of_data;
struct fsl_mxc_ldb_platform_data *plat_data = ldb->pdev->dev.platform_data;
struct resource *res;
@@ -801,7 +811,7 @@ static int ldb_disp_init(struct mxc_dispdrv_entry *disp)
return ret;
}
-static void ldb_disp_deinit(struct mxc_dispdrv_entry *disp)
+static void ldb_disp_deinit(struct mxc_dispdrv_handle *disp)
{
struct ldb_data *ldb = mxc_dispdrv_getdata(disp);
int i;
@@ -822,6 +832,7 @@ static struct mxc_dispdrv_driver ldb_drv = {
.name = DISPDRV_LDB,
.init = ldb_disp_init,
.deinit = ldb_disp_deinit,
+ .setup = ldb_disp_setup,
};
static void ldb_disp_pwr_up(struct platform_device *pdev)
@@ -878,6 +889,7 @@ static int ldb_remove(struct platform_device *pdev)
{
struct ldb_data *ldb = dev_get_drvdata(&pdev->dev);
+ mxc_dispdrv_puthandle(ldb->disp_ldb);
mxc_dispdrv_unregister(ldb->disp_ldb);
kfree(ldb);
return 0;
diff --git a/drivers/video/mxc/mxc_dispdrv.c b/drivers/video/mxc/mxc_dispdrv.c
index 06b7af944f5..b455a6ab06f 100644
--- a/drivers/video/mxc/mxc_dispdrv.c
+++ b/drivers/video/mxc/mxc_dispdrv.c
@@ -22,8 +22,9 @@
* Move all dev_suspend() things into fb_notifier for SUSPEND, if there is;
* Move all dev_resume() things into fb_notifier for RESUME, if there is;
*
- * ipuv3 fb driver could call mxc_dispdrv_init(setting) before a fb need be added, with fbi param
- * passing by setting, after mxc_dispdrv_init() return, FB driver should get the basic setting
+ * ipuv3 fb driver could call mxc_dispdrv_gethandle(name, setting) before a fb
+ * need be added, with fbi param passing by setting, after
+ * mxc_dispdrv_gethandle() return, FB driver should get the basic setting
* about fbi info and ipuv3-hw (ipu_id and disp_id).
*
* @ingroup Framebuffer
@@ -42,16 +43,15 @@ static LIST_HEAD(dispdrv_list);
static DEFINE_MUTEX(dispdrv_lock);
struct mxc_dispdrv_entry {
- const char *name;
- struct list_head list;
- int (*init) (struct mxc_dispdrv_entry *);
- void (*deinit) (struct mxc_dispdrv_entry *);
+ /* Note: drv always the first element */
+ struct mxc_dispdrv_driver *drv;
bool active;
struct mxc_dispdrv_setting setting;
void *priv;
+ struct list_head list;
};
-struct mxc_dispdrv_entry *mxc_dispdrv_register(struct mxc_dispdrv_driver *drv)
+struct mxc_dispdrv_handle *mxc_dispdrv_register(struct mxc_dispdrv_driver *drv)
{
struct mxc_dispdrv_entry *new;
@@ -63,23 +63,20 @@ struct mxc_dispdrv_entry *mxc_dispdrv_register(struct mxc_dispdrv_driver *drv)
return ERR_PTR(-ENOMEM);
}
- new->name = drv->name;
- new->init = drv->init;
- new->deinit = drv->deinit;
-
+ new->drv = drv;
list_add_tail(&new->list, &dispdrv_list);
mutex_unlock(&dispdrv_lock);
- return new;
+ return (struct mxc_dispdrv_handle *)new;
}
EXPORT_SYMBOL_GPL(mxc_dispdrv_register);
-int mxc_dispdrv_unregister(struct mxc_dispdrv_entry *entry)
+int mxc_dispdrv_unregister(struct mxc_dispdrv_handle *handle)
{
+ struct mxc_dispdrv_entry *entry = (struct mxc_dispdrv_entry *)handle;
+
if (entry) {
mutex_lock(&dispdrv_lock);
- if (entry->active && entry->deinit)
- entry->deinit(entry);
list_del(&entry->list);
mutex_unlock(&dispdrv_lock);
kfree(entry);
@@ -89,41 +86,48 @@ int mxc_dispdrv_unregister(struct mxc_dispdrv_entry *entry)
}
EXPORT_SYMBOL_GPL(mxc_dispdrv_unregister);
-int mxc_dispdrv_init(char *name, struct mxc_dispdrv_setting *setting)
+struct mxc_dispdrv_handle *mxc_dispdrv_gethandle(char *name,
+ struct mxc_dispdrv_setting *setting)
{
- int ret = 0, found = 0;
- struct mxc_dispdrv_entry *disp;
+ int ret, found = 0;
+ struct mxc_dispdrv_entry *entry;
mutex_lock(&dispdrv_lock);
- list_for_each_entry(disp, &dispdrv_list, list) {
- if (!strcmp(disp->name, name)) {
- if (disp->init) {
- memcpy(&disp->setting, setting,
- sizeof(struct mxc_dispdrv_setting));
- ret = disp->init(disp);
- if (ret >= 0) {
- disp->active = true;
- /* setting may need fix-up */
- memcpy(setting, &disp->setting,
- sizeof(struct mxc_dispdrv_setting));
- found = 1;
- break;
- }
+ list_for_each_entry(entry, &dispdrv_list, list) {
+ if (!strcmp(entry->drv->name, name) && (entry->drv->init)) {
+ ret = entry->drv->init((struct mxc_dispdrv_handle *)
+ entry, setting);
+ if (ret >= 0) {
+ entry->active = true;
+ found = 1;
+ break;
}
}
}
- if (!found)
- ret = -EINVAL;
-
mutex_unlock(&dispdrv_lock);
- return ret;
+ return found ? (struct mxc_dispdrv_handle *)entry:ERR_PTR(-ENODEV);
+}
+EXPORT_SYMBOL_GPL(mxc_dispdrv_gethandle);
+
+void mxc_dispdrv_puthandle(struct mxc_dispdrv_handle *handle)
+{
+ struct mxc_dispdrv_entry *entry = (struct mxc_dispdrv_entry *)handle;
+
+ mutex_lock(&dispdrv_lock);
+ if (entry && entry->active && entry->drv->deinit) {
+ entry->drv->deinit(handle);
+ entry->active = false;
+ }
+ mutex_unlock(&dispdrv_lock);
}
-EXPORT_SYMBOL_GPL(mxc_dispdrv_init);
+EXPORT_SYMBOL_GPL(mxc_dispdrv_puthandle);
-int mxc_dispdrv_setdata(struct mxc_dispdrv_entry *entry, void *data)
+int mxc_dispdrv_setdata(struct mxc_dispdrv_handle *handle, void *data)
{
+ struct mxc_dispdrv_entry *entry = (struct mxc_dispdrv_entry *)handle;
+
if (entry) {
entry->priv = data;
return 0;
@@ -132,21 +136,13 @@ int mxc_dispdrv_setdata(struct mxc_dispdrv_entry *entry, void *data)
}
EXPORT_SYMBOL_GPL(mxc_dispdrv_setdata);
-void *mxc_dispdrv_getdata(struct mxc_dispdrv_entry *entry)
+void *mxc_dispdrv_getdata(struct mxc_dispdrv_handle *handle)
{
+ struct mxc_dispdrv_entry *entry = (struct mxc_dispdrv_entry *)handle;
+
if (entry) {
return entry->priv;
} else
return ERR_PTR(-EINVAL);
}
EXPORT_SYMBOL_GPL(mxc_dispdrv_getdata);
-
-struct mxc_dispdrv_setting
- *mxc_dispdrv_getsetting(struct mxc_dispdrv_entry *entry)
-{
- if (entry) {
- return &entry->setting;
- } else
- return ERR_PTR(-EINVAL);
-}
-EXPORT_SYMBOL_GPL(mxc_dispdrv_getsetting);
diff --git a/drivers/video/mxc/mxc_dispdrv.h b/drivers/video/mxc/mxc_dispdrv.h
index f5f62a2c3cb..580e9d2c561 100644
--- a/drivers/video/mxc/mxc_dispdrv.h
+++ b/drivers/video/mxc/mxc_dispdrv.h
@@ -13,12 +13,8 @@
#ifndef __MXC_DISPDRV_H__
#define __MXC_DISPDRV_H__
-struct mxc_dispdrv_entry;
-
-struct mxc_dispdrv_driver {
- const char *name;
- int (*init) (struct mxc_dispdrv_entry *);
- void (*deinit) (struct mxc_dispdrv_entry *);
+struct mxc_dispdrv_handle {
+ struct mxc_dispdrv_driver *drv;
};
struct mxc_dispdrv_setting {
@@ -33,11 +29,23 @@ struct mxc_dispdrv_setting {
int disp_id;
};
-struct mxc_dispdrv_entry *mxc_dispdrv_register(struct mxc_dispdrv_driver *drv);
-int mxc_dispdrv_unregister(struct mxc_dispdrv_entry *entry);
-int mxc_dispdrv_init(char *name, struct mxc_dispdrv_setting *setting);
-int mxc_dispdrv_setdata(struct mxc_dispdrv_entry *entry, void *data);
-void *mxc_dispdrv_getdata(struct mxc_dispdrv_entry *entry);
-struct mxc_dispdrv_setting
- *mxc_dispdrv_getsetting(struct mxc_dispdrv_entry *entry);
+struct mxc_dispdrv_driver {
+ const char *name;
+ int (*init) (struct mxc_dispdrv_handle *, struct mxc_dispdrv_setting *);
+ void (*deinit) (struct mxc_dispdrv_handle *);
+ /* display driver enable function for extension */
+ int (*enable) (struct mxc_dispdrv_handle *);
+ /* display driver disable function, called at early part of fb_blank */
+ void (*disable) (struct mxc_dispdrv_handle *);
+ /* display driver setup function, called at early part of fb_set_par */
+ int (*setup) (struct mxc_dispdrv_handle *, struct fb_info *fbi);
+};
+
+struct mxc_dispdrv_handle *mxc_dispdrv_register(struct mxc_dispdrv_driver *drv);
+int mxc_dispdrv_unregister(struct mxc_dispdrv_handle *handle);
+struct mxc_dispdrv_handle *mxc_dispdrv_gethandle(char *name,
+ struct mxc_dispdrv_setting *setting);
+void mxc_dispdrv_puthandle(struct mxc_dispdrv_handle *handle);
+int mxc_dispdrv_setdata(struct mxc_dispdrv_handle *handle, void *data);
+void *mxc_dispdrv_getdata(struct mxc_dispdrv_handle *handle);
#endif
diff --git a/drivers/video/mxc/mxc_dvi.c b/drivers/video/mxc/mxc_dvi.c
index 765670c8098..a286e51014e 100644
--- a/drivers/video/mxc/mxc_dvi.c
+++ b/drivers/video/mxc/mxc_dvi.c
@@ -54,7 +54,7 @@
struct mxc_dvi_data {
struct i2c_client *client;
struct platform_device *pdev;
- struct mxc_dispdrv_entry *disp_dvi;
+ struct mxc_dispdrv_handle *disp_dvi;
struct delayed_work det_work;
struct fb_info *fbi;
struct mxc_edid_cfg edid_cfg;
@@ -180,11 +180,11 @@ static irqreturn_t mxc_dvi_detect_handler(int irq, void *data)
return IRQ_HANDLED;
}
-static int dvi_init(struct mxc_dispdrv_entry *disp)
+static int dvi_init(struct mxc_dispdrv_handle *disp,
+ struct mxc_dispdrv_setting *setting)
{
int ret = 0;
struct mxc_dvi_data *dvi = mxc_dispdrv_getdata(disp);
- struct mxc_dispdrv_setting *setting = mxc_dispdrv_getsetting(disp);
struct fsl_mxc_dvi_platform_data *plat = dvi->client->dev.platform_data;
setting->dev_id = dvi->ipu = plat->ipu_id;
@@ -283,7 +283,7 @@ err:
return ret;
}
-static void dvi_deinit(struct mxc_dispdrv_entry *disp)
+static void dvi_deinit(struct mxc_dispdrv_handle *disp)
{
struct mxc_dvi_data *dvi = mxc_dispdrv_getdata(disp);
@@ -341,6 +341,7 @@ static int __devexit mxc_dvi_remove(struct i2c_client *client)
{
struct mxc_dvi_data *dvi = i2c_get_clientdata(client);
+ mxc_dispdrv_puthandle(dvi->disp_dvi);
mxc_dispdrv_unregister(dvi->disp_dvi);
platform_device_unregister(dvi->pdev);
kfree(dvi);
diff --git a/drivers/video/mxc/mxc_edid.c b/drivers/video/mxc/mxc_edid.c
index 85a9063037a..de4ba8ec8bf 100644
--- a/drivers/video/mxc/mxc_edid.c
+++ b/drivers/video/mxc/mxc_edid.c
@@ -33,7 +33,6 @@
#include "../edid.h"
#undef DEBUG /* define this for verbose EDID parsing output */
-
#ifdef DEBUG
#define DPRINTK(fmt, args...) printk(fmt, ## args)
#else
@@ -140,9 +139,17 @@ const struct fb_videomode mxc_cea_mode[64] = {
* pixclock, since for many CEA modes, 2 frequencies are supported
* e.g. 640x480 @ 60Hz or 59.94Hz
*/
-int mxc_edid_fb_mode_is_equal(const struct fb_videomode *mode1,
- const struct fb_videomode *mode2)
+int mxc_edid_fb_mode_is_equal(bool use_aspect,
+ const struct fb_videomode *mode1,
+ const struct fb_videomode *mode2)
{
+ u32 mask;
+
+ if (use_aspect)
+ mask = ~0;
+ else
+ mask = ~FB_VMODE_ASPECT_MASK;
+
return (mode1->xres == mode2->xres &&
mode1->yres == mode2->yres &&
mode1->hsync_len == mode2->hsync_len &&
@@ -152,7 +159,7 @@ int mxc_edid_fb_mode_is_equal(const struct fb_videomode *mode1,
mode1->upper_margin == mode2->upper_margin &&
mode1->lower_margin == mode2->lower_margin &&
mode1->sync == mode2->sync &&
- mode1->vmode == mode2->vmode);
+ (mode1->vmode & mask) == (mode2->vmode & mask));
}
static void get_detailed_timing(unsigned char *block,
@@ -186,6 +193,19 @@ static void get_detailed_timing(unsigned char *block,
}
mode->flag = FB_MODE_IS_DETAILED;
+ if ((H_SIZE / 16) == (V_SIZE / 9))
+ mode->vmode |= FB_VMODE_ASPECT_16_9;
+ else if ((H_SIZE / 4) == (V_SIZE / 3))
+ mode->vmode |= FB_VMODE_ASPECT_4_3;
+ else if ((mode->xres / 16) == (mode->yres / 9))
+ mode->vmode |= FB_VMODE_ASPECT_16_9;
+ else if ((mode->xres / 4) == (mode->yres / 3))
+ mode->vmode |= FB_VMODE_ASPECT_4_3;
+
+ if (mode->vmode & FB_VMODE_ASPECT_16_9)
+ DPRINTK("Aspect ratio: 16:9\n");
+ if (mode->vmode & FB_VMODE_ASPECT_4_3)
+ DPRINTK("Aspect ratio: 4:3\n");
DPRINTK(" %d MHz ", PIXEL_CLOCK/1000000);
DPRINTK("%d %d %d %d ", H_ACTIVE, H_ACTIVE + H_SYNC_OFFSET,
H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH, H_ACTIVE + H_BLANKING);
@@ -439,7 +459,7 @@ int mxc_edid_var_to_vic(struct fb_var_screeninfo *var)
for (i = 0; i < ARRAY_SIZE(mxc_cea_mode); i++) {
fb_var_to_videomode(&m, var);
- if (mxc_edid_fb_mode_is_equal(&m, &mxc_cea_mode[i]))
+ if (mxc_edid_fb_mode_is_equal(false, &m, &mxc_cea_mode[i]))
break;
}
@@ -454,9 +474,10 @@ EXPORT_SYMBOL(mxc_edid_var_to_vic);
int mxc_edid_mode_to_vic(const struct fb_videomode *mode)
{
int i;
+ bool use_aspect = (mode->vmode & FB_VMODE_ASPECT_MASK);
for (i = 0; i < ARRAY_SIZE(mxc_cea_mode); i++) {
- if (mxc_edid_fb_mode_is_equal(mode, &mxc_cea_mode[i]))
+ if (mxc_edid_fb_mode_is_equal(use_aspect, mode, &mxc_cea_mode[i]))
break;
}
diff --git a/drivers/video/mxc/mxc_ipuv3_fb.c b/drivers/video/mxc/mxc_ipuv3_fb.c
index 1447c455202..9ccafc2d95b 100644
--- a/drivers/video/mxc/mxc_ipuv3_fb.c
+++ b/drivers/video/mxc/mxc_ipuv3_fb.c
@@ -77,6 +77,7 @@ struct mxcfb_info {
void *alpha_virt_addr1;
uint32_t alpha_mem_len;
uint32_t ipu_ch_irq;
+ uint32_t ipu_ch_nf_irq;
uint32_t ipu_alp_ch_irq;
uint32_t cur_ipu_buf;
uint32_t cur_ipu_alpha_buf;
@@ -84,7 +85,6 @@ struct mxcfb_info {
u32 pseudo_palette[16];
bool mode_found;
- volatile bool wait4vsync;
struct semaphore flip_sem;
struct semaphore alpha_flip_sem;
struct completion vsync_complete;
@@ -92,6 +92,7 @@ struct mxcfb_info {
void *ipu;
struct fb_info *ovfbi;
+ struct mxc_dispdrv_handle *dispdrv;
struct ipuv3_fb_platform_data of_data;
};
@@ -186,6 +187,7 @@ static struct fb_info *found_registered_fb(ipu_channel_t ipu_ch, int ipu_id)
}
static irqreturn_t mxcfb_irq_handler(int irq, void *dev_id);
+static irqreturn_t mxcfb_nf_irq_handler(int irq, void *dev_id);
static int mxcfb_blank(int blank, struct fb_info *info);
static int mxcfb_map_video_memory(struct fb_info *fbi);
static int mxcfb_unmap_video_memory(struct fb_info *fbi);
@@ -245,6 +247,7 @@ static int _setup_disp_channel2(struct fb_info *fbi)
struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
int fb_stride;
unsigned long base;
+ unsigned int fr_xoff, fr_yoff, fr_w, fr_h;
switch (bpp_to_pixfmt(fbi)) {
case IPU_PIX_FMT_YUV420P2:
@@ -259,16 +262,28 @@ static int _setup_disp_channel2(struct fb_info *fbi)
fb_stride = fbi->fix.line_length;
}
+ base = fbi->fix.smem_start;
+ fr_xoff = fbi->var.xoffset;
+ fr_w = fbi->var.xres_virtual;
+ if (!(fbi->var.vmode & FB_VMODE_YWRAP)) {
+ dev_dbg(fbi->device, "Y wrap disabled\n");
+ fr_yoff = fbi->var.yoffset % fbi->var.yres;
+ fr_h = fbi->var.yres;
+ base += fbi->fix.line_length * fbi->var.yres *
+ (fbi->var.yoffset / fbi->var.yres);
+ } else {
+ dev_dbg(fbi->device, "Y wrap enabled\n");
+ fr_yoff = fbi->var.yoffset;
+ fr_h = fbi->var.yres_virtual;
+ }
+ base += fr_yoff * fb_stride + fr_xoff;
+
mxc_fbi->cur_ipu_buf = 2;
sema_init(&mxc_fbi->flip_sem, 1);
if (mxc_fbi->alpha_chan_en) {
mxc_fbi->cur_ipu_alpha_buf = 1;
sema_init(&mxc_fbi->alpha_flip_sem, 1);
}
- fbi->var.xoffset = 0;
-
- base = (fbi->var.yoffset * fb_stride + fbi->var.xoffset);
- base += fbi->fix.smem_start;
retval = ipu_init_channel_buffer(mxc_fbi->ipu,
mxc_fbi->ipu_ch, IPU_INPUT_BUFFER,
@@ -286,6 +301,17 @@ static int _setup_disp_channel2(struct fb_info *fbi)
"ipu_init_channel_buffer error %d\n", retval);
}
+ /* update u/v offset */
+ ipu_update_channel_offset(mxc_fbi->ipu, mxc_fbi->ipu_ch,
+ IPU_INPUT_BUFFER,
+ bpp_to_pixfmt(fbi),
+ fr_w,
+ fr_h,
+ fr_w,
+ 0, 0,
+ fr_yoff,
+ fr_xoff);
+
if (mxc_fbi->alpha_chan_en) {
retval = ipu_init_channel_buffer(mxc_fbi->ipu,
mxc_fbi->ipu_ch,
@@ -322,8 +348,19 @@ static int mxcfb_set_par(struct fb_info *fbi)
dev_dbg(fbi->device, "Reconfiguring framebuffer\n");
+ if (mxc_fbi->dispdrv && mxc_fbi->dispdrv->drv->setup) {
+ retval = mxc_fbi->dispdrv->drv->setup(mxc_fbi->dispdrv, fbi);
+ if (retval < 0) {
+ dev_err(fbi->device, "setup error, dispdrv:%s.\n",
+ mxc_fbi->dispdrv->drv->name);
+ return -EINVAL;
+ }
+ }
+
ipu_clear_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_irq);
ipu_disable_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_irq);
+ ipu_clear_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_nf_irq);
+ ipu_disable_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_nf_irq);
ipu_disable_channel(mxc_fbi->ipu, mxc_fbi->ipu_ch, true);
ipu_uninit_channel(mxc_fbi->ipu, mxc_fbi->ipu_ch);
mxcfb_set_fix(fbi);
@@ -468,6 +505,9 @@ static int _swap_channels(struct fb_info *fbi_from,
tmp = mxc_fbi_from->ipu_ch_irq;
mxc_fbi_from->ipu_ch_irq = mxc_fbi_to->ipu_ch_irq;
mxc_fbi_to->ipu_ch_irq = tmp;
+ tmp = mxc_fbi_from->ipu_ch_nf_irq;
+ mxc_fbi_from->ipu_ch_nf_irq = mxc_fbi_to->ipu_ch_nf_irq;
+ mxc_fbi_to->ipu_ch_nf_irq = tmp;
ovfbi = mxc_fbi_from->ovfbi;
mxc_fbi_from->ovfbi = mxc_fbi_to->ovfbi;
mxc_fbi_to->ovfbi = ovfbi;
@@ -516,6 +556,10 @@ static int swap_channels(struct fb_info *fbi_from)
ipu_clear_irq(mxc_fbi_to->ipu, mxc_fbi_to->ipu_ch_irq);
ipu_free_irq(mxc_fbi_from->ipu, mxc_fbi_from->ipu_ch_irq, fbi_from);
ipu_free_irq(mxc_fbi_to->ipu, mxc_fbi_to->ipu_ch_irq, fbi_to);
+ ipu_clear_irq(mxc_fbi_from->ipu, mxc_fbi_from->ipu_ch_nf_irq);
+ ipu_clear_irq(mxc_fbi_to->ipu, mxc_fbi_to->ipu_ch_nf_irq);
+ ipu_free_irq(mxc_fbi_from->ipu, mxc_fbi_from->ipu_ch_nf_irq, fbi_from);
+ ipu_free_irq(mxc_fbi_to->ipu, mxc_fbi_to->ipu_ch_nf_irq, fbi_to);
if (mxc_fbi_from->cur_blank == FB_BLANK_UNBLANK) {
if (mxc_fbi_to->cur_blank == FB_BLANK_UNBLANK)
@@ -549,6 +593,9 @@ static int swap_channels(struct fb_info *fbi_from)
i = mxc_fbi_from->ipu_ch_irq;
mxc_fbi_from->ipu_ch_irq = mxc_fbi_to->ipu_ch_irq;
mxc_fbi_to->ipu_ch_irq = i;
+ i = mxc_fbi_from->ipu_ch_nf_irq;
+ mxc_fbi_from->ipu_ch_nf_irq = mxc_fbi_to->ipu_ch_nf_irq;
+ mxc_fbi_to->ipu_ch_nf_irq = i;
break;
default:
break;
@@ -568,6 +615,20 @@ static int swap_channels(struct fb_info *fbi_from)
return -EBUSY;
}
ipu_disable_irq(mxc_fbi_to->ipu, mxc_fbi_to->ipu_ch_irq);
+ if (ipu_request_irq(mxc_fbi_from->ipu, mxc_fbi_from->ipu_ch_nf_irq, mxcfb_nf_irq_handler, 0,
+ MXCFB_NAME, fbi_from) != 0) {
+ dev_err(fbi_from->device, "Error registering irq %d\n",
+ mxc_fbi_from->ipu_ch_nf_irq);
+ return -EBUSY;
+ }
+ ipu_disable_irq(mxc_fbi_from->ipu, mxc_fbi_from->ipu_ch_nf_irq);
+ if (ipu_request_irq(mxc_fbi_to->ipu, mxc_fbi_to->ipu_ch_nf_irq, mxcfb_irq_handler, 0,
+ MXCFB_NAME, fbi_to) != 0) {
+ dev_err(fbi_to->device, "Error registering irq %d\n",
+ mxc_fbi_to->ipu_ch_nf_irq);
+ return -EBUSY;
+ }
+ ipu_disable_irq(mxc_fbi_to->ipu, mxc_fbi_to->ipu_ch_nf_irq);
return 0;
}
@@ -963,17 +1024,14 @@ static int mxcfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
}
init_completion(&mxc_fbi->vsync_complete);
-
- ipu_clear_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_irq);
- mxc_fbi->wait4vsync = true;
- ipu_enable_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_irq);
+ ipu_clear_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_nf_irq);
+ ipu_enable_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_nf_irq);
retval = wait_for_completion_interruptible_timeout(
&mxc_fbi->vsync_complete, 1 * HZ);
if (retval == 0) {
dev_err(fbi->device,
"MXCFB_WAIT_FOR_VSYNC: timeout %d\n",
retval);
- mxc_fbi->wait4vsync = false;
retval = -ETIME;
} else if (retval > 0) {
retval = 0;
@@ -1190,6 +1248,7 @@ mxcfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)info->par,
*mxc_graphic_fbi = NULL;
u_int y_bottom;
+ unsigned int fr_xoff, fr_yoff, fr_w, fr_h;
unsigned long base, active_alpha_phy_addr = 0;
bool loc_alpha_en = false;
int fb_stride;
@@ -1216,9 +1275,6 @@ mxcfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
y_bottom = var->yoffset;
- if (!(var->vmode & FB_VMODE_YWRAP))
- y_bottom += var->yres;
-
if (y_bottom > info->var.yres_virtual)
return -EINVAL;
@@ -1235,8 +1291,21 @@ mxcfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
fb_stride = info->fix.line_length;
}
- base = (var->yoffset * fb_stride + var->xoffset);
- base += info->fix.smem_start;
+ base = info->fix.smem_start;
+ fr_xoff = var->xoffset;
+ fr_w = info->var.xres_virtual;
+ if (!(var->vmode & FB_VMODE_YWRAP)) {
+ dev_dbg(info->device, "Y wrap disabled\n");
+ fr_yoff = var->yoffset % info->var.yres;
+ fr_h = info->var.yres;
+ base += info->fix.line_length * info->var.yres *
+ (var->yoffset / info->var.yres);
+ } else {
+ dev_dbg(info->device, "Y wrap enabled\n");
+ fr_yoff = var->yoffset;
+ fr_h = info->var.yres_virtual;
+ }
+ base += fr_yoff * fb_stride + fr_xoff;
/* Check if DP local alpha is enabled and find the graphic fb */
if (mxc_fbi->ipu_ch == MEM_BG_SYNC || mxc_fbi->ipu_ch == MEM_FG_SYNC) {
@@ -1292,12 +1361,12 @@ mxcfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
ipu_update_channel_offset(mxc_fbi->ipu, mxc_fbi->ipu_ch,
IPU_INPUT_BUFFER,
bpp_to_pixfmt(info),
- info->var.xres_virtual,
- info->var.yres_virtual,
- info->var.xres_virtual,
+ fr_w,
+ fr_h,
+ fr_w,
0, 0,
- var->yoffset,
- var->xoffset);
+ fr_yoff,
+ fr_xoff);
ipu_select_buffer(mxc_fbi->ipu, mxc_fbi->ipu_ch, IPU_INPUT_BUFFER,
mxc_fbi->cur_ipu_buf);
@@ -1411,14 +1480,18 @@ static irqreturn_t mxcfb_irq_handler(int irq, void *dev_id)
struct fb_info *fbi = dev_id;
struct mxcfb_info *mxc_fbi = fbi->par;
- if (mxc_fbi->wait4vsync) {
- complete(&mxc_fbi->vsync_complete);
- ipu_disable_irq(mxc_fbi->ipu, irq);
- mxc_fbi->wait4vsync = false;
- } else {
- up(&mxc_fbi->flip_sem);
- ipu_disable_irq(mxc_fbi->ipu, irq);
- }
+ up(&mxc_fbi->flip_sem);
+ ipu_disable_irq(mxc_fbi->ipu, irq);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mxcfb_nf_irq_handler(int irq, void *dev_id)
+{
+ struct fb_info *fbi = dev_id;
+ struct mxcfb_info *mxc_fbi = fbi->par;
+
+ complete(&mxc_fbi->vsync_complete);
+ ipu_disable_irq(mxc_fbi->ipu, irq);
return IRQ_HANDLED;
}
@@ -1666,10 +1739,11 @@ static int mxcfb_dispdrv_init(struct platform_device *pdev,
dev_info(&pdev->dev, "register mxc display driver %s\n", disp_dev);
- ret = mxc_dispdrv_init(disp_dev, &setting);
- if (ret < 0) {
- dev_err(&pdev->dev,
- "register mxc display driver failed with %d\n", ret);
+ mxcfbi->dispdrv = mxc_dispdrv_gethandle(disp_dev, &setting);
+ if (IS_ERR(mxcfbi->dispdrv)) {
+ ret = PTR_ERR(mxcfbi->dispdrv);
+ dev_err(&pdev->dev, "NO mxc display driver found!\n");
+ return ret;
} else {
/* fix-up */
mxcfbi->ipu_di_pix_fmt = setting.if_fmt;
@@ -1754,11 +1828,18 @@ static int mxcfb_register(struct fb_info *fbi)
if (ipu_request_irq(mxcfbi->ipu, mxcfbi->ipu_ch_irq, mxcfb_irq_handler, 0,
MXCFB_NAME, fbi) != 0) {
- dev_err(fbi->device, "Error registering BG irq handler.\n");
+ dev_err(fbi->device, "Error registering EOF irq handler.\n");
ret = -EBUSY;
goto err0;
}
ipu_disable_irq(mxcfbi->ipu, mxcfbi->ipu_ch_irq);
+ if (ipu_request_irq(mxcfbi->ipu, mxcfbi->ipu_ch_nf_irq, mxcfb_nf_irq_handler, 0,
+ MXCFB_NAME, fbi) != 0) {
+ dev_err(fbi->device, "Error registering NFACK irq handler.\n");
+ ret = -EBUSY;
+ goto err1;
+ }
+ ipu_disable_irq(mxcfbi->ipu, mxcfbi->ipu_ch_nf_irq);
if (mxcfbi->ipu_alp_ch_irq != -1)
if (ipu_request_irq(mxcfbi->ipu, mxcfbi->ipu_alp_ch_irq,
@@ -1767,7 +1848,7 @@ static int mxcfb_register(struct fb_info *fbi)
dev_err(fbi->device, "Error registering alpha irq "
"handler.\n");
ret = -EBUSY;
- goto err1;
+ goto err2;
}
mxcfb_check_var(&fbi->var, fbi);
@@ -1795,12 +1876,14 @@ static int mxcfb_register(struct fb_info *fbi)
ret = register_framebuffer(fbi);
if (ret < 0)
- goto err2;
+ goto err3;
return ret;
-err2:
+err3:
if (mxcfbi->ipu_alp_ch_irq != -1)
ipu_free_irq(mxcfbi->ipu, mxcfbi->ipu_alp_ch_irq, fbi);
+err2:
+ ipu_free_irq(mxcfbi->ipu, mxcfbi->ipu_ch_nf_irq, fbi);
err1:
ipu_free_irq(mxcfbi->ipu, mxcfbi->ipu_ch_irq, fbi);
err0:
@@ -1815,6 +1898,8 @@ static void mxcfb_unregister(struct fb_info *fbi)
ipu_free_irq(mxcfbi->ipu, mxcfbi->ipu_alp_ch_irq, fbi);
if (mxcfbi->ipu_ch_irq)
ipu_free_irq(mxcfbi->ipu, mxcfbi->ipu_ch_irq, fbi);
+ if (mxcfbi->ipu_ch_nf_irq)
+ ipu_free_irq(mxcfbi->ipu, mxcfbi->ipu_ch_nf_irq, fbi);
unregister_framebuffer(fbi);
}
@@ -1841,6 +1926,7 @@ static int mxcfb_setup_overlay(struct platform_device *pdev,
}
mxcfbi_fg->ipu_id = mxcfbi_bg->ipu_id;
mxcfbi_fg->ipu_ch_irq = IPU_IRQ_FG_SYNC_EOF;
+ mxcfbi_fg->ipu_ch_nf_irq = IPU_IRQ_FG_SYNC_NFACK;
mxcfbi_fg->ipu_alp_ch_irq = IPU_IRQ_FG_ALPHA_SYNC_EOF;
mxcfbi_fg->ipu_ch = MEM_FG_SYNC;
mxcfbi_fg->ipu_di = -1;
@@ -2008,6 +2094,7 @@ static int mxcfb_probe(struct platform_device *pdev)
/* first user uses DP with alpha feature */
if (!g_dp_in_use[mxcfbi->ipu_id]) {
mxcfbi->ipu_ch_irq = IPU_IRQ_BG_SYNC_EOF;
+ mxcfbi->ipu_ch_nf_irq = IPU_IRQ_BG_SYNC_NFACK;
mxcfbi->ipu_alp_ch_irq = IPU_IRQ_BG_ALPHA_SYNC_EOF;
mxcfbi->ipu_ch = MEM_BG_SYNC;
mxcfbi->cur_blank = mxcfbi->next_blank = FB_BLANK_UNBLANK;
@@ -2028,6 +2115,7 @@ static int mxcfb_probe(struct platform_device *pdev)
g_dp_in_use[mxcfbi->ipu_id] = true;
} else {
mxcfbi->ipu_ch_irq = IPU_IRQ_DC_SYNC_EOF;
+ mxcfbi->ipu_ch_nf_irq = IPU_IRQ_DC_SYNC_NFACK;
mxcfbi->ipu_alp_ch_irq = -1;
mxcfbi->ipu_ch = MEM_DC_SYNC;
mxcfbi->cur_blank = mxcfbi->next_blank = FB_BLANK_POWERDOWN;
diff --git a/drivers/video/mxc/mxc_lcdif.c b/drivers/video/mxc/mxc_lcdif.c
index d9d7fa306a8..5cbdc73c4e7 100644
--- a/drivers/video/mxc/mxc_lcdif.c
+++ b/drivers/video/mxc/mxc_lcdif.c
@@ -21,7 +21,7 @@
struct mxc_lcdif_data {
struct platform_device *pdev;
- struct mxc_dispdrv_entry *disp_lcdif;
+ struct mxc_dispdrv_handle *disp_lcdif;
};
#define DISPDRV_LCD "lcd"
@@ -42,11 +42,11 @@ static struct fb_videomode lcdif_modedb[] = {
};
static int lcdif_modedb_sz = ARRAY_SIZE(lcdif_modedb);
-static int lcdif_init(struct mxc_dispdrv_entry *disp)
+static int lcdif_init(struct mxc_dispdrv_handle *disp,
+ struct mxc_dispdrv_setting *setting)
{
int ret, i;
struct mxc_lcdif_data *lcdif = mxc_dispdrv_getdata(disp);
- struct mxc_dispdrv_setting *setting = mxc_dispdrv_getsetting(disp);
struct fsl_mxc_lcd_platform_data *plat_data
= lcdif->pdev->dev.platform_data;
struct fb_videomode *modedb = lcdif_modedb;
@@ -77,7 +77,7 @@ static int lcdif_init(struct mxc_dispdrv_entry *disp)
return ret;
}
-void lcdif_deinit(struct mxc_dispdrv_entry *disp)
+void lcdif_deinit(struct mxc_dispdrv_handle *disp)
{
/*TODO*/
}
@@ -113,6 +113,7 @@ static int mxc_lcdif_remove(struct platform_device *pdev)
{
struct mxc_lcdif_data *lcdif = dev_get_drvdata(&pdev->dev);
+ mxc_dispdrv_puthandle(lcdif->disp_lcdif);
mxc_dispdrv_unregister(lcdif->disp_lcdif);
kfree(lcdif);
return 0;
diff --git a/drivers/video/mxc/mxcfb_sii902x.c b/drivers/video/mxc/mxcfb_sii902x.c
index f626f649f9c..f917674423c 100644
--- a/drivers/video/mxc/mxcfb_sii902x.c
+++ b/drivers/video/mxc/mxcfb_sii902x.c
@@ -188,7 +188,7 @@
struct sii902x_data {
struct platform_device *pdev;
struct i2c_client *client;
- struct mxc_dispdrv_entry *disp_hdmi;
+ struct mxc_dispdrv_handle *disp_hdmi;
struct regulator *io_reg;
struct regulator *analog_reg;
struct delayed_work det_work;
@@ -1064,11 +1064,11 @@ static int sii902x_TPI_init(struct i2c_client *client)
return 0;
}
-static int sii902x_disp_init(struct mxc_dispdrv_entry *disp)
+static int sii902x_disp_init(struct mxc_dispdrv_handle *disp,
+ struct mxc_dispdrv_setting *setting)
{
int ret = 0;
struct sii902x_data *sii902x = mxc_dispdrv_getdata(disp);
- struct mxc_dispdrv_setting *setting = mxc_dispdrv_getsetting(disp);
struct fsl_mxc_lcd_platform_data *plat = sii902x->client->dev.platform_data;
bool found = false;
static bool inited;
@@ -1215,7 +1215,7 @@ register_pltdev_failed:
return ret;
}
-static void sii902x_disp_deinit(struct mxc_dispdrv_entry *disp)
+static void sii902x_disp_deinit(struct mxc_dispdrv_handle *disp)
{
struct sii902x_data *sii902x = mxc_dispdrv_getdata(disp);
struct fsl_mxc_lcd_platform_data *plat = sii902x->client->dev.platform_data;
@@ -1273,6 +1273,7 @@ static int __devexit sii902x_remove(struct i2c_client *client)
{
struct sii902x_data *sii902x = i2c_get_clientdata(client);
+ mxc_dispdrv_puthandle(sii902x->disp_hdmi);
mxc_dispdrv_unregister(sii902x->disp_hdmi);
kfree(sii902x);
return 0;
diff --git a/drivers/video/mxc/tve.c b/drivers/video/mxc/tve.c
index 98b506d3730..bd125964a6d 100644
--- a/drivers/video/mxc/tve.c
+++ b/drivers/video/mxc/tve.c
@@ -120,8 +120,8 @@ struct tve_data {
struct delayed_work cd_work;
struct tve_reg_mapping *regs;
struct tve_reg_fields_mapping *reg_fields;
- struct mxc_dispdrv_entry *disp_tve;
- struct mxc_dispdrv_entry *disp_vga;
+ struct mxc_dispdrv_handle *disp_tve;
+ struct mxc_dispdrv_handle *disp_vga;
struct notifier_block nb;
};
@@ -934,6 +934,15 @@ int tve_fb_setup(struct tve_data *tve, struct fb_info *fbi)
return 0;
}
+static inline int tve_disp_setup(struct mxc_dispdrv_handle *disp,
+ struct fb_info *fbi)
+{
+ struct tve_data *tve = mxc_dispdrv_getdata(disp);
+
+ tve_fb_setup(tve, fbi);
+ return 0;
+}
+
int tve_fb_event(struct notifier_block *nb, unsigned long val, void *v)
{
struct tve_data *tve = container_of(nb, struct tve_data, nb);
@@ -945,11 +954,6 @@ int tve_fb_event(struct notifier_block *nb, unsigned long val, void *v)
return 0;
switch (val) {
- case FB_EVENT_PREMODE_CHANGE:
- {
- tve_fb_setup(tve, fbi);
- break;
- }
case FB_EVENT_BLANK:
if (fbi->mode == NULL)
return 0;
@@ -1028,11 +1032,11 @@ static int _tve_get_revision(struct tve_data *tve)
return rev;
}
-static int tve_drv_init(struct mxc_dispdrv_entry *disp, bool vga)
+static int tve_drv_init(struct mxc_dispdrv_handle *disp, bool vga,
+ struct mxc_dispdrv_setting *setting)
{
int ret;
struct tve_data *tve = mxc_dispdrv_getdata(disp);
- struct mxc_dispdrv_setting *setting = mxc_dispdrv_getsetting(disp);
struct fsl_mxc_tve_platform_data *plat_data
= tve->pdev->dev.platform_data;
struct resource *res;
@@ -1190,17 +1194,19 @@ get_res_failed:
}
-static int tvout_init(struct mxc_dispdrv_entry *disp)
+static int tvout_init(struct mxc_dispdrv_handle *disp,
+ struct mxc_dispdrv_setting *setting)
{
- return tve_drv_init(disp, 0);
+ return tve_drv_init(disp, 0, setting);
}
-static int vga_init(struct mxc_dispdrv_entry *disp)
+static int vga_init(struct mxc_dispdrv_handle *disp,
+ struct mxc_dispdrv_setting *setting)
{
- return tve_drv_init(disp, 1);
+ return tve_drv_init(disp, 1, setting);
}
-void tvout_deinit(struct mxc_dispdrv_entry *disp)
+void tvout_deinit(struct mxc_dispdrv_handle *disp)
{
struct tve_data *tve = mxc_dispdrv_getdata(disp);
@@ -1223,6 +1229,7 @@ static struct mxc_dispdrv_driver vga_drv = {
.name = DISPDRV_VGA,
.init = vga_init,
.deinit = tvout_deinit,
+ .setup = tve_disp_setup,
};
static int tve_dispdrv_init(struct tve_data *tve)
@@ -1236,6 +1243,8 @@ static int tve_dispdrv_init(struct tve_data *tve)
static void tve_dispdrv_deinit(struct tve_data *tve)
{
+ mxc_dispdrv_puthandle(tve->disp_tve);
+ mxc_dispdrv_puthandle(tve->disp_vga);
mxc_dispdrv_unregister(tve->disp_tve);
mxc_dispdrv_unregister(tve->disp_vga);
}
diff --git a/drivers/video/mxc_hdmi.c b/drivers/video/mxc_hdmi.c
index 924264c32e2..b212ad128b7 100644
--- a/drivers/video/mxc_hdmi.c
+++ b/drivers/video/mxc_hdmi.c
@@ -62,8 +62,11 @@
#define DISPDRV_HDMI "hdmi"
#define HDMI_EDID_LEN 512
-#define TRUE 1
-#define FALSE 0
+/* status codes for reading edid */
+#define HDMI_EDID_SUCCESS 0
+#define HDMI_EDID_FAIL -1
+#define HDMI_EDID_SAME -2
+#define HDMI_EDID_NO_MODES -3
#define NUM_CEA_VIDEO_MODES 64
#define DEFAULT_VIDEO_MODE 16 /* 1080P */
@@ -74,6 +77,38 @@
#define YCBCR422_8BITS 3
#define XVYCC444 4
+/*
+ * We follow a flowchart which is in the "Synopsys DesignWare Courses
+ * HDMI Transmitter Controller User Guide, 1.30a", section 3.1
+ * (dwc_hdmi_tx_user.pdf)
+ *
+ * Below are notes that say "HDMI Initialization Step X"
+ * These correspond to the flowchart.
+ */
+
+/*
+ * We are required to configure VGA mode before reading edid
+ * in HDMI Initialization Step B
+ */
+static const struct fb_videomode vga_mode = {
+ /* 640x480 @ 60 Hz, 31.5 kHz hsync */
+ NULL, 60, 640, 480, 39721, 48, 16, 33, 10, 96, 2, 0,
+ FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0,
+};
+
+static const struct fb_videomode xga_mode = {
+ /* 13 1024x768-60 VESA */
+ NULL, 60, 1024, 768, 15384, 160, 24, 29, 3, 136, 6,
+ 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA
+};
+
+static const struct fb_videomode sxga_mode = {
+ /* 20 1280x1024-60 VESA */
+ NULL, 60, 1280, 1024, 9259, 248, 48, 38, 1, 112, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA
+};
+
enum hdmi_datamap {
RGB444_8B = 0x01,
RGB444_10B = 0x03,
@@ -94,11 +129,12 @@ enum hdmi_colorimetry {
};
struct hdmi_vmode {
- unsigned int mHdmiDviSel;
- unsigned int mHSyncPolarity;
- unsigned int mVSyncPolarity;
- unsigned int mInterlaced;
- unsigned int mDataEnablePolarity;
+ bool mDVI;
+ bool mHSyncPolarity;
+ bool mVSyncPolarity;
+ bool mInterlaced;
+ bool mDataEnablePolarity;
+
unsigned int mPixelClock;
unsigned int mPixelRepetitionInput;
unsigned int mPixelRepetitionOutput;
@@ -117,11 +153,11 @@ struct hdmi_data_info {
struct mxc_hdmi {
struct platform_device *pdev;
struct platform_device *core_pdev;
- struct mxc_dispdrv_entry *disp_mxc_hdmi;
+ struct mxc_dispdrv_handle *disp_mxc_hdmi;
struct fb_info *fbi;
struct clk *hdmi_isfr_clk;
struct clk *hdmi_iahb_clk;
- struct delayed_work det_work;
+ struct delayed_work hotplug_work;
struct notifier_block nb;
struct hdmi_data_info hdmi_data;
@@ -129,17 +165,82 @@ struct mxc_hdmi {
struct mxc_edid_cfg edid_cfg;
u8 edid[HDMI_EDID_LEN];
bool fb_reg;
- bool need_mode_change;
bool cable_plugin;
+ bool dft_mode_set;
+ char *dft_mode_str;
+ int default_bpp;
u8 latest_intr_stat;
bool irq_enabled;
spinlock_t irq_lock;
+ bool phy_enabled;
+ struct fb_videomode previous_mode;
+ struct fb_videomode previous_non_vga_mode;
+ bool requesting_vga_for_initialization;
};
struct i2c_client *hdmi_i2c;
extern const struct fb_videomode mxc_cea_mode[64];
+#ifdef DEBUG
+static void dump_fb_videomode(struct fb_videomode *m)
+{
+ pr_debug("fb_videomode = %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
+ m->refresh, m->xres, m->yres, m->pixclock, m->left_margin,
+ m->right_margin, m->upper_margin, m->lower_margin,
+ m->hsync_len, m->vsync_len, m->sync, m->vmode, m->flag);
+}
+#else
+static void dump_fb_videomode(struct fb_videomode *m)
+{}
+#endif
+
+static ssize_t mxc_hdmi_show_name(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mxc_hdmi *hdmi = dev_get_drvdata(dev);
+
+ strcpy(buf, hdmi->fbi->fix.id);
+ sprintf(buf+strlen(buf), "\n");
+
+ return strlen(buf);
+}
+
+static DEVICE_ATTR(fb_name, S_IRUGO, mxc_hdmi_show_name, NULL);
+
+static ssize_t mxc_hdmi_show_state(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mxc_hdmi *hdmi = dev_get_drvdata(dev);
+
+ if (hdmi->cable_plugin == false)
+ strcpy(buf, "plugout\n");
+ else
+ strcpy(buf, "plugin\n");
+
+ return strlen(buf);
+}
+
+static DEVICE_ATTR(cable_state, S_IRUGO, mxc_hdmi_show_state, NULL);
+
+static ssize_t mxc_hdmi_show_edid(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mxc_hdmi *hdmi = dev_get_drvdata(dev);
+ int i, j, len = 0;
+
+ for (j = 0; j < HDMI_EDID_LEN/16; j++) {
+ for (i = 0; i < 16; i++)
+ len += sprintf(buf+len, "0x%02X ",
+ hdmi->edid[j*16 + i]);
+ len += sprintf(buf+len, "\n");
+ }
+
+ return len;
+}
+
+static DEVICE_ATTR(edid, S_IRUGO, mxc_hdmi_show_edid, NULL);
+
/*!
* this submodule is responsible for the video data synchronization.
* for example, for RGB 4:4:4 input, the data map is defined as
@@ -147,7 +248,7 @@ extern const struct fb_videomode mxc_cea_mode[64];
* pin{31~24} <==> G[7:0]
* pin{15~8} <==> B[7:0]
*/
-void hdmi_video_sample(struct mxc_hdmi *hdmi)
+static void hdmi_video_sample(struct mxc_hdmi *hdmi)
{
int color_format = 0;
u8 val;
@@ -206,28 +307,27 @@ void hdmi_video_sample(struct mxc_hdmi *hdmi)
static int isColorSpaceConversion(struct mxc_hdmi *hdmi)
{
return (hdmi->hdmi_data.enc_in_format !=
- hdmi->hdmi_data.enc_out_format) ? TRUE : FALSE;
+ hdmi->hdmi_data.enc_out_format);
}
static int isColorSpaceDecimation(struct mxc_hdmi *hdmi)
{
return ((hdmi->hdmi_data.enc_out_format == YCBCR422_8BITS) &&
(hdmi->hdmi_data.enc_in_format == RGB ||
- hdmi->hdmi_data.enc_in_format == XVYCC444)) ? TRUE : FALSE;
+ hdmi->hdmi_data.enc_in_format == XVYCC444));
}
static int isColorSpaceInterpolation(struct mxc_hdmi *hdmi)
{
return ((hdmi->hdmi_data.enc_in_format == YCBCR422_8BITS) &&
- (hdmi->hdmi_data.enc_out_format == RGB
- || hdmi->hdmi_data.enc_out_format == XVYCC444)) ?
- TRUE : FALSE;
+ (hdmi->hdmi_data.enc_out_format == RGB
+ || hdmi->hdmi_data.enc_out_format == XVYCC444));
}
/*!
* update the color space conversion coefficients.
*/
-void update_csc_coeffs(struct mxc_hdmi *hdmi)
+static void update_csc_coeffs(struct mxc_hdmi *hdmi)
{
unsigned short csc_coeff[3][4];
unsigned int csc_scale = 1;
@@ -391,7 +491,7 @@ void update_csc_coeffs(struct mxc_hdmi *hdmi)
hdmi_writeb(val, HDMI_CSC_SCALE);
}
-void hdmi_video_csc(struct mxc_hdmi *hdmi)
+static void hdmi_video_csc(struct mxc_hdmi *hdmi)
{
int color_depth = 0;
int interpolation = HDMI_CSC_CFG_INTMODE_DISABLE;
@@ -430,7 +530,7 @@ void hdmi_video_csc(struct mxc_hdmi *hdmi)
* for example, if input is YCC422 mode or repeater is used,
* data should be repacked this module can be bypassed.
*/
-void hdmi_video_packetize(struct mxc_hdmi *hdmi)
+static void hdmi_video_packetize(struct mxc_hdmi *hdmi)
{
unsigned int color_depth = 0;
unsigned int remap_size = HDMI_VP_REMAP_YCC422_16bit;
@@ -548,11 +648,15 @@ void hdmi_video_packetize(struct mxc_hdmi *hdmi)
hdmi_writeb(val, HDMI_VP_CONF);
}
-void hdmi_video_force_output(struct mxc_hdmi *hdmi, unsigned char force)
+#if 0
+/* Force a fixed color screen */
+static void hdmi_video_force_output(struct mxc_hdmi *hdmi, unsigned char force)
{
u8 val;
- if (force == TRUE) {
+ dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
+
+ if (force) {
hdmi_writeb(0x00, HDMI_FC_DBGTMDS2); /* R */
hdmi_writeb(0x00, HDMI_FC_DBGTMDS1); /* G */
hdmi_writeb(0xFF, HDMI_FC_DBGTMDS0); /* B */
@@ -568,6 +672,7 @@ void hdmi_video_force_output(struct mxc_hdmi *hdmi, unsigned char force)
hdmi_writeb(0x00, HDMI_FC_DBGTMDS0); /* B */
}
}
+#endif
static inline void hdmi_phy_test_clear(struct mxc_hdmi *hdmi,
unsigned char bit)
@@ -611,21 +716,21 @@ static inline void hdmi_phy_test_dout(struct mxc_hdmi *hdmi,
hdmi_writeb(bit, HDMI_PHY_TST2);
}
-int hdmi_phy_wait_i2c_done(struct mxc_hdmi *hdmi, int msec)
+static bool hdmi_phy_wait_i2c_done(struct mxc_hdmi *hdmi, int msec)
{
unsigned char val = 0;
val = hdmi_readb(HDMI_IH_I2CMPHY_STAT0) & 0x3;
while (val == 0) {
udelay(1000);
if (msec-- == 0)
- return FALSE;
+ return false;
val = hdmi_readb(HDMI_IH_I2CMPHY_STAT0) & 0x3;
}
- return TRUE;
+ return true;
}
-int hdmi_phy_i2c_write(struct mxc_hdmi *hdmi, unsigned short data,
- unsigned char addr)
+static void hdmi_phy_i2c_write(struct mxc_hdmi *hdmi, unsigned short data,
+ unsigned char addr)
{
hdmi_writeb(0xFF, HDMI_IH_I2CMPHY_STAT0);
hdmi_writeb(addr, HDMI_PHY_I2CM_ADDRESS_ADDR);
@@ -636,10 +741,11 @@ int hdmi_phy_i2c_write(struct mxc_hdmi *hdmi, unsigned short data,
hdmi_writeb(HDMI_PHY_I2CM_OPERATION_ADDR_WRITE,
HDMI_PHY_I2CM_OPERATION_ADDR);
hdmi_phy_wait_i2c_done(hdmi, 1000);
- return TRUE;
}
-unsigned short hdmi_phy_i2c_read(struct mxc_hdmi *hdmi, unsigned char addr)
+#if 0
+static unsigned short hdmi_phy_i2c_read(struct mxc_hdmi *hdmi,
+ unsigned char addr)
{
unsigned short data;
unsigned char msb = 0, lsb = 0;
@@ -654,31 +760,84 @@ unsigned short hdmi_phy_i2c_read(struct mxc_hdmi *hdmi, unsigned char addr)
return data;
}
-int hdmi_phy_i2c_write_verify(struct mxc_hdmi *hdmi, unsigned short data,
- unsigned char addr)
+static int hdmi_phy_i2c_write_verify(struct mxc_hdmi *hdmi, unsigned short data,
+ unsigned char addr)
{
unsigned short val = 0;
hdmi_phy_i2c_write(hdmi, data, addr);
val = hdmi_phy_i2c_read(hdmi, addr);
- if (val != data)
- return FALSE;
- return TRUE;
+ return (val == data);
+}
+#endif
+
+/* "Power-down enable (active low)"
+ * That mean that power up == 1! */
+static void mxc_hdmi_phy_enable_power(u8 enable)
+{
+ hdmi_mask_writeb(enable, HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_PDZ_OFFSET,
+ HDMI_PHY_CONF0_PDZ_MASK);
+}
+
+static void mxc_hdmi_phy_enable_tmds(u8 enable)
+{
+ hdmi_mask_writeb(enable, HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_ENTMDS_OFFSET,
+ HDMI_PHY_CONF0_ENTMDS_MASK);
}
-int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep,
- unsigned char cRes, int cscOn, int audioOn,
- int cecOn, int hdcpOn)
+static void mxc_hdmi_phy_gen2_pddq(u8 enable)
+{
+ hdmi_mask_writeb(enable, HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET,
+ HDMI_PHY_CONF0_GEN2_PDDQ_MASK);
+}
+
+static void mxc_hdmi_phy_gen2_txpwron(u8 enable)
+{
+ hdmi_mask_writeb(enable, HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET,
+ HDMI_PHY_CONF0_GEN2_TXPWRON_MASK);
+}
+
+#if 0
+static void mxc_hdmi_phy_gen2_enhpdrxsense(u8 enable)
+{
+ hdmi_mask_writeb(enable, HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE_OFFSET,
+ HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE_MASK);
+}
+#endif
+
+static void mxc_hdmi_phy_sel_data_en_pol(u8 enable)
+{
+ hdmi_mask_writeb(enable, HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_SELDATAENPOL_OFFSET,
+ HDMI_PHY_CONF0_SELDATAENPOL_MASK);
+}
+
+static void mxc_hdmi_phy_sel_interface_control(u8 enable)
+{
+ hdmi_mask_writeb(enable, HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_SELDIPIF_OFFSET,
+ HDMI_PHY_CONF0_SELDIPIF_MASK);
+}
+
+static int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep,
+ unsigned char cRes, int cscOn)
{
u8 val;
+ dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
+
/* color resolution 0 is 8 bit colour depth */
if (cRes == 0)
cRes = 8;
if (pRep != 0)
- return FALSE;
+ return false;
else if (cRes != 8 && cRes != 12)
- return FALSE;
+ return false;
if (cscOn)
val = HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH;
@@ -687,39 +846,17 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep,
hdmi_writeb(val, HDMI_MC_FLOWCTRL);
- /* clock gate == 0 => turn on modules */
- val = hdcpOn ? HDMI_MC_CLKDIS_HDCPCLK_DISABLE_ENABLE :
- HDMI_MC_CLKDIS_HDCPCLK_DISABLE_DISABLE;
- val |= HDMI_MC_CLKDIS_PIXELCLK_DISABLE_ENABLE;
- val |= HDMI_MC_CLKDIS_TMDSCLK_DISABLE_ENABLE;
- val |= (pRep > 0) ? HDMI_MC_CLKDIS_PREPCLK_DISABLE_ENABLE :
- HDMI_MC_CLKDIS_PREPCLK_DISABLE_DISABLE;
- val |= cecOn ? HDMI_MC_CLKDIS_CECCLK_DISABLE_ENABLE :
- HDMI_MC_CLKDIS_CECCLK_DISABLE_DISABLE;
- val |= cscOn ? HDMI_MC_CLKDIS_CSCCLK_DISABLE_ENABLE :
- HDMI_MC_CLKDIS_CSCCLK_DISABLE_DISABLE;
- val |= audioOn ? HDMI_MC_CLKDIS_AUDCLK_DISABLE_ENABLE :
- HDMI_MC_CLKDIS_AUDCLK_DISABLE_DISABLE;
- hdmi_writeb(val, HDMI_MC_CLKDIS);
-
/* gen2 tx power off */
- val = hdmi_readb(HDMI_PHY_CONF0);
- val &= ~HDMI_PHY_CONF0_GEN2_TXPWRON_MASK;
- val |= HDMI_PHY_CONF0_GEN2_TXPWRON_POWER_OFF;
- hdmi_writeb(val, HDMI_PHY_CONF0);
+ mxc_hdmi_phy_gen2_txpwron(0);
/* gen2 pddq */
- val = hdmi_readb(HDMI_PHY_CONF0);
- val &= ~HDMI_PHY_CONF0_GEN2_PDDQ_MASK;
- val |= HDMI_PHY_CONF0_GEN2_PDDQ_ENABLE;
- hdmi_writeb(val, HDMI_PHY_CONF0);
+ mxc_hdmi_phy_gen2_pddq(1);
/* PHY reset */
hdmi_writeb(HDMI_MC_PHYRSTZ_DEASSERT, HDMI_MC_PHYRSTZ);
hdmi_writeb(HDMI_MC_PHYRSTZ_ASSERT, HDMI_MC_PHYRSTZ);
- hdmi_writeb(HDMI_MC_HEACPHY_RST_ASSERT,
- HDMI_MC_HEACPHY_RST);
+ hdmi_writeb(HDMI_MC_HEACPHY_RST_ASSERT, HDMI_MC_HEACPHY_RST);
hdmi_phy_test_clear(hdmi, 1);
hdmi_writeb(HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2,
@@ -729,7 +866,7 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep,
if (hdmi->hdmi_data.video_mode.mPixelClock < 0) {
dev_dbg(&hdmi->pdev->dev, "Pixel clock (%d) must be positive\n",
hdmi->hdmi_data.video_mode.mPixelClock);
- return FALSE;
+ return false;
}
if (hdmi->hdmi_data.video_mode.mPixelClock <= 45250000) {
@@ -748,7 +885,7 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep,
hdmi_phy_i2c_write(hdmi, 0x0000, 0x15);
break;
default:
- return FALSE;
+ return false;
}
} else if (hdmi->hdmi_data.video_mode.mPixelClock <= 92500000) {
switch (cRes) {
@@ -764,7 +901,7 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep,
hdmi_phy_i2c_write(hdmi, 0x4142, 0x06);
hdmi_phy_i2c_write(hdmi, 0x0005, 0x15);
default:
- return FALSE;
+ return false;
}
} else if (hdmi->hdmi_data.video_mode.mPixelClock <= 148500000) {
switch (cRes) {
@@ -780,7 +917,7 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep,
hdmi_phy_i2c_write(hdmi, 0x40a2, 0x06);
hdmi_phy_i2c_write(hdmi, 0x000a, 0x15);
default:
- return FALSE;
+ return false;
}
} else {
switch (cRes) {
@@ -796,7 +933,7 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep,
hdmi_phy_i2c_write(hdmi, 0x4002, 0x06);
hdmi_phy_i2c_write(hdmi, 0x000f, 0x15);
default:
- return FALSE;
+ return false;
}
}
@@ -812,7 +949,7 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep,
hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10);
break;
default:
- return FALSE;
+ return false;
}
} else if (hdmi->hdmi_data.video_mode.mPixelClock <= 58400000) {
switch (cRes) {
@@ -826,7 +963,7 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep,
hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10);
break;
default:
- return FALSE;
+ return false;
}
} else if (hdmi->hdmi_data.video_mode.mPixelClock <= 72000000) {
switch (cRes) {
@@ -840,7 +977,7 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep,
hdmi_phy_i2c_write(hdmi, 0x091c, 0x10);
break;
default:
- return FALSE;
+ return false;
}
} else if (hdmi->hdmi_data.video_mode.mPixelClock <= 74250000) {
switch (cRes) {
@@ -854,7 +991,7 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep,
hdmi_phy_i2c_write(hdmi, 0x091c, 0x10);
break;
default:
- return FALSE;
+ return false;
}
} else if (hdmi->hdmi_data.video_mode.mPixelClock <= 118800000) {
switch (cRes) {
@@ -868,7 +1005,7 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep,
hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10);
break;
default:
- return FALSE;
+ return false;
}
} else if (hdmi->hdmi_data.video_mode.mPixelClock <= 216000000) {
switch (cRes) {
@@ -882,13 +1019,13 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep,
hdmi_phy_i2c_write(hdmi, 0x091c, 0x10);
break;
default:
- return FALSE;
+ return false;
}
} else {
dev_err(&hdmi->pdev->dev,
"Pixel clock %d - unsupported by HDMI\n",
hdmi->hdmi_data.video_mode.mPixelClock);
- return FALSE;
+ return false;
}
hdmi_phy_i2c_write(hdmi, 0x0000, 0x13); /* PLLPHBYCTRL */
@@ -907,50 +1044,50 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep,
hdmi_phy_i2c_write(hdmi, 0x0129, 0x0E);
}
- /* gen2 tx power on */
- val = hdmi_readb(HDMI_PHY_CONF0);
- val &= ~HDMI_PHY_CONF0_GEN2_TXPWRON_MASK;
- val |= HDMI_PHY_CONF0_GEN2_TXPWRON_POWER_ON;
- hdmi_writeb(val, HDMI_PHY_CONF0);
+ mxc_hdmi_phy_enable_power(1);
- val = hdmi_readb(HDMI_PHY_CONF0);
- val &= ~HDMI_PHY_CONF0_GEN2_PDDQ_MASK;
- val |= HDMI_PHY_CONF0_GEN2_PDDQ_DISABLE;
- hdmi_writeb(val, HDMI_PHY_CONF0);
+ /* toggle TMDS enable */
+ mxc_hdmi_phy_enable_tmds(0);
+ mxc_hdmi_phy_enable_tmds(1);
+
+ /* gen2 tx power on */
+ mxc_hdmi_phy_gen2_txpwron(1);
+ mxc_hdmi_phy_gen2_pddq(0);
udelay(1000);
- if ((hdmi_readb(HDMI_PHY_STAT0) & 0x01) == 0)
- return FALSE;
+ /* wait for the phy PLL to lock */
+ while ((hdmi_readb(HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK) == 0)
+ ;
+
+ /* Has the PHY PLL locked? */
+ if ((hdmi_readb(HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK) == 0)
+ return false;
- return TRUE;
+ return true;
}
-void hdmi_phy_init(struct mxc_hdmi *hdmi, unsigned char de)
+static void mxc_hdmi_phy_init(struct mxc_hdmi *hdmi)
{
- u8 val;
+ int i;
+
+ dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
+
+ /* HDMI Phy spec says to do the phy initialization sequence twice */
+ for (i = 0 ; i < 2 ; i++) {
+ mxc_hdmi_phy_sel_data_en_pol(1);
+ mxc_hdmi_phy_sel_interface_control(0);
+ mxc_hdmi_phy_enable_tmds(0);
+ mxc_hdmi_phy_enable_power(0);
- /* set the DE polarity */
- val = (de << HDMI_PHY_CONF0_SELDATAENPOL_OFFSET) &
- HDMI_PHY_CONF0_SELDATAENPOL_MASK;
- /* set ENHPDRXSENSE to 1 */
- val |= HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE;
- /* set the interface control to 0 */
- val |= (0 << HDMI_PHY_CONF0_SELDIPIF_OFFSET) &
- HDMI_PHY_CONF0_SELDIPIF_MASK;
- /* enable TMDS output */
- val |= (1 << HDMI_PHY_CONF0_ENTMDS_OFFSET) &
- HDMI_PHY_CONF0_ENTMDS_MASK;
- /* PHY power enable */
- val |= (1 << HDMI_PHY_CONF0_PDZ_OFFSET) &
- HDMI_PHY_CONF0_PDZ_MASK;
- hdmi_writeb(val, HDMI_PHY_CONF0);
+ /* TODO: Enable CSC */
+ hdmi_phy_configure(hdmi, 0, 8, false);
+ }
- /* TODO: Enable CSC */
- hdmi_phy_configure(hdmi, 0, 8, FALSE, TRUE, TRUE, FALSE);
+ hdmi->phy_enabled = true;
}
-void hdmi_tx_hdcp_config(struct mxc_hdmi *hdmi)
+static void hdmi_tx_hdcp_config(struct mxc_hdmi *hdmi)
{
u8 de, val;
@@ -976,20 +1113,6 @@ void hdmi_tx_hdcp_config(struct mxc_hdmi *hdmi)
hdmi_writeb(val, HDMI_A_HDCPCFG1);
}
-void preamble_filter_set(struct mxc_hdmi *hdmi, unsigned char value,
- unsigned char channel)
-{
- if (channel == 0)
- hdmi_writeb(value, HDMI_FC_CH0PREAM);
- else if (channel == 1)
- hdmi_writeb(value, HDMI_FC_CH1PREAM);
- else if (channel == 2)
- hdmi_writeb(value, HDMI_FC_CH2PREAM);
- else
-
- return;
-}
-
static void hdmi_config_AVI(struct mxc_hdmi *hdmi)
{
u8 val;
@@ -1117,23 +1240,21 @@ static void hdmi_config_AVI(struct mxc_hdmi *hdmi)
/*!
* this submodule is responsible for the video/audio data composition.
*/
-void hdmi_av_composer(struct mxc_hdmi *hdmi)
+static void hdmi_av_composer(struct mxc_hdmi *hdmi)
{
- unsigned char i = 0;
- u8 val;
+ u8 inv_val;
struct fb_info *fbi = hdmi->fbi;
struct fb_videomode fb_mode;
struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode;
int hblank, vblank;
+ dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
+
fb_var_to_videomode(&fb_mode, &fbi->var);
- vmode->mHSyncPolarity =
- (fb_mode.sync & FB_SYNC_HOR_HIGH_ACT) ? TRUE : FALSE;
- vmode->mVSyncPolarity =
- (fb_mode.sync & FB_SYNC_VERT_HIGH_ACT) ? TRUE : FALSE;
- vmode->mInterlaced =
- (fb_mode.vmode & FB_VMODE_INTERLACED) ? TRUE : FALSE;
+ vmode->mHSyncPolarity = ((fb_mode.sync & FB_SYNC_HOR_HIGH_ACT) != 0);
+ vmode->mVSyncPolarity = ((fb_mode.sync & FB_SYNC_VERT_HIGH_ACT) != 0);
+ vmode->mInterlaced = ((fb_mode.vmode & FB_VMODE_INTERLACED) != 0);
vmode->mPixelClock = (fb_mode.xres + fb_mode.left_margin +
fb_mode.right_margin + fb_mode.hsync_len) * (fb_mode.yres +
fb_mode.upper_margin + fb_mode.lower_margin +
@@ -1142,47 +1263,52 @@ void hdmi_av_composer(struct mxc_hdmi *hdmi)
dev_dbg(&hdmi->pdev->dev, "final pixclk = %d\n", vmode->mPixelClock);
/* Set up HDMI_FC_INVIDCONF */
- val = ((hdmi->hdmi_data.hdcp_enable == TRUE) ?
+ inv_val = (hdmi->hdmi_data.hdcp_enable ?
HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE :
HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE);
- val |= ((vmode->mVSyncPolarity == TRUE) ?
+
+ inv_val |= (vmode->mVSyncPolarity ?
HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_HIGH :
HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_LOW);
- val |= ((vmode->mHSyncPolarity == TRUE) ?
+
+ inv_val |= (vmode->mHSyncPolarity ?
HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_HIGH :
HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_LOW);
- val |= ((vmode->mDataEnablePolarity == TRUE) ?
+
+ inv_val |= (vmode->mDataEnablePolarity ?
HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH :
HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_LOW);
- val |= ((vmode->mHdmiDviSel == TRUE) ?
- HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE :
- HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE);
+
if (hdmi->vic == 39)
- val |= HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH;
+ inv_val |= HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH;
else
- val |= ((vmode->mInterlaced == TRUE) ?
+ inv_val |= (vmode->mInterlaced ?
HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH :
HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_LOW);
- val |= ((vmode->mInterlaced == TRUE) ?
+
+ inv_val |= (vmode->mInterlaced ?
HDMI_FC_INVIDCONF_IN_I_P_INTERLACED :
HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE);
- hdmi_writeb(val, HDMI_FC_INVIDCONF);
+
+ inv_val |= (vmode->mDVI ?
+ HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE :
+ HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE);
+
+ hdmi_writeb(inv_val, HDMI_FC_INVIDCONF);
/* Set up horizontal active pixel region width */
- hdmi_writeb(fb_mode.xres,
- HDMI_FC_INHACTV0);
- hdmi_writeb(fb_mode.xres >> 8,
- HDMI_FC_INHACTV1);
+ hdmi_writeb(fb_mode.xres >> 8, HDMI_FC_INHACTV1);
+ hdmi_writeb(fb_mode.xres, HDMI_FC_INHACTV0);
+
+ /* Set up vertical blanking pixel region width */
+ hdmi_writeb(fb_mode.yres >> 8, HDMI_FC_INVACTV1);
+ hdmi_writeb(fb_mode.yres, HDMI_FC_INVACTV0);
/* Set up horizontal blanking pixel region width */
hblank = fb_mode.left_margin + fb_mode.right_margin +
fb_mode.hsync_len;
- hdmi_writeb(hblank, HDMI_FC_INHBLANK0);
hdmi_writeb(hblank >> 8, HDMI_FC_INHBLANK1);
-
- /* Set up vertical blanking pixel region width */
- hdmi_writeb(fb_mode.yres, HDMI_FC_INVACTV0);
- hdmi_writeb(fb_mode.yres >> 8, HDMI_FC_INVACTV1);
+ hdmi_writeb(hblank, HDMI_FC_INHBLANK0);
/* Set up vertical blanking pixel region width */
vblank = fb_mode.upper_margin + fb_mode.lower_margin +
@@ -1190,222 +1316,389 @@ void hdmi_av_composer(struct mxc_hdmi *hdmi)
hdmi_writeb(vblank, HDMI_FC_INVBLANK);
/* Set up HSYNC active edge delay width (in pixel clks) */
- hdmi_writeb(fb_mode.right_margin, HDMI_FC_HSYNCINDELAY0);
hdmi_writeb(fb_mode.right_margin >> 8, HDMI_FC_HSYNCINDELAY1);
-
- /* Set up HSYNC active pulse width (in pixel clks) */
- hdmi_writeb(fb_mode.hsync_len, HDMI_FC_HSYNCINWIDTH0);
- hdmi_writeb(fb_mode.hsync_len >> 8, HDMI_FC_HSYNCINWIDTH1);
+ hdmi_writeb(fb_mode.right_margin, HDMI_FC_HSYNCINDELAY0);
/* Set up VSYNC active edge delay (in pixel clks) */
hdmi_writeb(fb_mode.lower_margin, HDMI_FC_VSYNCINDELAY);
+ /* Set up HSYNC active pulse width (in pixel clks) */
+ hdmi_writeb(fb_mode.hsync_len >> 8, HDMI_FC_HSYNCINWIDTH1);
+ hdmi_writeb(fb_mode.hsync_len, HDMI_FC_HSYNCINWIDTH0);
+
/* Set up VSYNC active edge delay (in pixel clks) */
hdmi_writeb(fb_mode.vsync_len, HDMI_FC_VSYNCINWIDTH);
- /* control period minimum duration */
- hdmi_writeb(12, HDMI_FC_CTRLDUR);
- hdmi_writeb(32, HDMI_FC_EXCTRLDUR);
- hdmi_writeb(1, HDMI_FC_EXCTRLSPAC);
-
- for (i = 0; i < 3; i++)
- preamble_filter_set(hdmi, (i + 1) * 11, i);
-
- /* configure AVI InfoFrame */
- hdmi_config_AVI(hdmi);
+ dev_dbg(&hdmi->pdev->dev, "%s exit\n", __func__);
}
-static int mxc_hdmi_read_edid(struct mxc_hdmi *hdmi,
- struct fb_info *fbi)
+static int mxc_hdmi_read_edid(struct mxc_hdmi *hdmi)
{
int ret;
u8 edid_old[HDMI_EDID_LEN];
+ dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
+
/* save old edid */
memcpy(edid_old, hdmi->edid, HDMI_EDID_LEN);
- /* edid reading */
- ret = mxc_edid_read(hdmi_i2c->adapter, hdmi_i2c->addr,
- hdmi->edid, &hdmi->edid_cfg, fbi);
+ ret = mxc_edid_read(hdmi_i2c->adapter, hdmi_i2c->addr, hdmi->edid,
+ &hdmi->edid_cfg, hdmi->fbi);
if (ret < 0)
- return ret;
+ return HDMI_EDID_FAIL;
- if (!memcmp(edid_old, hdmi->edid, HDMI_EDID_LEN))
- ret = -2;
- return ret;
+ if (!memcmp(edid_old, hdmi->edid, HDMI_EDID_LEN)) {
+ dev_info(&hdmi->pdev->dev, "same edid\n");
+ return HDMI_EDID_SAME;
+ }
+
+ if (hdmi->fbi->monspecs.modedb_len == 0) {
+ dev_info(&hdmi->pdev->dev, "No modes read from edid\n");
+ return HDMI_EDID_NO_MODES;
+ }
+
+ return HDMI_EDID_SUCCESS;
}
-static void mxc_hdmi_poweron(struct mxc_hdmi *hdmi)
+static void mxc_hdmi_enable_pins(struct mxc_hdmi *hdmi)
{
struct fsl_mxc_hdmi_platform_data *plat = hdmi->pdev->dev.platform_data;
- dev_dbg(&hdmi->pdev->dev, "power on\n");
+ dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
/* Enable pins to HDMI */
if (plat->enable_pins)
plat->enable_pins();
}
-static void mxc_hdmi_poweroff(struct mxc_hdmi *hdmi)
+static void mxc_hdmi_disable_pins(struct mxc_hdmi *hdmi)
{
struct fsl_mxc_hdmi_platform_data *plat = hdmi->pdev->dev.platform_data;
- dev_dbg(&hdmi->pdev->dev, "power off\n");
+ dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
/* Disable pins to HDMI */
if (plat->disable_pins)
plat->disable_pins();
}
-static void mxc_hdmi_enable(struct mxc_hdmi *hdmi)
+static void mxc_hdmi_phy_disable(struct mxc_hdmi *hdmi)
{
- u8 val;
+ dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
- dev_dbg(&hdmi->pdev->dev, "hdmi enable\n");
+ if (!hdmi->phy_enabled)
+ return;
- clk_enable(hdmi->hdmi_iahb_clk);
+ mxc_hdmi_phy_enable_tmds(0);
+ mxc_hdmi_phy_enable_power(0);
- /* Enable HDMI PHY - Set PDDQ=0 and TXPWRON=1 */
- val = hdmi_readb(HDMI_PHY_CONF0);
- val &= ~(HDMI_PHY_CONF0_GEN2_PDDQ_MASK |
- HDMI_PHY_CONF0_GEN2_TXPWRON_MASK);
- val |= HDMI_PHY_CONF0_GEN2_PDDQ_DISABLE |
- HDMI_PHY_CONF0_GEN2_TXPWRON_POWER_ON;
- hdmi_writeb(val, HDMI_PHY_CONF0);
-
- if (hdmi->need_mode_change && hdmi->fb_reg) {
- dev_dbg(&hdmi->pdev->dev, "HDMI changing FB mode\n");
- hdmi->fbi->var.activate |= FB_ACTIVATE_FORCE;
- console_lock();
- hdmi->fbi->flags |= FBINFO_MISC_USEREVENT;
- fb_set_var(hdmi->fbi, &hdmi->fbi->var);
- hdmi->fbi->flags &= ~FBINFO_MISC_USEREVENT;
- console_unlock();
- hdmi->need_mode_change = false;
- }
+ hdmi->phy_enabled = false;
+ dev_dbg(&hdmi->pdev->dev, "%s - exit\n", __func__);
}
-static void mxc_hdmi_disable(struct mxc_hdmi *hdmi)
+/* HDMI Initialization Step B.4 */
+static void mxc_hdmi_enable_video_path(struct mxc_hdmi *hdmi)
{
+ u8 clkdis;
+
+ dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
+
+ /* control period minimum duration */
+ hdmi_writeb(12, HDMI_FC_CTRLDUR);
+ hdmi_writeb(32, HDMI_FC_EXCTRLDUR);
+ hdmi_writeb(1, HDMI_FC_EXCTRLSPAC);
+
+ /* Set to fill TMDS data channels */
+ hdmi_writeb(0x0B, HDMI_FC_CH0PREAM);
+ hdmi_writeb(0x16, HDMI_FC_CH1PREAM);
+ hdmi_writeb(0x21, HDMI_FC_CH2PREAM);
+
+ /* Enable pixel clock and tmds data path */
+ clkdis = 0x7F;
+ clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE;
+ hdmi_writeb(clkdis, HDMI_MC_CLKDIS);
+
+ clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
+ hdmi_writeb(clkdis, HDMI_MC_CLKDIS);
+}
+
+static void hdmi_enable_audio_clk(struct mxc_hdmi *hdmi)
+{
+ u8 clkdis;
+
+ dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
+
+ clkdis = hdmi_readb(HDMI_MC_CLKDIS);
+ clkdis &= ~HDMI_MC_CLKDIS_AUDCLK_DISABLE;
+ hdmi_writeb(clkdis, HDMI_MC_CLKDIS);
+}
+
+/* Workaround to clear the overflow condition */
+static void mxc_hdmi_clear_overflow(void)
+{
+ int count;
u8 val;
- dev_dbg(&hdmi->pdev->dev, "hdmi disable\n");
+ val = hdmi_readb(HDMI_FC_INVIDCONF);
- /* Disable HDMI PHY - Set PDDQ=1 and TXPWRON=0 */
- val = hdmi_readb(HDMI_PHY_CONF0);
- val &= ~(HDMI_PHY_CONF0_GEN2_PDDQ_MASK |
- HDMI_PHY_CONF0_GEN2_TXPWRON_MASK);
- val |= HDMI_PHY_CONF0_GEN2_PDDQ_ENABLE |
- HDMI_PHY_CONF0_GEN2_TXPWRON_POWER_OFF;
- hdmi_writeb(val, HDMI_PHY_CONF0);
+ for (count = 0 ; count < 5 ; count++)
+ hdmi_writeb(val, HDMI_FC_INVIDCONF);
- clk_disable(hdmi->hdmi_iahb_clk);
+ /* TMDS software reset */
+ hdmi_writeb((u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, HDMI_MC_SWRSTZ);
+}
+
+static void hdmi_enable_overflow_interrupts(void)
+{
+ pr_debug("%s\n", __func__);
+ hdmi_writeb(0, HDMI_FC_MASK2);
+ hdmi_writeb(0, HDMI_IH_MUTE_FC_STAT2);
+}
+
+static void hdmi_disable_overflow_interrupts(void)
+{
+ pr_debug("%s\n", __func__);
+ hdmi_writeb(HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK,
+ HDMI_IH_MUTE_FC_STAT2);
+}
+
+static void mxc_hdmi_notify_fb(struct mxc_hdmi *hdmi)
+{
+ dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
+
+ /* Don't notify if we aren't registered yet */
+ WARN_ON(!hdmi->fb_reg);
+
+ /* disable the phy before ipu changes mode */
+ mxc_hdmi_phy_disable(hdmi);
+
+ /*
+ * Note that fb_set_var will block. During this time,
+ * FB_EVENT_MODE_CHANGE callback will happen.
+ * So by the end of this function, mxc_hdmi_setup()
+ * will be done.
+ */
+ hdmi->fbi->var.activate |= FB_ACTIVATE_FORCE;
+ console_lock();
+ hdmi->fbi->flags |= FBINFO_MISC_USEREVENT;
+ fb_set_var(hdmi->fbi, &hdmi->fbi->var);
+ hdmi->fbi->flags &= ~FBINFO_MISC_USEREVENT;
+ console_unlock();
+
+ dev_dbg(&hdmi->pdev->dev, "%s exit\n", __func__);
+}
+
+static void mxc_hdmi_set_mode_to_previous(struct mxc_hdmi *hdmi)
+{
+ const struct fb_videomode *mode;
+
+ dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
+
+ mode = fb_find_nearest_mode(&hdmi->previous_non_vga_mode,
+ &hdmi->fbi->modelist);
+ if (mode) {
+ fb_videomode_to_var(&hdmi->fbi->var, mode);
+ mxc_hdmi_notify_fb(hdmi);
+ } else
+ pr_err("%s: could not find mode in modelist\n", __func__);
+}
+
+static void mxc_hdmi_edid_rebuild_modelist(struct mxc_hdmi *hdmi)
+{
+ int i;
+ const struct fb_videomode *mode;
+ struct fb_videomode m;
+
+ dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
+
+ console_lock();
+
+ fb_destroy_modelist(&hdmi->fbi->modelist);
+ fb_add_videomode(&vga_mode, &hdmi->fbi->modelist);
+
+ for (i = 0; i < hdmi->fbi->monspecs.modedb_len; i++) {
+ /*
+ * We might check here if mode is supported by HDMI.
+ * We do not currently support interlaced modes
+ */
+ if (!(hdmi->fbi->monspecs.modedb[i].vmode &
+ FB_VMODE_INTERLACED)) {
+ dev_dbg(&hdmi->pdev->dev, "Added mode %d:", i);
+ dev_dbg(&hdmi->pdev->dev,
+ "xres = %d, yres = %d, freq = %d\n",
+ hdmi->fbi->monspecs.modedb[i].xres,
+ hdmi->fbi->monspecs.modedb[i].yres,
+ hdmi->fbi->monspecs.modedb[i].refresh);
+
+ fb_add_videomode(&hdmi->fbi->monspecs.modedb[i],
+ &hdmi->fbi->modelist);
+ }
+ }
+
+ console_unlock();
+
+ /* Set the default mode only once. */
+ if (!hdmi->dft_mode_set) {
+ dev_dbg(&hdmi->pdev->dev, "%s: setting to default=%s bpp=%d\n",
+ __func__, hdmi->dft_mode_str, hdmi->default_bpp);
+
+ fb_find_mode(&hdmi->fbi->var, hdmi->fbi,
+ hdmi->dft_mode_str, NULL, 0, NULL,
+ hdmi->default_bpp);
+
+ hdmi->dft_mode_set = true;
+ } else
+ mxc_hdmi_set_mode_to_previous(hdmi);
+
+ fb_var_to_videomode(&m, &hdmi->fbi->var);
+ dump_fb_videomode(&m);
+ mode = fb_find_nearest_mode(&m, &hdmi->fbi->modelist);
+ if (mode) {
+ fb_videomode_to_var(&hdmi->fbi->var, mode);
+ dump_fb_videomode((struct fb_videomode *)mode);
+ mxc_hdmi_notify_fb(hdmi);
+ } else
+ pr_err("%s: could not find mode in modelist\n", __func__);
}
static void mxc_hdmi_default_modelist(struct mxc_hdmi *hdmi)
{
u32 i;
const struct fb_videomode *mode;
+ struct fb_videomode m;
+
+ dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
+
+ /* If not EDID data read, set up default modelist */
+ dev_info(&hdmi->pdev->dev, "No modes read from edid\n");
+ dev_info(&hdmi->pdev->dev, "create default modelist\n");
+
+ /* Set the default mode only once. */
+ if (!hdmi->dft_mode_set) {
+ dev_dbg(&hdmi->pdev->dev, "%s: setting to default=%s bpp=%d\n",
+ __func__, hdmi->dft_mode_str, hdmi->default_bpp);
+
+ fb_find_mode(&hdmi->fbi->var, hdmi->fbi,
+ hdmi->dft_mode_str, NULL, 0, NULL,
+ hdmi->default_bpp);
+
+ hdmi->dft_mode_set = true;
+ } else {
+ fb_videomode_to_var(&hdmi->fbi->var, &hdmi->previous_non_vga_mode);
+ }
+
+ console_lock();
fb_destroy_modelist(&hdmi->fbi->modelist);
+ /*Add all no interlaced CEA mode to default modelist */
for (i = 0; i < ARRAY_SIZE(mxc_cea_mode); i++) {
mode = &mxc_cea_mode[i];
- if ((mode->xres == hdmi->fbi->var.xres) &&
- (mode->yres == hdmi->fbi->var.yres) &&
- !(mode->vmode & FB_VMODE_INTERLACED))
- fb_add_videomode(mode, &hdmi->fbi->modelist);
+ if (!(mode->vmode & FB_VMODE_INTERLACED) && (mode->xres != 0))
+ fb_add_videomode(mode, &hdmi->fbi->modelist);
}
+
+ /*Add XGA and SXGA to default modelist */
+ fb_add_videomode(&xga_mode, &hdmi->fbi->modelist);
+ fb_add_videomode(&sxga_mode, &hdmi->fbi->modelist);
+
+ console_unlock();
+
+ fb_var_to_videomode(&m, &hdmi->fbi->var);
+ dump_fb_videomode(&m);
+ mode = fb_find_nearest_mode(&m, &hdmi->fbi->modelist);
+ if (mode) {
+ fb_videomode_to_var(&hdmi->fbi->var, mode);
+ dump_fb_videomode((struct fb_videomode *)mode);
+ dev_warn(&hdmi->pdev->dev,
+ "Default modelist,the video mode may not support by monitor.\n");
+ mxc_hdmi_notify_fb(hdmi);
+ } else
+ pr_err("%s: could not find mode in default modelist\n", __func__);
}
-static int mxc_hdmi_cable_connected(struct mxc_hdmi *hdmi)
+static void mxc_hdmi_set_mode_to_vga_dvi(struct mxc_hdmi *hdmi)
{
- int ret;
- struct fb_videomode m;
- const struct fb_videomode *mode;
+ dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
- dev_dbg(&hdmi->pdev->dev, "cable connected\n");
+ hdmi_disable_overflow_interrupts();
- hdmi->cable_plugin = true;
+ fb_videomode_to_var(&hdmi->fbi->var, &vga_mode);
- /* edid read */
- ret = mxc_hdmi_read_edid(hdmi, hdmi->fbi);
- if (ret == -2)
- dev_info(&hdmi->pdev->dev, "same edid\n");
- else if (hdmi->fbi->monspecs.modedb_len > 0) {
- int i;
+ hdmi->requesting_vga_for_initialization = true;
+ mxc_hdmi_notify_fb(hdmi);
+ hdmi->requesting_vga_for_initialization = false;
+}
- fb_destroy_modelist(&hdmi->fbi->modelist);
+static void mxc_hdmi_cable_connected(struct mxc_hdmi *hdmi)
+{
+ int edid_status;
- for (i = 0; i < hdmi->fbi->monspecs.modedb_len; i++) {
- /*
- * We might check here if mode is supported by HDMI.
- * We do not currently support interlaced modes
- */
- if (!(hdmi->fbi->monspecs.modedb[i].vmode
- & FB_VMODE_INTERLACED)) {
- dev_dbg(&hdmi->pdev->dev, "Added mode %d:", i);
- dev_dbg(&hdmi->pdev->dev,
- "xres = %d, yres = %d, freq = %d\n",
- hdmi->fbi->monspecs.modedb[i].xres,
- hdmi->fbi->monspecs.modedb[i].yres,
- hdmi->fbi->monspecs.modedb[i].refresh);
- fb_add_videomode(&hdmi->fbi->monspecs.modedb[i],
- &hdmi->fbi->modelist);
- }
- }
+ dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
- fb_var_to_videomode(&m, &hdmi->fbi->var);
- mode = fb_find_nearest_mode(&m,
- &hdmi->fbi->modelist);
+ hdmi->cable_plugin = true;
- fb_videomode_to_var(&hdmi->fbi->var, mode);
- hdmi->need_mode_change = true;
- } else {
- /* If not EDID data readed, setup default modelist */
- dev_info(&hdmi->pdev->dev, "No modes read from edid\n");
- mxc_hdmi_default_modelist(hdmi);
+ /* HDMI Initialization Step B */
+ mxc_hdmi_set_mode_to_vga_dvi(hdmi);
- fb_var_to_videomode(&m, &hdmi->fbi->var);
- mode = fb_find_nearest_mode(&m,
- &hdmi->fbi->modelist);
+ /* HDMI Initialization Step C */
+ edid_status = mxc_hdmi_read_edid(hdmi);
- fb_videomode_to_var(&hdmi->fbi->var, mode);
- hdmi->need_mode_change = true;
- }
+ /* HDMI Initialization Steps D, E, F */
+ switch (edid_status) {
+ case HDMI_EDID_SUCCESS:
+ mxc_hdmi_edid_rebuild_modelist(hdmi);
+ break;
+ case HDMI_EDID_SAME:
+ mxc_hdmi_set_mode_to_previous(hdmi);
+ break;
- return 0;
+ case HDMI_EDID_NO_MODES:
+ case HDMI_EDID_FAIL:
+ default:
+ mxc_hdmi_default_modelist(hdmi);
+ break;
+ }
+
+ dev_dbg(&hdmi->pdev->dev, "%s exit\n", __func__);
}
static void mxc_hdmi_cable_disconnected(struct mxc_hdmi *hdmi)
{
+ dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
+
+ hdmi_disable_overflow_interrupts();
+
hdmi->cable_plugin = false;
}
-static void det_worker(struct work_struct *work)
+static void hotplug_worker(struct work_struct *work)
{
struct delayed_work *delay_work = to_delayed_work(work);
struct mxc_hdmi *hdmi =
- container_of(delay_work, struct mxc_hdmi, det_work);
+ container_of(delay_work, struct mxc_hdmi, hotplug_work);
u32 phy_int_stat, phy_int_pol, phy_int_mask;
u8 val;
bool hdmi_disable = false;
int irq = platform_get_irq(hdmi->pdev, 0);
unsigned long flags;
+ char event_string[16];
+ char *envp[] = { event_string, NULL };
if (!hdmi->irq_enabled) {
+ /* Enable clock long enough to do a few register accesses */
clk_enable(hdmi->hdmi_iahb_clk);
- /* Capture status - used in det_worker ISR */
+ /* Capture status - used in hotplug_worker ISR */
phy_int_stat = hdmi_readb(HDMI_IH_PHY_STAT0);
if ((phy_int_stat & HDMI_IH_PHY_STAT0_HPD) == 0) {
clk_disable(hdmi->hdmi_iahb_clk);
return; /* No interrupts to handle */
}
- dev_dbg(&hdmi->pdev->dev, "Hotplug interrupt received\n");
+ dev_dbg(&hdmi->pdev->dev, "\nHotplug interrupt received\n");
/* Unmask interrupts until handled */
val = hdmi_readb(HDMI_PHY_MASK0);
@@ -1431,15 +1724,26 @@ static void det_worker(struct work_struct *work)
if (phy_int_stat & HDMI_IH_PHY_STAT0_HPD) {
/* cable connection changes */
if (phy_int_pol & HDMI_PHY_HPD) {
+ /*
+ * Plugin event = assume that iahb clock was disabled.
+ */
dev_dbg(&hdmi->pdev->dev, "EVENT=plugin\n");
+
+ clk_enable(hdmi->hdmi_iahb_clk);
mxc_hdmi_cable_connected(hdmi);
- mxc_hdmi_enable(hdmi);
/* Make HPD intr active low to capture unplug event */
val = hdmi_readb(HDMI_PHY_POL0);
val &= ~HDMI_PHY_HPD;
hdmi_writeb(val, HDMI_PHY_POL0);
+
+ sprintf(event_string, "EVENT=plugin");
+ kobject_uevent_env(&hdmi->pdev->dev.kobj, KOBJ_CHANGE, envp);
+
} else if (!(phy_int_pol & HDMI_PHY_HPD)) {
+ /*
+ * Plugout event = assume that iahb clock was enabled.
+ */
dev_dbg(&hdmi->pdev->dev, "EVENT=plugout\n");
mxc_hdmi_cable_disconnected(hdmi);
hdmi_disable = true;
@@ -1448,6 +1752,10 @@ static void det_worker(struct work_struct *work)
val = hdmi_readb(HDMI_PHY_POL0);
val |= HDMI_PHY_HPD;
hdmi_writeb(val, HDMI_PHY_POL0);
+
+ sprintf(event_string, "EVENT=plugout");
+ kobject_uevent_env(&hdmi->pdev->dev.kobj, KOBJ_CHANGE, envp);
+
} else
dev_dbg(&hdmi->pdev->dev, "EVENT=none?\n");
}
@@ -1461,8 +1769,14 @@ static void det_worker(struct work_struct *work)
phy_int_mask &= ~HDMI_PHY_HPD;
hdmi_writeb(phy_int_mask, HDMI_PHY_MASK0);
- if (hdmi_disable)
- mxc_hdmi_disable(hdmi);
+ if (hdmi_readb(HDMI_FC_INT2) & HDMI_FC_INT2_OVERFLOW_MASK)
+ mxc_hdmi_clear_overflow();
+
+ /* We keep the iahb clock enabled only if we are plugged in. */
+ if (hdmi_disable) {
+ mxc_hdmi_phy_disable(hdmi);
+ clk_disable(hdmi->hdmi_iahb_clk);
+ }
spin_unlock_irqrestore(&hdmi->irq_lock, flags);
}
@@ -1486,7 +1800,19 @@ static irqreturn_t mxc_hdmi_hotplug(int irq, void *data)
*/
ret = hdmi_irq_disable(irq);
if (ret == IRQ_DISABLE_FAIL) {
- /* Capture status - used in det_worker ISR */
+ if (hdmi_readb(HDMI_FC_INT2) & HDMI_FC_INT2_OVERFLOW_MASK) {
+ mxc_hdmi_clear_overflow();
+
+ /* clear irq status */
+ hdmi_writeb(HDMI_IH_FC_STAT2_OVERFLOW_MASK,
+ HDMI_IH_FC_STAT2);
+ }
+
+ /*
+ * We could not disable the irq. Probably the audio driver
+ * has enabled it. That also means that iahb clk is enabled.
+ */
+ /* Capture status - used in hotplug_worker ISR */
intr_stat = hdmi_readb(HDMI_IH_PHY_STAT0);
if ((intr_stat & HDMI_IH_PHY_STAT0_HPD) == 0) {
hdmi_irq_enable(irq);
@@ -1508,31 +1834,59 @@ static irqreturn_t mxc_hdmi_hotplug(int irq, void *data)
} else
hdmi->irq_enabled = false;
- schedule_delayed_work(&(hdmi->det_work), msecs_to_jiffies(20));
+ schedule_delayed_work(&(hdmi->hotplug_work), msecs_to_jiffies(20));
spin_unlock_irqrestore(&hdmi->irq_lock, flags);
return IRQ_HANDLED;
}
-static int mxc_hdmi_setup(struct mxc_hdmi *hdmi)
+static void mxc_hdmi_setup(struct mxc_hdmi *hdmi)
{
struct fb_videomode m;
const struct fb_videomode *edid_mode;
+ dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
+
fb_var_to_videomode(&m, &hdmi->fbi->var);
- if (!list_empty(&hdmi->fbi->modelist)) {
- edid_mode = fb_find_nearest_mode(&m, &hdmi->fbi->modelist);
+ dump_fb_videomode(&m);
- hdmi->vic = mxc_edid_mode_to_vic(edid_mode);
- } else
- hdmi->vic = 0;
+ /* Exit the setup if we are already set to this video mode */
+ if (fb_mode_is_equal(&hdmi->previous_mode, &m)) {
+ dev_dbg(&hdmi->pdev->dev,
+ "%s video mode did not change.\n", __func__);
+ mxc_hdmi_phy_init(hdmi);
+ return;
+ }
+ dev_dbg(&hdmi->pdev->dev, "%s - video mode changed\n", __func__);
+
+ hdmi_disable_overflow_interrupts();
+
+ /* Save mode as 'previous_mode' so that we can know if mode changed. */
+ memcpy(&hdmi->previous_mode, &m, sizeof(struct fb_videomode));
+
+ hdmi->vic = 0;
+ if (!hdmi->requesting_vga_for_initialization) {
+ /* Save mode if this isn't the result of requesting
+ * vga default. */
+ memcpy(&hdmi->previous_non_vga_mode, &m,
+ sizeof(struct fb_videomode));
+
+ if (!list_empty(&hdmi->fbi->modelist)) {
+ edid_mode = fb_find_nearest_mode(&m, &hdmi->fbi->modelist);
+ pr_debug("edid mode ");
+ dump_fb_videomode((struct fb_videomode *)edid_mode);
+ hdmi->vic = mxc_edid_mode_to_vic(edid_mode);
+ }
+ }
if (hdmi->vic == 0) {
dev_dbg(&hdmi->pdev->dev, "Non-CEA mode used in HDMI\n");
- hdmi->hdmi_data.video_mode.mHdmiDviSel = FALSE;
- } else
- hdmi->hdmi_data.video_mode.mHdmiDviSel = TRUE;
+ hdmi->hdmi_data.video_mode.mDVI = true;
+ } else {
+ dev_dbg(&hdmi->pdev->dev, "CEA mode used vic=%d\n", hdmi->vic);
+ hdmi->hdmi_data.video_mode.mDVI = false;
+ }
if ((hdmi->vic == 6) || (hdmi->vic == 7) ||
(hdmi->vic == 21) || (hdmi->vic == 22) ||
@@ -1570,20 +1924,78 @@ static int mxc_hdmi_setup(struct mxc_hdmi *hdmi)
hdmi->hdmi_data.enc_color_depth = 8;
hdmi->hdmi_data.pix_repet_factor = 0;
hdmi->hdmi_data.hdcp_enable = 0;
- hdmi->hdmi_data.video_mode.mDataEnablePolarity = TRUE;
+ hdmi->hdmi_data.video_mode.mDataEnablePolarity = true;
- hdmi_video_force_output(hdmi, TRUE);
+ /* HDMI Initialization Step B.1 */
hdmi_av_composer(hdmi);
+
+ /* HDMI Initializateion Step B.2 */
+ mxc_hdmi_phy_init(hdmi);
+
+ /* HDMI Initialization Step B.3 */
+ mxc_hdmi_enable_video_path(hdmi);
+
+ /* not for DVI mode */
+ if (hdmi->hdmi_data.video_mode.mDVI)
+ dev_dbg(&hdmi->pdev->dev, "%s DVI mode\n", __func__);
+ else {
+ dev_dbg(&hdmi->pdev->dev, "%s CEA mode\n", __func__);
+
+ /* HDMI Initialization Step E - Configure audio */
+ hdmi_clk_regenerator_update_pixel_clock();
+ hdmi_enable_audio_clk(hdmi);
+
+ /* HDMI Initialization Step F - Configure AVI InfoFrame */
+ hdmi_config_AVI(hdmi);
+ }
+
hdmi_video_packetize(hdmi);
hdmi_video_csc(hdmi);
hdmi_video_sample(hdmi);
hdmi_tx_hdcp_config(hdmi);
- hdmi_phy_init(hdmi, TRUE);
- hdmi_video_force_output(hdmi, FALSE);
- /*FIXME: audio CTS/N should be set after ipu display timming finish */
- /*hdmi_set_clk_regenerator();*/
- return 0;
+ mxc_hdmi_clear_overflow();
+ if (hdmi->cable_plugin && !hdmi->hdmi_data.video_mode.mDVI)
+ hdmi_enable_overflow_interrupts();
+
+ dev_dbg(&hdmi->pdev->dev, "%s exit\n\n", __func__);
+}
+
+/* Wait until we are registered to enable interrupts */
+static void mxc_hdmi_fb_registered(struct mxc_hdmi *hdmi)
+{
+ unsigned long flags;
+
+ if (hdmi->fb_reg)
+ return;
+
+ clk_enable(hdmi->hdmi_iahb_clk);
+
+ spin_lock_irqsave(&hdmi->irq_lock, flags);
+
+ dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
+
+ hdmi_writeb(HDMI_PHY_I2CM_INT_ADDR_DONE_POL,
+ HDMI_PHY_I2CM_INT_ADDR);
+
+ hdmi_writeb(HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL |
+ HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL,
+ HDMI_PHY_I2CM_CTLINT_ADDR);
+
+ /* enable cable hot plug irq */
+ hdmi_writeb((u8)~HDMI_PHY_HPD, HDMI_PHY_MASK0);
+
+ /* Clear Hotplug interrupts */
+ hdmi_writeb(HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0);
+
+ /* Unmute interrupts */
+ hdmi_writeb(~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0);
+
+ hdmi->fb_reg = true;
+
+ spin_unlock_irqrestore(&hdmi->irq_lock, flags);
+
+ clk_disable(hdmi->hdmi_iahb_clk);
}
static int mxc_hdmi_fb_event(struct notifier_block *nb,
@@ -1597,44 +2009,61 @@ static int mxc_hdmi_fb_event(struct notifier_block *nb,
switch (val) {
case FB_EVENT_FB_REGISTERED:
- hdmi->fb_reg = true;
+ dev_dbg(&hdmi->pdev->dev, "event=FB_EVENT_FB_REGISTERED\n");
+ mxc_hdmi_fb_registered(hdmi);
break;
+
case FB_EVENT_FB_UNREGISTERED:
+ dev_dbg(&hdmi->pdev->dev, "event=FB_EVENT_FB_UNREGISTERED\n");
hdmi->fb_reg = false;
break;
+
case FB_EVENT_MODE_CHANGE:
- mxc_hdmi_setup(hdmi);
+ dev_dbg(&hdmi->pdev->dev, "event=FB_EVENT_MODE_CHANGE\n");
+ if (hdmi->fb_reg)
+ mxc_hdmi_setup(hdmi);
break;
+
case FB_EVENT_BLANK:
- if (*((int *)event->data) == FB_BLANK_UNBLANK)
- mxc_hdmi_poweron(hdmi);
- else
- mxc_hdmi_poweroff(hdmi);
+ if (*((int *)event->data) == FB_BLANK_UNBLANK) {
+ dev_dbg(&hdmi->pdev->dev,
+ "event=FB_EVENT_BLANK - UNBLANK\n");
+ mxc_hdmi_enable_pins(hdmi);
+ } else {
+ dev_dbg(&hdmi->pdev->dev,
+ "event=FB_EVENT_BLANK - BLANK\n");
+ mxc_hdmi_disable_pins(hdmi);
+ }
break;
}
return 0;
}
-static int mxc_hdmi_disp_init(struct mxc_dispdrv_entry *disp)
+/* HDMI Initialization Step A */
+static int mxc_hdmi_disp_init(struct mxc_dispdrv_handle *disp,
+ struct mxc_dispdrv_setting *setting)
{
int ret = 0;
struct mxc_hdmi *hdmi = mxc_dispdrv_getdata(disp);
- struct mxc_dispdrv_setting *setting = mxc_dispdrv_getsetting(disp);
struct fsl_mxc_hdmi_platform_data *plat = hdmi->pdev->dev.platform_data;
int irq = platform_get_irq(hdmi->pdev, 0);
- bool found = false;
- u8 val;
- const struct fb_videomode *mode;
- struct fb_videomode m;
- struct fb_var_screeninfo var;
+
+ dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
if (!plat || irq < 0)
return -ENODEV;
+ hdmi->dft_mode_set = false;
+
setting->dev_id = mxc_hdmi_ipu_id;
setting->disp_id = mxc_hdmi_disp_id;
setting->if_fmt = IPU_PIX_FMT_RGB24;
+ hdmi->dft_mode_str = setting->dft_mode_str;
+ hdmi->default_bpp = setting->default_bpp;
+ dev_dbg(&hdmi->pdev->dev, "%s - default mode %s bpp=%d\n",
+ __func__, hdmi->dft_mode_str, hdmi->default_bpp);
+
hdmi->fbi = setting->fbi;
/* Claim HDMI pins */
@@ -1688,84 +2117,26 @@ static int mxc_hdmi_disp_init(struct mxc_dispdrv_entry *disp)
hdmi_readb(HDMI_PRODUCT_ID0),
hdmi_readb(HDMI_PRODUCT_ID1));
- INIT_LIST_HEAD(&hdmi->fbi->modelist);
-
- /* try to read edid */
- ret = mxc_hdmi_read_edid(hdmi, hdmi->fbi);
- if (ret < 0) {
- /* If not EDID data readed, setup default modelist */
- dev_info(&hdmi->pdev->dev, "No modes read from edid\n");
- mxc_hdmi_default_modelist(hdmi);
-
- fb_var_to_videomode(&m, &hdmi->fbi->var);
- mode = fb_find_nearest_mode(&m,
- &hdmi->fbi->modelist);
+ /* To prevent overflows in HDMI_IH_FC_STAT2, set the clk regenerator
+ * N and cts values before enabling phy */
+ hdmi_init_clk_regenerator();
- fb_videomode_to_var(&hdmi->fbi->var, mode);
- hdmi->need_mode_change = true;
- } else if (hdmi->fbi->monspecs.modedb_len > 0) {
- int i;
-
- for (i = 0; i < hdmi->fbi->monspecs.modedb_len; i++) {
- /*
- * We might check here if mode is supported by HDMI.
- * Also, we do not currently support interlaced modes
- */
- fb_videomode_to_var(&var, &hdmi->fbi->monspecs.modedb[i]);
- if (!(hdmi->fbi->monspecs.modedb[i].vmode
- & FB_VMODE_INTERLACED)) {
- dev_dbg(&hdmi->pdev->dev, "Adding mode %d:", i);
- dev_dbg(&hdmi->pdev->dev,
- "xres = %d, yres = %d, freq = %d\n",
- hdmi->fbi->monspecs.modedb[i].xres,
- hdmi->fbi->monspecs.modedb[i].yres,
- hdmi->fbi->monspecs.modedb[i].refresh);
- fb_add_videomode(
- &hdmi->fbi->monspecs.modedb[i],
- &hdmi->fbi->modelist);
- }
- }
-
- fb_find_mode(&hdmi->fbi->var, hdmi->fbi,
- setting->dft_mode_str, NULL, 0, NULL,
- setting->default_bpp);
-
- fb_var_to_videomode(&m, &hdmi->fbi->var);
- mode = fb_find_nearest_mode(&m,
- &hdmi->fbi->modelist);
- fb_videomode_to_var(&hdmi->fbi->var, mode);
- found = 1;
-
- hdmi->need_mode_change = true;
- }
-
- if (!found) {
- ret = fb_find_mode(&hdmi->fbi->var, hdmi->fbi,
- setting->dft_mode_str, NULL, 0, NULL,
- setting->default_bpp);
- if (!ret) {
- ret = -EINVAL;
- goto efindmode;
- }
- }
+ INIT_LIST_HEAD(&hdmi->fbi->modelist);
spin_lock_init(&hdmi->irq_lock);
- INIT_DELAYED_WORK(&(hdmi->det_work), det_worker);
+ fb_add_videomode(&vga_mode, &hdmi->fbi->modelist);
+ fb_videomode_to_var(&hdmi->fbi->var, &vga_mode);
+
+ INIT_DELAYED_WORK(&hdmi->hotplug_work, hotplug_worker);
/* Configure registers related to HDMI interrupt
* generation before registering IRQ. */
hdmi_writeb(HDMI_PHY_HPD, HDMI_PHY_POL0);
- /* enable cable hot plug irq */
- val = ~HDMI_PHY_HPD;
- hdmi_writeb(val, HDMI_PHY_MASK0);
-
/* Clear Hotplug interrupts */
hdmi_writeb(HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0);
- hdmi_writeb(~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0);
-
hdmi->nb.notifier_call = mxc_hdmi_fb_event;
ret = fb_register_client(&hdmi->nb);
if (ret < 0)
@@ -1773,8 +2144,6 @@ static int mxc_hdmi_disp_init(struct mxc_dispdrv_entry *disp)
memset(&hdmi->hdmi_data, 0, sizeof(struct hdmi_data_info));
- mxc_hdmi_setup(hdmi);
-
/* Disable IAHB clock while waiting for hotplug interrupt.
* ISFR clock must remain enabled for hotplug to work. */
clk_disable(hdmi->hdmi_iahb_clk);
@@ -1790,11 +2159,25 @@ static int mxc_hdmi_disp_init(struct mxc_dispdrv_entry *disp)
goto ereqirq;
}
+ ret = device_create_file(&hdmi->pdev->dev, &dev_attr_fb_name);
+ if (ret < 0)
+ dev_warn(&hdmi->pdev->dev,
+ "cound not create sys node for fb name\n");
+ ret = device_create_file(&hdmi->pdev->dev, &dev_attr_cable_state);
+ if (ret < 0)
+ dev_warn(&hdmi->pdev->dev,
+ "cound not create sys node for cable state\n");
+ ret = device_create_file(&hdmi->pdev->dev, &dev_attr_edid);
+ if (ret < 0)
+ dev_warn(&hdmi->pdev->dev,
+ "cound not create sys node for edid\n");
+
+ dev_dbg(&hdmi->pdev->dev, "%s exit\n", __func__);
+
return ret;
efbclient:
free_irq(irq, hdmi);
-efindmode:
ereqirq:
clk_disable(hdmi->hdmi_iahb_clk);
erate2:
@@ -1806,17 +2189,21 @@ erate1:
egetclk1:
plat->put_pins();
egetpins:
+ dev_dbg(&hdmi->pdev->dev, "%s error exit\n", __func__);
+
return ret;
}
-static void mxc_hdmi_disp_deinit(struct mxc_dispdrv_entry *disp)
+static void mxc_hdmi_disp_deinit(struct mxc_dispdrv_handle *disp)
{
struct mxc_hdmi *hdmi = mxc_dispdrv_getdata(disp);
struct fsl_mxc_hdmi_platform_data *plat = hdmi->pdev->dev.platform_data;
+ dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
+
fb_unregister_client(&hdmi->nb);
- mxc_hdmi_poweroff(hdmi);
+ mxc_hdmi_disable_pins(hdmi);
clk_disable(hdmi->hdmi_isfr_clk);
clk_put(hdmi->hdmi_isfr_clk);
@@ -1891,6 +2278,8 @@ static int mxc_hdmi_remove(struct platform_device *pdev)
fb_unregister_client(&hdmi->nb);
+ mxc_dispdrv_puthandle(hdmi->disp_mxc_hdmi);
+ mxc_dispdrv_unregister(hdmi->disp_mxc_hdmi);
/* No new work will be scheduled, wait for running ISR */
free_irq(irq, hdmi);
kfree(hdmi);
@@ -1919,7 +2308,6 @@ static void __exit mxc_hdmi_exit(void)
}
module_exit(mxc_hdmi_exit);
-
static int __devinit mxc_hdmi_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
diff --git a/include/linux/fb.h b/include/linux/fb.h
index a67fe1e2b02..1d6836c498d 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -549,8 +549,6 @@ struct fb_cursor_user {
#define FB_EVENT_FB_UNBIND 0x0E
/* CONSOLE-SPECIFIC: remap all consoles to new fb - for vga switcheroo */
#define FB_EVENT_REMAP_ALL_CONSOLE 0x0F
-/* PRE MODE CHANGE added by fsl */
-#define FB_EVENT_PREMODE_CHANGE 0x10
struct fb_event {
struct fb_info *info;
diff --git a/include/linux/mfd/mxc-hdmi-core.h b/include/linux/mfd/mxc-hdmi-core.h
index f449946a50f..8c6ce456dda 100644
--- a/include/linux/mfd/mxc-hdmi-core.h
+++ b/include/linux/mfd/mxc-hdmi-core.h
@@ -22,16 +22,21 @@
#define IRQ_DISABLE_SUCCEED 0
#define IRQ_DISABLE_FAIL 1
+bool hdmi_check_overflow(void);
+
u8 hdmi_readb(unsigned int reg);
void hdmi_writeb(u8 value, unsigned int reg);
void hdmi_mask_writeb(u8 data, unsigned int addr, u8 shift, u8 mask);
unsigned int hdmi_read4(unsigned int reg);
void hdmi_write4(unsigned int value, unsigned int reg);
+
void hdmi_irq_init(void);
void hdmi_irq_enable(int irq);
unsigned int hdmi_irq_disable(int irq);
+
void hdmi_set_sample_rate(unsigned int rate);
-void hdmi_set_clk_regenerator(void);
+void hdmi_init_clk_regenerator(void);
+void hdmi_clk_regenerator_update_pixel_clock(void);
extern int mxc_hdmi_ipu_id;
extern int mxc_hdmi_disp_id;