diff options
author | Eric Miao <eric.miao@linaro.org> | 2011-12-30 22:38:31 +0800 |
---|---|---|
committer | Eric Miao <eric.miao@linaro.org> | 2011-12-30 22:38:31 +0800 |
commit | 2d8bcd8e88e6e5a5fadd3367815c2b2da1029ecb (patch) | |
tree | 13aa97923c3fd2c3da82e7ac0667fdcf1f21c82f | |
parent | dd6acff5a2c1c55ef1f1fb18e4d1b05215bf3ad3 (diff) | |
parent | aa87db76e6334652c529839df7eb6659e41c156a (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
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; |