diff options
Diffstat (limited to 'drivers/video/omap2')
29 files changed, 8190 insertions, 1333 deletions
diff --git a/drivers/video/omap2/Kconfig b/drivers/video/omap2/Kconfig index d877c361abda..18bb8359c72a 100644 --- a/drivers/video/omap2/Kconfig +++ b/drivers/video/omap2/Kconfig @@ -3,6 +3,10 @@ config OMAP2_VRAM config OMAP2_VRFB bool + depends on ARCH_OMAP2 || ARCH_OMAP3 + default y if FB_OMAP2 + help + OMAP VRFB buffer support is efficient for rotation source "drivers/video/omap2/dss/Kconfig" source "drivers/video/omap2/omapfb/Kconfig" diff --git a/drivers/video/omap2/displays/Kconfig b/drivers/video/omap2/displays/Kconfig index dfb57ee50861..45b92f666506 100644 --- a/drivers/video/omap2/displays/Kconfig +++ b/drivers/video/omap2/displays/Kconfig @@ -17,8 +17,15 @@ config PANEL_SHARP_LQ043T1DG01 tristate "Sharp LQ043T1DG01 LCD Panel" depends on OMAP2_DSS help + LCD Panel used in TI's OMAP3517 EVM boards +config PANEL_PICO_DLP + tristate "OMAP PICO DLP Panel" + depends on OMAP2_DSS + help + LCD Panel used in TI's SDP4430 and EVM boards + config PANEL_TAAL tristate "Taal DSI Panel" depends on OMAP2_DSS_DSI @@ -33,8 +40,14 @@ config PANEL_TOPPOLY_TDO35S config PANEL_TPO_TD043MTEA1 tristate "TPO TD043MTEA1 LCD Panel" - depends on OMAP2_DSS && I2C + depends on OMAP2_DSS && SPI help LCD Panel used in OMAP3 Pandora +config PANEL_ACX565AKM + tristate "ACX565AKM Panel" + depends on OMAP2_DSS_SDI + select BACKLIGHT_CLASS_DEVICE + help + This is the LCD panel used on Nokia N900 endmenu diff --git a/drivers/video/omap2/displays/Makefile b/drivers/video/omap2/displays/Makefile index e2bb32168dee..eb7945dca099 100644 --- a/drivers/video/omap2/displays/Makefile +++ b/drivers/video/omap2/displays/Makefile @@ -3,5 +3,7 @@ obj-$(CONFIG_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o obj-$(CONFIG_PANEL_SHARP_LQ043T1DG01) += panel-sharp-lq043t1dg01.o obj-$(CONFIG_PANEL_TAAL) += panel-taal.o +obj-$(CONFIG_PANEL_PICO_DLP) += panel-picodlp.o obj-$(CONFIG_PANEL_TOPPOLY_TDO35S) += panel-toppoly-tdo35s.o obj-$(CONFIG_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o +obj-$(CONFIG_PANEL_ACX565AKM) += panel-acx565akm.o diff --git a/drivers/video/omap2/displays/panel-acx565akm.c b/drivers/video/omap2/displays/panel-acx565akm.c new file mode 100644 index 000000000000..1f8eb70e2937 --- /dev/null +++ b/drivers/video/omap2/displays/panel-acx565akm.c @@ -0,0 +1,819 @@ +/* + * Support for ACX565AKM LCD Panel used on Nokia N900 + * + * Copyright (C) 2010 Nokia Corporation + * + * Original Driver Author: Imre Deak <imre.deak@nokia.com> + * Based on panel-generic.c by Tomi Valkeinen <tomi.valkeinen@nokia.com> + * Adapted to new DSS2 framework: Roger Quadros <roger.quadros@nokia.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/spi/spi.h> +#include <linux/jiffies.h> +#include <linux/sched.h> +#include <linux/backlight.h> +#include <linux/fb.h> + +#include <plat/display.h> + +#define MIPID_CMD_READ_DISP_ID 0x04 +#define MIPID_CMD_READ_RED 0x06 +#define MIPID_CMD_READ_GREEN 0x07 +#define MIPID_CMD_READ_BLUE 0x08 +#define MIPID_CMD_READ_DISP_STATUS 0x09 +#define MIPID_CMD_RDDSDR 0x0F +#define MIPID_CMD_SLEEP_IN 0x10 +#define MIPID_CMD_SLEEP_OUT 0x11 +#define MIPID_CMD_DISP_OFF 0x28 +#define MIPID_CMD_DISP_ON 0x29 +#define MIPID_CMD_WRITE_DISP_BRIGHTNESS 0x51 +#define MIPID_CMD_READ_DISP_BRIGHTNESS 0x52 +#define MIPID_CMD_WRITE_CTRL_DISP 0x53 + +#define CTRL_DISP_BRIGHTNESS_CTRL_ON (1 << 5) +#define CTRL_DISP_AMBIENT_LIGHT_CTRL_ON (1 << 4) +#define CTRL_DISP_BACKLIGHT_ON (1 << 2) +#define CTRL_DISP_AUTO_BRIGHTNESS_ON (1 << 1) + +#define MIPID_CMD_READ_CTRL_DISP 0x54 +#define MIPID_CMD_WRITE_CABC 0x55 +#define MIPID_CMD_READ_CABC 0x56 + +#define MIPID_VER_LPH8923 3 +#define MIPID_VER_LS041Y3 4 +#define MIPID_VER_L4F00311 8 +#define MIPID_VER_ACX565AKM 9 + +struct acx565akm_device { + char *name; + int enabled; + int model; + int revision; + u8 display_id[3]; + unsigned has_bc:1; + unsigned has_cabc:1; + unsigned cabc_mode; + unsigned long hw_guard_end; /* next value of jiffies + when we can issue the + next sleep in/out command */ + unsigned long hw_guard_wait; /* max guard time in jiffies */ + + struct spi_device *spi; + struct mutex mutex; + + struct omap_dss_device *dssdev; + struct backlight_device *bl_dev; +}; + +static struct acx565akm_device acx_dev; +static int acx565akm_bl_update_status(struct backlight_device *dev); + +/*--------------------MIPID interface-----------------------------*/ + +static void acx565akm_transfer(struct acx565akm_device *md, int cmd, + const u8 *wbuf, int wlen, u8 *rbuf, int rlen) +{ + struct spi_message m; + struct spi_transfer *x, xfer[5]; + int r; + + BUG_ON(md->spi == NULL); + + spi_message_init(&m); + + memset(xfer, 0, sizeof(xfer)); + x = &xfer[0]; + + cmd &= 0xff; + x->tx_buf = &cmd; + x->bits_per_word = 9; + x->len = 2; + + if (rlen > 1 && wlen == 0) { + /* + * Between the command and the response data there is a + * dummy clock cycle. Add an extra bit after the command + * word to account for this. + */ + x->bits_per_word = 10; + cmd <<= 1; + } + spi_message_add_tail(x, &m); + + if (wlen) { + x++; + x->tx_buf = wbuf; + x->len = wlen; + x->bits_per_word = 9; + spi_message_add_tail(x, &m); + } + + if (rlen) { + x++; + x->rx_buf = rbuf; + x->len = rlen; + spi_message_add_tail(x, &m); + } + + r = spi_sync(md->spi, &m); + if (r < 0) + dev_dbg(&md->spi->dev, "spi_sync %d\n", r); +} + +static inline void acx565akm_cmd(struct acx565akm_device *md, int cmd) +{ + acx565akm_transfer(md, cmd, NULL, 0, NULL, 0); +} + +static inline void acx565akm_write(struct acx565akm_device *md, + int reg, const u8 *buf, int len) +{ + acx565akm_transfer(md, reg, buf, len, NULL, 0); +} + +static inline void acx565akm_read(struct acx565akm_device *md, + int reg, u8 *buf, int len) +{ + acx565akm_transfer(md, reg, NULL, 0, buf, len); +} + +static void hw_guard_start(struct acx565akm_device *md, int guard_msec) +{ + md->hw_guard_wait = msecs_to_jiffies(guard_msec); + md->hw_guard_end = jiffies + md->hw_guard_wait; +} + +static void hw_guard_wait(struct acx565akm_device *md) +{ + unsigned long wait = md->hw_guard_end - jiffies; + + if ((long)wait > 0 && wait <= md->hw_guard_wait) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(wait); + } +} + +/*----------------------MIPID wrappers----------------------------*/ + +static void set_sleep_mode(struct acx565akm_device *md, int on) +{ + int cmd; + + if (on) + cmd = MIPID_CMD_SLEEP_IN; + else + cmd = MIPID_CMD_SLEEP_OUT; + /* + * We have to keep 120msec between sleep in/out commands. + * (8.2.15, 8.2.16). + */ + hw_guard_wait(md); + acx565akm_cmd(md, cmd); + hw_guard_start(md, 120); +} + +static void set_display_state(struct acx565akm_device *md, int enabled) +{ + int cmd = enabled ? MIPID_CMD_DISP_ON : MIPID_CMD_DISP_OFF; + + acx565akm_cmd(md, cmd); +} + +static int panel_enabled(struct acx565akm_device *md) +{ + u32 disp_status; + int enabled; + + acx565akm_read(md, MIPID_CMD_READ_DISP_STATUS, (u8 *)&disp_status, 4); + disp_status = __be32_to_cpu(disp_status); + enabled = (disp_status & (1 << 17)) && (disp_status & (1 << 10)); + dev_dbg(&md->spi->dev, + "LCD panel %senabled by bootloader (status 0x%04x)\n", + enabled ? "" : "not ", disp_status); + return enabled; +} + +static int panel_detect(struct acx565akm_device *md) +{ + acx565akm_read(md, MIPID_CMD_READ_DISP_ID, md->display_id, 3); + dev_dbg(&md->spi->dev, "MIPI display ID: %02x%02x%02x\n", + md->display_id[0], md->display_id[1], md->display_id[2]); + + switch (md->display_id[0]) { + case 0x10: + md->model = MIPID_VER_ACX565AKM; + md->name = "acx565akm"; + md->has_bc = 1; + md->has_cabc = 1; + break; + case 0x29: + md->model = MIPID_VER_L4F00311; + md->name = "l4f00311"; + break; + case 0x45: + md->model = MIPID_VER_LPH8923; + md->name = "lph8923"; + break; + case 0x83: + md->model = MIPID_VER_LS041Y3; + md->name = "ls041y3"; + break; + default: + md->name = "unknown"; + dev_err(&md->spi->dev, "invalid display ID\n"); + return -ENODEV; + } + + md->revision = md->display_id[1]; + + dev_info(&md->spi->dev, "omapfb: %s rev %02x LCD detected\n", + md->name, md->revision); + + return 0; +} + +/*----------------------Backlight Control-------------------------*/ + +static void enable_backlight_ctrl(struct acx565akm_device *md, int enable) +{ + u16 ctrl; + + acx565akm_read(md, MIPID_CMD_READ_CTRL_DISP, (u8 *)&ctrl, 1); + if (enable) { + ctrl |= CTRL_DISP_BRIGHTNESS_CTRL_ON | + CTRL_DISP_BACKLIGHT_ON; + } else { + ctrl &= ~(CTRL_DISP_BRIGHTNESS_CTRL_ON | + CTRL_DISP_BACKLIGHT_ON); + } + + ctrl |= 1 << 8; + acx565akm_write(md, MIPID_CMD_WRITE_CTRL_DISP, (u8 *)&ctrl, 2); +} + +static void set_cabc_mode(struct acx565akm_device *md, unsigned mode) +{ + u16 cabc_ctrl; + + md->cabc_mode = mode; + if (!md->enabled) + return; + cabc_ctrl = 0; + acx565akm_read(md, MIPID_CMD_READ_CABC, (u8 *)&cabc_ctrl, 1); + cabc_ctrl &= ~3; + cabc_ctrl |= (1 << 8) | (mode & 3); + acx565akm_write(md, MIPID_CMD_WRITE_CABC, (u8 *)&cabc_ctrl, 2); +} + +static unsigned get_cabc_mode(struct acx565akm_device *md) +{ + return md->cabc_mode; +} + +static unsigned get_hw_cabc_mode(struct acx565akm_device *md) +{ + u8 cabc_ctrl; + + acx565akm_read(md, MIPID_CMD_READ_CABC, &cabc_ctrl, 1); + return cabc_ctrl & 3; +} + +static void acx565akm_set_brightness(struct acx565akm_device *md, int level) +{ + int bv; + + bv = level | (1 << 8); + acx565akm_write(md, MIPID_CMD_WRITE_DISP_BRIGHTNESS, (u8 *)&bv, 2); + + if (level) + enable_backlight_ctrl(md, 1); + else + enable_backlight_ctrl(md, 0); +} + +static int acx565akm_get_actual_brightness(struct acx565akm_device *md) +{ + u8 bv; + + acx565akm_read(md, MIPID_CMD_READ_DISP_BRIGHTNESS, &bv, 1); + + return bv; +} + + +static int acx565akm_bl_update_status(struct backlight_device *dev) +{ + struct acx565akm_device *md = dev_get_drvdata(&dev->dev); + int r; + int level; + + dev_dbg(&md->spi->dev, "%s\n", __func__); + + mutex_lock(&md->mutex); + + if (dev->props.fb_blank == FB_BLANK_UNBLANK && + dev->props.power == FB_BLANK_UNBLANK) + level = dev->props.brightness; + else + level = 0; + + r = 0; + if (md->has_bc) + acx565akm_set_brightness(md, level); + else if (md->dssdev->set_backlight) + r = md->dssdev->set_backlight(md->dssdev, level); + else + r = -ENODEV; + + mutex_unlock(&md->mutex); + + return r; +} + +static int acx565akm_bl_get_intensity(struct backlight_device *dev) +{ + struct acx565akm_device *md = dev_get_drvdata(&dev->dev); + + dev_dbg(&dev->dev, "%s\n", __func__); + + if (!md->has_bc && md->dssdev->set_backlight == NULL) + return -ENODEV; + + if (dev->props.fb_blank == FB_BLANK_UNBLANK && + dev->props.power == FB_BLANK_UNBLANK) { + if (md->has_bc) + return acx565akm_get_actual_brightness(md); + else + return dev->props.brightness; + } + + return 0; +} + +static const struct backlight_ops acx565akm_bl_ops = { + .get_brightness = acx565akm_bl_get_intensity, + .update_status = acx565akm_bl_update_status, +}; + +/*--------------------Auto Brightness control via Sysfs---------------------*/ + +static const char *cabc_modes[] = { + "off", /* always used when CABC is not supported */ + "ui", + "still-image", + "moving-image", +}; + +static ssize_t show_cabc_mode(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acx565akm_device *md = dev_get_drvdata(dev); + const char *mode_str; + int mode; + int len; + + if (!md->has_cabc) + mode = 0; + else + mode = get_cabc_mode(md); + mode_str = "unknown"; + if (mode >= 0 && mode < ARRAY_SIZE(cabc_modes)) + mode_str = cabc_modes[mode]; + len = snprintf(buf, PAGE_SIZE, "%s\n", mode_str); + + return len < PAGE_SIZE - 1 ? len : PAGE_SIZE - 1; +} + +static ssize_t store_cabc_mode(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct acx565akm_device *md = dev_get_drvdata(dev); + int i; + + for (i = 0; i < ARRAY_SIZE(cabc_modes); i++) { + const char *mode_str = cabc_modes[i]; + int cmp_len = strlen(mode_str); + + if (count > 0 && buf[count - 1] == '\n') + count--; + if (count != cmp_len) + continue; + + if (strncmp(buf, mode_str, cmp_len) == 0) + break; + } + + if (i == ARRAY_SIZE(cabc_modes)) + return -EINVAL; + + if (!md->has_cabc && i != 0) + return -EINVAL; + + mutex_lock(&md->mutex); + set_cabc_mode(md, i); + mutex_unlock(&md->mutex); + + return count; +} + +static ssize_t show_cabc_available_modes(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acx565akm_device *md = dev_get_drvdata(dev); + int len; + int i; + + if (!md->has_cabc) + return snprintf(buf, PAGE_SIZE, "%s\n", cabc_modes[0]); + + for (i = 0, len = 0; + len < PAGE_SIZE && i < ARRAY_SIZE(cabc_modes); i++) + len += snprintf(&buf[len], PAGE_SIZE - len, "%s%s%s", + i ? " " : "", cabc_modes[i], + i == ARRAY_SIZE(cabc_modes) - 1 ? "\n" : ""); + + return len < PAGE_SIZE ? len : PAGE_SIZE - 1; +} + +static DEVICE_ATTR(cabc_mode, S_IRUGO | S_IWUSR, + show_cabc_mode, store_cabc_mode); +static DEVICE_ATTR(cabc_available_modes, S_IRUGO, + show_cabc_available_modes, NULL); + +static struct attribute *bldev_attrs[] = { + &dev_attr_cabc_mode.attr, + &dev_attr_cabc_available_modes.attr, + NULL, +}; + +static struct attribute_group bldev_attr_group = { + .attrs = bldev_attrs, +}; + + +/*---------------------------ACX Panel----------------------------*/ + +static int acx_get_recommended_bpp(struct omap_dss_device *dssdev) +{ + return 16; +} + +static struct omap_video_timings acx_panel_timings = { + .x_res = 800, + .y_res = 480, + .pixel_clock = 24000, + .hfp = 28, + .hsw = 4, + .hbp = 24, + .vfp = 3, + .vsw = 3, + .vbp = 4, +}; + +static int acx_panel_probe(struct omap_dss_device *dssdev) +{ + int r; + struct acx565akm_device *md = &acx_dev; + struct backlight_device *bldev; + int max_brightness, brightness; + struct backlight_properties props; + + dev_dbg(&dssdev->dev, "%s\n", __func__); + dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | + OMAP_DSS_LCD_IHS; + /* FIXME AC bias ? */ + dssdev->panel.timings = acx_panel_timings; + + if (dssdev->platform_enable) + dssdev->platform_enable(dssdev); + /* + * After reset we have to wait 5 msec before the first + * command can be sent. + */ + msleep(5); + + md->enabled = panel_enabled(md); + + r = panel_detect(md); + if (r) { + dev_err(&dssdev->dev, "%s panel detect error\n", __func__); + if (!md->enabled && dssdev->platform_disable) + dssdev->platform_disable(dssdev); + return r; + } + + mutex_lock(&acx_dev.mutex); + acx_dev.dssdev = dssdev; + mutex_unlock(&acx_dev.mutex); + + if (!md->enabled) { + if (dssdev->platform_disable) + dssdev->platform_disable(dssdev); + } + + /*------- Backlight control --------*/ + + props.fb_blank = FB_BLANK_UNBLANK; + props.power = FB_BLANK_UNBLANK; + + bldev = backlight_device_register("acx565akm", &md->spi->dev, + md, &acx565akm_bl_ops, &props); + md->bl_dev = bldev; + if (md->has_cabc) { + r = sysfs_create_group(&bldev->dev.kobj, &bldev_attr_group); + if (r) { + dev_err(&bldev->dev, + "%s failed to create sysfs files\n", __func__); + backlight_device_unregister(bldev); + return r; + } + md->cabc_mode = get_hw_cabc_mode(md); + } + + if (md->has_bc) + max_brightness = 255; + else + max_brightness = dssdev->max_backlight_level; + + if (md->has_bc) + brightness = acx565akm_get_actual_brightness(md); + else if (dssdev->get_backlight) + brightness = dssdev->get_backlight(dssdev); + else + brightness = 0; + + bldev->props.max_brightness = max_brightness; + bldev->props.brightness = brightness; + + acx565akm_bl_update_status(bldev); + return 0; +} + +static void acx_panel_remove(struct omap_dss_device *dssdev) +{ + struct acx565akm_device *md = &acx_dev; + + dev_dbg(&dssdev->dev, "%s\n", __func__); + sysfs_remove_group(&md->bl_dev->dev.kobj, &bldev_attr_group); + backlight_device_unregister(md->bl_dev); + mutex_lock(&acx_dev.mutex); + acx_dev.dssdev = NULL; + mutex_unlock(&acx_dev.mutex); +} + +static int acx_panel_power_on(struct omap_dss_device *dssdev) +{ + struct acx565akm_device *md = &acx_dev; + int r; + + dev_dbg(&dssdev->dev, "%s\n", __func__); + + mutex_lock(&md->mutex); + + r = omapdss_sdi_display_enable(dssdev); + if (r) { + pr_err("%s sdi enable failed\n", __func__); + return r; + } + + /*FIXME tweak me */ + msleep(50); + + if (dssdev->platform_enable) { + r = dssdev->platform_enable(dssdev); + if (r) + goto fail; + } + + if (md->enabled) { + dev_dbg(&md->spi->dev, "panel already enabled\n"); + mutex_unlock(&md->mutex); + return 0; + } + + /* + * We have to meet all the following delay requirements: + * 1. tRW: reset pulse width 10usec (7.12.1) + * 2. tRT: reset cancel time 5msec (7.12.1) + * 3. Providing PCLK,HS,VS signals for 2 frames = ~50msec worst + * case (7.6.2) + * 4. 120msec before the sleep out command (7.12.1) + */ + msleep(120); + + set_sleep_mode(md, 0); + md->enabled = 1; + + /* 5msec between sleep out and the next command. (8.2.16) */ + msleep(5); + set_display_state(md, 1); + set_cabc_mode(md, md->cabc_mode); + + mutex_unlock(&md->mutex); + + return acx565akm_bl_update_status(md->bl_dev); +fail: + omapdss_sdi_display_disable(dssdev); + return r; +} + +static void acx_panel_power_off(struct omap_dss_device *dssdev) +{ + struct acx565akm_device *md = &acx_dev; + + dev_dbg(&dssdev->dev, "%s\n", __func__); + + mutex_lock(&md->mutex); + + if (!md->enabled) { + mutex_unlock(&md->mutex); + return; + } + set_display_state(md, 0); + set_sleep_mode(md, 1); + md->enabled = 0; + /* + * We have to provide PCLK,HS,VS signals for 2 frames (worst case + * ~50msec) after sending the sleep in command and asserting the + * reset signal. We probably could assert the reset w/o the delay + * but we still delay to avoid possible artifacts. (7.6.1) + */ + msleep(50); + + if (dssdev->platform_disable) + dssdev->platform_disable(dssdev); + + /* FIXME need to tweak this delay */ + msleep(100); + + omapdss_sdi_display_disable(dssdev); + + mutex_unlock(&md->mutex); +} + +static int acx_panel_enable(struct omap_dss_device *dssdev) +{ + int r; + + dev_dbg(&dssdev->dev, "%s\n", __func__); + r = acx_panel_power_on(dssdev); + + if (r) + return r; + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + return 0; +} + +static void acx_panel_disable(struct omap_dss_device *dssdev) +{ + dev_dbg(&dssdev->dev, "%s\n", __func__); + acx_panel_power_off(dssdev); + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; +} + +static int acx_panel_suspend(struct omap_dss_device *dssdev) +{ + dev_dbg(&dssdev->dev, "%s\n", __func__); + acx_panel_power_off(dssdev); + dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; + return 0; +} + +static int acx_panel_resume(struct omap_dss_device *dssdev) +{ + int r; + + dev_dbg(&dssdev->dev, "%s\n", __func__); + r = acx_panel_power_on(dssdev); + if (r) + return r; + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + return 0; +} + +static void acx_panel_set_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + int r; + + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) + omapdss_sdi_display_disable(dssdev); + + dssdev->panel.timings = *timings; + + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { + r = omapdss_sdi_display_enable(dssdev); + if (r) + dev_err(&dssdev->dev, "%s enable failed\n", __func__); + } +} + +static void acx_panel_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + *timings = dssdev->panel.timings; +} + +static int acx_panel_check_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + return 0; +} + + +static struct omap_dss_driver acx_panel_driver = { + .probe = acx_panel_probe, + .remove = acx_panel_remove, + + .enable = acx_panel_enable, + .disable = acx_panel_disable, + .suspend = acx_panel_suspend, + .resume = acx_panel_resume, + + .set_timings = acx_panel_set_timings, + .get_timings = acx_panel_get_timings, + .check_timings = acx_panel_check_timings, + + .get_recommended_bpp = acx_get_recommended_bpp, + + .driver = { + .name = "panel-acx565akm", + .owner = THIS_MODULE, + }, +}; + +/*--------------------SPI probe-------------------------*/ + +static int acx565akm_spi_probe(struct spi_device *spi) +{ + struct acx565akm_device *md = &acx_dev; + + dev_dbg(&spi->dev, "%s\n", __func__); + + spi->mode = SPI_MODE_3; + md->spi = spi; + mutex_init(&md->mutex); + dev_set_drvdata(&spi->dev, md); + + omap_dss_register_driver(&acx_panel_driver); + + return 0; +} + +static int acx565akm_spi_remove(struct spi_device *spi) +{ + struct acx565akm_device *md = dev_get_drvdata(&spi->dev); + + dev_dbg(&md->spi->dev, "%s\n", __func__); + omap_dss_unregister_driver(&acx_panel_driver); + + return 0; +} + +static struct spi_driver acx565akm_spi_driver = { + .driver = { + .name = "acx565akm", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = acx565akm_spi_probe, + .remove = __devexit_p(acx565akm_spi_remove), +}; + +static int __init acx565akm_init(void) +{ + return spi_register_driver(&acx565akm_spi_driver); +} + +static void __exit acx565akm_exit(void) +{ + spi_unregister_driver(&acx565akm_spi_driver); +} + +module_init(acx565akm_init); +module_exit(acx565akm_exit); + +MODULE_AUTHOR("Nokia Corporation"); +MODULE_DESCRIPTION("acx565akm LCD Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/displays/panel-picodlp.c b/drivers/video/omap2/displays/panel-picodlp.c new file mode 100644 index 000000000000..76b01e2a73c9 --- /dev/null +++ b/drivers/video/omap2/displays/panel-picodlp.c @@ -0,0 +1,584 @@ +/* + * pico_i2c_driver.c + * pico DLP driver + * + * Copyright (C) 2009 Texas Instruments + * Author: mythripk <mythripk@ti.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ +#include <linux/input.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <plat/display.h> +#include<../drivers/video/omap2/dss/dss.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/firmware.h> +#include "panel-picodlp.h" +#include <linux/slab.h> + + +#define DRIVER_NAME "pico_i2c" +/* How much data we can put into single write block */ +#define MAX_I2C_WRITE_BLOCK_SIZE 32 +#define PICO_MAJOR 1 /* 2 bits */ +#define PICO_MINOR 1 /* 2 bits */ +#define DSI_DIV2 (0x40C) +#define DSI_DIV_LCD (16) +#define DSI_DIV_PCD (0) +#define DSI_CONTROL2 (0x238) + +static int display_control_reg = (0x58000000 + 0x1000); +void __iomem *dispc_base; + +static struct omap_video_timings pico_ls_timings = { + .x_res = 864, + .y_res = 480, + .hsw = 7, + .hfp = 11, + .hbp = 7, + + .vsw = 2, + .vfp = 3, + .vbp = 14, +}; + +struct pico { + struct i2c_client *client; + struct mutex xfer_lock; + } *sd; + + +static int dlp_read_block(int reg, u8 *data, int len); +static int pico_i2c_write(int reg, u32 value); + +static int dlp_write_block(int reg, const u8 *data, int len) +{ + unsigned char wb[MAX_I2C_WRITE_BLOCK_SIZE + 1]; + struct i2c_msg msg; + int r; + int i; + + if (len < 1 || + len > MAX_I2C_WRITE_BLOCK_SIZE) { + dev_info(&sd->client->dev, "too long syn_write_block len %d\n", + len); + return -EIO; + } + + wb[0] = reg & 0xff; + + for (i = 0; i < len; i++) + wb[i + 1] = data[i]; + + mutex_lock(&sd->xfer_lock); + + msg.addr = sd->client->addr; + msg.flags = 0; + msg.len = len + 1; + msg.buf = wb; + + r = i2c_transfer(sd->client->adapter, &msg, 1); + mutex_unlock(&sd->xfer_lock); + + if (r == 1) { + for (i = 0; i < len; i++) + dev_info(&sd->client->dev, + "addr %x bw 0x%02x[%d]: 0x%02x\n", + sd->client->addr, reg + i, i, data[i]); + } + + + if (r == 1) + return 0; + + return r; +} + +static int pico_i2c_write(int reg, u32 value) +{ + u8 data[4]; + + data[0] = (value & 0xFF000000) >> 24; + data[1] = (value & 0x00FF0000) >> 16; + data[2] = (value & 0x0000FF00) >> 8; + data[3] = (value & 0x000000FF); + + return dlp_write_block(reg, data, 4); +} + +static int dlp_read_block(int reg, u8 *data, int len) +{ + unsigned char wb[2]; + struct i2c_msg msg[2]; + int r; + mutex_lock(&sd->xfer_lock); + wb[0] = 0x15 & 0xff; + wb[1] = reg & 0xff; + msg[0].addr = sd->client->addr; + msg[0].len = 2; + msg[0].flags = 0; + msg[0].buf = wb; + msg[1].addr = sd->client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = len; + msg[1].buf = data; + + r = i2c_transfer(sd->client->adapter, msg, 2); + mutex_unlock(&sd->xfer_lock); + + + if (r == 2) { + int i; + + for (i = 0; i < len; i++) + dev_info(&sd->client->dev, + "addr %x br 0x%02x[%d]: 0x%02x\n", + sd->client->addr, reg + i, i, data[i]); + } + + + if (r == 2) + return len; + + return r; +} + + +static __attribute__ ((unused)) int pico_i2c_read(int reg) +{ + int r; + u8 data[4]; + data[1] = data[2] = data[3] = data[0] = 0; + + r = dlp_read_block(reg, data, 4); + return (int)data[3] | ((int)(data[2]) << 8) | ((int)(data[1]) << 16) | ((int)(data[0]) << 24); +} + +/* + * Configure datapath for splash image operation + * @param flash_address - I - splash image to load from flash + * @param flash_num_bytes - I - splash image to load from flash + * @param CMT_SEQz - I - select mailbox to load data to: 0=sequence/DRC, 1=CMT/splash + * @param table_number - I - splash image to load from flash + * @return 0 - no errors + * 1 - invalid flash address specified + * 2 - invalid mailbox specified + * 3 - invalid table_number / mailbox combination + */ +int dpp2600_flash_dma(int flash_address, int flash_num_bytes, int CMT_SEQz, int table_number) + +{ + int mailbox_address, mailbox_select; + + /* check argument validity */ + if (flash_address > 0x1fffff) + return 1; + if (CMT_SEQz > 1) + return 2; + if ((CMT_SEQz == 0 && table_number > 6) || + (CMT_SEQz == 1 && table_number > 5)) + return 3; + /* set mailbox parameters */ + if (CMT_SEQz) { + mailbox_address = CMT_SPLASH_LUT_START_ADDR; + mailbox_select = CMT_SPLASH_LUT_DEST_SELECT; + } else { + mailbox_address = SEQ_RESET_LUT_START_ADDR; + mailbox_select = SEQ_RESET_LUT_DEST_SELECT; + } + + /* configure DMA from flash to LUT */ + pico_i2c_write(PBC_CONTROL, 0); + pico_i2c_write(FLASH_START_ADDR, flash_address); + pico_i2c_write(FLASH_READ_BYTES, flash_num_bytes); + pico_i2c_write(mailbox_address, 0); + pico_i2c_write(mailbox_select, table_number); + /* transfer control to flash controller */ + pico_i2c_write(PBC_CONTROL, 1); + mdelay(1000); + /* return register access to I2c */ + pico_i2c_write(PBC_CONTROL, 0); + /* close LUT access */ + pico_i2c_write(mailbox_select, 0); + return 0; +} + +/* Configure datapath for parallel RGB operation */ +static void dpp2600_config_rgb(void) +{ + /* enable video board output drivers */ + pico_i2c_write(SEQ_CONTROL, 0); + pico_i2c_write(ACTGEN_CONTROL, 0x10); + pico_i2c_write(SEQUENCE_MODE, SEQ_LOCK); + pico_i2c_write(DATA_FORMAT, RGB888); + pico_i2c_write(INPUT_RESOLUTION, WVGA_864_LANDSCAPE); + pico_i2c_write(INPUT_SOURCE, PARALLEL_RGB); + pico_i2c_write(CPU_IF_SYNC_METHOD, 1); + /* turn image back on */ + pico_i2c_write(SEQ_CONTROL, 1); +} + +/* + * Configure datapath for splash image operation + * @param image_number - I - splash image to load from flash + * @return 0 - no errors + * 1 - invalid image_number specified + */ +int dpp2600_config_splash(int image_number) +{ + int address, size, resolution; + printk("dpp2600 config splash"); + resolution = QWVGA_LANDSCAPE; + switch (image_number) { + case 0: + address = SPLASH_0_START_ADDR; + size = SPLASH_0_SIZE; + break; + case 1: + address = SPLASH_1_START_ADDR; + size = SPLASH_1_SIZE; + break; + case 2: + address = SPLASH_2_START_ADDR; + size = SPLASH_2_SIZE; + break; + case 3: + address = SPLASH_3_START_ADDR; + size = SPLASH_3_SIZE; + break; + case 4: + address = OPT_SPLASH_0_START_ADDR; + size = OPT_SPLASH_0_SIZE; + resolution = WVGA_DMD_OPTICAL_TEST; + break; + default: + return 1; + }; + /* configure sequence, data format and resolution */ + pico_i2c_write(SEQ_CONTROL, 0); + pico_i2c_write(SEQUENCE_MODE, SEQ_FREE_RUN); + pico_i2c_write(DATA_FORMAT, RGB565); + pico_i2c_write(INPUT_RESOLUTION, resolution); + pico_i2c_write(INPUT_SOURCE, SPLASH_SCREEN); + dpp2600_flash_dma(address, size, 1, SPLASH_LUT); + /* turn image back on */ + pico_i2c_write(SEQ_CONTROL, 1); + return 0; +} + +/* + * Modify contents of a 32-bit register + * @param anAddr Register address + * @param aClearMask Any bits set in this mask will be cleared + * @param aSetMask Bits to be set after the clear + */ +void modify_pico_register(unsigned int Addr, unsigned int ClearMask, + unsigned int SetMask) +{ + u32 val; + val = __raw_readl(Addr); + val &= ~(ClearMask); + val |= (SetMask); + __raw_writel(val, Addr); +} + +/* + * Configure datapath for test pattern generator operation + * + * @param pattern_select - I - color table to load + * + * @return 0 - no errors + * 1 - invalid pattern specified + */ +int dpp2600_config_tpg(int pattern_select) +{ + if (pattern_select > TPG_ANSI_CHECKERBOARD) + return 1; + pico_i2c_write(SEQ_CONTROL, 0); + pico_i2c_write(INPUT_RESOLUTION, WVGA_854_LANDSCAPE); + pico_i2c_write(SEQUENCE_MODE, SEQ_LOCK); + pico_i2c_write(TEST_PAT_SELECT, pattern_select); + pico_i2c_write(INPUT_SOURCE, 1); + pico_i2c_write(SEQ_CONTROL, 1); + return 0; +} + +static int pico_i2c_initialize(void) +{ + + mutex_init(&sd->xfer_lock); + mdelay(100); + /* pico Soft reset */ + pico_i2c_write(SOFT_RESET, 1); + /*Front end reset*/ + pico_i2c_write(DMD_PARK_TRIGGER, 1); + /* write the software version number to a spare register field */ + pico_i2c_write(MISC_REG, PICO_MAJOR<<2 | PICO_MINOR); + pico_i2c_write(SEQ_CONTROL, 0); + pico_i2c_write(SEQ_VECTOR, 0x100); + pico_i2c_write(DMD_BLOCK_COUNT, 7); + pico_i2c_write(DMD_VCC_CONTROL, 0x109); + pico_i2c_write(DMD_PARK_PULSE_COUNT, 0xA); + pico_i2c_write(DMD_PARK_PULSE_WIDTH, 0xB); + pico_i2c_write(DMD_PARK_DELAY, 0x2ED); + pico_i2c_write(DMD_SHADOW_ENABLE, 0); + /* serial flash common config */ + pico_i2c_write(FLASH_OPCODE, 0xB); + pico_i2c_write(FLASH_DUMMY_BYTES, 1); + pico_i2c_write(FLASH_ADDR_BYTES, 3); + /* configure DMA from flash to LUT */ + dpp2600_flash_dma(CMT_LUT_0_START_ADDR, CMT_LUT_0_SIZE, 1, CMT_LUT_ALL); + /* SEQ and DRC look-up tables */ + dpp2600_flash_dma(SEQUENCE_0_START_ADDR, SEQUENCE_0_SIZE, 0, SEQ_SEQ_LUT); + dpp2600_flash_dma(DRC_TABLE_0_START_ADDR, DRC_TABLE_0_SIZE, 0, SEQ_DRC_LUT_ALL); + /* frame buffer memory controller enable */ + pico_i2c_write(SDC_ENABLE, 1); + /* AGC control */ + pico_i2c_write(AGC_CTRL, 7); + /*CCA */ + pico_i2c_write(CCA_C1A, 0x100); + pico_i2c_write(CCA_C1B, 0x000); + pico_i2c_write(CCA_C1C, 0x000); + pico_i2c_write(CCA_C2A, 0x000); + pico_i2c_write(CCA_C2B, 0x100); + pico_i2c_write(CCA_C2C, 0x000); + pico_i2c_write(CCA_C3A, 0x000); + pico_i2c_write(CCA_C3B, 0x000); + pico_i2c_write(CCA_C3C, 0x100); + pico_i2c_write(CCA_C7A, 0x100); + pico_i2c_write(CCA_C7B, 0x100); + pico_i2c_write(CCA_C7C, 0x100); + pico_i2c_write(CCA_ENABLE, 1); + /* main datapath setup */ + pico_i2c_write(CPU_IF_MODE, 1); + pico_i2c_write(SHORT_FLIP, 1); + pico_i2c_write(CURTAIN_CONTROL, 0); + /* display Logo splash image */ + dpp2600_config_splash(1); + pico_i2c_write(DMD_PARK_TRIGGER, 0); + /* LED PWM and enables */ + pico_i2c_write(R_DRIVE_CURRENT, 0x298); + pico_i2c_write(G_DRIVE_CURRENT, 0x298); + pico_i2c_write(B_DRIVE_CURRENT, 0x298); + pico_i2c_write(RGB_DRIVER_ENABLE, 7); + mdelay(10000); + dpp2600_config_rgb(); + return 0; + + +} + +static int pico_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + printk("pico probe called"); + sd = kzalloc(sizeof(struct pico), GFP_KERNEL); + if (sd == NULL) + return -ENOMEM; + i2c_set_clientdata(client, sd); + sd->client = client; + return 0; +} + +static int __exit pico_remove(struct i2c_client *client) +{ + struct pico *sd1 = i2c_get_clientdata(client); + kfree(sd1); + i2c_set_clientdata(client, NULL); + + return 0; +} + +static const struct i2c_device_id pico_id[] = { + { "picoDLP_i2c_driver", 0 }, + { }, +}; + + +static int picoDLP_panel_enable(struct omap_dss_device *dssdev) +{ + int r = 0; + printk("pico DLP init is called "); + if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) { + r = -EINVAL; + return r; + } + if (dssdev->platform_enable) { + r = dssdev->platform_enable(dssdev); + if (r) + return r; + } + r = omapdss_dpi_display_enable(dssdev); + if (r) { + dev_err(&dssdev->dev, "failed to enable DPI\n"); + return r; + } + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + display_control_reg = (int)dispc_base; + /* Specify the Display Controller Logic Clock Divisor*/ + modify_pico_register(display_control_reg + DSI_DIV2, 0xFF | + (0XFF << DSI_DIV_LCD), (1 << DSI_DIV_LCD) | (4 << DSI_DIV_PCD)); + /* LCD output Enabled */ + modify_pico_register(display_control_reg + DSI_CONTROL2, (1<<11), 0x00000000); + pico_i2c_initialize(); + return 0; + +} +static void pico_get_resolution(struct omap_dss_device *dssdev, + u16 *xres, u16 *yres) +{ + *xres = dssdev->panel.timings.x_res; + *yres = dssdev->panel.timings.y_res; +} + +static int picoDLP_panel_probe(struct omap_dss_device *dssdev) +{ + dssdev->panel.config &= ~((OMAP_DSS_LCD_IPC) | (OMAP_DSS_LCD_IEO)); + dssdev->panel.config = (OMAP_DSS_LCD_TFT) | (OMAP_DSS_LCD_ONOFF) | + (OMAP_DSS_LCD_IHS) | + (OMAP_DSS_LCD_IVS) ; + dssdev->panel.acb = 0x0; + dssdev->panel.timings = pico_ls_timings; + + return 0; +} + +static void picoDLP_panel_remove(struct omap_dss_device *dssdev) +{ + return ; +} + +static void picoDLP_panel_disable(struct omap_dss_device *dssdev) +{ + int r = 0; + /* Turn of DLP Power */ + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { + r = -EINVAL; + } + + omapdss_dpi_display_disable(dssdev); + + if (dssdev->platform_disable) + dssdev->platform_disable(dssdev); + + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; + +} + +static int picoDLP_panel_suspend(struct omap_dss_device *dssdev) +{ + int r = 0; + /* Turn of DLP Power */ + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { + r = -EINVAL; + return r; + } + + omapdss_dpi_display_disable(dssdev); + + if (dssdev->platform_disable) + dssdev->platform_disable(dssdev); + + dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; + + return 0; +} + +static int picoDLP_panel_resume(struct omap_dss_device *dssdev) +{ + int r = 0; + printk("pico DLP resume is called "); + if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) { + r = -EINVAL; + return r; + } + if (dssdev->platform_enable) { + r = dssdev->platform_enable(dssdev); + if (r) + return r; + } + r = omapdss_dpi_display_enable(dssdev); + if (r) { + dev_err(&dssdev->dev, "failed to enable DPI\n"); + return r; + } + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + display_control_reg = (int)dispc_base; + /* Specify the Display Controller Logic Clock Divisor*/ + modify_pico_register(display_control_reg + DSI_DIV2, 0xFF | + (0XFF << DSI_DIV_LCD), (1 << DSI_DIV_LCD) | (4 << DSI_DIV_PCD)); + /* LCD output Enabled */ + modify_pico_register(display_control_reg + DSI_CONTROL2, (1<<11), 0x00000000); + pico_i2c_initialize(); + return 0; + +} + +static struct omap_dss_driver picoDLP_driver = { + .probe = picoDLP_panel_probe, + .remove = picoDLP_panel_remove, + .enable = picoDLP_panel_enable, + .disable = picoDLP_panel_disable, + .get_resolution = pico_get_resolution, + .suspend = picoDLP_panel_suspend, + .resume = picoDLP_panel_resume, + .driver = { + .name = "picoDLP_panel", + .owner = THIS_MODULE, + }, +}; + +static struct i2c_driver pico_i2c_driver = { + .driver = { + .name = "pico_i2c_driver", + }, + .probe = pico_probe, + .remove = __exit_p(pico_remove), + .id_table = pico_id, + +}; + +static int __init pico_i2c_init(void) +{ + int r; + r = i2c_add_driver(&pico_i2c_driver); + if (r < 0) { + printk(KERN_WARNING DRIVER_NAME + " driver registration failed\n"); + return r; + } + omap_dss_register_driver(&picoDLP_driver); + return 0; +} + + +static void __exit pico_i2c_exit(void) +{ + i2c_del_driver(&pico_i2c_driver); + omap_dss_unregister_driver(&picoDLP_driver); +} + + +module_init(pico_i2c_init); +module_exit(pico_i2c_exit); + + + +MODULE_DESCRIPTION("pico DLP driver"); +MODULE_LICENSE("GPL"); + + + diff --git a/drivers/video/omap2/displays/panel-picodlp.h b/drivers/video/omap2/displays/panel-picodlp.h new file mode 100644 index 000000000000..01c1b9528467 --- /dev/null +++ b/drivers/video/omap2/displays/panel-picodlp.h @@ -0,0 +1,281 @@ +#define MAIN_STATUS 0x03 +#define PBC_CONTROL 0x08 +#define INPUT_SOURCE 0x0B +#define INPUT_RESOLUTION 0x0C +#define DATA_FORMAT 0x0D +#define IMG_ROTATION 0x0E +#define LONG_FLIP 0x0F +#define SHORT_FLIP 0x10 +#define TEST_PAT_SELECT 0x11 +#define R_DRIVE_CURRENT 0x12 +#define G_DRIVE_CURRENT 0x13 +#define B_DRIVE_CURRENT 0x14 +#define READ_REG_SELECT 0x15 +#define RGB_DRIVER_ENABLE 0x16 + +#define CPU_IF_MODE 0x18 +#define FRAME_RATE 0x19 +#define CPU_IF_SYNC_METHOD 0x1A +#define CPU_IF_SOF 0x1B +#define CPU_IF_EOF 0x1C +#define CPU_IF_SLEEP 0x1D + +#define SEQUENCE_MODE 0x1E +#define SOFT_RESET 0x1F +#define FRONT_END_RESET 0x21 +#define AUTO_PWR_ENABLE 0x22 + +#define VSYNC_LINE_DELAY 0x23 +#define CPU_PI_HORIZ_START 0x24 +#define CPU_PI_VERT_START 0x25 +#define CPU_PI_HORIZ_WIDTH 0x26 +#define CPU_PI_VERT_HEIGHT 0x27 + +#define PIXEL_MASK_CROP 0x28 +#define CROP_FIRST_LINE 0x29 +#define CROP_LAST_LINE 0x2A +#define CROP_FIRST_PIXEL 0x2B +#define CROP_LAST_PIXEL 0x2C +#define DMD_PARK_TRIGGER 0x2D + +#define MISC_REG 0x30 + +/* AGC registers */ +#define AGC_CTRL 0x50 +#define AGC_CLIPPED_PIXS 0x55 +#define AGC_BRIGHT_PIXS 0x56 +#define AGC_BG_PIXS 0x57 +#define AGC_SAFETY_MARGIN 0x17 + +/* CCA registers */ +#define CCA_ENABLE 0x5E +#define CCA_C1A 0x5F +#define CCA_C1B 0x60 +#define CCA_C1C 0x61 +#define CCA_C2A 0x62 +#define CCA_C2B 0x63 +#define CCA_C2C 0x64 +#define CCA_C3A 0x65 +#define CCA_C3B 0x66 +#define CCA_C3C 0x67 +#define CCA_C7A 0x71 +#define CCA_C7B 0x72 +#define CCA_C7C 0x73 + +/* registers for DMA operations from flash to DPP2600 LUTs */ +#define FLASH_ADDR_BYTES 0x74 +#define FLASH_DUMMY_BYTES 0x75 +#define FLASH_WRITE_BYTES 0x76 +#define FLASH_READ_BYTES 0x77 +#define FLASH_OPCODE 0x78 +#define FLASH_START_ADDR 0x79 +#define FLASH_DUMMY2 0x7A +#define FLASH_WRITE_DATA 0x7B + +#define TEMPORAL_DITH_DISABLE 0x7E +#define SEQ_CONTROL 0x82 +#define SEQ_VECTOR 0x83 +#define DMD_BLOCK_COUNT 0x84 +#define DMD_VCC_CONTROL 0x86 +#define DMD_PARK_PULSE_COUNT 0x87 +#define DMD_PARK_PULSE_WIDTH 0x88 +#define DMD_PARK_DELAY 0x89 +#define DMD_SHADOW_ENABLE 0x8E +#define SEQ_STATUS 0x8F +#define FLASH_CLOCK_CONTROL 0x98 +#define DMD_PARK 0x2D + +#define SDRAM_BIST_ENABLE 0x46 +#define DDR_DRIVER_STRENGTH 0x9A +#define SDC_ENABLE 0x9D +#define SDC_BUFF_SWAP_DISABLE 0xA3 +#define CURTAIN_CONTROL 0xA6 +#define DDR_BUS_SWAP_ENABLE 0xA7 +#define DMD_TRC_ENABLE 0xA8 +#define DMD_BUS_SWAP_ENABLE 0xA9 + +#define ACTGEN_ENABLE 0xAE +#define ACTGEN_CONTROL 0xAF +#define ACTGEN_HORIZ_BP 0xB0 +#define ACTGEN_VERT_BP 0xB1 + +/* LUT access */ +#define CMT_SPLASH_LUT_START_ADDR 0xFA +#define CMT_SPLASH_LUT_DEST_SELECT 0xFB +#define CMT_SPLASH_LUT_DATA 0xFC +#define SEQ_RESET_LUT_START_ADDR 0xFD +#define SEQ_RESET_LUT_DEST_SELECT 0xFE +#define SEQ_RESET_LUT_DATA 0xFF + +/* input source defines */ +#define PARALLEL_RGB 0 +#define INT_TEST_PATTERN 1 +#define SPLASH_SCREEN 2 +#define CPU_INTF 3 +#define BT656 4 + +/* input resolution defines */ +#define QVGA_PORTRAIT 0 /* (240h*320v) */ +#define QVGA_LANDSCAPE 1 /* (320h*240v) */ +#define QWVGA_LANDSCAPE 3 /* (427h*240v) */ +#define VGA_PORTRAIT_2_3 4 /* (430h*640v) */ +#define VGA_LANDSCAPE_3_2 5 /* (640h*430v) */ +#define VGA_PORTRAIT 6 /* (480h*640v) */ +#define VGA_LANDSCAPE 7 /* (640h*480v) */ +#define WVGA_720_PORTRAIT 8 /* (480h*720v) */ +#define WVGA_720_LANDSCAPE 9 /* (720h*480v) */ +#define WVGA_752_PORTRAIT 10 /* (480h*752v) */ +#define WVGA_752_LANDSCAPE 11 /* (752h*480v) */ +#define WVGA_800_PORTRAIT 12 /* (480h*800v) */ +#define WVGA_800_LANDSCAPE 13 /* (800h*480v) */ +#define WVGA_852_PORTRAIT 14 /* (480h*852v) */ +#define WVGA_852_LANDSCAPE 15 /* (852h*480v) */ +#define WVGA_853_PORTRAIT 16 /* (480h*853v) */ +#define WVGA_853_LANDSCAPE 17 /* (853h*480v) */ +#define WVGA_854_PORTRAIT 18 /* (480h*854v) */ +#define WVGA_854_LANDSCAPE 19 /* (854h*480v) */ +#define WVGA_864_PORTRAIT 20 /* (480h*864v) */ +#define WVGA_864_LANDSCAPE 21 /* (864h*480v) */ +#define NTSC_LANDSCAPE 23 /* (720h*240v) */ +#define PAL_LANDSCAPE 25 /* (720h*288v) */ +#define VGA_DMD_OPTICAL_TEST 33 /* (456h*684v) */ +#define WVGA_DMD_OPTICAL_TEST 35 /* (608h*684v) */ + +/* data format defines */ +#define RGB565 0 +#define RGB666 1 +#define RGB888 2 + +/* test pattern defines */ +#define TPG_CHECKERBOARD 0 +#define TPG_BLACK 1 +#define TPG_WHITE 2 +#define TPG_RED 3 +#define TPG_BLUE 4 +#define TPG_GREEN 5 +#define TPG_VLINES_BLACK 6 +#define TPG_HLINES_BLACK 7 +#define TPG_VLINES_ALT 8 +#define TPG_HLINES_ALT 9 +#define TPG_DIAG_LINES 10 +#define TPG_GREYRAMP_VERT 11 +#define TPG_GREYRAMP_HORIZ 12 +#define TPG_ANSI_CHECKERBOARD 13 + +/* sequence mode defines */ +#define SEQ_FREE_RUN 0 +#define SEQ_LOCK 1 + +/* curtain color defines */ +#define CURTAIN_BLACK 0 +#define CURTAIN_RED 1 +#define CURTAIN_GREEN 2 +#define CURTAIN_BLUE 3 +#define CURTAIN_YELLOW 4 +#define CURTAIN_MAGENTA 5 +#define CURTAIN_CYAN 6 +#define CURTAIN_WHITE 7 + +/* LUT defines */ +#define CMT_LUT_NONE 0 +#define CMT_LUT_GREEN 1 +#define CMT_LUT_RED 2 +#define CMT_LUT_BLUE 3 +#define CMT_LUT_ALL 4 +#define SPLASH_LUT 5 + +#define SEQ_LUT_NONE 0 +#define SEQ_DRC_LUT_0 1 +#define SEQ_DRC_LUT_1 2 +#define SEQ_DRC_LUT_2 3 +#define SEQ_DRC_LUT_3 4 +#define SEQ_SEQ_LUT 5 +#define SEQ_DRC_LUT_ALL 6 +#define WPC_PROGRAM_LUT 7 + +/*#define DMA_STATUS BIT8 */ + + +#define BITSTREAM_START_ADDR 0x00000000 +#define BITSTREAM_SIZE 0x00040000 + +#define WPC_FW_0_START_ADDR 0x00040000 +#define WPC_FW_0_SIZE 0x00000ce8 + +#define SEQUENCE_0_START_ADDR 0x00044000 +#define SEQUENCE_0_SIZE 0x00001000 + +#define SEQUENCE_1_START_ADDR 0x00045000 +#define SEQUENCE_1_SIZE 0x00000d10 + +#define SEQUENCE_2_START_ADDR 0x00046000 +#define SEQUENCE_2_SIZE 0x00000d10 + +#define SEQUENCE_3_START_ADDR 0x00047000 +#define SEQUENCE_3_SIZE 0x00000d10 + +#define SEQUENCE_4_START_ADDR 0x00048000 +#define SEQUENCE_4_SIZE 0x00000d10 + +#define SEQUENCE_5_START_ADDR 0x00049000 +#define SEQUENCE_5_SIZE 0x00000d10 + +#define SEQUENCE_6_START_ADDR 0x0004a000 +#define SEQUENCE_6_SIZE 0x00000d10 + +#define CMT_LUT_0_START_ADDR 0x0004b200 +#define CMT_LUT_0_SIZE 0x00000600 + +#define CMT_LUT_1_START_ADDR 0x0004b800 +#define CMT_LUT_1_SIZE 0x00000600 + +#define CMT_LUT_2_START_ADDR 0x0004be00 +#define CMT_LUT_2_SIZE 0x00000600 + +#define CMT_LUT_3_START_ADDR 0x0004c400 +#define CMT_LUT_3_SIZE 0x00000600 + +#define CMT_LUT_4_START_ADDR 0x0004ca00 +#define CMT_LUT_4_SIZE 0x00000600 + +#define CMT_LUT_5_START_ADDR 0x0004d000 +#define CMT_LUT_5_SIZE 0x00000600 + +#define CMT_LUT_6_START_ADDR 0x0004d600 +#define CMT_LUT_6_SIZE 0x00000600 + +#define DRC_TABLE_0_START_ADDR 0x0004dc00 +#define DRC_TABLE_0_SIZE 0x00000100 + +#define SPLASH_0_START_ADDR 0x0004dd00 +#define SPLASH_0_SIZE 0x00032280 + +#define SEQUENCE_7_START_ADDR 0x00080000 +#define SEQUENCE_7_SIZE 0x00000d10 + +#define SEQUENCE_8_START_ADDR 0x00081800 +#define SEQUENCE_8_SIZE 0x00000d10 + +#define SEQUENCE_9_START_ADDR 0x00083000 +#define SEQUENCE_9_SIZE 0x00000d10 + +#define CMT_LUT_7_START_ADDR 0x0008e000 +#define CMT_LUT_7_SIZE 0x00000600 + +#define CMT_LUT_8_START_ADDR 0x0008e800 +#define CMT_LUT_8_SIZE 0x00000600 + +#define CMT_LUT_9_START_ADDR 0x0008f000 +#define CMT_LUT_9_SIZE 0x00000600 + +#define SPLASH_1_START_ADDR 0x0009a000 +#define SPLASH_1_SIZE 0x00032280 + +#define SPLASH_2_START_ADDR 0x000cd000 +#define SPLASH_2_SIZE 0x00032280 + +#define SPLASH_3_START_ADDR 0x00100000 +#define SPLASH_3_SIZE 0x00032280 + +#define OPT_SPLASH_0_START_ADDR 0x00134000 +#define OPT_SPLASH_0_SIZE 0x000cb100 diff --git a/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c index 8d51a5e6341c..7d9eb2b1f5af 100644 --- a/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c +++ b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c @@ -20,10 +20,17 @@ #include <linux/module.h> #include <linux/delay.h> #include <linux/device.h> +#include <linux/backlight.h> +#include <linux/fb.h> #include <linux/err.h> +#include <linux/slab.h> #include <plat/display.h> +struct sharp_data { + struct backlight_device *bl; +}; + static struct omap_video_timings sharp_ls_timings = { .x_res = 480, .y_res = 640, @@ -39,18 +46,89 @@ static struct omap_video_timings sharp_ls_timings = { .vbp = 1, }; +static int sharp_ls_bl_update_status(struct backlight_device *bl) +{ + struct omap_dss_device *dssdev = dev_get_drvdata(&bl->dev); + int level; + + if (!dssdev->set_backlight) + return -EINVAL; + + if (bl->props.fb_blank == FB_BLANK_UNBLANK && + bl->props.power == FB_BLANK_UNBLANK) + level = bl->props.brightness; + else + level = 0; + + return dssdev->set_backlight(dssdev, level); +} + +static int sharp_ls_bl_get_brightness(struct backlight_device *bl) +{ + if (bl->props.fb_blank == FB_BLANK_UNBLANK && + bl->props.power == FB_BLANK_UNBLANK) + return bl->props.brightness; + + return 0; +} + +static const struct backlight_ops sharp_ls_bl_ops = { + .get_brightness = sharp_ls_bl_get_brightness, + .update_status = sharp_ls_bl_update_status, +}; + + + static int sharp_ls_panel_probe(struct omap_dss_device *dssdev) { + struct backlight_properties props; + struct backlight_device *bl; + struct sharp_data *sd; + int r; + dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS; dssdev->panel.acb = 0x28; dssdev->panel.timings = sharp_ls_timings; + sd = kzalloc(sizeof(*sd), GFP_KERNEL); + if (!sd) + return -ENOMEM; + + dev_set_drvdata(&dssdev->dev, sd); + + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = dssdev->max_backlight_level; + + bl = backlight_device_register("sharp-ls", &dssdev->dev, dssdev, + &sharp_ls_bl_ops, &props); + if (IS_ERR(bl)) { + r = PTR_ERR(bl); + kfree(sd); + return r; + } + sd->bl = bl; + + bl->props.fb_blank = FB_BLANK_UNBLANK; + bl->props.power = FB_BLANK_UNBLANK; + bl->props.brightness = dssdev->max_backlight_level; + r = sharp_ls_bl_update_status(bl); + if (r < 0) + dev_err(&dssdev->dev, "failed to set lcd brightness\n"); + return 0; } static void sharp_ls_panel_remove(struct omap_dss_device *dssdev) { + struct sharp_data *sd = dev_get_drvdata(&dssdev->dev); + struct backlight_device *bl = sd->bl; + + bl->props.power = FB_BLANK_POWERDOWN; + sharp_ls_bl_update_status(bl); + backlight_device_unregister(bl); + + kfree(sd); } static int sharp_ls_power_on(struct omap_dss_device *dssdev) diff --git a/drivers/video/omap2/displays/panel-taal.c b/drivers/video/omap2/displays/panel-taal.c index 4f3988a41082..9d4115df7782 100644 --- a/drivers/video/omap2/displays/panel-taal.c +++ b/drivers/video/omap2/displays/panel-taal.c @@ -31,6 +31,7 @@ #include <linux/completion.h> #include <linux/workqueue.h> #include <linux/slab.h> +#include <linux/mutex.h> #include <plat/display.h> @@ -64,9 +65,17 @@ /* #define TAAL_USE_ESD_CHECK */ #define TAAL_ESD_CHECK_PERIOD msecs_to_jiffies(5000) +static bool dsi_te_sync = 1; +module_param_named(dsi_te_sync, dsi_te_sync, bool, 0644); +MODULE_PARM_DESC(dsi_te_sync, "enable/disable tearing"); + static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable); +static int taal_update(struct omap_dss_device *dssdev, + u16 x, u16 y, u16 w, u16 h); struct taal_data { + struct mutex lock; + struct backlight_device *bldev; unsigned long hw_guard_end; /* next value of jiffies when we can @@ -113,12 +122,12 @@ static void hw_guard_wait(struct taal_data *td) } } -static int taal_dcs_read_1(u8 dcs_cmd, u8 *data) +static int taal_dcs_read_1(enum omap_dsi_index ix, u8 dcs_cmd, u8 *data) { int r; u8 buf[1]; - r = dsi_vc_dcs_read(TCH, dcs_cmd, buf, 1); + r = dsi_vc_dcs_read(ix, TCH, dcs_cmd, buf, 1); if (r < 0) return r; @@ -128,20 +137,20 @@ static int taal_dcs_read_1(u8 dcs_cmd, u8 *data) return 0; } -static int taal_dcs_write_0(u8 dcs_cmd) +static int taal_dcs_write_0(enum omap_dsi_index ix, u8 dcs_cmd) { - return dsi_vc_dcs_write(TCH, &dcs_cmd, 1); + return dsi_vc_dcs_write(ix, TCH, &dcs_cmd, 1); } -static int taal_dcs_write_1(u8 dcs_cmd, u8 param) +static int taal_dcs_write_1(enum omap_dsi_index ix, u8 dcs_cmd, u8 param) { u8 buf[2]; buf[0] = dcs_cmd; buf[1] = param; - return dsi_vc_dcs_write(TCH, buf, 2); + return dsi_vc_dcs_write(ix, TCH, buf, 2); } -static int taal_sleep_in(struct taal_data *td) +static int taal_sleep_in(enum omap_dsi_index ix, struct taal_data *td) { u8 cmd; @@ -150,7 +159,7 @@ static int taal_sleep_in(struct taal_data *td) hw_guard_wait(td); cmd = DCS_SLEEP_IN; - r = dsi_vc_dcs_write_nosync(TCH, &cmd, 1); + r = dsi_vc_dcs_write_nosync(ix, TCH, &cmd, 1); if (r) return r; @@ -161,13 +170,13 @@ static int taal_sleep_in(struct taal_data *td) return 0; } -static int taal_sleep_out(struct taal_data *td) +static int taal_sleep_out(enum omap_dsi_index ix, struct taal_data *td) { int r; hw_guard_wait(td); - r = taal_dcs_write_0(DCS_SLEEP_OUT); + r = taal_dcs_write_0(ix, DCS_SLEEP_OUT); if (r) return r; @@ -178,30 +187,32 @@ static int taal_sleep_out(struct taal_data *td) return 0; } -static int taal_get_id(u8 *id1, u8 *id2, u8 *id3) +static int taal_get_id(enum omap_dsi_index ix, + u8 *id1, u8 *id2, u8 *id3) { int r; - r = taal_dcs_read_1(DCS_GET_ID1, id1); + r = taal_dcs_read_1(ix, DCS_GET_ID1, id1); if (r) return r; - r = taal_dcs_read_1(DCS_GET_ID2, id2); + r = taal_dcs_read_1(ix, DCS_GET_ID2, id2); if (r) return r; - r = taal_dcs_read_1(DCS_GET_ID3, id3); + r = taal_dcs_read_1(ix, DCS_GET_ID3, id3); if (r) return r; return 0; } -static int taal_set_addr_mode(u8 rotate, bool mirror) +static int taal_set_addr_mode(enum omap_dsi_index ix, + u8 rotate, bool mirror) { int r; u8 mode; int b5, b6, b7; - r = taal_dcs_read_1(DCS_READ_MADCTL, &mode); + r = taal_dcs_read_1(ix, DCS_READ_MADCTL, &mode); if (r) return r; @@ -235,10 +246,11 @@ static int taal_set_addr_mode(u8 rotate, bool mirror) mode &= ~((1<<7) | (1<<6) | (1<<5)); mode |= (b7 << 7) | (b6 << 6) | (b5 << 5); - return taal_dcs_write_1(DCS_MEM_ACC_CTRL, mode); + return taal_dcs_write_1(ix, DCS_MEM_ACC_CTRL, mode); } -static int taal_set_update_window(u16 x, u16 y, u16 w, u16 h) +static int taal_set_update_window(enum omap_dsi_index ix, + u16 x, u16 y, u16 w, u16 h) { int r; u16 x1 = x; @@ -253,7 +265,7 @@ static int taal_set_update_window(u16 x, u16 y, u16 w, u16 h) buf[3] = (x2 >> 8) & 0xff; buf[4] = (x2 >> 0) & 0xff; - r = dsi_vc_dcs_write_nosync(TCH, buf, sizeof(buf)); + r = dsi_vc_dcs_write_nosync(ix, TCH, buf, sizeof(buf)); if (r) return r; @@ -263,11 +275,11 @@ static int taal_set_update_window(u16 x, u16 y, u16 w, u16 h) buf[3] = (y2 >> 8) & 0xff; buf[4] = (y2 >> 0) & 0xff; - r = dsi_vc_dcs_write_nosync(TCH, buf, sizeof(buf)); + r = dsi_vc_dcs_write_nosync(ix, TCH, buf, sizeof(buf)); if (r) return r; - dsi_vc_send_bta_sync(TCH); + dsi_vc_send_bta_sync(ix, TCH); return r; } @@ -278,6 +290,12 @@ static int taal_bl_update_status(struct backlight_device *dev) struct taal_data *td = dev_get_drvdata(&dssdev->dev); int r; int level; + enum omap_dsi_index ix; + + if (cpu_is_omap44xx()) + return; + + ix = (dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? DSI1 : DSI2; if (dev->props.fb_blank == FB_BLANK_UNBLANK && dev->props.power == FB_BLANK_UNBLANK) @@ -289,9 +307,9 @@ static int taal_bl_update_status(struct backlight_device *dev) if (td->use_dsi_bl) { if (td->enabled) { - dsi_bus_lock(); - r = taal_dcs_write_1(DCS_BRIGHTNESS, level); - dsi_bus_unlock(); + dsi_bus_lock(ix); + r = taal_dcs_write_1(ix, DCS_BRIGHTNESS, level); + dsi_bus_unlock(ix); if (r) return r; } @@ -351,6 +369,15 @@ static irqreturn_t taal_te_isr(int irq, void *data) return IRQ_HANDLED; } +static irqreturn_t taal_te_isr2(int irq, void *data) +{ + struct omap_dss_device *dssdev = data; + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + complete_all(&td->te_completion); + + return IRQ_HANDLED; +} + static ssize_t taal_num_errors_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -358,11 +385,14 @@ static ssize_t taal_num_errors_show(struct device *dev, struct taal_data *td = dev_get_drvdata(&dssdev->dev); u8 errors; int r; + enum omap_dsi_index ix; + + ix = (dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? DSI1 : DSI2; if (td->enabled) { - dsi_bus_lock(); - r = taal_dcs_read_1(DCS_READ_NUM_ERRORS, &errors); - dsi_bus_unlock(); + dsi_bus_lock(ix); + r = taal_dcs_read_1(ix, DCS_READ_NUM_ERRORS, &errors); + dsi_bus_unlock(ix); } else { r = -ENODEV; } @@ -380,11 +410,14 @@ static ssize_t taal_hw_revision_show(struct device *dev, struct taal_data *td = dev_get_drvdata(&dssdev->dev); u8 id1, id2, id3; int r; + enum omap_dsi_index ix; + + ix = (dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? DSI1 : DSI2; if (td->enabled) { - dsi_bus_lock(); - r = taal_get_id(&id1, &id2, &id3); - dsi_bus_unlock(); + dsi_bus_lock(ix); + r = taal_get_id(ix, &id1, &id2, &id3); + dsi_bus_unlock(ix); } else { r = -ENODEV; } @@ -429,6 +462,9 @@ static ssize_t store_cabc_mode(struct device *dev, struct omap_dss_device *dssdev = to_dss_device(dev); struct taal_data *td = dev_get_drvdata(&dssdev->dev); int i; + enum omap_dsi_index ix; + + ix = (dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? DSI1 : DSI2; for (i = 0; i < ARRAY_SIZE(cabc_modes); i++) { if (sysfs_streq(cabc_modes[i], buf)) @@ -439,10 +475,10 @@ static ssize_t store_cabc_mode(struct device *dev, return -EINVAL; if (td->enabled) { - dsi_bus_lock(); + dsi_bus_lock(ix); if (!td->cabc_broken) - taal_dcs_write_1(DCS_WRITE_CABC, i); - dsi_bus_unlock(); + taal_dcs_write_1(ix, DCS_WRITE_CABC, i); + dsi_bus_unlock(ix); } td->cabc_mode = i; @@ -499,10 +535,12 @@ static int taal_probe(struct omap_dss_device *dssdev) dev_dbg(&dssdev->dev, "probe\n"); - dssdev->panel.config = OMAP_DSS_LCD_TFT; + dssdev->panel.config = OMAP_DSS_LCD_TFT | + OMAP_DSS_LCD_ONOFF | OMAP_DSS_LCD_RF; dssdev->panel.timings = taal_panel_timings; dssdev->ctrl.pixel_size = 24; - + dssdev->panel.acbi = 0; + dssdev->panel.acb = 0; td = kzalloc(sizeof(*td), GFP_KERNEL); if (!td) { r = -ENOMEM; @@ -510,12 +548,15 @@ static int taal_probe(struct omap_dss_device *dssdev) } td->dssdev = dssdev; + mutex_init(&td->lock); + td->esd_wq = create_singlethread_workqueue("taal_esd"); if (td->esd_wq == NULL) { dev_err(&dssdev->dev, "can't create ESD workqueue\n"); r = -ENOMEM; goto err1; } + INIT_DELAYED_WORK_DEFERRABLE(&td->esd_work, taal_esd_work); dev_set_drvdata(&dssdev->dev, td); @@ -530,8 +571,10 @@ static int taal_probe(struct omap_dss_device *dssdev) props.max_brightness = 255; else props.max_brightness = 127; - bldev = backlight_device_register("taal", &dssdev->dev, dssdev, - &taal_bl_ops, &props); + + bldev = backlight_device_register(dssdev->name, + &dssdev->dev, dssdev, &taal_bl_ops, &props); + if (IS_ERR(bldev)) { r = PTR_ERR(bldev); goto err2; @@ -548,8 +591,27 @@ static int taal_probe(struct omap_dss_device *dssdev) taal_bl_update_status(bldev); + if (dssdev->channel == OMAP_DSS_CHANNEL_LCD2) + td->te_enabled = true; + if (dssdev->phy.dsi.ext_te) { int gpio = dssdev->phy.dsi.ext_te_gpio; + void __iomem *phymux_base = NULL; + int val; + + phymux_base = ioremap(0x4A100000, 0x1000); + + if (dssdev->channel == OMAP_DSS_CHANNEL_LCD) { + val = __raw_readl(phymux_base + 0x90); + val = val & 0xFFFFFFE0; + val = val | 0x11B; + __raw_writel(val, phymux_base + 0x90); + } else { + val = __raw_readl(phymux_base + 0x94); + val = val & 0xFFFFFFE0; + val = val | 0x11B; + __raw_writel(val, phymux_base + 0x94); + } r = gpio_request(gpio, "taal irq"); if (r) { @@ -559,10 +621,15 @@ static int taal_probe(struct omap_dss_device *dssdev) gpio_direction_input(gpio); - r = request_irq(gpio_to_irq(gpio), taal_te_isr, + if (dssdev->channel == OMAP_DSS_CHANNEL_LCD) { + r = request_irq(gpio_to_irq(gpio), taal_te_isr, + IRQF_DISABLED | IRQF_TRIGGER_RISING, + "taal vsync", dssdev); + } else { + r = request_irq(gpio_to_irq(gpio), taal_te_isr2, IRQF_DISABLED | IRQF_TRIGGER_RISING, - "taal vsync", dssdev); - + "taal vsync2", dssdev); + } if (r) { dev_err(&dssdev->dev, "IRQ request failed\n"); gpio_free(gpio); @@ -572,6 +639,7 @@ static int taal_probe(struct omap_dss_device *dssdev) init_completion(&td->te_completion); td->use_ext_te = true; + iounmap(phymux_base); } r = sysfs_create_group(&dssdev->dev.kobj, &taal_attr_group); @@ -629,6 +697,9 @@ static int taal_power_on(struct omap_dss_device *dssdev) struct taal_data *td = dev_get_drvdata(&dssdev->dev); u8 id1, id2, id3; int r; + enum omap_dsi_index ix; + + ix = (dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? DSI1 : DSI2; if (dssdev->platform_enable) { r = dssdev->platform_enable(dssdev); @@ -639,7 +710,7 @@ static int taal_power_on(struct omap_dss_device *dssdev) /* it seems we have to wait a bit until taal is ready */ msleep(5); - dsi_bus_lock(); + dsi_bus_lock(ix); r = omapdss_dsi_display_enable(dssdev); if (r) { @@ -647,13 +718,13 @@ static int taal_power_on(struct omap_dss_device *dssdev) goto err0; } - omapdss_dsi_vc_enable_hs(TCH, false); + omapdss_dsi_vc_enable_hs(ix, TCH, false); - r = taal_sleep_out(td); + r = taal_sleep_out(ix, td); if (r) goto err; - r = taal_get_id(&id1, &id2, &id3); + r = taal_get_id(ix, &id1, &id2, &id3); if (r) goto err; @@ -661,22 +732,23 @@ static int taal_power_on(struct omap_dss_device *dssdev) if (id2 == 0x00 || id2 == 0xff || id2 == 0x81) td->cabc_broken = true; - taal_dcs_write_1(DCS_BRIGHTNESS, 0xff); - taal_dcs_write_1(DCS_CTRL_DISPLAY, (1<<2) | (1<<5)); /* BL | BCTRL */ + taal_dcs_write_1(ix, DCS_BRIGHTNESS, 0xff); + taal_dcs_write_1(ix, DCS_CTRL_DISPLAY, + (1<<2) | (1<<5)); /* BL | BCTRL */ - taal_dcs_write_1(DCS_PIXEL_FORMAT, 0x7); /* 24bit/pixel */ + taal_dcs_write_1(ix, DCS_PIXEL_FORMAT, 0x7); /* 24bit/pixel */ - taal_set_addr_mode(td->rotate, td->mirror); + taal_set_addr_mode(ix, td->rotate, td->mirror); if (!td->cabc_broken) - taal_dcs_write_1(DCS_WRITE_CABC, td->cabc_mode); + taal_dcs_write_1(ix, DCS_WRITE_CABC, td->cabc_mode); - taal_dcs_write_0(DCS_DISPLAY_ON); + taal_dcs_write_0(ix, DCS_DISPLAY_ON); r = _taal_enable_te(dssdev, td->te_enabled); if (r) goto err; -#ifdef TAAL_USE_ESD_CHECK +#if TAAL_USE_ESD_CHECK queue_delayed_work(td->esd_wq, &td->esd_work, TAAL_ESD_CHECK_PERIOD); #endif @@ -691,16 +763,15 @@ static int taal_power_on(struct omap_dss_device *dssdev) td->intro_printed = true; } - omapdss_dsi_vc_enable_hs(TCH, true); + omapdss_dsi_vc_enable_hs(ix, TCH, true); - dsi_bus_unlock(); + dsi_bus_unlock(ix); return 0; err: - dsi_bus_unlock(); - omapdss_dsi_display_disable(dssdev); err0: + dsi_bus_unlock(ix); if (dssdev->platform_disable) dssdev->platform_disable(dssdev); @@ -710,13 +781,16 @@ err0: static void taal_power_off(struct omap_dss_device *dssdev) { struct taal_data *td = dev_get_drvdata(&dssdev->dev); + enum omap_dsi_index ix; + + ix = (dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? DSI1 : DSI2; - dsi_bus_lock(); + dsi_bus_lock(ix); cancel_delayed_work(&td->esd_work); - taal_dcs_write_0(DCS_DISPLAY_OFF); - taal_sleep_in(td); + taal_dcs_write_0(ix, DCS_DISPLAY_OFF); + taal_sleep_in(ix, td); /* wait a bit so that the message goes through */ msleep(10); @@ -728,67 +802,143 @@ static void taal_power_off(struct omap_dss_device *dssdev) td->enabled = 0; - dsi_bus_unlock(); + dsi_bus_unlock(ix); } static int taal_enable(struct omap_dss_device *dssdev) { + struct taal_data *td = dev_get_drvdata(&dssdev->dev); int r; + dev_dbg(&dssdev->dev, "enable\n"); - if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) - return -EINVAL; + mutex_lock(&td->lock); + + if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) { + r = -EINVAL; + goto err; + } r = taal_power_on(dssdev); if (r) - return r; + goto err; dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + mutex_unlock(&td->lock); + + /* auto update for OMAP4 */ + if (cpu_is_omap44xx()) + taal_update(dssdev, 0, 0, 864, 480); + + return 0; +err: + dev_dbg(&dssdev->dev, "enable failed\n"); + mutex_unlock(&td->lock); return r; } static void taal_disable(struct omap_dss_device *dssdev) { + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + dev_dbg(&dssdev->dev, "disable\n"); + mutex_lock(&td->lock); + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) taal_power_off(dssdev); dssdev->state = OMAP_DSS_DISPLAY_DISABLED; + + mutex_unlock(&td->lock); } static int taal_suspend(struct omap_dss_device *dssdev) { + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + int r; + + if (cpu_is_omap44xx()) + return; dev_dbg(&dssdev->dev, "suspend\n"); - if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) - return -EINVAL; + mutex_lock(&td->lock); + + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { + r = -EINVAL; + goto err; + } taal_power_off(dssdev); dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; + mutex_unlock(&td->lock); + return 0; +err: + mutex_unlock(&td->lock); + return r; } static int taal_resume(struct omap_dss_device *dssdev) { + struct taal_data *td = dev_get_drvdata(&dssdev->dev); int r; + + if (cpu_is_omap44xx()) + return; + dev_dbg(&dssdev->dev, "resume\n"); - if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) - return -EINVAL; + mutex_lock(&td->lock); + + if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) { + r = -EINVAL; + goto err; + } r = taal_power_on(dssdev); dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + mutex_unlock(&td->lock); + return r; +err: + mutex_unlock(&td->lock); + return r; +} + +static int taal_wait_te(struct omap_dss_device *dssdev) +{ + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + long wait = msecs_to_jiffies(500); + + if (!td->use_ext_te || !td->te_enabled) + return 0; + + INIT_COMPLETION(td->te_completion); + wait = wait_for_completion_timeout(&td->te_completion, wait); + if (wait == 0) { + dev_err(&dssdev->dev, "timeout waiting TE\n"); + return -ETIME; + } + + return 0; } static void taal_framedone_cb(int err, void *data) { struct omap_dss_device *dssdev = data; + enum omap_dsi_index ix; + + ix = (dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? DSI1 : DSI2; + dev_dbg(&dssdev->dev, "framedone, err %d\n", err); - dsi_bus_unlock(); + dsi_bus_unlock(ix); + /* auto update for OMAP4 */ + if (cpu_is_omap44xx()) + taal_update(dssdev, 0, 0, 864, 480); } static int taal_update(struct omap_dss_device *dssdev, @@ -796,10 +946,14 @@ static int taal_update(struct omap_dss_device *dssdev, { struct taal_data *td = dev_get_drvdata(&dssdev->dev); int r; + enum omap_dsi_index ix; + + ix = (dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? DSI1 : DSI2; dev_dbg(&dssdev->dev, "update %d, %d, %d x %d\n", x, y, w, h); - dsi_bus_lock(); + mutex_lock(&td->lock); + dsi_bus_lock(ix); if (!td->enabled) { r = 0; @@ -810,28 +964,45 @@ static int taal_update(struct omap_dss_device *dssdev, if (r) goto err; - r = taal_set_update_window(x, y, w, h); + r = taal_set_update_window(ix, x, y, w, h); if (r) goto err; - r = omap_dsi_update(dssdev, TCH, x, y, w, h, - taal_framedone_cb, dssdev); + if (dsi_te_sync && dssdev->phy.dsi.ext_te) + dssdev->driver->wait_for_te(dssdev); + + /* We use VC(1) for VideoPort Data and VC(0) for L4 data */ + if (cpu_is_omap44xx()) + r = omap_dsi_update(dssdev, 1, x, y, w, h, + taal_framedone_cb, dssdev); + else + r = omap_dsi_update(dssdev, TCH, x, y, w, h, + taal_framedone_cb, dssdev); if (r) goto err; /* note: no bus_unlock here. unlock is in framedone_cb */ + mutex_unlock(&td->lock); return 0; err: - dsi_bus_unlock(); + dsi_bus_unlock(ix); + mutex_unlock(&td->lock); return r; } static int taal_sync(struct omap_dss_device *dssdev) { + struct taal_data *td = dev_get_drvdata(&dssdev->dev); + enum omap_dsi_index ix; + + ix = (dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? DSI1 : DSI2; + dev_dbg(&dssdev->dev, "sync\n"); - dsi_bus_lock(); - dsi_bus_unlock(); + mutex_lock(&td->lock); + dsi_bus_lock(ix); + dsi_bus_unlock(ix); + mutex_unlock(&td->lock); dev_dbg(&dssdev->dev, "sync done\n"); @@ -842,13 +1013,16 @@ static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable) { struct taal_data *td = dev_get_drvdata(&dssdev->dev); int r; + enum omap_dsi_index ix; + + ix = (dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? DSI1 : DSI2; td->te_enabled = enable; if (enable) - r = taal_dcs_write_1(DCS_TEAR_ON, 0); + r = taal_dcs_write_1(ix, DCS_TEAR_ON, 0); else - r = taal_dcs_write_0(DCS_TEAR_OFF); + r = taal_dcs_write_0(ix, DCS_TEAR_OFF); omapdss_dsi_enable_te(dssdev, enable); @@ -861,13 +1035,19 @@ static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable) static int taal_enable_te(struct omap_dss_device *dssdev, bool enable) { + struct taal_data *td = dev_get_drvdata(&dssdev->dev); int r; + enum omap_dsi_index ix; + + ix = (dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? DSI1 : DSI2; - dsi_bus_lock(); + mutex_lock(&td->lock); + dsi_bus_lock(ix); r = _taal_enable_te(dssdev, enable); - dsi_bus_unlock(); + dsi_bus_unlock(ix); + mutex_unlock(&td->lock); return r; } @@ -875,89 +1055,126 @@ static int taal_enable_te(struct omap_dss_device *dssdev, bool enable) static int taal_get_te(struct omap_dss_device *dssdev) { struct taal_data *td = dev_get_drvdata(&dssdev->dev); - return td->te_enabled; + int r; + + mutex_lock(&td->lock); + r = td->te_enabled; + mutex_unlock(&td->lock); + + return r; } static int taal_rotate(struct omap_dss_device *dssdev, u8 rotate) { struct taal_data *td = dev_get_drvdata(&dssdev->dev); int r; + enum omap_dsi_index ix; + + ix = (dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? DSI1 : DSI2; dev_dbg(&dssdev->dev, "rotate %d\n", rotate); - dsi_bus_lock(); + mutex_lock(&td->lock); + dsi_bus_lock(ix); if (td->enabled) { - r = taal_set_addr_mode(rotate, td->mirror); + r = taal_set_addr_mode(ix, rotate, td->mirror); if (r) goto err; } td->rotate = rotate; - dsi_bus_unlock(); + dsi_bus_unlock(ix); + mutex_unlock(&td->lock); return 0; err: - dsi_bus_unlock(); + dsi_bus_unlock(ix); + mutex_unlock(&td->lock); return r; } static u8 taal_get_rotate(struct omap_dss_device *dssdev) { struct taal_data *td = dev_get_drvdata(&dssdev->dev); - return td->rotate; + int r; + + mutex_lock(&td->lock); + r = td->rotate; + mutex_unlock(&td->lock); + + return r; } static int taal_mirror(struct omap_dss_device *dssdev, bool enable) { struct taal_data *td = dev_get_drvdata(&dssdev->dev); int r; + enum omap_dsi_index ix; + + ix = (dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? DSI1 : DSI2; dev_dbg(&dssdev->dev, "mirror %d\n", enable); - dsi_bus_lock(); + mutex_lock(&td->lock); + dsi_bus_lock(ix); if (td->enabled) { - r = taal_set_addr_mode(td->rotate, enable); + r = taal_set_addr_mode(ix, td->rotate, enable); if (r) goto err; } td->mirror = enable; - dsi_bus_unlock(); + dsi_bus_unlock(ix); + mutex_unlock(&td->lock); return 0; err: - dsi_bus_unlock(); + dsi_bus_unlock(ix); + mutex_unlock(&td->lock); return r; } static bool taal_get_mirror(struct omap_dss_device *dssdev) { struct taal_data *td = dev_get_drvdata(&dssdev->dev); - return td->mirror; + int r; + + mutex_lock(&td->lock); + r = td->mirror; + mutex_unlock(&td->lock); + + return r; } static int taal_run_test(struct omap_dss_device *dssdev, int test_num) { + struct taal_data *td = dev_get_drvdata(&dssdev->dev); u8 id1, id2, id3; int r; + enum omap_dsi_index ix; + + ix = (dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? DSI1 : DSI2; - dsi_bus_lock(); + mutex_lock(&td->lock); + dsi_bus_lock(ix); - r = taal_dcs_read_1(DCS_GET_ID1, &id1); + r = taal_dcs_read_1(ix, DCS_GET_ID1, &id1); if (r) goto err; - r = taal_dcs_read_1(DCS_GET_ID2, &id2); + r = taal_dcs_read_1(ix, DCS_GET_ID2, &id2); if (r) goto err; - r = taal_dcs_read_1(DCS_GET_ID3, &id3); + r = taal_dcs_read_1(ix, DCS_GET_ID3, &id3); if (r) goto err; - dsi_bus_unlock(); + dsi_bus_unlock(ix); + mutex_unlock(&td->lock); return 0; err: - dsi_bus_unlock(); + dsi_bus_unlock(ix); + mutex_unlock(&td->lock); return r; } @@ -970,18 +1187,25 @@ static int taal_memory_read(struct omap_dss_device *dssdev, int plen; unsigned buf_used = 0; struct taal_data *td = dev_get_drvdata(&dssdev->dev); + enum omap_dsi_index ix; - if (!td->enabled) - return -ENODEV; + ix = (dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? DSI1 : DSI2; if (size < w * h * 3) return -ENOMEM; + mutex_lock(&td->lock); + + if (!td->enabled) { + r = -ENODEV; + goto err1; + } + size = min(w * h * 3, dssdev->panel.timings.x_res * dssdev->panel.timings.y_res * 3); - dsi_bus_lock(); + dsi_bus_lock(ix); /* plen 1 or 2 goes into short packet. until checksum error is fixed, * use short packets. plen 32 works, but bigger packets seem to cause @@ -991,22 +1215,22 @@ static int taal_memory_read(struct omap_dss_device *dssdev, else plen = 2; - taal_set_update_window(x, y, w, h); + taal_set_update_window(ix, x, y, w, h); - r = dsi_vc_set_max_rx_packet_size(TCH, plen); + r = dsi_vc_set_max_rx_packet_size(ix, TCH, plen); if (r) - goto err0; + goto err2; while (buf_used < size) { u8 dcs_cmd = first ? 0x2e : 0x3e; first = 0; - r = dsi_vc_dcs_read(TCH, dcs_cmd, + r = dsi_vc_dcs_read(ix, TCH, dcs_cmd, buf + buf_used, size - buf_used); if (r < 0) { dev_err(&dssdev->dev, "read error\n"); - goto err; + goto err3; } buf_used += r; @@ -1020,16 +1244,18 @@ static int taal_memory_read(struct omap_dss_device *dssdev, dev_err(&dssdev->dev, "signal pending, " "aborting memory read\n"); r = -ERESTARTSYS; - goto err; + goto err3; } } r = buf_used; -err: - dsi_vc_set_max_rx_packet_size(TCH, 1); -err0: - dsi_bus_unlock(); +err3: + dsi_vc_set_max_rx_packet_size(ix, TCH, 1); +err2: + dsi_bus_unlock(ix); +err1: + mutex_unlock(&td->lock); return r; } @@ -1040,26 +1266,32 @@ static void taal_esd_work(struct work_struct *work) struct omap_dss_device *dssdev = td->dssdev; u8 state1, state2; int r; + enum omap_dsi_index ix; + ix = (dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? DSI1 : DSI2; - if (!td->enabled) + mutex_lock(&td->lock); + + if (!td->enabled) { + mutex_unlock(&td->lock); return; + } - dsi_bus_lock(); + dsi_bus_lock(ix); - r = taal_dcs_read_1(DCS_RDDSDR, &state1); + r = taal_dcs_read_1(ix, DCS_RDDSDR, &state1); if (r) { dev_err(&dssdev->dev, "failed to read Taal status\n"); goto err; } /* Run self diagnostics */ - r = taal_sleep_out(td); + r = taal_sleep_out(ix, td); if (r) { dev_err(&dssdev->dev, "failed to run Taal self-diagnostics\n"); goto err; } - r = taal_dcs_read_1(DCS_RDDSDR, &state2); + r = taal_dcs_read_1(ix, DCS_RDDSDR, &state2); if (r) { dev_err(&dssdev->dev, "failed to read Taal status\n"); goto err; @@ -1075,31 +1307,34 @@ static void taal_esd_work(struct work_struct *work) /* Self-diagnostics result is also shown on TE GPIO line. We need * to re-enable TE after self diagnostics */ if (td->use_ext_te && td->te_enabled) { - r = taal_dcs_write_1(DCS_TEAR_ON, 0); + r = taal_dcs_write_1(ix, DCS_TEAR_ON, 0); if (r) goto err; } - dsi_bus_unlock(); + dsi_bus_unlock(ix); queue_delayed_work(td->esd_wq, &td->esd_work, TAAL_ESD_CHECK_PERIOD); + mutex_unlock(&td->lock); return; err: dev_err(&dssdev->dev, "performing LCD reset\n"); - taal_disable(dssdev); - taal_enable(dssdev); + taal_power_off(dssdev); + taal_power_on(dssdev); - dsi_bus_unlock(); + dsi_bus_unlock(ix); queue_delayed_work(td->esd_wq, &td->esd_work, TAAL_ESD_CHECK_PERIOD); + + mutex_unlock(&td->lock); } static int taal_set_update_mode(struct omap_dss_device *dssdev, enum omap_dss_update_mode mode) { - if (mode != OMAP_DSS_UPDATE_MANUAL) + if (mode != OMAP_DSS_UPDATE_AUTO) return -EINVAL; return 0; } @@ -1107,7 +1342,7 @@ static int taal_set_update_mode(struct omap_dss_device *dssdev, static enum omap_dss_update_mode taal_get_update_mode( struct omap_dss_device *dssdev) { - return OMAP_DSS_UPDATE_MANUAL; + return OMAP_DSS_UPDATE_AUTO; } static struct omap_dss_driver taal_driver = { @@ -1130,7 +1365,7 @@ static struct omap_dss_driver taal_driver = { .enable_te = taal_enable_te, .get_te = taal_get_te, - + .wait_for_te = taal_wait_te, .set_rotate = taal_rotate, .get_rotate = taal_get_rotate, .set_mirror = taal_mirror, @@ -1146,9 +1381,48 @@ static struct omap_dss_driver taal_driver = { }, }; +static struct omap_dss_driver taal2_driver = { + .probe = taal_probe, + .remove = taal_remove, + + .enable = taal_enable, + .disable = taal_disable, + .suspend = taal_suspend, + .resume = taal_resume, + + .set_update_mode = taal_set_update_mode, + .get_update_mode = taal_get_update_mode, + + .update = taal_update, + .sync = taal_sync, + + .get_resolution = taal_get_resolution, + .get_recommended_bpp = omapdss_default_get_recommended_bpp, + + .enable_te = taal_enable_te, + .get_te = taal_get_te, + .wait_for_te = taal_wait_te, + .set_rotate = taal_rotate, + .get_rotate = taal_get_rotate, + .set_mirror = taal_mirror, + .get_mirror = taal_get_mirror, + .run_test = taal_run_test, + .memory_read = taal_memory_read, + + .get_timings = taal_get_timings, + + .driver = { + .name = "taal2", + .owner = THIS_MODULE, + }, +}; + + static int __init taal_init(void) { omap_dss_register_driver(&taal_driver); + if (cpu_is_omap44xx()) + omap_dss_register_driver(&taal2_driver); return 0; } @@ -1156,6 +1430,8 @@ static int __init taal_init(void) static void __exit taal_exit(void) { omap_dss_unregister_driver(&taal_driver); + if (cpu_is_omap44xx()) + omap_dss_unregister_driver(&taal2_driver); } module_init(taal_init); diff --git a/drivers/video/omap2/dss/Kconfig b/drivers/video/omap2/dss/Kconfig index 87afb81b2c44..810586c56968 100644 --- a/drivers/video/omap2/dss/Kconfig +++ b/drivers/video/omap2/dss/Kconfig @@ -1,8 +1,8 @@ menuconfig OMAP2_DSS - tristate "OMAP2/3 Display Subsystem support (EXPERIMENTAL)" - depends on ARCH_OMAP2 || ARCH_OMAP3 + tristate "OMAP2/3/4 Display Subsystem support (EXPERIMENTAL)" + depends on ARCH_OMAP2 || ARCH_OMAP3 || ARCH_OMAP4 help - OMAP2/3 Display Subsystem support. + OMAP2/3/4 Display Subsystem support. if OMAP2_DSS @@ -36,6 +36,12 @@ config OMAP2_DSS_COLLECT_IRQ_STATS <debugfs>/omapdss/dispc_irq for DISPC interrupts, and <debugfs>/omapdss/dsi_irq for DSI interrupts. +config OMAP2_DSS_DPI + bool "DPI support" + default y + help + DPI Interface. This is the Parallel Display Interface. + config OMAP2_DSS_RFBI bool "RFBI support" default n @@ -54,6 +60,12 @@ config OMAP2_DSS_VENC help OMAP Video Encoder support for S-Video and composite TV-out. +config OMAP2_DSS_HDMI + bool "HDMI support" + default n + help + OMAP HDMI panel support. + config OMAP2_DSS_SDI bool "SDI support" depends on ARCH_OMAP3 @@ -66,7 +78,7 @@ config OMAP2_DSS_SDI config OMAP2_DSS_DSI bool "DSI support" - depends on ARCH_OMAP3 + depends on ARCH_OMAP3 || ARCH_OMAP4 default n help MIPI DSI (Display Serial Interface) support. diff --git a/drivers/video/omap2/dss/Makefile b/drivers/video/omap2/dss/Makefile index 980c72c2db98..c1dd777e9727 100644 --- a/drivers/video/omap2/dss/Makefile +++ b/drivers/video/omap2/dss/Makefile @@ -1,6 +1,8 @@ obj-$(CONFIG_OMAP2_DSS) += omapdss.o -omapdss-y := core.o dss.o dispc.o dpi.o display.o manager.o overlay.o +omapdss-y := core.o dss.o dispc.o display.o manager.o overlay.o wb.o +omapdss-$(CONFIG_OMAP2_DSS_DPI) += dpi.o omapdss-$(CONFIG_OMAP2_DSS_RFBI) += rfbi.o omapdss-$(CONFIG_OMAP2_DSS_VENC) += venc.o omapdss-$(CONFIG_OMAP2_DSS_SDI) += sdi.o omapdss-$(CONFIG_OMAP2_DSS_DSI) += dsi.o +omapdss-$(CONFIG_OMAP2_DSS_HDMI) += hdmi.o diff --git a/drivers/video/omap2/dss/core.c b/drivers/video/omap2/dss/core.c index 7ebe50b335ed..ed9f76912ae9 100644 --- a/drivers/video/omap2/dss/core.c +++ b/drivers/video/omap2/dss/core.c @@ -68,6 +68,11 @@ unsigned int dss_debug; module_param_named(debug, dss_debug, bool, 0644); #endif +static int hdmi_code = 16; +static int hdmi_mode = 1; +module_param_named(hdmicode, hdmi_code, int, 0644); +module_param_named(hdmimode, hdmi_mode, int, 0644); + /* CONTEXT */ static int dss_get_ctx_id(void) { @@ -224,16 +229,19 @@ err: static void dss_put_clocks(void) { +#if 0 if (core.dss_96m_fck) clk_put(core.dss_96m_fck); clk_put(core.dss_54m_fck); clk_put(core.dss1_fck); clk_put(core.dss2_fck); clk_put(core.dss_ick); +#endif } unsigned long dss_clk_get_rate(enum dss_clock clk) { +#if 0 switch (clk) { case DSS_CLK_ICK: return clk_get_rate(core.dss_ick); @@ -248,7 +256,8 @@ unsigned long dss_clk_get_rate(enum dss_clock clk) } BUG(); - return 0; +#endif + return 153600000; } static unsigned count_clk_bits(enum dss_clock clks) @@ -272,7 +281,7 @@ static unsigned count_clk_bits(enum dss_clock clks) static void dss_clk_enable_no_ctx(enum dss_clock clks) { unsigned num_clks = count_clk_bits(clks); - +#if 0 if (clks & DSS_CLK_ICK) clk_enable(core.dss_ick); if (clks & DSS_CLK_FCK1) @@ -283,7 +292,7 @@ static void dss_clk_enable_no_ctx(enum dss_clock clks) clk_enable(core.dss_54m_fck); if (clks & DSS_CLK_96M) clk_enable(core.dss_96m_fck); - +#endif core.num_clks_enabled += num_clks; } @@ -300,7 +309,7 @@ void dss_clk_enable(enum dss_clock clks) static void dss_clk_disable_no_ctx(enum dss_clock clks) { unsigned num_clks = count_clk_bits(clks); - +#if 0 if (clks & DSS_CLK_ICK) clk_disable(core.dss_ick); if (clks & DSS_CLK_FCK1) @@ -311,7 +320,7 @@ static void dss_clk_disable_no_ctx(enum dss_clock clks) clk_disable(core.dss_54m_fck); if (clks & DSS_CLK_96M) clk_disable(core.dss_96m_fck); - +#endif core.num_clks_enabled -= num_clks; } @@ -411,7 +420,8 @@ static void dss_debug_dump_clocks(struct seq_file *s) dss_dump_clocks(s); dispc_dump_clocks(s); #ifdef CONFIG_OMAP2_DSS_DSI - dsi_dump_clocks(s); + dsi_dump_clocks(DSI1, s); + dsi_dump_clocks(DSI2, s); #endif } @@ -489,7 +499,7 @@ static int omap_dss_probe(struct platform_device *pdev) { struct omap_dss_board_info *pdata = pdev->dev.platform_data; int skip_init = 0; - int r; + int r = 0; int i; core.pdev = pdev; @@ -497,6 +507,10 @@ static int omap_dss_probe(struct platform_device *pdev) dss_init_overlay_managers(pdev); dss_init_overlays(pdev); + if (cpu_is_omap44xx()) + dss_init_writeback(pdev); /*Write back init*/ + + if (!cpu_is_omap44xx()) r = dss_get_clocks(); if (r) goto fail0; @@ -526,11 +540,13 @@ static int omap_dss_probe(struct platform_device *pdev) } #endif +#ifdef CONFIG_OMAP2_DSS_DPI r = dpi_init(pdev); if (r) { DSSERR("Failed to initialize dpi\n"); goto fail0; } +#endif r = dispc_init(); if (r) { @@ -552,15 +568,31 @@ static int omap_dss_probe(struct platform_device *pdev) goto fail0; } #endif + } + if (!cpu_is_omap24xx()) { #ifdef CONFIG_OMAP2_DSS_DSI r = dsi_init(pdev); if (r) { DSSERR("Failed to initialize DSI\n"); goto fail0; } + if (cpu_is_omap44xx()) { + r = dsi2_init(pdev); + if (r) { + DSSERR("Failed to initialize DSI2\n"); + goto fail0; + } + } #endif } +#ifdef CONFIG_OMAP2_DSS_HDMI + r = hdmi_init(pdev, hdmi_code, hdmi_mode); + if (r) { + DSSERR("Failed to initialize hdmi\n"); + goto fail0; + } +#endif #if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) r = dss_initialize_debugfs(); if (r) @@ -600,17 +632,25 @@ static int omap_dss_remove(struct platform_device *pdev) #ifdef CONFIG_OMAP2_DSS_VENC venc_exit(); #endif +#ifdef CONFIG_OMAP2_DSS_HDMI + hdmi_exit(); +#endif dispc_exit(); +#ifdef CONFIG_OMAP2_DSS_DPI dpi_exit(); +#endif #ifdef CONFIG_OMAP2_DSS_RFBI rfbi_exit(); #endif - if (cpu_is_omap34xx()) { + if (!cpu_is_omap24xx()) { #ifdef CONFIG_OMAP2_DSS_DSI dsi_exit(); + if (cpu_is_omap44xx()) + dsi2_exit(); #endif #ifdef CONFIG_OMAP2_DSS_SDI - sdi_exit(); + if (cpu_is_omap34xx()) + sdi_exit(); #endif } diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c index e777e352dbcd..6b5695f6025a 100644 --- a/drivers/video/omap2/dss/dispc.c +++ b/drivers/video/omap2/dss/dispc.c @@ -38,14 +38,18 @@ #include <plat/display.h> #include "dss.h" +#include <mach/tiler.h> -/* DISPC */ -#define DISPC_BASE 0x48050400 +#ifndef CONFIG_ARCH_OMAP4 +#define DISPC_BASE 0x48050400 +#else +#define DISPC_BASE 0x58001000 +#endif -#define DISPC_SZ_REGS SZ_1K +#define DISPC_SZ_REGS SZ_16K struct dispc_reg { u16 idx; }; - +extern void __iomem *dispc_base; #define DISPC_REG(idx) ((const struct dispc_reg) { idx }) /* DISPC common */ @@ -67,9 +71,11 @@ struct dispc_reg { u16 idx; }; #define DISPC_TIMING_V DISPC_REG(0x0068) #define DISPC_POL_FREQ DISPC_REG(0x006C) #define DISPC_DIVISOR DISPC_REG(0x0070) +#define DISPC_DIVISOR1 DISPC_REG(0x0804) #define DISPC_GLOBAL_ALPHA DISPC_REG(0x0074) #define DISPC_SIZE_DIG DISPC_REG(0x0078) #define DISPC_SIZE_LCD DISPC_REG(0x007C) +#define DISPC_GLOBAL_BUFFER DISPC_REG(0x0800) /* DISPC GFX plane */ #define DISPC_GFX_BA0 DISPC_REG(0x0080) @@ -130,6 +136,118 @@ struct dispc_reg { u16 idx; }; DISPC_IRQ_SYNC_LOST | \ DISPC_IRQ_SYNC_LOST_DIGIT) +#define DISPC_CONTROL2 DISPC_REG(0x0238) + +/******** registers related to VID3 and WB pipelines ****/ +/* DISPC Video plane, n = 0 for VID3, n = 1 for WB _VID_V3_WB_ */ +#define DISPC_VID_V3_WB_REG(n, idx) DISPC_REG(0x0300 + (n)*0x200 + idx) + +#define DISPC_VID_V3_WB_ACCU0(n) DISPC_VID_V3_WB_REG(n, 0x0000) +#define DISPC_VID_V3_WB_ACCU1(n) DISPC_VID_V3_WB_REG(n, 0x0004) + +#define DISPC_VID_V3_WB_BA0(n) DISPC_VID_V3_WB_REG(n, 0x0008) +#define DISPC_VID_V3_WB_BA1(n) DISPC_VID_V3_WB_REG(n, 0x000C) + +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +#define DISPC_VID_V3_WB_FIR_COEF_H(n, i) DISPC_REG(0x0310+(n)*0x200+(i)*0x8) + +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +#define DISPC_VID_V3_WB_FIR_COEF_HV(n, i) DISPC_REG(0x0314+(n)*0x200+(i)*0x8) + +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +#define DISPC_VID_V3_WB_FIR_COEF_V(n, i) DISPC_REG(0x0350+(n)*0x200+(i)*0x4) + +#define DISPC_VID_V3_WB_ATTRIBUTES(n) DISPC_VID_V3_WB_REG(n, 0x0070) + +/* coef index i = {0, 1, 2, 3, 4} */ +#define DISPC_VID_V3_WB_CONV_COEF(n, i) DISPC_REG(0x0374 + (n)*0x200 + (i)*0x4) + +#define DISPC_VID_V3_WB_BUF_SIZE_STATUS(n) DISPC_VID_V3_WB_REG(n, 0x0088) +#define DISPC_VID_V3_WB_BUF_THRESHOLD(n) DISPC_VID_V3_WB_REG(n, 0x008C) +#define DISPC_VID_V3_WB_FIR(n) DISPC_VID_V3_WB_REG(n, 0x0090) +#define DISPC_VID_V3_WB_PICTURE_SIZE(n) DISPC_VID_V3_WB_REG(n, 0x0094) +#define DISPC_VID_V3_WB_PIXEL_INC(n) DISPC_VID_V3_WB_REG(n, 0x0098) + +#define DISPC_VID_VID3_POSITION DISPC_REG(0x039C) +#define DISPC_VID_VID3_PRELOAD DISPC_REG(0x03A0) + + +#define DISPC_VID_V3_WB_ROW_INC(n) DISPC_VID_V3_WB_REG(n, 0x00A4) +#define DISPC_VID_V3_WB_SIZE(n) DISPC_VID_V3_WB_REG(n, 0x00A8) + +#define DISPC_VID_V3_WB_FIR2(n) DISPC_REG(0x0724 + (n)*0x6C) + /* n=0: VID3, n=1: WB*/ + +#define DISPC_VID_V3_WB_ACCU2_0(n) DISPC_REG(0x0728 + (n)*0x6C) + /* n=0: VID3, n=1: WB*/ +#define DISPC_VID_V3_WB_ACCU2_1(n) DISPC_REG(0x072C + (n)*0x6C) + /* n=0: VID3, n=1: WB*/ + +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} n=0: VID3, n=1: WB */ +#define DISPC_VID_V3_WB_FIR_COEF_H2(n, i) DISPC_REG(0x0730+(n)*0x6C+(i)*0x8) + +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +#define DISPC_VID_V3_WB_FIR_COEF_HV2(n, i) DISPC_REG(0x0734+(n)*0x6C+(i)*0x8) + +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +#define DISPC_VID_V3_WB_FIR_COEF_V2(n, i) DISPC_REG(0x0770+(n)*0x6C+(i)*0x4) + + +/*********End Vid3 and WB Registers ***************/ + +/********** OMAP4 new global registers **/ +#define DISPC_DEFAULT_COLOR2 DISPC_REG(0x03AC) +#define DISPC_TRANS_COLOR2 DISPC_REG(0x03B0) +#define DISPC_CPR2_COEF_B DISPC_REG(0x03B4) +#define DISPC_CPR2_COEF_G DISPC_REG(0x03B8) +#define DISPC_CPR2_COEF_R DISPC_REG(0x03BC) +#define DISPC_DATA2_CYCLE1 DISPC_REG(0x03C0) +#define DISPC_DATA2_CYCLE2 DISPC_REG(0x03C4) +#define DISPC_DATA2_CYCLE3 DISPC_REG(0x03C8) +#define DISPC_SIZE_LCD2 DISPC_REG(0x03CC) +#define DISPC_TIMING_H2 DISPC_REG(0x0400) +#define DISPC_TIMING_V2 DISPC_REG(0x0404) +#define DISPC_POL_FREQ2 DISPC_REG(0x0408) +#define DISPC_DIVISOR2 DISPC_REG(0x040C) +/* DISPC Video plane, + n = 0 for VID1 + n = 1 for VID2 + and n = 2 for VID3, + n = 3 for WB*/ + +#define DISPC_VID_OMAP4_REG(n, idx) DISPC_REG(0x0600 + (n)*0x04 + idx) + +#define DISPC_VID_BA_UV0(n) DISPC_VID_OMAP4_REG((n)*2, 0x0000) +#define DISPC_VID_BA_UV1(n) DISPC_VID_OMAP4_REG((n)*2, 0x0004) + +#define DISPC_CONFIG2 DISPC_REG(0x0620) + +#define DISPC_VID_ATTRIBUTES2(n) DISPC_VID_OMAP4_REG(n, 0x0024) + /* n = {0,1,2,3} */ +#define DISPC_GAMMA_TABLE(n) DISPC_VID_OMAP4_REG(n, 0x0030) + /* n = {0,1,2,3} */ + +/* VID1/VID2 specific new registers */ +#define DISPC_VID_FIR2(n) DISPC_REG(0x063C + (n)*0x6C) + /* n=0: VID1, n=1: VID2*/ + +#define DISPC_VID_ACCU2_0(n) DISPC_REG(0x0640 + (n)*0x6C) + /* n=0: VID1, n=1: VID2*/ +#define DISPC_VID_ACCU2_1(n) DISPC_REG(0x0644 + (n)*0x6C) + /* n=0: VID1, n=1: VID2*/ + +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} n=0: VID1, n=1: VID2 */ +#define DISPC_VID_FIR_COEF_H2(n, i) DISPC_REG(0x0648 + (n)*0x6C + (i)*0x8) + +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +#define DISPC_VID_FIR_COEF_HV2(n, i) DISPC_REG(0x064C + (n)*0x6C + (i)*0x8) + +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +#define DISPC_VID_FIR_COEF_V2(n, i) DISPC_REG(0x0688 + (n)*0x6C + (i)*0x4) +/*end of VID1/VID2 specific new registers*/ + + + #define DISPC_MAX_NR_ISRS 8 struct omap_dispc_isr_data { @@ -146,7 +264,10 @@ struct omap_dispc_isr_data { static const struct dispc_reg dispc_reg_att[] = { DISPC_GFX_ATTRIBUTES, DISPC_VID_ATTRIBUTES(0), - DISPC_VID_ATTRIBUTES(1) }; + DISPC_VID_ATTRIBUTES(1), + DISPC_VID_V3_WB_ATTRIBUTES(0), /* VID 3 pipeline */ + DISPC_VID_V3_WB_ATTRIBUTES(1)};/* WB pipeline */ + struct dispc_irq_stats { unsigned long last_reset; @@ -185,6 +306,28 @@ static inline u32 dispc_read_reg(const struct dispc_reg idx) return __raw_readl(dispc.base + idx.idx); } +static inline u8 calc_tiler_orientation(u8 rotation, u8 mir) +{ + static u8 orientation; + switch (rotation) { + case 0: + orientation = (mir ? 0x2 : 0x0); + break; + case 1: + orientation = (mir ? 0x7 : 0x6); + break; + case 2: + orientation = (mir ? 0x1 : 0x3); + break; + case 3: + orientation = (mir ? 0x4 : 0x5); + break; + } + return orientation; +} + + + #define SR(reg) \ dispc.ctx[(DISPC_##reg).idx / sizeof(u32)] = dispc_read_reg(DISPC_##reg) #define RR(reg) \ @@ -233,6 +376,214 @@ void dispc_save_context(void) SR(GFX_PRELOAD); + if (cpu_is_omap44xx()) { + SR(DIVISOR1); + SR(GLOBAL_BUFFER); + SR(CONTROL2); + SR(DEFAULT_COLOR2); + SR(TRANS_COLOR2); + SR(CPR2_COEF_B); + SR(CPR2_COEF_G); + SR(CPR2_COEF_R); + SR(DATA2_CYCLE1); + SR(DATA2_CYCLE2); + SR(DATA2_CYCLE3); + SR(SIZE_LCD2); + SR(TIMING_H2); + SR(TIMING_V2); + SR(POL_FREQ2); + SR(DIVISOR2); + + SR(CONFIG2); + + /**** VID3 ****/; + + SR(VID_V3_WB_ACCU0(0)); + SR(VID_V3_WB_ACCU1(0)); + SR(VID_V3_WB_BA0(0)); + SR(VID_V3_WB_BA1(0)); + + SR(VID_V3_WB_FIR_COEF_H(0, 0)); + SR(VID_V3_WB_FIR_COEF_H(0, 1)); + SR(VID_V3_WB_FIR_COEF_H(0, 2)); + SR(VID_V3_WB_FIR_COEF_H(0, 3)); + SR(VID_V3_WB_FIR_COEF_H(0, 4)); + SR(VID_V3_WB_FIR_COEF_H(0, 5)); + SR(VID_V3_WB_FIR_COEF_H(0, 6)); + SR(VID_V3_WB_FIR_COEF_H(0, 7)); + + SR(VID_V3_WB_FIR_COEF_HV(0, 0)); + SR(VID_V3_WB_FIR_COEF_HV(0, 1)); + SR(VID_V3_WB_FIR_COEF_HV(0, 2)); + SR(VID_V3_WB_FIR_COEF_HV(0, 3)); + SR(VID_V3_WB_FIR_COEF_HV(0, 4)); + SR(VID_V3_WB_FIR_COEF_HV(0, 5)); + SR(VID_V3_WB_FIR_COEF_HV(0, 6)); + SR(VID_V3_WB_FIR_COEF_HV(0, 7)); + + SR(VID_V3_WB_FIR_COEF_V(0, 0)); + SR(VID_V3_WB_FIR_COEF_V(0, 1)); + SR(VID_V3_WB_FIR_COEF_V(0, 2)); + SR(VID_V3_WB_FIR_COEF_V(0, 3)); + SR(VID_V3_WB_FIR_COEF_V(0, 4)); + SR(VID_V3_WB_FIR_COEF_V(0, 5)); + SR(VID_V3_WB_FIR_COEF_V(0, 6)); + SR(VID_V3_WB_FIR_COEF_V(0, 7)); + + SR(VID_V3_WB_ATTRIBUTES(0)); + SR(VID_V3_WB_CONV_COEF(0, 0)); + SR(VID_V3_WB_CONV_COEF(0, 1)); + SR(VID_V3_WB_CONV_COEF(0, 2)); + SR(VID_V3_WB_CONV_COEF(0, 3)); + SR(VID_V3_WB_CONV_COEF(0, 4)); + SR(VID_V3_WB_CONV_COEF(0, 5)); + SR(VID_V3_WB_CONV_COEF(0, 6)); + SR(VID_V3_WB_CONV_COEF(0, 7)); + + SR(VID_V3_WB_BUF_SIZE_STATUS(0)); + SR(VID_V3_WB_BUF_THRESHOLD(0)); + SR(VID_V3_WB_FIR(0)); + SR(VID_V3_WB_PICTURE_SIZE(0)); + SR(VID_V3_WB_PIXEL_INC(0)); + SR(VID_VID3_POSITION); + SR(VID_VID3_PRELOAD); + + SR(VID_V3_WB_ROW_INC(0)); + SR(VID_V3_WB_SIZE(0)); + SR(VID_V3_WB_FIR2(0)); + SR(VID_V3_WB_ACCU2_0(0)); + SR(VID_V3_WB_ACCU2_1(0)); + + SR(VID_V3_WB_FIR_COEF_H2(0, 0)); + SR(VID_V3_WB_FIR_COEF_H2(0, 1)); + SR(VID_V3_WB_FIR_COEF_H2(0, 2)); + SR(VID_V3_WB_FIR_COEF_H2(0, 3)); + SR(VID_V3_WB_FIR_COEF_H2(0, 4)); + SR(VID_V3_WB_FIR_COEF_H2(0, 5)); + SR(VID_V3_WB_FIR_COEF_H2(0, 6)); + SR(VID_V3_WB_FIR_COEF_H2(0, 7)); + + SR(VID_V3_WB_FIR_COEF_HV2(0, 0)); + SR(VID_V3_WB_FIR_COEF_HV2(0, 1)); + SR(VID_V3_WB_FIR_COEF_HV2(0, 2)); + SR(VID_V3_WB_FIR_COEF_HV2(0, 3)); + SR(VID_V3_WB_FIR_COEF_HV2(0, 4)); + SR(VID_V3_WB_FIR_COEF_HV2(0, 5)); + SR(VID_V3_WB_FIR_COEF_HV2(0, 6)); + SR(VID_V3_WB_FIR_COEF_HV2(0, 7)); + + SR(VID_V3_WB_FIR_COEF_V2(0, 0)); + SR(VID_V3_WB_FIR_COEF_V2(0, 1)); + SR(VID_V3_WB_FIR_COEF_V2(0, 2)); + SR(VID_V3_WB_FIR_COEF_V2(0, 3)); + SR(VID_V3_WB_FIR_COEF_V2(0, 4)); + SR(VID_V3_WB_FIR_COEF_V2(0, 5)); + SR(VID_V3_WB_FIR_COEF_V2(0, 6)); + SR(VID_V3_WB_FIR_COEF_V2(0, 7)); + + /******* WB Registers *********/; + + SR(VID_V3_WB_ACCU0(1)); + SR(VID_V3_WB_ACCU1(1)); + SR(VID_V3_WB_BA0(1)); + SR(VID_V3_WB_BA1(1)); + + SR(VID_V3_WB_FIR_COEF_H(1, 0)); + SR(VID_V3_WB_FIR_COEF_H(1, 1)); + SR(VID_V3_WB_FIR_COEF_H(1, 2)); + SR(VID_V3_WB_FIR_COEF_H(1, 3)); + SR(VID_V3_WB_FIR_COEF_H(1, 4)); + SR(VID_V3_WB_FIR_COEF_H(1, 5)); + SR(VID_V3_WB_FIR_COEF_H(1, 6)); + SR(VID_V3_WB_FIR_COEF_H(1, 7)); + + SR(VID_V3_WB_FIR_COEF_HV(1, 0)); + SR(VID_V3_WB_FIR_COEF_HV(1, 1)); + SR(VID_V3_WB_FIR_COEF_HV(1, 2)); + SR(VID_V3_WB_FIR_COEF_HV(1, 3)); + SR(VID_V3_WB_FIR_COEF_HV(1, 4)); + SR(VID_V3_WB_FIR_COEF_HV(1, 5)); + SR(VID_V3_WB_FIR_COEF_HV(1, 6)); + SR(VID_V3_WB_FIR_COEF_HV(1, 7)); + + SR(VID_V3_WB_FIR_COEF_V(1, 0)); + SR(VID_V3_WB_FIR_COEF_V(1, 1)); + SR(VID_V3_WB_FIR_COEF_V(1, 2)); + SR(VID_V3_WB_FIR_COEF_V(1, 3)); + SR(VID_V3_WB_FIR_COEF_V(1, 4)); + SR(VID_V3_WB_FIR_COEF_V(1, 5)); + SR(VID_V3_WB_FIR_COEF_V(1, 6)); + SR(VID_V3_WB_FIR_COEF_V(1, 7)); + + SR(VID_V3_WB_ATTRIBUTES(1)); + SR(VID_V3_WB_CONV_COEF(1, 0)); + SR(VID_V3_WB_CONV_COEF(1, 1)); + SR(VID_V3_WB_CONV_COEF(1, 2)); + SR(VID_V3_WB_CONV_COEF(1, 3)); + SR(VID_V3_WB_CONV_COEF(1, 4)); + SR(VID_V3_WB_CONV_COEF(1, 5)); + SR(VID_V3_WB_CONV_COEF(1, 6)); + SR(VID_V3_WB_CONV_COEF(1, 7)); + + SR(VID_V3_WB_BUF_SIZE_STATUS(1)); + SR(VID_V3_WB_BUF_THRESHOLD(1)); + SR(VID_V3_WB_FIR(1)); + SR(VID_V3_WB_PICTURE_SIZE(1)); + SR(VID_V3_WB_PIXEL_INC(1)); + SR(VID_VID3_POSITION); + SR(VID_VID3_PRELOAD); + + SR(VID_V3_WB_ROW_INC(1)); + SR(VID_V3_WB_SIZE(1)); + SR(VID_V3_WB_FIR2(1)); + SR(VID_V3_WB_ACCU2_0(1)); + SR(VID_V3_WB_ACCU2_1(1)); + + SR(VID_V3_WB_FIR_COEF_H2(1, 0)); + SR(VID_V3_WB_FIR_COEF_H2(1, 1)); + SR(VID_V3_WB_FIR_COEF_H2(1, 2)); + SR(VID_V3_WB_FIR_COEF_H2(1, 3)); + SR(VID_V3_WB_FIR_COEF_H2(1, 4)); + SR(VID_V3_WB_FIR_COEF_H2(1, 5)); + SR(VID_V3_WB_FIR_COEF_H2(1, 6)); + SR(VID_V3_WB_FIR_COEF_H2(1, 7)); + + SR(VID_V3_WB_FIR_COEF_HV2(1, 0)); + SR(VID_V3_WB_FIR_COEF_HV2(1, 1)); + SR(VID_V3_WB_FIR_COEF_HV2(1, 2)); + SR(VID_V3_WB_FIR_COEF_HV2(1, 3)); + SR(VID_V3_WB_FIR_COEF_HV2(1, 4)); + SR(VID_V3_WB_FIR_COEF_HV2(1, 5)); + SR(VID_V3_WB_FIR_COEF_HV2(1, 6)); + SR(VID_V3_WB_FIR_COEF_HV2(1, 7)); + + SR(VID_V3_WB_FIR_COEF_V2(1, 0)); + SR(VID_V3_WB_FIR_COEF_V2(1, 1)); + SR(VID_V3_WB_FIR_COEF_V2(1, 2)); + SR(VID_V3_WB_FIR_COEF_V2(1, 3)); + SR(VID_V3_WB_FIR_COEF_V2(1, 4)); + SR(VID_V3_WB_FIR_COEF_V2(1, 5)); + SR(VID_V3_WB_FIR_COEF_V2(1, 6)); + SR(VID_V3_WB_FIR_COEF_V2(1, 7)); + + SR(VID_BA_UV0(0)); + SR(VID_BA_UV0(1)); + + SR(VID_BA_UV1(0)); + SR(VID_BA_UV1(1)); + + SR(VID_ATTRIBUTES2(0)); + SR(VID_ATTRIBUTES2(1)); + SR(VID_ATTRIBUTES2(2)); + SR(VID_ATTRIBUTES2(3)); + + SR(GAMMA_TABLE(0)); + SR(GAMMA_TABLE(1)); + SR(GAMMA_TABLE(2)); + SR(GAMMA_TABLE(3)); + + } + /* VID1 */ SR(VID_BA0(0)); SR(VID_BA1(0)); @@ -351,6 +702,214 @@ void dispc_restore_context(void) RR(SIZE_DIG); RR(SIZE_LCD); + if (cpu_is_omap44xx()) { + RR(DIVISOR1); + RR(GLOBAL_BUFFER); + RR(CONTROL2); + RR(DEFAULT_COLOR2); + RR(TRANS_COLOR2); + RR(CPR2_COEF_B); + RR(CPR2_COEF_G); + RR(CPR2_COEF_R); + RR(DATA2_CYCLE1); + RR(DATA2_CYCLE2); + RR(DATA2_CYCLE3); + RR(SIZE_LCD2); + RR(TIMING_H2); + RR(TIMING_V2); + RR(POL_FREQ2); + RR(DIVISOR2); + + RR(CONFIG2); + + /**** VID3 ****/; + + RR(VID_V3_WB_ACCU0(0)); + RR(VID_V3_WB_ACCU1(0)); + RR(VID_V3_WB_BA0(0)); + RR(VID_V3_WB_BA1(0)); + + RR(VID_V3_WB_FIR_COEF_H(0, 0)); + RR(VID_V3_WB_FIR_COEF_H(0, 1)); + RR(VID_V3_WB_FIR_COEF_H(0, 2)); + RR(VID_V3_WB_FIR_COEF_H(0, 3)); + RR(VID_V3_WB_FIR_COEF_H(0, 4)); + RR(VID_V3_WB_FIR_COEF_H(0, 5)); + RR(VID_V3_WB_FIR_COEF_H(0, 6)); + RR(VID_V3_WB_FIR_COEF_H(0, 7)); + + RR(VID_V3_WB_FIR_COEF_HV(0, 0)); + RR(VID_V3_WB_FIR_COEF_HV(0, 1)); + RR(VID_V3_WB_FIR_COEF_HV(0, 2)); + RR(VID_V3_WB_FIR_COEF_HV(0, 3)); + RR(VID_V3_WB_FIR_COEF_HV(0, 4)); + RR(VID_V3_WB_FIR_COEF_HV(0, 5)); + RR(VID_V3_WB_FIR_COEF_HV(0, 6)); + RR(VID_V3_WB_FIR_COEF_HV(0, 7)); + + RR(VID_V3_WB_FIR_COEF_V(0, 0)); + RR(VID_V3_WB_FIR_COEF_V(0, 1)); + RR(VID_V3_WB_FIR_COEF_V(0, 2)); + RR(VID_V3_WB_FIR_COEF_V(0, 3)); + RR(VID_V3_WB_FIR_COEF_V(0, 4)); + RR(VID_V3_WB_FIR_COEF_V(0, 5)); + RR(VID_V3_WB_FIR_COEF_V(0, 6)); + RR(VID_V3_WB_FIR_COEF_V(0, 7)); + + RR(VID_V3_WB_ATTRIBUTES(0)); + RR(VID_V3_WB_CONV_COEF(0, 0)); + RR(VID_V3_WB_CONV_COEF(0, 1)); + RR(VID_V3_WB_CONV_COEF(0, 2)); + RR(VID_V3_WB_CONV_COEF(0, 3)); + RR(VID_V3_WB_CONV_COEF(0, 4)); + RR(VID_V3_WB_CONV_COEF(0, 5)); + RR(VID_V3_WB_CONV_COEF(0, 6)); + RR(VID_V3_WB_CONV_COEF(0, 7)); + + RR(VID_V3_WB_BUF_SIZE_STATUS(0)); + RR(VID_V3_WB_BUF_THRESHOLD(0)); + RR(VID_V3_WB_FIR(0)); + RR(VID_V3_WB_PICTURE_SIZE(0)); + RR(VID_V3_WB_PIXEL_INC(0)); + RR(VID_VID3_POSITION); + RR(VID_VID3_PRELOAD); + + RR(VID_V3_WB_ROW_INC(0)); + RR(VID_V3_WB_SIZE(0)); + RR(VID_V3_WB_FIR2(0)); + RR(VID_V3_WB_ACCU2_0(0)); + RR(VID_V3_WB_ACCU2_1(0)); + + RR(VID_V3_WB_FIR_COEF_H2(0, 0)); + RR(VID_V3_WB_FIR_COEF_H2(0, 1)); + RR(VID_V3_WB_FIR_COEF_H2(0, 2)); + RR(VID_V3_WB_FIR_COEF_H2(0, 3)); + RR(VID_V3_WB_FIR_COEF_H2(0, 4)); + RR(VID_V3_WB_FIR_COEF_H2(0, 5)); + RR(VID_V3_WB_FIR_COEF_H2(0, 6)); + RR(VID_V3_WB_FIR_COEF_H2(0, 7)); + + RR(VID_V3_WB_FIR_COEF_HV2(0, 0)); + RR(VID_V3_WB_FIR_COEF_HV2(0, 1)); + RR(VID_V3_WB_FIR_COEF_HV2(0, 2)); + RR(VID_V3_WB_FIR_COEF_HV2(0, 3)); + RR(VID_V3_WB_FIR_COEF_HV2(0, 4)); + RR(VID_V3_WB_FIR_COEF_HV2(0, 5)); + RR(VID_V3_WB_FIR_COEF_HV2(0, 6)); + RR(VID_V3_WB_FIR_COEF_HV2(0, 7)); + + RR(VID_V3_WB_FIR_COEF_V2(0, 0)); + RR(VID_V3_WB_FIR_COEF_V2(0, 1)); + RR(VID_V3_WB_FIR_COEF_V2(0, 2)); + RR(VID_V3_WB_FIR_COEF_V2(0, 3)); + RR(VID_V3_WB_FIR_COEF_V2(0, 4)); + RR(VID_V3_WB_FIR_COEF_V2(0, 5)); + RR(VID_V3_WB_FIR_COEF_V2(0, 6)); + RR(VID_V3_WB_FIR_COEF_V2(0, 7)); + + /******* WB Registers *********/; + + RR(VID_V3_WB_ACCU0(1)); + RR(VID_V3_WB_ACCU1(1)); + RR(VID_V3_WB_BA0(1)); + RR(VID_V3_WB_BA1(1)); + + RR(VID_V3_WB_FIR_COEF_H(1, 0)); + RR(VID_V3_WB_FIR_COEF_H(1, 1)); + RR(VID_V3_WB_FIR_COEF_H(1, 2)); + RR(VID_V3_WB_FIR_COEF_H(1, 3)); + RR(VID_V3_WB_FIR_COEF_H(1, 4)); + RR(VID_V3_WB_FIR_COEF_H(1, 5)); + RR(VID_V3_WB_FIR_COEF_H(1, 6)); + RR(VID_V3_WB_FIR_COEF_H(1, 7)); + + RR(VID_V3_WB_FIR_COEF_HV(1, 0)); + RR(VID_V3_WB_FIR_COEF_HV(1, 1)); + RR(VID_V3_WB_FIR_COEF_HV(1, 2)); + RR(VID_V3_WB_FIR_COEF_HV(1, 3)); + RR(VID_V3_WB_FIR_COEF_HV(1, 4)); + RR(VID_V3_WB_FIR_COEF_HV(1, 5)); + RR(VID_V3_WB_FIR_COEF_HV(1, 6)); + RR(VID_V3_WB_FIR_COEF_HV(1, 7)); + + RR(VID_V3_WB_FIR_COEF_V(1, 0)); + RR(VID_V3_WB_FIR_COEF_V(1, 1)); + RR(VID_V3_WB_FIR_COEF_V(1, 2)); + RR(VID_V3_WB_FIR_COEF_V(1, 3)); + RR(VID_V3_WB_FIR_COEF_V(1, 4)); + RR(VID_V3_WB_FIR_COEF_V(1, 5)); + RR(VID_V3_WB_FIR_COEF_V(1, 6)); + RR(VID_V3_WB_FIR_COEF_V(1, 7)); + + RR(VID_V3_WB_ATTRIBUTES(1)); + RR(VID_V3_WB_CONV_COEF(1, 0)); + RR(VID_V3_WB_CONV_COEF(1, 1)); + RR(VID_V3_WB_CONV_COEF(1, 2)); + RR(VID_V3_WB_CONV_COEF(1, 3)); + RR(VID_V3_WB_CONV_COEF(1, 4)); + RR(VID_V3_WB_CONV_COEF(1, 5)); + RR(VID_V3_WB_CONV_COEF(1, 6)); + RR(VID_V3_WB_CONV_COEF(1, 7)); + + RR(VID_V3_WB_BUF_SIZE_STATUS(1)); + RR(VID_V3_WB_BUF_THRESHOLD(1)); + RR(VID_V3_WB_FIR(1)); + RR(VID_V3_WB_PICTURE_SIZE(1)); + RR(VID_V3_WB_PIXEL_INC(1)); + RR(VID_VID3_POSITION); + RR(VID_VID3_PRELOAD); + + RR(VID_V3_WB_ROW_INC(1)); + RR(VID_V3_WB_SIZE(1)); + RR(VID_V3_WB_FIR2(1)); + RR(VID_V3_WB_ACCU2_0(1)); + RR(VID_V3_WB_ACCU2_1(1)); + + RR(VID_V3_WB_FIR_COEF_H2(1, 0)); + RR(VID_V3_WB_FIR_COEF_H2(1, 1)); + RR(VID_V3_WB_FIR_COEF_H2(1, 2)); + RR(VID_V3_WB_FIR_COEF_H2(1, 3)); + RR(VID_V3_WB_FIR_COEF_H2(1, 4)); + RR(VID_V3_WB_FIR_COEF_H2(1, 5)); + RR(VID_V3_WB_FIR_COEF_H2(1, 6)); + RR(VID_V3_WB_FIR_COEF_H2(1, 7)); + + RR(VID_V3_WB_FIR_COEF_HV2(1, 0)); + RR(VID_V3_WB_FIR_COEF_HV2(1, 1)); + RR(VID_V3_WB_FIR_COEF_HV2(1, 2)); + RR(VID_V3_WB_FIR_COEF_HV2(1, 3)); + RR(VID_V3_WB_FIR_COEF_HV2(1, 4)); + RR(VID_V3_WB_FIR_COEF_HV2(1, 5)); + RR(VID_V3_WB_FIR_COEF_HV2(1, 6)); + RR(VID_V3_WB_FIR_COEF_HV2(1, 7)); + + RR(VID_V3_WB_FIR_COEF_V2(1, 0)); + RR(VID_V3_WB_FIR_COEF_V2(1, 1)); + RR(VID_V3_WB_FIR_COEF_V2(1, 2)); + RR(VID_V3_WB_FIR_COEF_V2(1, 3)); + RR(VID_V3_WB_FIR_COEF_V2(1, 4)); + RR(VID_V3_WB_FIR_COEF_V2(1, 5)); + RR(VID_V3_WB_FIR_COEF_V2(1, 6)); + RR(VID_V3_WB_FIR_COEF_V2(1, 7)); + + RR(VID_BA_UV0(0)); + RR(VID_BA_UV0(1)); + + RR(VID_BA_UV1(0)); + RR(VID_BA_UV1(1)); + + RR(VID_ATTRIBUTES2(0)); + RR(VID_ATTRIBUTES2(1)); + RR(VID_ATTRIBUTES2(2)); + RR(VID_ATTRIBUTES2(3)); + + RR(GAMMA_TABLE(0)); + RR(GAMMA_TABLE(1)); + RR(GAMMA_TABLE(2)); + RR(GAMMA_TABLE(3)); + + } + RR(GFX_BA0); RR(GFX_BA1); RR(GFX_POSITION); @@ -489,219 +1048,168 @@ bool dispc_go_busy(enum omap_channel channel) { int bit; - if (channel == OMAP_DSS_CHANNEL_LCD) + if ((!cpu_is_omap44xx()) && (channel == OMAP_DSS_CHANNEL_LCD)) + bit = 5; /* GOLCD */ + else if (cpu_is_omap44xx() && (channel != OMAP_DSS_CHANNEL_DIGIT)) bit = 5; /* GOLCD */ else bit = 6; /* GODIGIT */ - return REG_GET(DISPC_CONTROL, bit, bit) == 1; + if (cpu_is_omap44xx() && (channel == OMAP_DSS_CHANNEL_LCD2)) + return REG_GET(DISPC_CONTROL2, bit, bit) == 1; + else + return REG_GET(DISPC_CONTROL, bit, bit) == 1; + } + void dispc_go(enum omap_channel channel) { int bit; enable_clocks(1); - if (channel == OMAP_DSS_CHANNEL_LCD) + if ((channel == OMAP_DSS_CHANNEL_LCD) || + (channel == OMAP_DSS_CHANNEL_LCD2)) bit = 0; /* LCDENABLE */ else bit = 1; /* DIGITALENABLE */ /* if the channel is not enabled, we don't need GO */ - if (REG_GET(DISPC_CONTROL, bit, bit) == 0) - goto end; + if (cpu_is_omap44xx() && (channel == OMAP_DSS_CHANNEL_LCD2)) { + if (REG_GET(DISPC_CONTROL2, bit, bit) == 0) + goto end; + } else { + if (REG_GET(DISPC_CONTROL, bit, bit) == 0) + goto end; + } - if (channel == OMAP_DSS_CHANNEL_LCD) + if ((channel == OMAP_DSS_CHANNEL_LCD) || + (channel == OMAP_DSS_CHANNEL_LCD2)) bit = 5; /* GOLCD */ else bit = 6; /* GODIGIT */ - if (REG_GET(DISPC_CONTROL, bit, bit) == 1) { - DSSERR("GO bit not down for channel %d\n", channel); - goto end; + if (cpu_is_omap44xx() && (channel == OMAP_DSS_CHANNEL_LCD2)) { + if (REG_GET(DISPC_CONTROL2, bit, bit) == 1) { + /* FIXME PICO DLP on Channel 2 needs GO bit to be UP + it will come as error so changing to DSSDBG*/ + DSSDBG("GO bit not down for channel %d\n", channel); + goto end; + } + } else { + if (REG_GET(DISPC_CONTROL, bit, bit) == 1) { + DSSERR("GO bit not down for channel %d\n", channel); + goto end; + } } - DSSDBG("GO %s\n", channel == OMAP_DSS_CHANNEL_LCD ? "LCD" : "DIGIT"); + DSSDBG("GO %s\n", channel == OMAP_DSS_CHANNEL_LCD ? "LCD" : + channel == OMAP_DSS_CHANNEL_LCD2 ? "LCD2" : "DIGIT"); + + if (cpu_is_omap44xx() && (channel == OMAP_DSS_CHANNEL_LCD2)) + REG_FLD_MOD(DISPC_CONTROL2, 1, bit, bit); + else + REG_FLD_MOD(DISPC_CONTROL, 1, bit, bit); - REG_FLD_MOD(DISPC_CONTROL, 1, bit, bit); end: enable_clocks(0); } + + static void _dispc_write_firh_reg(enum omap_plane plane, int reg, u32 value) { BUG_ON(plane == OMAP_DSS_GFX); - dispc_write_reg(DISPC_VID_FIR_COEF_H(plane-1, reg), value); + if ((OMAP_DSS_VIDEO1 == plane) || (OMAP_DSS_VIDEO2 == plane)) + dispc_write_reg(DISPC_VID_FIR_COEF_H(plane-1, reg), value); + else if (OMAP_DSS_VIDEO3 == plane) + dispc_write_reg(DISPC_VID_V3_WB_FIR_COEF_H(0, reg), value); + else if (OMAP_DSS_WB == plane) + dispc_write_reg(DISPC_VID_V3_WB_FIR_COEF_H(1, reg), value); } static void _dispc_write_firhv_reg(enum omap_plane plane, int reg, u32 value) { BUG_ON(plane == OMAP_DSS_GFX); - dispc_write_reg(DISPC_VID_FIR_COEF_HV(plane-1, reg), value); + if ((OMAP_DSS_VIDEO1 == plane) || (OMAP_DSS_VIDEO2 == plane)) + dispc_write_reg(DISPC_VID_FIR_COEF_HV(plane-1, reg), value); + else if (OMAP_DSS_VIDEO3 == plane) + dispc_write_reg(DISPC_VID_V3_WB_FIR_COEF_HV(0, reg), value); + + else if (OMAP_DSS_WB == plane) + dispc_write_reg(DISPC_VID_V3_WB_FIR_COEF_HV(1, reg), value); } static void _dispc_write_firv_reg(enum omap_plane plane, int reg, u32 value) { BUG_ON(plane == OMAP_DSS_GFX); - dispc_write_reg(DISPC_VID_FIR_COEF_V(plane-1, reg), value); + if ((OMAP_DSS_VIDEO1 == plane) || (OMAP_DSS_VIDEO2 == plane)) + dispc_write_reg(DISPC_VID_FIR_COEF_V(plane-1, reg), value); + else if (OMAP_DSS_VIDEO3 == plane) + dispc_write_reg(DISPC_VID_V3_WB_FIR_COEF_V(0, reg), value); + else if (OMAP_DSS_WB == plane) + dispc_write_reg(DISPC_VID_V3_WB_FIR_COEF_V(1, reg), value); } -static void _dispc_set_scale_coef(enum omap_plane plane, int hscaleup, - int vscaleup, int five_taps) +static void _dispc_write_firh2_reg(enum omap_plane plane, int reg, u32 value) { - /* Coefficients for horizontal up-sampling */ - static const u32 coef_hup[8] = { - 0x00800000, - 0x0D7CF800, - 0x1E70F5FF, - 0x335FF5FE, - 0xF74949F7, - 0xF55F33FB, - 0xF5701EFE, - 0xF87C0DFF, - }; - - /* Coefficients for horizontal down-sampling */ - static const u32 coef_hdown[8] = { - 0x24382400, - 0x28371FFE, - 0x2C361BFB, - 0x303516F9, - 0x11343311, - 0x1635300C, - 0x1B362C08, - 0x1F372804, - }; + BUG_ON(plane == OMAP_DSS_GFX); - /* Coefficients for horizontal and vertical up-sampling */ - static const u32 coef_hvup[2][8] = { - { - 0x00800000, - 0x037B02FF, - 0x0C6F05FE, - 0x205907FB, - 0x00404000, - 0x075920FE, - 0x056F0CFF, - 0x027B0300, - }, - { - 0x00800000, - 0x0D7CF8FF, - 0x1E70F5FE, - 0x335FF5FB, - 0xF7404000, - 0xF55F33FE, - 0xF5701EFF, - 0xF87C0D00, - }, - }; + if ((OMAP_DSS_VIDEO1 == plane) || (OMAP_DSS_VIDEO2 == plane)) + dispc_write_reg(DISPC_VID_FIR_COEF_H2(plane-1, reg), value); + else if (OMAP_DSS_VIDEO3 == plane) + dispc_write_reg(DISPC_VID_V3_WB_FIR_COEF_H2(0, reg), value); + else if (OMAP_DSS_WB == plane) + dispc_write_reg(DISPC_VID_V3_WB_FIR_COEF_H2(1, reg), value); +} - /* Coefficients for horizontal and vertical down-sampling */ - static const u32 coef_hvdown[2][8] = { - { - 0x24382400, - 0x28391F04, - 0x2D381B08, - 0x3237170C, - 0x123737F7, - 0x173732F9, - 0x1B382DFB, - 0x1F3928FE, - }, - { - 0x24382400, - 0x28371F04, - 0x2C361B08, - 0x3035160C, - 0x113433F7, - 0x163530F9, - 0x1B362CFB, - 0x1F3728FE, - }, - }; +static void _dispc_write_firhv2_reg(enum omap_plane plane, int reg, u32 value) +{ + BUG_ON(plane == OMAP_DSS_GFX); - /* Coefficients for vertical up-sampling */ - static const u32 coef_vup[8] = { - 0x00000000, - 0x0000FF00, - 0x0000FEFF, - 0x0000FBFE, - 0x000000F7, - 0x0000FEFB, - 0x0000FFFE, - 0x000000FF, - }; + if ((OMAP_DSS_VIDEO1 == plane) || (OMAP_DSS_VIDEO2 == plane)) + dispc_write_reg(DISPC_VID_FIR_COEF_HV2(plane-1, reg), value); + else if (OMAP_DSS_VIDEO3 == plane) + dispc_write_reg(DISPC_VID_V3_WB_FIR_COEF_HV2(0, reg), value); + else if (OMAP_DSS_WB == plane) + dispc_write_reg(DISPC_VID_V3_WB_FIR_COEF_HV2(1, reg), value); +} +static void _dispc_write_firv2_reg(enum omap_plane plane, int reg, u32 value) +{ + BUG_ON(plane == OMAP_DSS_GFX); - /* Coefficients for vertical down-sampling */ - static const u32 coef_vdown[8] = { - 0x00000000, - 0x000004FE, - 0x000008FB, - 0x00000CF9, - 0x0000F711, - 0x0000F90C, - 0x0000FB08, - 0x0000FE04, - }; + if ((OMAP_DSS_VIDEO1 == plane) || (OMAP_DSS_VIDEO2 == plane)) + dispc_write_reg(DISPC_VID_FIR_COEF_V2(plane-1, reg), value); + else if (OMAP_DSS_VIDEO3 == plane) + dispc_write_reg(DISPC_VID_V3_WB_FIR_COEF_V2(0, reg), value); + else if (OMAP_DSS_WB == plane) + dispc_write_reg(DISPC_VID_V3_WB_FIR_COEF_V2(1, reg), value); +} - const u32 *h_coef; - const u32 *hv_coef; - const u32 *hv_coef_mod; - const u32 *v_coef; +static void _dispc_set_scale_coef(enum omap_plane plane, const s8 *hfir, + const s8 *vfir, int three_taps) +{ int i; - - if (hscaleup) - h_coef = coef_hup; - else - h_coef = coef_hdown; - - if (vscaleup) { - hv_coef = coef_hvup[five_taps]; - v_coef = coef_vup; - - if (hscaleup) - hv_coef_mod = NULL; - else - hv_coef_mod = coef_hvdown[five_taps]; - } else { - hv_coef = coef_hvdown[five_taps]; - v_coef = coef_vdown; - - if (hscaleup) - hv_coef_mod = coef_hvup[five_taps]; - else - hv_coef_mod = NULL; - } - - for (i = 0; i < 8; i++) { - u32 h, hv; - - h = h_coef[i]; - - hv = hv_coef[i]; - - if (hv_coef_mod) { - hv &= 0xffffff00; - hv |= (hv_coef_mod[i] & 0xff); - } + for (i = 0; i < 8; i++, hfir++, vfir++) { + u32 h, hv, v; + h = ((hfir[0] & 0xFF) | ((hfir[8] << 8) & 0xFF00) | + ((hfir[16] << 16) & 0xFF0000) | + ((hfir[24] << 24) & 0xFF000000)); + hv = ((hfir[32] & 0xFF) | ((vfir[8] << 8) & 0xFF00) | + ((vfir[16] << 16) & 0xFF0000) | + ((vfir[24] << 24) & 0xFF000000)); + v = ((vfir[0] & 0xFF) | ((vfir[32] << 8) & 0xFF00)); _dispc_write_firh_reg(plane, i, h); _dispc_write_firhv_reg(plane, i, hv); - } - - if (!five_taps) - return; - - for (i = 0; i < 8; i++) { - u32 v; - v = v_coef[i]; _dispc_write_firv_reg(plane, i, v); + if (three_taps && v) + printk(KERN_ERR "three_tap v is %x\n", v); } } @@ -731,6 +1239,33 @@ static void _dispc_setup_color_conv_coef(void) dispc_write_reg(DISPC_VID_CONV_COEF(1, 2), CVAL(ct->gcb, ct->gcr)); dispc_write_reg(DISPC_VID_CONV_COEF(1, 3), CVAL(ct->bcr, ct->by)); dispc_write_reg(DISPC_VID_CONV_COEF(1, 4), CVAL(0, ct->bcb)); + if (cpu_is_omap44xx()) { + dispc_write_reg(DISPC_VID_V3_WB_CONV_COEF(0, 0), + CVAL(ct->rcr, ct->ry)); + dispc_write_reg(DISPC_VID_V3_WB_CONV_COEF(0, 1), + CVAL(ct->gy, ct->rcb)); + dispc_write_reg(DISPC_VID_V3_WB_CONV_COEF(0, 2), + CVAL(ct->gcb, ct->gcr)); + dispc_write_reg(DISPC_VID_V3_WB_CONV_COEF(0, 3), + CVAL(ct->bcr, ct->by)); + dispc_write_reg(DISPC_VID_V3_WB_CONV_COEF(0, 4), + CVAL(0, ct->bcb)); + REG_FLD_MOD(DISPC_VID_V3_WB_ATTRIBUTES(0), ct->full_range, 11, 11); + /* Writeback */ + dispc_write_reg(DISPC_VID_V3_WB_CONV_COEF(1, 0), + CVAL(ct->rcr, ct->ry)); + dispc_write_reg(DISPC_VID_V3_WB_CONV_COEF(1, 1), + CVAL(ct->gy, ct->rcb)); + dispc_write_reg(DISPC_VID_V3_WB_CONV_COEF(1, 2), + CVAL(ct->gcb, ct->gcr)); + dispc_write_reg(DISPC_VID_V3_WB_CONV_COEF(1, 3), + CVAL(ct->bcr, ct->by)); + dispc_write_reg(DISPC_VID_V3_WB_CONV_COEF(1, 4), + CVAL(0, ct->bcb)); + + REG_FLD_MOD(DISPC_VID_V3_WB_ATTRIBUTES(1), ct->full_range, 11, 11); + + } #undef CVAL @@ -741,46 +1276,93 @@ static void _dispc_setup_color_conv_coef(void) static void _dispc_set_plane_ba0(enum omap_plane plane, u32 paddr) { - const struct dispc_reg ba0_reg[] = { DISPC_GFX_BA0, + struct dispc_reg ba0_reg[5] = { DISPC_GFX_BA0, DISPC_VID_BA0(0), DISPC_VID_BA0(1) }; + if (cpu_is_omap44xx()) { + ba0_reg[3] = DISPC_VID_V3_WB_BA0(0); /* VID 3 pipeline*/ + ba0_reg[4] = DISPC_VID_V3_WB_BA0(1); /* WB pipeline*/ + } dispc_write_reg(ba0_reg[plane], paddr); } static void _dispc_set_plane_ba1(enum omap_plane plane, u32 paddr) { - const struct dispc_reg ba1_reg[] = { DISPC_GFX_BA1, + struct dispc_reg ba1_reg[5] = { DISPC_GFX_BA1, DISPC_VID_BA1(0), DISPC_VID_BA1(1) }; + if (cpu_is_omap44xx()) { + ba1_reg[3] = DISPC_VID_V3_WB_BA1(0); /* VID 3 pipeline*/ + ba1_reg[4] = DISPC_VID_V3_WB_BA1(1); /* WB pipeline*/ + } dispc_write_reg(ba1_reg[plane], paddr); } +static void _dispc_set_plane_ba_uv0(enum omap_plane plane, u32 paddr) +{ + const struct dispc_reg ba_uv0_reg[] = { DISPC_VID_BA_UV0(0), + DISPC_VID_BA_UV0(1), + DISPC_VID_BA_UV0(2), /* VID 3 pipeline*/ + DISPC_VID_BA_UV0(3), /* WB pipeline*/ + }; + + BUG_ON(plane == OMAP_DSS_GFX); + + dispc_write_reg(ba_uv0_reg[plane - 1], paddr); + /* plane - 1 => no UV_BA for GFX*/ + +} + +static void _dispc_set_plane_ba_uv1(enum omap_plane plane, u32 paddr) +{ + const struct dispc_reg ba_uv1_reg[] = { DISPC_VID_BA_UV1(0), + DISPC_VID_BA_UV1(1), + DISPC_VID_BA_UV1(2) + }; + + BUG_ON(plane == OMAP_DSS_GFX); + + dispc_write_reg(ba_uv1_reg[plane - 1], paddr); + /* plane - 1 => no UV_BA for GFX*/ +} + + static void _dispc_set_plane_pos(enum omap_plane plane, int x, int y) { - const struct dispc_reg pos_reg[] = { DISPC_GFX_POSITION, + struct dispc_reg pos_reg[4] = { DISPC_GFX_POSITION, DISPC_VID_POSITION(0), DISPC_VID_POSITION(1) }; - u32 val = FLD_VAL(y, 26, 16) | FLD_VAL(x, 10, 0); + if (cpu_is_omap44xx()) + pos_reg[3] = DISPC_VID_VID3_POSITION; /* VID 3 pipeline*/ + dispc_write_reg(pos_reg[plane], val); } static void _dispc_set_pic_size(enum omap_plane plane, int width, int height) { - const struct dispc_reg siz_reg[] = { DISPC_GFX_SIZE, + struct dispc_reg siz_reg[5] = { DISPC_GFX_SIZE, DISPC_VID_PICTURE_SIZE(0), DISPC_VID_PICTURE_SIZE(1) }; u32 val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); + if (cpu_is_omap44xx()) { + siz_reg[3] = DISPC_VID_V3_WB_PICTURE_SIZE(0); /* VID3 pipeline*/ + siz_reg[4] = DISPC_VID_V3_WB_PICTURE_SIZE(1); /* WB pipeline*/ + } dispc_write_reg(siz_reg[plane], val); } static void _dispc_set_vid_size(enum omap_plane plane, int width, int height) { u32 val; - const struct dispc_reg vsi_reg[] = { DISPC_VID_SIZE(0), + struct dispc_reg vsi_reg[4] = { DISPC_VID_SIZE(0), DISPC_VID_SIZE(1) }; + if (cpu_is_omap44xx()) { + vsi_reg[2] = DISPC_VID_V3_WB_SIZE(0); /* VID 3 pipeline*/ + vsi_reg[3] = DISPC_VID_V3_WB_SIZE(1); /* WB pipeline*/ + } BUG_ON(plane == OMAP_DSS_GFX); @@ -790,8 +1372,8 @@ static void _dispc_set_vid_size(enum omap_plane plane, int width, int height) static void _dispc_setup_global_alpha(enum omap_plane plane, u8 global_alpha) { - - BUG_ON(plane == OMAP_DSS_VIDEO1); + if (!cpu_is_omap44xx()) + BUG_ON(plane == OMAP_DSS_VIDEO1); if (cpu_is_omap24xx()) return; @@ -800,22 +1382,34 @@ static void _dispc_setup_global_alpha(enum omap_plane plane, u8 global_alpha) REG_FLD_MOD(DISPC_GLOBAL_ALPHA, global_alpha, 7, 0); else if (plane == OMAP_DSS_VIDEO2) REG_FLD_MOD(DISPC_GLOBAL_ALPHA, global_alpha, 23, 16); + else if (plane == OMAP_DSS_VIDEO1) + REG_FLD_MOD(DISPC_GLOBAL_ALPHA, global_alpha, 15, 8); + else if (plane == OMAP_DSS_VIDEO3) + REG_FLD_MOD(DISPC_GLOBAL_ALPHA, global_alpha, 31, 24); + } static void _dispc_set_pix_inc(enum omap_plane plane, s32 inc) { - const struct dispc_reg ri_reg[] = { DISPC_GFX_PIXEL_INC, + struct dispc_reg ri_reg[5] = { DISPC_GFX_PIXEL_INC, DISPC_VID_PIXEL_INC(0), DISPC_VID_PIXEL_INC(1) }; - + if (cpu_is_omap44xx()) { + ri_reg[3] = DISPC_VID_V3_WB_PIXEL_INC(0); + ri_reg[4] = DISPC_VID_V3_WB_PIXEL_INC(1); /* WB pipeline*/ + } dispc_write_reg(ri_reg[plane], inc); } static void _dispc_set_row_inc(enum omap_plane plane, s32 inc) { - const struct dispc_reg ri_reg[] = { DISPC_GFX_ROW_INC, + struct dispc_reg ri_reg[5] = { DISPC_GFX_ROW_INC, DISPC_VID_ROW_INC(0), - DISPC_VID_ROW_INC(1) }; + DISPC_VID_ROW_INC(1)}; + if (cpu_is_omap44xx()) { + ri_reg[3] = DISPC_VID_V3_WB_ROW_INC(0); + ri_reg[4] = DISPC_VID_V3_WB_ROW_INC(1); /* WB pipeline*/ + } dispc_write_reg(ri_reg[plane], inc); } @@ -824,38 +1418,74 @@ static void _dispc_set_color_mode(enum omap_plane plane, enum omap_color_mode color_mode) { u32 m = 0; - - switch (color_mode) { - case OMAP_DSS_COLOR_CLUT1: - m = 0x0; break; - case OMAP_DSS_COLOR_CLUT2: - m = 0x1; break; - case OMAP_DSS_COLOR_CLUT4: - m = 0x2; break; - case OMAP_DSS_COLOR_CLUT8: - m = 0x3; break; - case OMAP_DSS_COLOR_RGB12U: - m = 0x4; break; - case OMAP_DSS_COLOR_ARGB16: - m = 0x5; break; - case OMAP_DSS_COLOR_RGB16: - m = 0x6; break; - case OMAP_DSS_COLOR_RGB24U: - m = 0x8; break; - case OMAP_DSS_COLOR_RGB24P: - m = 0x9; break; - case OMAP_DSS_COLOR_YUV2: - m = 0xa; break; - case OMAP_DSS_COLOR_UYVY: - m = 0xb; break; - case OMAP_DSS_COLOR_ARGB32: - m = 0xc; break; - case OMAP_DSS_COLOR_RGBA32: - m = 0xd; break; - case OMAP_DSS_COLOR_RGBX32: - m = 0xe; break; - default: - BUG(); break; + if ((!cpu_is_omap44xx()) || (OMAP_DSS_GFX == plane)) { + switch (color_mode) { + case OMAP_DSS_COLOR_CLUT1: + m = 0x0; break; + case OMAP_DSS_COLOR_CLUT2: + m = 0x1; break; + case OMAP_DSS_COLOR_CLUT4: + m = 0x2; break; + case OMAP_DSS_COLOR_CLUT8: + m = 0x3; break; + case OMAP_DSS_COLOR_RGB12U: + m = 0x4; break; + case OMAP_DSS_COLOR_ARGB16: + m = 0x5; break; + case OMAP_DSS_COLOR_RGB16: + m = 0x6; break; + case OMAP_DSS_COLOR_RGB24U: + m = 0x8; break; + case OMAP_DSS_COLOR_RGB24P: + m = 0x9; break; + case OMAP_DSS_COLOR_YUV2: + m = 0xa; break; + case OMAP_DSS_COLOR_UYVY: + m = 0xb; break; + case OMAP_DSS_COLOR_ARGB32: + m = 0xc; break; + case OMAP_DSS_COLOR_RGBA32: + m = 0xd; break; + case OMAP_DSS_COLOR_RGBX32: + m = 0xe; break; + default: + BUG(); break; + } + } else { + switch (color_mode) { + case OMAP_DSS_COLOR_NV12: + m = 0x0; break; + case OMAP_DSS_COLOR_RGB12U: + m = 0x1; break; + case OMAP_DSS_COLOR_RGBA12: + m = 0x2; break; + case OMAP_DSS_COLOR_XRGB12: + m = 0x4; break; + case OMAP_DSS_COLOR_ARGB16: + m = 0x5; break; + case OMAP_DSS_COLOR_RGB16: + m = 0x6; break; + case OMAP_DSS_COLOR_ARGB16_1555: + m = 0x7; break; + case OMAP_DSS_COLOR_RGB24U: + m = 0x8; break; + case OMAP_DSS_COLOR_RGB24P: + m = 0x9; break; + case OMAP_DSS_COLOR_YUV2: + m = 0xA; break; + case OMAP_DSS_COLOR_UYVY: + m = 0xB; break; + case OMAP_DSS_COLOR_ARGB32: + m = 0xC; break; + case OMAP_DSS_COLOR_RGBA32: + m = 0xD; break; + case OMAP_DSS_COLOR_RGBX24_32_ALGN: + m = 0xE; break; + case OMAP_DSS_COLOR_XRGB15: + m = 0xF; break; + default: + BUG(); break; + } } REG_FLD_MOD(dispc_reg_att[plane], m, 4, 1); @@ -866,6 +1496,7 @@ static void _dispc_set_channel_out(enum omap_plane plane, { int shift; u32 val; + int chan = 0, chan2 = 0; switch (plane) { case OMAP_DSS_GFX: @@ -873,6 +1504,7 @@ static void _dispc_set_channel_out(enum omap_plane plane, break; case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: + case OMAP_DSS_VIDEO3: shift = 16; break; default: @@ -881,7 +1513,30 @@ static void _dispc_set_channel_out(enum omap_plane plane, } val = dispc_read_reg(dispc_reg_att[plane]); - val = FLD_MOD(val, channel, shift, shift); + if (cpu_is_omap44xx()) { + switch (channel) { + + case OMAP_DSS_CHANNEL_LCD: + chan = 0; + chan2 = 0; + break; + case OMAP_DSS_CHANNEL_DIGIT: + chan = 1; + chan2 = 0; + break; + case OMAP_DSS_CHANNEL_LCD2: + chan = 0; + chan2 = 1; + break; + default: + BUG(); + } + + val = FLD_MOD(val, chan, shift, shift); + val = FLD_MOD(val, chan2, 31, 30); + } else { + val = FLD_MOD(val, channel, shift, shift); + } dispc_write_reg(dispc_reg_att[plane], val); } @@ -899,6 +1554,8 @@ void dispc_set_burst_size(enum omap_plane plane, break; case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: + case OMAP_DSS_VIDEO3: + case OMAP_DSS_WB: /* WB pipeline */ shift = 14; break; default: @@ -913,6 +1570,48 @@ void dispc_set_burst_size(enum omap_plane plane, enable_clocks(0); } +void dispc_set_zorder(enum omap_plane plane, + enum omap_overlay_zorder zorder) +{ + u32 val; + + BUG_ON(plane == OMAP_DSS_WB); + val = dispc_read_reg(dispc_reg_att[plane]); + val = FLD_MOD(val, zorder, 27, 26); + dispc_write_reg(dispc_reg_att[plane], val); + +} + +void dispc_enable_zorder(enum omap_plane plane, bool enable) +{ + u32 val; + + BUG_ON(plane == OMAP_DSS_WB); + val = dispc_read_reg(dispc_reg_att[plane]); + val = FLD_MOD(val, enable, 25, 25); + dispc_write_reg(dispc_reg_att[plane], val); + +} + + +void dispc_set_idle_mode(void) +{ + u32 l; + + l = dispc_read_reg(DISPC_SYSCONFIG); + l = FLD_MOD(l, 1, 13, 12); /* MIDLEMODE: smart standby */ + l = FLD_MOD(l, 1, 4, 3); /* SIDLEMODE: smart idle */ + l = FLD_MOD(l, 0, 2, 2); /* ENWAKEUP */ + l = FLD_MOD(l, 0, 0, 0); /* AUTOIDLE */ + dispc_write_reg(DISPC_SYSCONFIG, l); + +} + +void dispc_enable_gamma_table(bool enable) +{ + REG_FLD_MOD(DISPC_CONFIG, enable, 9, 9); +} + static void _dispc_set_vid_color_conv(enum omap_plane plane, bool enable) { u32 val; @@ -927,6 +1626,7 @@ static void _dispc_set_vid_color_conv(enum omap_plane plane, bool enable) void dispc_enable_replication(enum omap_plane plane, bool enable) { int bit; + BUG_ON(plane == OMAP_DSS_WB); if (plane == OMAP_DSS_GFX) bit = 5; @@ -938,13 +1638,16 @@ void dispc_enable_replication(enum omap_plane plane, bool enable) enable_clocks(0); } -void dispc_set_lcd_size(u16 width, u16 height) +void dispc_set_lcd_size(enum omap_channel channel, u16 width, u16 height) { u32 val; BUG_ON((width > (1 << 11)) || (height > (1 << 11))); val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); enable_clocks(1); - dispc_write_reg(DISPC_SIZE_LCD, val); + if (OMAP_DSS_CHANNEL_LCD2 == channel) + dispc_write_reg(DISPC_SIZE_LCD2, val); + else + dispc_write_reg(DISPC_SIZE_LCD, val); enable_clocks(0); } @@ -960,11 +1663,15 @@ void dispc_set_digit_size(u16 width, u16 height) static void dispc_read_plane_fifo_sizes(void) { - const struct dispc_reg fsz_reg[] = { DISPC_GFX_FIFO_SIZE_STATUS, + struct dispc_reg fsz_reg[5] = { DISPC_GFX_FIFO_SIZE_STATUS, DISPC_VID_FIFO_SIZE_STATUS(0), DISPC_VID_FIFO_SIZE_STATUS(1) }; u32 size; int plane; + if (cpu_is_omap44xx()) { + fsz_reg[3] = DISPC_VID_V3_WB_BUF_SIZE_STATUS(0); + fsz_reg[4] = DISPC_VID_V3_WB_BUF_SIZE_STATUS(1); /* WB pipeline*/ + } enable_clocks(1); @@ -973,6 +1680,8 @@ static void dispc_read_plane_fifo_sizes(void) size = FLD_GET(dispc_read_reg(fsz_reg[plane]), 8, 0); else if (cpu_is_omap34xx()) size = FLD_GET(dispc_read_reg(fsz_reg[plane]), 10, 0); + else if (cpu_is_omap44xx()) + size = FLD_GET(dispc_read_reg(fsz_reg[plane]), 15, 0); else BUG(); @@ -989,9 +1698,13 @@ u32 dispc_get_plane_fifo_size(enum omap_plane plane) void dispc_setup_plane_fifo(enum omap_plane plane, u32 low, u32 high) { - const struct dispc_reg ftrs_reg[] = { DISPC_GFX_FIFO_THRESHOLD, + struct dispc_reg ftrs_reg[5] = { DISPC_GFX_FIFO_THRESHOLD, DISPC_VID_FIFO_THRESHOLD(0), DISPC_VID_FIFO_THRESHOLD(1) }; + if (cpu_is_omap44xx()) { + ftrs_reg[3] = DISPC_VID_V3_WB_BUF_THRESHOLD(0); + ftrs_reg[4] = DISPC_VID_V3_WB_BUF_THRESHOLD(1); + } enable_clocks(1); DSSDBG("fifo(%d) low/high old %u/%u, new %u/%u\n", @@ -1003,9 +1716,12 @@ void dispc_setup_plane_fifo(enum omap_plane plane, u32 low, u32 high) if (cpu_is_omap24xx()) dispc_write_reg(ftrs_reg[plane], FLD_VAL(high, 24, 16) | FLD_VAL(low, 8, 0)); - else + else if (cpu_is_omap34xx()) + dispc_write_reg(ftrs_reg[plane], + FLD_VAL(high, 27, 16) | FLD_VAL(low, 11, 0)); + else /* cpu is omap44xx */ dispc_write_reg(ftrs_reg[plane], - FLD_VAL(high, 27, 16) | FLD_VAL(low, 11, 0)); + FLD_VAL(high, 31, 16) | FLD_VAL(low, 15, 0)); enable_clocks(0); } @@ -1023,9 +1739,12 @@ void dispc_enable_fifomerge(bool enable) static void _dispc_set_fir(enum omap_plane plane, int hinc, int vinc) { u32 val; - const struct dispc_reg fir_reg[] = { DISPC_VID_FIR(0), + struct dispc_reg fir_reg[4] = { DISPC_VID_FIR(0), DISPC_VID_FIR(1) }; - + if (cpu_is_omap44xx()) { + fir_reg[2] = DISPC_VID_V3_WB_FIR(0); + fir_reg[3] = DISPC_VID_V3_WB_FIR(1); /* WB pipeline*/ + } BUG_ON(plane == OMAP_DSS_GFX); if (cpu_is_omap24xx()) @@ -1038,71 +1757,287 @@ static void _dispc_set_fir(enum omap_plane plane, int hinc, int vinc) static void _dispc_set_vid_accu0(enum omap_plane plane, int haccu, int vaccu) { u32 val; - const struct dispc_reg ac0_reg[] = { DISPC_VID_ACCU0(0), + struct dispc_reg ac0_reg[4] = { DISPC_VID_ACCU0(0), DISPC_VID_ACCU0(1) }; + if (cpu_is_omap44xx()) { + ac0_reg[2] = DISPC_VID_V3_WB_ACCU0(0); /* VID 3 pipeline*/ + ac0_reg[3] = DISPC_VID_V3_WB_ACCU0(1); /* WB pipeline*/ + } BUG_ON(plane == OMAP_DSS_GFX); + if (cpu_is_omap44xx()) + val = FLD_VAL(vaccu, 26, 16) | FLD_VAL(haccu, 10, 0); + else + val = FLD_VAL(vaccu, 25, 16) | FLD_VAL(haccu, 9, 0); - val = FLD_VAL(vaccu, 25, 16) | FLD_VAL(haccu, 9, 0); dispc_write_reg(ac0_reg[plane-1], val); } static void _dispc_set_vid_accu1(enum omap_plane plane, int haccu, int vaccu) { u32 val; - const struct dispc_reg ac1_reg[] = { DISPC_VID_ACCU1(0), + struct dispc_reg ac1_reg[4] = { DISPC_VID_ACCU1(0), DISPC_VID_ACCU1(1) }; + if (cpu_is_omap44xx()) { + ac1_reg[2] = DISPC_VID_V3_WB_ACCU1(0); /* VID 3 pipeline*/ + ac1_reg[3] = DISPC_VID_V3_WB_ACCU1(1); /* WB pipeline*/ + } + + BUG_ON(plane == OMAP_DSS_GFX); + if (cpu_is_omap44xx()) + val = FLD_VAL(vaccu, 26, 16) | FLD_VAL(haccu, 10, 0); + else + val = FLD_VAL(vaccu, 25, 16) | FLD_VAL(haccu, 9, 0); + dispc_write_reg(ac1_reg[plane-1], val); +} + +static void _dispc_set_fir2(enum omap_plane plane, int hinc, int vinc) +{ + u32 val; + const struct dispc_reg fir_reg[] = { DISPC_VID_FIR2(0), + DISPC_VID_FIR2(1), + DISPC_VID_V3_WB_FIR2(0), + /* VID 3 pipeline*/ + DISPC_VID_V3_WB_FIR2(1) + /* WB pipeline*/ + + }; + + BUG_ON(plane == OMAP_DSS_GFX); + + val = FLD_VAL(vinc, 28, 16) | FLD_VAL(hinc, 12, 0); + + dispc_write_reg(fir_reg[plane-1], val); +} + +static void _dispc_set_vid_accu2_0(enum omap_plane plane, int haccu, int vaccu) +{ + u32 val; + const struct dispc_reg ac0_reg[] = { DISPC_VID_ACCU2_0(0), + DISPC_VID_ACCU2_0(1), + DISPC_VID_V3_WB_ACCU2_0(0), + DISPC_VID_V3_WB_ACCU2_0(1) /* WB */ + }; + + BUG_ON(plane == OMAP_DSS_GFX); + + val = FLD_VAL(vaccu, 26, 16) | FLD_VAL(haccu, 10, 0); + dispc_write_reg(ac0_reg[plane-1], val); +} + +static void _dispc_set_vid_accu2_1(enum omap_plane plane, int haccu, int vaccu) +{ + u32 val; + const struct dispc_reg ac1_reg[] = { DISPC_VID_ACCU2_1(0), + DISPC_VID_ACCU2_1(1), + DISPC_VID_V3_WB_ACCU2_1(0), + DISPC_VID_V3_WB_ACCU2_1(1) + }; BUG_ON(plane == OMAP_DSS_GFX); - val = FLD_VAL(vaccu, 25, 16) | FLD_VAL(haccu, 9, 0); + val = FLD_VAL(vaccu, 26, 16) | FLD_VAL(haccu, 10, 0); dispc_write_reg(ac1_reg[plane-1], val); } +static const s8 fir5_zero[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +}; +static const s8 fir3_m8[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2, 5, 7, 64, 32, 12, 3, + 128, 123, 111, 89, 64, 89, 111, 123, + 0, 3, 12, 32, 0, 7, 5, 2, + 0, 0, 0, 0, 0, 0, 0, 0, +}; +static const s8 fir5_m8[] = { + 17, 18, 15, 9, -18, -6, 5, 13, + -20, -27, -30, -27, 81, 47, 17, -4, + 134, 127, 121, 105, 81, 105, 121, 127, + -20, -4, 17, 47, -18, -27, -30, -27, + 17, 14, 5, -6, 2, 9, 15, 19, +}; +static const s8 fir5_m8b[] = { + 0, 0, -1, -2, -9, -5, -2, -1, + 0, -8, -11, -11, 73, 51, 30, 13, + 128, 124, 112, 95, 73, 95, 112, 124, + 0, 13, 30, 51, -9, -11, -11, -8, + 0, -1, -2, -5, 0, -2, -1, 0, +}; +static const s8 fir5_m9[] = { + 8, 14, 17, 17, -26, -18, -9, 1, + -8, -21, -27, -30, 83, 56, 30, 8, + 128, 126, 117, 103, 83, 103, 117, 126, + -8, 8, 30, 56, -26, -30, -27, -21, + 8, 1, -9, -18, 14, 17, 17, 14, +}; +static const s8 fir5_m10[] = { + -2, 5, 11, 15, -28, -24, -18, -10, + 2, -12, -22, -27, 83, 62, 41, 20, + 128, 125, 116, 102, 83, 102, 116, 125, + 2, 20, 41, 62, -28, -27, -22, -12, + -2, -10, -18, -24, 18, 15, 11, 5, +}; +static const s8 fir5_m11[] = { + -12, -4, 3, 9, -26, -27, -24, -19, + 12, -3, -15, -22, 83, 67, 49, 30, + 128, 124, 115, 101, 83, 101, 115, 124, + 12, 30, 49, 67, -26, -22, -15, -3, + -12, -19, -24, -27, 14, 9, 3, -4, +}; +static const s8 fir5_m12[] = { + -19, -12, -6, 1, -21, -25, -26, -24, + 21, 6, -7, -16, 82, 70, 55, 38, + 124, 120, 112, 98, 82, 98, 112, 120, + 21, 38, 55, 70, -21, -16, -7, 6, + -19, -24, -26, -25, 6, 1, -6, -12, +}; +static const s8 fir5_m13[] = { + -22, -18, -12, -6, -17, -22, -25, -25, + 27, 13, 0, -10, 81, 71, 58, 43, + 118, 115, 107, 95, 81, 95, 107, 115, + 27, 43, 58, 71, -17, -10, 0, 13, + -22, -25, -25, -22, 0, -6, -12, -18, +}; +static const s8 fir5_m14[] = { + -23, -20, -16, -11, -11, -18, -22, -24, + 32, 18, 6, -4, 78, 70, 59, 46, + 110, 108, 101, 91, 78, 91, 101, 108, + 32, 46, 59, 70, -11, -4, 6, 18, + -23, -24, -22, -18, -6, -11, -16, -20, +}; +static const s8 fir3_m16[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 36, 31, 27, 23, 55, 50, 45, 40, + 56, 57, 56, 55, 55, 55, 56, 57, + 36, 40, 45, 50, 18, 23, 27, 31, + 0, 0, 0, 0, 0, 0, 0, 0, +}; +static const s8 fir5_m16[] = { + -20, -21, -19, -17, -2, -9, -14, -18, + 37, 26, 15, 6, 73, 66, 58, 48, + 94, 93, 88, 82, 73, 82, 88, 93, + 37, 48, 58, 66, -2, 6, 15, 26, + -20, -18, -14, -9, -14, -17, -19, -21, +}; +static const s8 fir5_m19[] = { + -12, -14, -16, -16, 8, 1, -4, -9, + 38, 31, 22, 15, 64, 59, 53, 47, + 76, 72, 73, 69, 64, 69, 73, 72, + 38, 47, 53, 59, 8, 15, 22, 31, + -12, -8, -4, 1, -16, -16, -16, -13, +}; +static const s8 fir5_m22[] = { + -6, -8, -11, -13, 13, 8, 3, -2, + 37, 32, 25, 19, 58, 53, 48, 44, + 66, 61, 63, 61, 58, 61, 63, 61, + 37, 44, 48, 53, 13, 19, 25, 32, + -6, -1, 3, 8, -14, -13, -11, -7, +}; +static const s8 fir5_m26[] = { + 1, -2, -5, -8, 18, 13, 8, 4, + 36, 31, 27, 22, 51, 48, 44, 40, + 54, 55, 54, 53, 51, 53, 54, 55, + 36, 40, 44, 48, 18, 22, 27, 31, + 1, 4, 8, 13, -10, -8, -5, -2, +}; +static const s8 fir5_m32[] = { + 7, 4, 1, -1, 21, 17, 14, 10, + 34, 31, 27, 24, 45, 42, 39, 37, + 46, 46, 46, 46, 45, 46, 46, 46, + 34, 37, 39, 42, 21, 24, 28, 31, + 7, 10, 14, 17, -4, -1, 1, 4, +}; + +static const s8 *get_scaling_coef(int orig_size, int out_size, + int orig_ilaced, int out_ilaced, + int three_tap) +{ + /* ranges from 2 to 32 */ + int two_m = 16 * orig_size / out_size; + + if (orig_size > 4 * out_size) + return fir5_zero; + + if (out_size > 8 * orig_size) + return three_tap ? fir3_m8 : fir5_m8; + /* interlaced output needs at least M = 16 */ + if (out_ilaced) { + if (two_m < 32) + two_m = 32; + } + if (three_tap) + return two_m < 24 ? fir3_m8 : fir3_m16; + + return orig_size < out_size ? fir5_m8b : + two_m < 17 ? fir5_m8 : + two_m < 19 ? fir5_m9 : + two_m < 21 ? fir5_m10 : + two_m < 23 ? fir5_m11 : + two_m < 25 ? fir5_m12 : + two_m < 27 ? fir5_m13 : + two_m < 30 ? fir5_m14 : + two_m < 35 ? fir5_m16 : + two_m < 41 ? fir5_m19 : + two_m < 48 ? fir5_m22 : + two_m < 58 ? fir5_m26 : + fir5_m32; +} static void _dispc_set_scaling(enum omap_plane plane, u16 orig_width, u16 orig_height, u16 out_width, u16 out_height, - bool ilace, bool five_taps, - bool fieldmode) + bool ilace, bool three_taps, + bool fieldmode, int scale_x, int scale_y) { int fir_hinc; int fir_vinc; - int hscaleup, vscaleup; int accu0 = 0; int accu1 = 0; u32 l; + const s8 *hfir, *vfir; BUG_ON(plane == OMAP_DSS_GFX); - hscaleup = orig_width <= out_width; - vscaleup = orig_height <= out_height; - - _dispc_set_scale_coef(plane, hscaleup, vscaleup, five_taps); - - if (!orig_width || orig_width == out_width) + if (scale_x) { + fir_hinc = 1024 * (orig_width - 1) / (out_width - 1); + if (fir_hinc > 4095) + fir_hinc = 4095; + hfir = get_scaling_coef(orig_width, out_width, 0, 0, 0); + } else { fir_hinc = 0; - else - fir_hinc = 1024 * orig_width / out_width; + hfir = fir5_zero; + } - if (!orig_height || orig_height == out_height) + if (scale_y) { + fir_vinc = 1024 * (orig_height - 1) / (out_height - 1); + if (fir_vinc > 4095) + fir_vinc = 4095; + vfir = get_scaling_coef(orig_height, out_height, 0, 0, + three_taps); + } else { fir_vinc = 0; - else - fir_vinc = 1024 * orig_height / out_height; + vfir = fir5_zero; + } + _dispc_set_scale_coef(plane, hfir, vfir, three_taps); _dispc_set_fir(plane, fir_hinc, fir_vinc); l = dispc_read_reg(dispc_reg_att[plane]); - l &= ~((0x0f << 5) | (0x3 << 21)); - + if (!cpu_is_omap44xx()) { + l &= ~((0x0f << 5) | (0x3 << 21)); + l |= out_width > orig_width ? 0 : (1 << 7); + l |= out_height > orig_height ? 0 : (1 << 8); + } else + l &= ~((0x03 << 5) | (0x1 << 21)); l |= fir_hinc ? (1 << 5) : 0; l |= fir_vinc ? (1 << 6) : 0; - l |= hscaleup ? 0 : (1 << 7); - l |= vscaleup ? 0 : (1 << 8); - - l |= five_taps ? (1 << 21) : 0; - l |= five_taps ? (1 << 22) : 0; + l |= three_taps ? 0 : (1 << 21); dispc_write_reg(dispc_reg_att[plane], l); @@ -1123,54 +2058,140 @@ static void _dispc_set_scaling(enum omap_plane plane, _dispc_set_vid_accu1(plane, 0, accu1); } -static void _dispc_set_rotation_attrs(enum omap_plane plane, u8 rotation, - bool mirroring, enum omap_color_mode color_mode) +static void _dispc_set_scaling_uv(enum omap_plane plane, + u16 orig_width, u16 orig_height, + u16 out_width, u16 out_height, + bool ilace, bool three_taps, + bool fieldmode, int scale_x, int scale_y) { - if (color_mode == OMAP_DSS_COLOR_YUV2 || - color_mode == OMAP_DSS_COLOR_UYVY) { - int vidrot = 0; + int i; + int fir_hinc, fir_vinc; + int accu0, accu1, accuh; + const s8 *hfir, *vfir; + + if (scale_x) { + fir_hinc = 1024 * (orig_width - 1) / (out_width - 1); + if (fir_hinc > 4095) + fir_hinc = 4095; + hfir = get_scaling_coef(orig_width, out_width, 0, 0, 0); + } else { + fir_hinc = 0; + hfir = fir5_zero; + } - if (mirroring) { - switch (rotation) { - case OMAP_DSS_ROT_0: - vidrot = 2; - break; - case OMAP_DSS_ROT_90: - vidrot = 1; - break; - case OMAP_DSS_ROT_180: - vidrot = 0; - break; - case OMAP_DSS_ROT_270: - vidrot = 3; - break; - } - } else { - switch (rotation) { - case OMAP_DSS_ROT_0: - vidrot = 0; - break; - case OMAP_DSS_ROT_90: - vidrot = 1; - break; - case OMAP_DSS_ROT_180: - vidrot = 2; - break; - case OMAP_DSS_ROT_270: - vidrot = 3; - break; - } + if (scale_y) { + fir_vinc = 1024 * (orig_height - 0) / (out_height - 0); + if (fir_vinc > 4095) + fir_vinc = 4095; + vfir = get_scaling_coef(orig_height, out_height, 0, + ilace, three_taps); + } else { + fir_vinc = 0; + vfir = fir5_zero; } - REG_FLD_MOD(dispc_reg_att[plane], vidrot, 13, 12); + for (i = 0; i < 8; i++, hfir++, vfir++) { + u32 h, hv, v; + h = ((hfir[0] & 0xFF) | ((hfir[8] << 8) & 0xFF00) | + ((hfir[16] << 16) & 0xFF0000) | + ((hfir[24] << 24) & 0xFF000000)); + hv = ((hfir[32] & 0xFF) | ((vfir[8] << 8) & 0xFF00) | + ((vfir[16] << 16) & 0xFF0000) | + ((vfir[24] << 24) & 0xFF000000)); + v = ((vfir[0] & 0xFF) | ((vfir[32] << 8) & 0xFF00)); + + _dispc_write_firh2_reg(plane, i, h); + _dispc_write_firhv2_reg(plane, i, hv); + _dispc_write_firv2_reg(plane, i, v); + if (three_taps && v) + printk(KERN_ERR "three_tap v is %x\n", v); + } - if (rotation == OMAP_DSS_ROT_90 || rotation == OMAP_DSS_ROT_270) - REG_FLD_MOD(dispc_reg_att[plane], 0x1, 18, 18); - else - REG_FLD_MOD(dispc_reg_att[plane], 0x0, 18, 18); + /* set chroma resampling */ + REG_FLD_MOD(DISPC_VID_ATTRIBUTES2(plane - 1), + (fir_hinc || fir_vinc) ? 1 : 0, 8, 8); + + /* set H scaling */ + REG_FLD_MOD(dispc_reg_att[plane], fir_hinc ? 1 : 0, 6, 6); + + /* set V scaling */ + REG_FLD_MOD(dispc_reg_att[plane], fir_vinc ? 1 : 0, 5, 5); + + _dispc_set_fir2(plane, fir_hinc, fir_vinc); + + if (ilace) { + accu0 = (-3 * fir_vinc / 4) % 1024; + accu1 = (-fir_vinc / 4) % 1024; } else { - REG_FLD_MOD(dispc_reg_att[plane], 0, 13, 12); - REG_FLD_MOD(dispc_reg_att[plane], 0, 18, 18); + accu0 = accu1 = (-fir_vinc / 2) % 1024; + } + accuh = (-fir_hinc / 2) % 1024; + + _dispc_set_vid_accu2_0(plane, 0x80, 0); + _dispc_set_vid_accu2_1(plane, 0x80, 0); + /* _dispc_set_vid_accu2_0(plane, accuh, accu0); + _dispc_set_vid_accu2_1(plane, accuh, accu1); */ +} +static void _dispc_set_rotation_attrs(enum omap_plane plane, u8 rotation, + bool mirroring, enum omap_color_mode color_mode) +{ + BUG_ON(plane == OMAP_DSS_WB); + if (!cpu_is_omap44xx()) { + if (color_mode == OMAP_DSS_COLOR_YUV2 || + color_mode == OMAP_DSS_COLOR_UYVY) { + int vidrot = 0; + + if (mirroring) { + switch (rotation) { + case OMAP_DSS_ROT_0: + vidrot = 2; + break; + case OMAP_DSS_ROT_90: + vidrot = 1; + break; + case OMAP_DSS_ROT_180: + vidrot = 0; + break; + case OMAP_DSS_ROT_270: + vidrot = 3; + break; + } + } else { + switch (rotation) { + case OMAP_DSS_ROT_0: + vidrot = 0; + break; + case OMAP_DSS_ROT_90: + vidrot = 1; + break; + case OMAP_DSS_ROT_180: + vidrot = 2; + break; + case OMAP_DSS_ROT_270: + vidrot = 3; + break; + } + } + + REG_FLD_MOD(dispc_reg_att[plane], vidrot, 13, 12); + + if (rotation == OMAP_DSS_ROT_90 || + rotation == OMAP_DSS_ROT_270) + REG_FLD_MOD(dispc_reg_att[plane], 0x1, 18, 18); + else + REG_FLD_MOD(dispc_reg_att[plane], 0x0, 18, 18); + } else { + REG_FLD_MOD(dispc_reg_att[plane], 0, 13, 12); + REG_FLD_MOD(dispc_reg_att[plane], 0, 18, 18); + } + } else if (plane != OMAP_DSS_GFX) { + if (color_mode == OMAP_DSS_COLOR_NV12) { + /* DOUBLESTRIDE : 0 for 90-, 270-; 1 for 0- and 180- */ + if (rotation == 1 || rotation == 3) + REG_FLD_MOD(dispc_reg_att[plane], 0x0, 22, 22); + else + REG_FLD_MOD(dispc_reg_att[plane], 0x1, 22, 22); + } } } @@ -1215,6 +2236,69 @@ static s32 pixinc(int pixels, u8 ps) BUG(); } + static void calc_tiler_row_rotation(u8 rotation, + u16 width, u16 height, + enum omap_color_mode color_mode, + s32 *row_inc) + { + u8 ps = 1; + DSSDBG("calc_tiler_rot(%d): %dx%d\n", rotation, width, height); + + switch (color_mode) { + case OMAP_DSS_COLOR_RGB16: + case OMAP_DSS_COLOR_ARGB16: + + case OMAP_DSS_COLOR_YUV2: + case OMAP_DSS_COLOR_UYVY: + ps = 2; + break; + + case OMAP_DSS_COLOR_RGB24P: + case OMAP_DSS_COLOR_RGB24U: + case OMAP_DSS_COLOR_ARGB32: + case OMAP_DSS_COLOR_RGBA32: + case OMAP_DSS_COLOR_RGBX32: + ps = 4; + break; + + case OMAP_DSS_COLOR_NV12: + ps = 1; + break; + + default: + BUG(); + return; + } + switch (rotation) { + case 0: + case 2: + if (1 == ps) + *row_inc = 16384 + 1 - (width); + else + *row_inc = 32768 + 1 - (width * ps); + break; + + case 1: + case 3: + if (4 == ps) + *row_inc = 16384 + 1 - (width * ps); + else + *row_inc = 8192 + 1 - (width * ps); + break; + + default: + BUG(); + return; + } + + DSSDBG(" colormode: %d, rotation: %d, ps: %d, width: %d," + " height: %d, row_inc:%d\n", + color_mode, rotation, ps, width, height, *row_inc); + + return; + } + + static void calc_vrfb_rotation_offset(u8 rotation, bool mirror, u16 screen_width, u16 width, u16 height, @@ -1439,12 +2523,13 @@ static void calc_dma_rotation_offset(u8 rotation, bool mirror, } } -static unsigned long calc_fclk_five_taps(u16 width, u16 height, - u16 out_width, u16 out_height, enum omap_color_mode color_mode) +static unsigned long calc_fclk_five_taps(enum omap_channel channel, + u16 width, u16 height, u16 out_width, u16 out_height, + enum omap_color_mode color_mode) { u32 fclk = 0; /* FIXME venc pclk? */ - u64 tmp, pclk = dispc_pclk_rate(); + u64 tmp, pclk = dispc_pclk_rate(channel); if (height > out_height) { /* FIXME get real display PPL */ @@ -1476,8 +2561,8 @@ static unsigned long calc_fclk_five_taps(u16 width, u16 height, return fclk; } -static unsigned long calc_fclk(u16 width, u16 height, - u16 out_width, u16 out_height) +static unsigned long calc_fclk(enum omap_channel channel, u16 width, + u16 height, u16 out_width, u16 out_height) { unsigned int hf, vf; @@ -1501,7 +2586,7 @@ static unsigned long calc_fclk(u16 width, u16 height, vf = 1; /* FIXME venc pclk? */ - return dispc_pclk_rate() * vf * hf; + return dispc_pclk_rate(channel) * vf * hf; } void dispc_set_channel_out(enum omap_plane plane, enum omap_channel channel_out) @@ -1519,11 +2604,11 @@ static int _dispc_setup_plane(enum omap_plane plane, enum omap_color_mode color_mode, bool ilace, enum omap_dss_rotation_type rotation_type, - u8 rotation, int mirror, - u8 global_alpha) + u8 rotation, int mirror, enum omap_channel channel, + u8 global_alpha, u32 puv_addr) { - const int maxdownscale = cpu_is_omap34xx() ? 4 : 2; - bool five_taps = 0; + const int maxdownscale = (cpu_is_omap34xx() | cpu_is_omap44xx()) ? 4 : 2; + bool three_taps = 0; bool fieldmode = 0; int cconv = 0; unsigned offset0, offset1; @@ -1558,6 +2643,7 @@ static int _dispc_setup_plane(enum omap_plane plane, case OMAP_DSS_COLOR_ARGB32: case OMAP_DSS_COLOR_RGBA32: case OMAP_DSS_COLOR_RGBX32: + if (cpu_is_omap24xx()) return -EINVAL; /* fall through */ @@ -1575,12 +2661,10 @@ static int _dispc_setup_plane(enum omap_plane plane, unsigned long fclk = 0; - if (out_width < width / maxdownscale || - out_width > width * 8) + if (out_width < width / maxdownscale) return -EINVAL; - if (out_height < height / maxdownscale || - out_height > height * 8) + if (out_height < height / maxdownscale) return -EINVAL; switch (color_mode) { @@ -1597,12 +2681,18 @@ static int _dispc_setup_plane(enum omap_plane plane, case OMAP_DSS_COLOR_ARGB16: case OMAP_DSS_COLOR_ARGB32: case OMAP_DSS_COLOR_RGBA32: + case OMAP_DSS_COLOR_RGBA12: + case OMAP_DSS_COLOR_XRGB12: + case OMAP_DSS_COLOR_ARGB16_1555: + case OMAP_DSS_COLOR_RGBX24_32_ALGN: + case OMAP_DSS_COLOR_XRGB15: if (cpu_is_omap24xx()) return -EINVAL; - if (plane == OMAP_DSS_VIDEO1) + if (!cpu_is_omap44xx() && plane == OMAP_DSS_VIDEO1) return -EINVAL; break; + case OMAP_DSS_COLOR_NV12: case OMAP_DSS_COLOR_YUV2: case OMAP_DSS_COLOR_UYVY: cconv = 1; @@ -1612,32 +2702,36 @@ static int _dispc_setup_plane(enum omap_plane plane, return -EINVAL; } - /* Must use 5-tap filter? */ - five_taps = height > out_height * 2; + /* Must use 3-tap filter */ + three_taps = width > 1280; - if (!five_taps) { - fclk = calc_fclk(width, height, + /* Should use 3-tap filter for upscaling, but HDMI gets + out of sync if using 3-tap */ + /* if (out_height > height) + three_taps = 1; */ + + if (three_taps) { + fclk = calc_fclk(channel, width, height, out_width, out_height); /* Try 5-tap filter if 3-tap fclk is too high */ if (cpu_is_omap34xx() && height > out_height && - fclk > dispc_fclk_rate()) - five_taps = true; + fclk > dispc_fclk_rate()) { + printk(KERN_ERR + "Should use 5 tap but cannot\n"); + } + } else { + fclk = calc_fclk_five_taps(channel, width, height, + out_width, out_height, color_mode); } - if (width > (2048 >> five_taps)) { - DSSERR("failed to set up scaling, fclk too low\n"); + if (!cpu_is_omap44xx()) + if (width > (1024 << three_taps)) return -EINVAL; - } - - if (five_taps) - fclk = calc_fclk_five_taps(width, height, - out_width, out_height, color_mode); - DSSDBG("required fclk rate = %lu Hz\n", fclk); DSSDBG("current fclk rate = %lu Hz\n", dispc_fclk_rate()); - if (!fclk || fclk > dispc_fclk_rate()) { + if (fclk > dispc_fclk_rate()) { DSSERR("failed to set up scaling, " "required fclk rate = %lu Hz, " "current fclk rate = %lu Hz\n", @@ -1663,18 +2757,73 @@ static int _dispc_setup_plane(enum omap_plane plane, /* Fields are independent but interleaved in memory. */ if (fieldmode) field_offset = 1; + pix_inc = 0x1; + offset0 = 0x0; + offset1 = 0x0; + /* check if tiler address; else set row_inc = 1*/ + if ((paddr >= 0x60000000) && (paddr <= 0x7fffffff)) { + struct tiler_view_orient orient; + u8 mir_x = 0, mir_y = 0; + unsigned long tiler_width, tiler_height; + + calc_tiler_row_rotation(rotation, width, frame_height, + color_mode, &row_inc); + + /* get rotated top-left coordinate + (if rotation is applied before mirroring) */ + memset(&orient, 0, sizeof(orient)); + + if (rotation & 1) + rotation ^= 2; + + tiler_rotate_view(&orient, rotation * 90); + + if (mirror) { + if (rotation & 1) + mir_x = 1; + else + mir_y = 1; + } + orient.x_invert ^= mir_x; + orient.y_invert ^= mir_y; - if (rotation_type == OMAP_DSS_ROT_DMA) - calc_dma_rotation_offset(rotation, mirror, + DSSDBG("RXY = %d %d %d\n", orient.rotate_90, + orient.x_invert, orient.y_invert); + + if (orient.rotate_90 & 1) { + tiler_height = width; + tiler_width = height; + } else { + tiler_height = height; + tiler_width = width; + } + DSSDBG("w, h = %ld %ld\n", tiler_width, tiler_height); + + paddr = tiler_reorient_topleft(tiler_get_natural_addr((void *)paddr), + orient, tiler_width, tiler_height); + + if (puv_addr) + puv_addr = tiler_reorient_topleft( + tiler_get_natural_addr((void *)puv_addr), + orient, tiler_width/2, tiler_height/2); + DSSDBG("rotated addresses: 0x%0x, 0x%0x\n", + paddr, puv_addr); + /* set BURSTTYPE if rotation is non-zero */ + REG_FLD_MOD(dispc_reg_att[plane], 0x1, 29, 29); + } else + row_inc = 0x1; + if (!cpu_is_omap44xx()) { + if (rotation_type == OMAP_DSS_ROT_DMA) + calc_dma_rotation_offset(rotation, mirror, screen_width, width, frame_height, color_mode, fieldmode, field_offset, &offset0, &offset1, &row_inc, &pix_inc); - else - calc_vrfb_rotation_offset(rotation, mirror, + else + calc_vrfb_rotation_offset(rotation, mirror, screen_width, width, frame_height, color_mode, fieldmode, field_offset, &offset0, &offset1, &row_inc, &pix_inc); - + } DSSDBG("offset0 %u, offset1 %u, row_inc %d, pix_inc %d\n", offset0, offset1, row_inc, pix_inc); @@ -1683,6 +2832,11 @@ static int _dispc_setup_plane(enum omap_plane plane, _dispc_set_plane_ba0(plane, paddr + offset0); _dispc_set_plane_ba1(plane, paddr + offset1); + if (OMAP_DSS_COLOR_NV12 == color_mode) { + _dispc_set_plane_ba_uv0(plane, puv_addr + offset0); + _dispc_set_plane_ba_uv1(plane, puv_addr + offset1); + } + _dispc_set_row_inc(plane, row_inc); _dispc_set_pix_inc(plane, pix_inc); @@ -1694,16 +2848,56 @@ static int _dispc_setup_plane(enum omap_plane plane, _dispc_set_pic_size(plane, width, height); if (plane != OMAP_DSS_GFX) { + int scale_x = width != out_width; + int scale_y = height != out_height; + u16 out_ch_height = out_height; + u16 out_ch_width = out_width; + u16 ch_height = height; + u16 ch_width = width; + int scale_uv = 0; + + if (cpu_is_omap44xx()) { + /* account for chroma decimation */ + switch (color_mode) { + case OMAP_DSS_COLOR_NV12: + ch_height >>= 1; /* Y downsampled by 2 */ + case OMAP_DSS_COLOR_YUV2: + case OMAP_DSS_COLOR_UYVY: + ch_width >>= 1; /* X downsampled by 2 */ + /* must use FIR for YUV422 if rotated */ + if (color_mode != OMAP_DSS_COLOR_NV12 && rotation % 4) + scale_x = scale_y = 1; + scale_uv = 1; + break; + default: + /* no UV scaling for RGB formats for now */ + break; + } + + if (out_ch_width != ch_width) + scale_x = true; + if (out_ch_height != ch_height) + scale_y = true; + /* set up UV scaling */ + _dispc_set_scaling_uv(plane, ch_width, ch_height, + out_ch_width, out_ch_height, ilace, + three_taps, fieldmode, scale_uv && scale_x, + scale_uv && scale_y); + if (!scale_uv || (!scale_x && !scale_y)) + /* :TRICKY: set chroma resampling for RGB formats */ + REG_FLD_MOD(DISPC_VID_ATTRIBUTES2(plane - 1), 0, 8, 8); + } _dispc_set_scaling(plane, width, height, out_width, out_height, - ilace, five_taps, fieldmode); + ilace, three_taps, fieldmode, + scale_x, scale_y); _dispc_set_vid_size(plane, out_width, out_height); _dispc_set_vid_color_conv(plane, cconv); } _dispc_set_rotation_attrs(plane, rotation, mirror, color_mode); - if (plane != OMAP_DSS_VIDEO1) + if ((plane != OMAP_DSS_VIDEO1) || (cpu_is_omap44xx())) _dispc_setup_global_alpha(plane, global_alpha); return 0; @@ -1712,6 +2906,10 @@ static int _dispc_setup_plane(enum omap_plane plane, static void _dispc_enable_plane(enum omap_plane plane, bool enable) { REG_FLD_MOD(dispc_reg_att[plane], enable ? 1 : 0, 0, 0); + if (!enable && cpu_is_omap44xx()) { /* clear out resizer related bits */ + REG_FLD_MOD(dispc_reg_att[plane], 0x00, 6, 5); + REG_FLD_MOD(dispc_reg_att[plane], 0x00, 21, 21); + } } static void dispc_disable_isr(void *data, u32 mask) @@ -1720,36 +2918,47 @@ static void dispc_disable_isr(void *data, u32 mask) complete(compl); } -static void _enable_lcd_out(bool enable) +static void _enable_lcd_out(enum omap_channel channel, bool enable) { - REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 0, 0); + if (OMAP_DSS_CHANNEL_LCD2 == channel) + REG_FLD_MOD(DISPC_CONTROL2, enable ? 1 : 0, 0, 0); + else + REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 0, 0); } -static void dispc_enable_lcd_out(bool enable) +void dispc_enable_lcd_out(enum omap_channel channel, bool enable) { struct completion frame_done_completion; bool is_on; int r; + int irq; enable_clocks(1); /* When we disable LCD output, we need to wait until frame is done. * Otherwise the DSS is still working, and turning off the clocks * prevents DSS from going to OFF mode */ - is_on = REG_GET(DISPC_CONTROL, 0, 0); + if (OMAP_DSS_CHANNEL_LCD2 == channel) { + is_on = REG_GET(DISPC_CONTROL2, 0, 0); + irq = DISPC_IRQ_FRAMEDONE2; + } else { + is_on = REG_GET(DISPC_CONTROL, 0, 0); + irq = DISPC_IRQ_FRAMEDONE; + } + if (!enable && is_on) { init_completion(&frame_done_completion); r = omap_dispc_register_isr(dispc_disable_isr, &frame_done_completion, - DISPC_IRQ_FRAMEDONE); + irq); if (r) DSSERR("failed to register FRAMEDONE isr\n"); } - _enable_lcd_out(enable); + _enable_lcd_out(channel, enable); if (!enable && is_on) { if (!wait_for_completion_timeout(&frame_done_completion, @@ -1758,7 +2967,7 @@ static void dispc_enable_lcd_out(bool enable) r = omap_dispc_unregister_isr(dispc_disable_isr, &frame_done_completion, - DISPC_IRQ_FRAMEDONE); + irq); if (r) DSSERR("failed to unregister FRAMEDONE isr\n"); @@ -1772,7 +2981,7 @@ static void _enable_digit_out(bool enable) REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 1, 1); } -static void dispc_enable_digit_out(bool enable) +void dispc_enable_digit_out(bool enable) { struct completion frame_done_completion; int r; @@ -1810,13 +3019,15 @@ static void dispc_enable_digit_out(bool enable) /* XXX I understand from TRM that we should only wait for the * current field to complete. But it seems we have to wait * for both fields */ - if (!wait_for_completion_timeout(&frame_done_completion, + if (!cpu_is_omap44xx()) { + if (!wait_for_completion_timeout(&frame_done_completion, msecs_to_jiffies(100))) - DSSERR("timeout waiting for EVSYNC\n"); + DSSERR("timeout waiting for EVSYNC\n"); - if (!wait_for_completion_timeout(&frame_done_completion, + if (!wait_for_completion_timeout(&frame_done_completion, msecs_to_jiffies(100))) - DSSERR("timeout waiting for EVSYNC\n"); + DSSERR("timeout waiting for EVSYNC\n"); + } r = omap_dispc_unregister_isr(dispc_disable_isr, &frame_done_completion, @@ -1848,8 +3059,9 @@ bool dispc_is_channel_enabled(enum omap_channel channel) void dispc_enable_channel(enum omap_channel channel, bool enable) { - if (channel == OMAP_DSS_CHANNEL_LCD) - dispc_enable_lcd_out(enable); + if (channel == OMAP_DSS_CHANNEL_LCD || + channel == OMAP_DSS_CHANNEL_LCD2) + dispc_enable_lcd_out(channel, enable); else if (channel == OMAP_DSS_CHANNEL_DIGIT) dispc_enable_digit_out(enable); else @@ -1885,7 +3097,8 @@ void dispc_enable_fifohandcheck(bool enable) } -void dispc_set_lcd_display_type(enum omap_lcd_display_type type) +void dispc_set_lcd_display_type(enum omap_channel channel, + enum omap_lcd_display_type type) { int mode; @@ -1904,7 +3117,11 @@ void dispc_set_lcd_display_type(enum omap_lcd_display_type type) } enable_clocks(1); - REG_FLD_MOD(DISPC_CONTROL, mode, 3, 3); + if (OMAP_DSS_CHANNEL_LCD2 == channel) + REG_FLD_MOD(DISPC_CONTROL2, mode, 3, 3); + else + REG_FLD_MOD(DISPC_CONTROL, mode, 3, 3); + enable_clocks(0); } @@ -1919,8 +3136,7 @@ void dispc_set_loadmode(enum omap_dss_load_mode mode) void dispc_set_default_color(enum omap_channel channel, u32 color) { const struct dispc_reg def_reg[] = { DISPC_DEFAULT_COLOR0, - DISPC_DEFAULT_COLOR1 }; - + DISPC_DEFAULT_COLOR1, DISPC_DEFAULT_COLOR2 }; enable_clocks(1); dispc_write_reg(def_reg[channel], color); enable_clocks(0); @@ -1929,11 +3145,12 @@ void dispc_set_default_color(enum omap_channel channel, u32 color) u32 dispc_get_default_color(enum omap_channel channel) { const struct dispc_reg def_reg[] = { DISPC_DEFAULT_COLOR0, - DISPC_DEFAULT_COLOR1 }; + DISPC_DEFAULT_COLOR1, DISPC_DEFAULT_COLOR2 }; u32 l; - BUG_ON(channel != OMAP_DSS_CHANNEL_DIGIT && - channel != OMAP_DSS_CHANNEL_LCD); + if (!cpu_is_omap44xx()) + BUG_ON(channel != OMAP_DSS_CHANNEL_DIGIT && + channel != OMAP_DSS_CHANNEL_LCD); enable_clocks(1); l = dispc_read_reg(def_reg[channel]); @@ -1947,10 +3164,13 @@ void dispc_set_trans_key(enum omap_channel ch, u32 trans_key) { const struct dispc_reg tr_reg[] = { - DISPC_TRANS_COLOR0, DISPC_TRANS_COLOR1 }; + DISPC_TRANS_COLOR0, DISPC_TRANS_COLOR1, + DISPC_TRANS_COLOR2}; enable_clocks(1); - if (ch == OMAP_DSS_CHANNEL_LCD) + if (ch == OMAP_DSS_CHANNEL_LCD2) + REG_FLD_MOD(DISPC_CONFIG2, type, 11, 11); + else if (ch == OMAP_DSS_CHANNEL_LCD) REG_FLD_MOD(DISPC_CONFIG, type, 11, 11); else /* OMAP_DSS_CHANNEL_DIGIT */ REG_FLD_MOD(DISPC_CONFIG, type, 13, 13); @@ -1964,11 +3184,14 @@ void dispc_get_trans_key(enum omap_channel ch, u32 *trans_key) { const struct dispc_reg tr_reg[] = { - DISPC_TRANS_COLOR0, DISPC_TRANS_COLOR1 }; + DISPC_TRANS_COLOR0, DISPC_TRANS_COLOR1, + DISPC_TRANS_COLOR2 }; enable_clocks(1); if (type) { - if (ch == OMAP_DSS_CHANNEL_LCD) + if (ch == OMAP_DSS_CHANNEL_LCD2) + *type = REG_GET(DISPC_CONFIG2, 11, 11); + else if (ch == OMAP_DSS_CHANNEL_LCD) *type = REG_GET(DISPC_CONFIG, 11, 11); else if (ch == OMAP_DSS_CHANNEL_DIGIT) *type = REG_GET(DISPC_CONFIG, 13, 13); @@ -1984,7 +3207,9 @@ void dispc_get_trans_key(enum omap_channel ch, void dispc_enable_trans_key(enum omap_channel ch, bool enable) { enable_clocks(1); - if (ch == OMAP_DSS_CHANNEL_LCD) + if (ch == OMAP_DSS_CHANNEL_LCD2) + REG_FLD_MOD(DISPC_CONFIG2, enable, 10, 10); + else if (ch == OMAP_DSS_CHANNEL_LCD) REG_FLD_MOD(DISPC_CONFIG, enable, 10, 10); else /* OMAP_DSS_CHANNEL_DIGIT */ REG_FLD_MOD(DISPC_CONFIG, enable, 12, 12); @@ -2026,8 +3251,12 @@ bool dispc_alpha_blending_enabled(enum omap_channel ch) bool dispc_trans_key_enabled(enum omap_channel ch) { bool enabled; + BUG_ON(ch == OMAP_DSS_CHANNEL_LCD2); enable_clocks(1); + if (ch == OMAP_DSS_CHANNEL_LCD2) + enabled = REG_GET(DISPC_CONFIG2, 10, 10); + else if (ch == OMAP_DSS_CHANNEL_LCD) enabled = REG_GET(DISPC_CONFIG, 10, 10); else if (ch == OMAP_DSS_CHANNEL_DIGIT) @@ -2040,7 +3269,7 @@ bool dispc_trans_key_enabled(enum omap_channel ch) } -void dispc_set_tft_data_lines(u8 data_lines) +void dispc_set_tft_data_lines(enum omap_channel channel, u8 data_lines) { int code; @@ -2063,11 +3292,15 @@ void dispc_set_tft_data_lines(u8 data_lines) } enable_clocks(1); - REG_FLD_MOD(DISPC_CONTROL, code, 9, 8); + if (channel == OMAP_DSS_CHANNEL_LCD2) + REG_FLD_MOD(DISPC_CONTROL2, code, 9, 8); + else + REG_FLD_MOD(DISPC_CONTROL, code, 9, 8); enable_clocks(0); } -void dispc_set_parallel_interface_mode(enum omap_parallel_interface_mode mode) +void dispc_set_parallel_interface_mode(enum omap_channel channel, + enum omap_parallel_interface_mode mode) { u32 l; int stallmode; @@ -2097,13 +3330,18 @@ void dispc_set_parallel_interface_mode(enum omap_parallel_interface_mode mode) enable_clocks(1); - l = dispc_read_reg(DISPC_CONTROL); - - l = FLD_MOD(l, stallmode, 11, 11); - l = FLD_MOD(l, gpout0, 15, 15); - l = FLD_MOD(l, gpout1, 16, 16); + if (OMAP_DSS_CHANNEL_LCD2 == channel) { + l = dispc_read_reg(DISPC_CONTROL2); + l = FLD_MOD(l, stallmode, 11, 11); + dispc_write_reg(DISPC_CONTROL2, l); + } else { + l = dispc_read_reg(DISPC_CONTROL); + l = FLD_MOD(l, stallmode, 11, 11); + l = FLD_MOD(l, gpout0, 15, 15); + l = FLD_MOD(l, gpout1, 16, 16); - dispc_write_reg(DISPC_CONTROL, l); + dispc_write_reg(DISPC_CONTROL, l); + } enable_clocks(0); } @@ -2139,8 +3377,8 @@ bool dispc_lcd_timings_ok(struct omap_video_timings *timings) timings->vfp, timings->vbp); } -static void _dispc_set_lcd_timings(int hsw, int hfp, int hbp, - int vsw, int vfp, int vbp) +static void _dispc_set_lcd_timings(enum omap_channel channel, int hsw, + int hfp, int hbp, int vsw, int vfp, int vbp) { u32 timing_h, timing_v; @@ -2159,13 +3397,19 @@ static void _dispc_set_lcd_timings(int hsw, int hfp, int hbp, } enable_clocks(1); - dispc_write_reg(DISPC_TIMING_H, timing_h); - dispc_write_reg(DISPC_TIMING_V, timing_v); + if (OMAP_DSS_CHANNEL_LCD2 == channel) { + dispc_write_reg(DISPC_TIMING_H2, timing_h); + dispc_write_reg(DISPC_TIMING_V2, timing_v); + } else { + dispc_write_reg(DISPC_TIMING_H, timing_h); + dispc_write_reg(DISPC_TIMING_V, timing_v); + } enable_clocks(0); } /* change name to mode? */ -void dispc_set_lcd_timings(struct omap_video_timings *timings) +void dispc_set_lcd_timings(enum omap_channel channel, + struct omap_video_timings *timings) { unsigned xtot, ytot; unsigned long ht, vt; @@ -2175,10 +3419,11 @@ void dispc_set_lcd_timings(struct omap_video_timings *timings) timings->vfp, timings->vbp)) BUG(); - _dispc_set_lcd_timings(timings->hsw, timings->hfp, timings->hbp, - timings->vsw, timings->vfp, timings->vbp); + _dispc_set_lcd_timings(channel, timings->hsw, timings->hfp, + timings->hbp, timings->vsw, timings->vfp, + timings->vbp); - dispc_set_lcd_size(timings->x_res, timings->y_res); + dispc_set_lcd_size(channel, timings->x_res, timings->y_res); xtot = timings->x_res + timings->hfp + timings->hsw + timings->hbp; ytot = timings->y_res + timings->vfp + timings->vsw + timings->vbp; @@ -2186,7 +3431,8 @@ void dispc_set_lcd_timings(struct omap_video_timings *timings) ht = (timings->pixel_clock * 1000) / xtot; vt = (timings->pixel_clock * 1000) / xtot / ytot; - DSSDBG("xres %u yres %u\n", timings->x_res, timings->y_res); + DSSDBG("channel %u xres %u yres %u\n", channel, timings->x_res, + timings->y_res); DSSDBG("pck %u\n", timings->pixel_clock); DSSDBG("hsw %d hfp %d hbp %d vsw %d vfp %d vbp %d\n", timings->hsw, timings->hfp, timings->hbp, @@ -2195,21 +3441,30 @@ void dispc_set_lcd_timings(struct omap_video_timings *timings) DSSDBG("hsync %luHz, vsync %luHz\n", ht, vt); } -static void dispc_set_lcd_divisor(u16 lck_div, u16 pck_div) +static void dispc_set_lcd_divisor(enum omap_channel channel, u16 lck_div, + u16 pck_div) { BUG_ON(lck_div < 1); BUG_ON(pck_div < 2); enable_clocks(1); - dispc_write_reg(DISPC_DIVISOR, + if (OMAP_DSS_CHANNEL_LCD2 == channel) + dispc_write_reg(DISPC_DIVISOR2, + FLD_VAL(lck_div, 23, 16) | FLD_VAL(pck_div, 7, 0)); + else + dispc_write_reg(DISPC_DIVISOR, FLD_VAL(lck_div, 23, 16) | FLD_VAL(pck_div, 7, 0)); enable_clocks(0); } -static void dispc_get_lcd_divisor(int *lck_div, int *pck_div) +static void dispc_get_lcd_divisor(enum omap_channel channel, + int *lck_div, int *pck_div) { u32 l; - l = dispc_read_reg(DISPC_DIVISOR); + if (OMAP_DSS_CHANNEL_LCD2 == channel) + l = dispc_read_reg(DISPC_DIVISOR2); + else + l = dispc_read_reg(DISPC_DIVISOR); *lck_div = FLD_GET(l, 23, 16); *pck_div = FLD_GET(l, 7, 0); } @@ -2222,20 +3477,22 @@ unsigned long dispc_fclk_rate(void) r = dss_clk_get_rate(DSS_CLK_FCK1); else #ifdef CONFIG_OMAP2_DSS_DSI - r = dsi_get_dsi1_pll_rate(); + r = dsi_get_dsi1_pll_rate(DSI1); #else BUG(); #endif return r; } -unsigned long dispc_lclk_rate(void) +unsigned long dispc_lclk_rate(enum omap_channel channel) { int lcd; unsigned long r; u32 l; - - l = dispc_read_reg(DISPC_DIVISOR); + if (OMAP_DSS_CHANNEL_LCD2 == channel) + l = dispc_read_reg(DISPC_DIVISOR2); + else + l = dispc_read_reg(DISPC_DIVISOR); lcd = FLD_GET(l, 23, 16); @@ -2244,13 +3501,16 @@ unsigned long dispc_lclk_rate(void) return r / lcd; } -unsigned long dispc_pclk_rate(void) +unsigned long dispc_pclk_rate(enum omap_channel channel) { int lcd, pcd; unsigned long r; u32 l; - l = dispc_read_reg(DISPC_DIVISOR); + if (OMAP_DSS_CHANNEL_LCD2 == channel) + l = dispc_read_reg(DISPC_DIVISOR2); + else + l = dispc_read_reg(DISPC_DIVISOR); lcd = FLD_GET(l, 23, 16); pcd = FLD_GET(l, 7, 0); @@ -2266,7 +3526,7 @@ void dispc_dump_clocks(struct seq_file *s) enable_clocks(1); - dispc_get_lcd_divisor(&lcd, &pcd); + dispc_get_lcd_divisor(OMAP_DSS_CHANNEL_LCD, &lcd, &pcd); seq_printf(s, "- DISPC -\n"); @@ -2275,8 +3535,26 @@ void dispc_dump_clocks(struct seq_file *s) "dss1_alwon_fclk" : "dsi1_pll_fclk"); seq_printf(s, "fck\t\t%-16lu\n", dispc_fclk_rate()); - seq_printf(s, "lck\t\t%-16lulck div\t%u\n", dispc_lclk_rate(), lcd); - seq_printf(s, "pck\t\t%-16lupck div\t%u\n", dispc_pclk_rate(), pcd); + seq_printf(s, "lck\t\t%-16lulck div\t%u\n", + dispc_lclk_rate(OMAP_DSS_CHANNEL_LCD), lcd); + seq_printf(s, "pck\t\t%-16lupck div\t%u\n", + dispc_pclk_rate(OMAP_DSS_CHANNEL_LCD), pcd); + + if (cpu_is_omap44xx()) { + dispc_get_lcd_divisor(OMAP_DSS_CHANNEL_LCD2, &lcd, &pcd); + + seq_printf(s, "- DISPC - LCD 2\n"); + + seq_printf(s, "dispc fclk source = %s\n", + dss_get_dispc_clk_source() == 0 ? + "dss1_alwon_fclk" : "dsi1_pll_fclk"); + + seq_printf(s, "fck\t\t%-16lu\n", dispc_fclk_rate()); + seq_printf(s, "lck\t\t%-16lulck div\t%u\n", + dispc_lclk_rate(OMAP_DSS_CHANNEL_LCD2), lcd); + seq_printf(s, "pck\t\t%-16lupck div\t%u\n", + dispc_pclk_rate(OMAP_DSS_CHANNEL_LCD2), pcd); + } enable_clocks(0); } @@ -2392,6 +3670,7 @@ void dispc_dump_regs(struct seq_file *s) DUMPREG(DISPC_VID_POSITION(1)); DUMPREG(DISPC_VID_SIZE(1)); DUMPREG(DISPC_VID_ATTRIBUTES(1)); + DUMPREG(DISPC_VID_V3_WB_ATTRIBUTES(1)); DUMPREG(DISPC_VID_FIFO_THRESHOLD(1)); DUMPREG(DISPC_VID_FIFO_SIZE_STATUS(1)); DUMPREG(DISPC_VID_ROW_INC(1)); @@ -2468,8 +3747,9 @@ void dispc_dump_regs(struct seq_file *s) #undef DUMPREG } -static void _dispc_set_pol_freq(bool onoff, bool rf, bool ieo, bool ipc, - bool ihs, bool ivs, u8 acbi, u8 acb) +static void _dispc_set_pol_freq(enum omap_channel channel, bool onoff, bool rf, + bool ieo, bool ipc, bool ihs, bool ivs, + u8 acbi, u8 acb) { u32 l = 0; @@ -2486,13 +3766,17 @@ static void _dispc_set_pol_freq(bool onoff, bool rf, bool ieo, bool ipc, l |= FLD_VAL(acb, 7, 0); enable_clocks(1); - dispc_write_reg(DISPC_POL_FREQ, l); + if (OMAP_DSS_CHANNEL_LCD2) + dispc_write_reg(DISPC_POL_FREQ2, l); + else + dispc_write_reg(DISPC_POL_FREQ, l); enable_clocks(0); } -void dispc_set_pol_freq(enum omap_panel_config config, u8 acbi, u8 acb) +void dispc_set_pol_freq(enum omap_channel ch, enum omap_panel_config config, + u8 acbi, u8 acb) { - _dispc_set_pol_freq((config & OMAP_DSS_LCD_ONOFF) != 0, + _dispc_set_pol_freq(ch, (config & OMAP_DSS_LCD_ONOFF) != 0, (config & OMAP_DSS_LCD_RF) != 0, (config & OMAP_DSS_LCD_IEO) != 0, (config & OMAP_DSS_LCD_IPC) != 0, @@ -2561,25 +3845,31 @@ int dispc_calc_clock_rates(unsigned long dispc_fclk_rate, return 0; } -int dispc_set_clock_div(struct dispc_clock_info *cinfo) +int dispc_set_clock_div(enum omap_channel channel, + struct dispc_clock_info *cinfo) { DSSDBG("lck = %lu (%u)\n", cinfo->lck, cinfo->lck_div); DSSDBG("pck = %lu (%u)\n", cinfo->pck, cinfo->pck_div); - dispc_set_lcd_divisor(cinfo->lck_div, cinfo->pck_div); + dispc_set_lcd_divisor(channel, cinfo->lck_div, + cinfo->pck_div); return 0; } -int dispc_get_clock_div(struct dispc_clock_info *cinfo) +int dispc_get_clock_div(enum omap_channel channel, + struct dispc_clock_info *cinfo) { unsigned long fck; fck = dispc_fclk_rate(); - - cinfo->lck_div = REG_GET(DISPC_DIVISOR, 23, 16); - cinfo->pck_div = REG_GET(DISPC_DIVISOR, 7, 0); - + if (OMAP_DSS_CHANNEL_LCD2 == channel) { + cinfo->lck_div = REG_GET(DISPC_DIVISOR2, 23, 16); + cinfo->pck_div = REG_GET(DISPC_DIVISOR2, 7, 0); + } else { + cinfo->lck_div = REG_GET(DISPC_DIVISOR, 23, 16); + cinfo->pck_div = REG_GET(DISPC_DIVISOR, 7, 0); + } cinfo->lck = fck / cinfo->lck_div; cinfo->pck = cinfo->lck / cinfo->pck_div; @@ -2857,6 +4147,23 @@ static void dispc_error_worker(struct work_struct *work) } } } + if (errors & DISPC_IRQ_VID3_FIFO_UNDERFLOW) { + DSSERR("VID3_FIFO_UNDERFLOW, disabling VID2\n"); + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { + struct omap_overlay *ovl; + ovl = omap_dss_get_overlay(i); + + if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) + continue; + + if (ovl->id == 3) { + dispc_enable_plane(ovl->id, 0); + dispc_go(ovl->manager->id); + mdelay(50); + break; + } + } + } if (errors & DISPC_IRQ_SYNC_LOST) { struct omap_overlay_manager *manager = NULL; @@ -2897,7 +4204,52 @@ static void dispc_error_worker(struct work_struct *work) } } + if (errors & DISPC_IRQ_SYNC_LOST_2) { + struct omap_overlay_manager *manager = NULL; + bool enable = false; + + DSSERR("SYNC_LOST for LCD2, disabling LCD2\n"); + + for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { + struct omap_overlay_manager *mgr; + mgr = omap_dss_get_overlay_manager(i); + + if (mgr->id == OMAP_DSS_CHANNEL_LCD2) { + manager = mgr; + enable = mgr->device->state == + OMAP_DSS_DISPLAY_ACTIVE; + mgr->device->driver->disable(mgr->device); + break; + } + } + + if (manager) { + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { + struct omap_overlay *ovl; + ovl = omap_dss_get_overlay(i); + + if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) + continue; + + if (ovl->id != 0 && ovl->manager == manager) + dispc_enable_plane(ovl->id, 0); + } + + dispc_go(manager->id); + mdelay(50); + if (enable) + manager->device->driver->enable( + manager->device); + } + } + + if (errors & DISPC_IRQ_SYNC_LOST_DIGIT) { + + DSSERR("SYNC_LOST_DIGIT\n"); +/*commenting below code as with 1080P Decode we see a sync lost digit for +first frame as it takes long time to decode but it later recovers*/ +#if 0 struct omap_overlay_manager *manager = NULL; bool enable = false; @@ -2914,6 +4266,7 @@ static void dispc_error_worker(struct work_struct *work) mgr->device->driver->disable(mgr->device); break; } + } if (manager) { @@ -2934,6 +4287,7 @@ static void dispc_error_worker(struct work_struct *work) if (enable) dssdev->driver->enable(dssdev); } +#endif } if (errors & DISPC_IRQ_OCP_ERR) { @@ -3014,13 +4368,24 @@ int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask, } #ifdef CONFIG_OMAP2_DSS_FAKE_VSYNC -void dispc_fake_vsync_irq(void) +void dispc_fake_vsync_irq(enum omap_dsi_index ix) { u32 irqstatus = DISPC_IRQ_VSYNC; int i; local_irq_disable(); - + switch (ix) { + case DSI1: + irqstatus = DISPC_IRQ_VSYNC; + break; + case DSI2: + irqstatus = DISPC_IRQ_VSYNC2; + break; + default: + DSSERR("Invalid display id for fake vsync\n"); + local_irq_enable(); + return; + } for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { struct omap_dispc_isr_data *isr_data; isr_data = &dispc.registered_isr[i]; @@ -3077,7 +4442,8 @@ static void _omap_dispc_initial_config(void) dispc_write_reg(DISPC_SYSCONFIG, l); /* FUNCGATED */ - REG_FLD_MOD(DISPC_CONFIG, 1, 9, 9); + if (!cpu_is_omap44xx()) + REG_FLD_MOD(DISPC_CONFIG, 1, 9, 9); /* L3 firewall setting: enable access to OCM RAM */ /* XXX this should be somewhere in plat-omap */ @@ -3104,7 +4470,7 @@ int dispc_init(void) INIT_WORK(&dispc.error_work, dispc_error_worker); - dispc.base = ioremap(DISPC_BASE, DISPC_SZ_REGS); + dispc_base = dispc.base = ioremap(DISPC_BASE, DISPC_SZ_REGS); if (!dispc.base) { DSSERR("can't ioremap DISPC\n"); return -ENOMEM; @@ -3151,7 +4517,8 @@ int dispc_setup_plane(enum omap_plane plane, enum omap_color_mode color_mode, bool ilace, enum omap_dss_rotation_type rotation_type, - u8 rotation, bool mirror, u8 global_alpha) + u8 rotation, bool mirror, u8 global_alpha, + enum omap_channel channel, u32 puv_addr) { int r = 0; @@ -3173,9 +4540,246 @@ int dispc_setup_plane(enum omap_plane plane, color_mode, ilace, rotation_type, rotation, mirror, - global_alpha); + channel, + global_alpha, + puv_addr); enable_clocks(0); return r; } + +/* Writeback*/ +int dispc_setup_wb(struct writeback_cache_data *wb) +{ + unsigned long mir_x, mir_y; + unsigned long tiler_width, tiler_height; + u8 orientation = 0, rotation = 0, mirror = 0 ; + int ch_width, ch_height, out_ch_width, out_ch_height, scale_x, scale_y; + struct tiler_view_orient orient; + u32 paddr = wb->paddr; + u32 puv_addr = wb->puv_addr; /* relevant for NV12 format only */ + u16 out_width = wb->width; + u16 out_height = wb->height; + u16 width = wb->input_width; + u16 height = wb->input_height; + + enum omap_color_mode color_mode = wb->color_mode; /* output color */ + + u32 fifo_low = wb->fifo_low; + u32 fifo_high = wb->fifo_high; + enum omap_writeback_source source = wb->source; + + enum omap_plane plane = OMAP_DSS_WB; + enum omap_plane input_plane; + + const int maxdownscale = 2; + bool three_taps = 0; + int cconv = 0; + s32 row_inc; + s32 pix_inc; + u16 frame_height = height; + + DSSDBG("dispc_setup_wb"); + DSSDBG("Maxds = %d", maxdownscale); + DSSDBG("out_width, width = %d, %d", (int) out_width, (int) width); + DSSDBG("out_height, height = %d, %d", (int) out_height, (int) height); + + if (paddr == 0) { + printk("KERN_ERR dispc_setup_wb paddr NULL"); + return -EINVAL; + } + { + /* validate color format and 5taps*/ + + if (out_width < width / maxdownscale || + out_width > width * 8){ + printk("KERN_ERR dispc_setup_wb out_width not in range "); + return -EINVAL; + } + if (out_height < height / maxdownscale || + out_height > height * 8){ + printk("KERN_ERR dispc_setup_wb out_height not in range "); + return -EINVAL; + } + switch (color_mode) { + case OMAP_DSS_COLOR_RGB16: + case OMAP_DSS_COLOR_RGB24P: + case OMAP_DSS_COLOR_RGB24U: + case OMAP_DSS_COLOR_ARGB16: + case OMAP_DSS_COLOR_ARGB32: + case OMAP_DSS_COLOR_RGBA32: + case OMAP_DSS_COLOR_RGBA12: + case OMAP_DSS_COLOR_XRGB12: + case OMAP_DSS_COLOR_ARGB16_1555: + case OMAP_DSS_COLOR_RGBX24_32_ALGN: + case OMAP_DSS_COLOR_XRGB15: + break; + + case OMAP_DSS_COLOR_NV12: + case OMAP_DSS_COLOR_YUV2: + case OMAP_DSS_COLOR_UYVY: + cconv = 1; + break; + + default: + return -EINVAL; + } + } + + if (width > 1280) + three_taps = 1; + +/* we handle ONLY overlays as source yet, NOT Managers */ +/* TODO: remove this check once managers are accepted as source */ + if (source > OMAP_WB_TV_MANAGER) { + input_plane = (source - 3); + + DSSDBG("input pipeline is not an overlay manager so overlay %d is configured ", input_plane); + /* Set the channel out for input source: DISPC_VIDX_ATTRIBUTES[31:30] CHANNELOUT2 as WB (0x3)*/ + REG_FLD_MOD(dispc_reg_att[input_plane], 0x3, 31, 30); + /* Set the channel out for input source: DISPC_VIDX_ATTRIBUTES[16] CHANNELOUT as LCD / WB (0x0)*/ + REG_FLD_MOD(dispc_reg_att[input_plane], 0x0, 16, 16); + + REG_FLD_MOD(dispc_reg_att[input_plane], 0x1, 10, 10); + REG_FLD_MOD(dispc_reg_att[input_plane], 0x1, 19, 19); + REG_FLD_MOD(dispc_reg_att[plane], source, 18, 16); +#ifdef OMAP4430_REV_ES2_0 + /* Memory to memory mode bit is set on ES 2.0 */ + REG_FLD_MOD(dispc_reg_att[plane], 1, 19, 19); +#endif + } + + pix_inc = 0x1; + if ((paddr >= 0x60000000) && (paddr <= 0x7fffffff)) { + calc_tiler_row_rotation(rotation, width, frame_height, + color_mode, &row_inc); + orientation = calc_tiler_orientation(rotation, (u8)mirror); + /* get rotated top-left coordinate + (if rotation is applied before mirroring) */ + memset(&orient, 0, sizeof(orient)); + tiler_rotate_view(&orient, rotation * 90); + + if (mirror) { + /* Horizontal mirroring */ + if (rotation == 1 || rotation == 3) + mir_x = 1; + else + mir_y = 1; + } else { + mir_x = 0; + mir_y = 0; + } + orient.x_invert ^= mir_x; + orient.y_invert ^= mir_y; + + if (orient.rotate_90 & 1) { + tiler_height = width; + tiler_width = height; + } else { + tiler_height = height; + tiler_width = width; + } + + paddr = tiler_reorient_topleft(tiler_get_natural_addr((void *)paddr), + orient, tiler_width, tiler_height); + + if (puv_addr) + puv_addr = tiler_reorient_topleft( + tiler_get_natural_addr((void *)puv_addr), + orient, tiler_width/2, tiler_height/2); + DSSDBG( + "rotated addresses: 0x%0x, 0x%0x\n", + paddr, puv_addr); + /* set BURSTTYPE if rotation is non-zero */ + REG_FLD_MOD(dispc_reg_att[plane], 0x1, 8, 8); + } else + row_inc = 1; + + + _dispc_set_color_mode(plane, color_mode); + + _dispc_set_plane_ba0(plane, paddr); + _dispc_set_plane_ba1(plane, paddr); + if (OMAP_DSS_COLOR_NV12 == color_mode) { + _dispc_set_plane_ba_uv0(plane, puv_addr); + _dispc_set_plane_ba_uv1(plane, puv_addr); + } + _dispc_set_row_inc(plane, row_inc); + _dispc_set_pix_inc(plane, pix_inc); + + DSSDBG("%dx%d -> %p,%p %dx%d\n", + width, height, + (void *)paddr, (void *)puv_addr, out_width, out_height); + + _dispc_set_pic_size(plane, width, height); + dispc_setup_plane_fifo(plane, fifo_low, fifo_high); + + /* non interlaced */ + ch_width = width; + ch_height = height; + out_ch_width = out_width; + out_ch_height = out_height; + + /* account for output color decimation */ + switch (color_mode) { + case OMAP_DSS_COLOR_NV12: + out_ch_height >>= 1; + case OMAP_DSS_COLOR_UYVY: + case OMAP_DSS_COLOR_YUV2: + out_ch_width >>= 1; + default: + ; + } + + /* account for input color decimation */ + switch (wb->input_color_mode) { + case OMAP_DSS_COLOR_NV12: + ch_height >>= 1; + case OMAP_DSS_COLOR_UYVY: + case OMAP_DSS_COLOR_YUV2: + ch_width >>= 1; + default: + ; + } + + /* we must scale NV12 format */ + scale_x = width != out_width || ch_width != out_ch_width; + scale_y = height != out_height || ch_height != out_ch_height; + _dispc_set_scaling(plane, width, height, + out_width, out_height, + 0, three_taps, false, scale_x, scale_y); + + if (out_ch_width != out_width) { + /* this is true for YUV formats */ + _dispc_set_scaling_uv(plane, ch_width, ch_height, + out_ch_width, out_ch_height, 0, + three_taps, false, scale_x, scale_y); + } else { + /* set chroma resampling */ + REG_FLD_MOD(DISPC_VID_ATTRIBUTES2(plane - 1), 0, 8, 8); + } + + _dispc_set_vid_size(plane, out_width, out_height); + _dispc_set_vid_color_conv(plane, cconv); + + pix_inc = dispc_read_reg(dispc_reg_att[plane]); + DSSDBG("vid[%d] attributes = %x\n", plane, pix_inc); + + return 0; + +} + +void dispc_go_wb(void) +{ + enable_clocks(1); + + if (REG_GET(DISPC_CONTROL2, 6, 6) == 1) { + DSSERR("GO bit not down for WB\n"); + goto end; + } + REG_FLD_MOD(DISPC_CONTROL2, 1, 6, 6); + DSSDBG("dispc_go_wb\n"); +end: + enable_clocks(0); +} diff --git a/drivers/video/omap2/dss/display.c b/drivers/video/omap2/dss/display.c index 6a74ea116d29..05801825394e 100644 --- a/drivers/video/omap2/dss/display.c +++ b/drivers/video/omap2/dss/display.c @@ -29,6 +29,7 @@ #include <linux/platform_device.h> #include <plat/display.h> +#include <plat/cpu.h> #include "dss.h" static LIST_HEAD(display_list); @@ -278,6 +279,55 @@ static ssize_t display_wss_store(struct device *dev, return size; } +static ssize_t display_edid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + + + if (!dssdev->driver->get_edid) + return -ENOENT; + dssdev->driver->get_edid(dssdev); + return snprintf(buf, PAGE_SIZE, "EDID-Information"); + +} +static ssize_t display_custom_edid_timing_store(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + int val, code, mode; + val = simple_strtoul(buf, NULL, 0); + code = val / 10; + mode = val % 10; + if (!dssdev->driver->set_custom_edid_timing_code) + return -ENOENT; + dssdev->driver->set_custom_edid_timing_code(dssdev, code, mode); + return snprintf(buf, PAGE_SIZE, "EDID-Information %d mode % d code", mode, code); + +} + +static ssize_t display_hpd_enabled_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + bool enabled, r; + + enabled = simple_strtoul(buf, NULL, 10); + + if (enabled != (dssdev->state != OMAP_DSS_DISPLAY_DISABLED)) { + if (enabled) { + r = dssdev->driver->hpd_enable(dssdev); + if (r) + return r; + } else { + dssdev->driver->disable(dssdev); + } + } + + return size; +} + static DEVICE_ATTR(enabled, S_IRUGO|S_IWUSR, display_enabled_show, display_enabled_store); static DEVICE_ATTR(update_mode, S_IRUGO|S_IWUSR, @@ -292,6 +342,10 @@ static DEVICE_ATTR(mirror, S_IRUGO|S_IWUSR, display_mirror_show, display_mirror_store); static DEVICE_ATTR(wss, S_IRUGO|S_IWUSR, display_wss_show, display_wss_store); +static DEVICE_ATTR(custom_edid_timing, S_IRUGO|S_IWUSR, + display_edid_show, display_custom_edid_timing_store); +static DEVICE_ATTR(hpd_enabled, S_IRUGO|S_IWUSR, + NULL, display_hpd_enabled_store); static struct device_attribute *display_sysfs_attrs[] = { &dev_attr_enabled, @@ -301,6 +355,8 @@ static struct device_attribute *display_sysfs_attrs[] = { &dev_attr_rotate, &dev_attr_mirror, &dev_attr_wss, + &dev_attr_custom_edid_timing, + &dev_attr_hpd_enabled, NULL }; @@ -319,7 +375,11 @@ void default_get_overlay_fifo_thresholds(enum omap_plane plane, unsigned burst_size_bytes; *burst_size = OMAP_DSS_BURST_16x32; - burst_size_bytes = 16 * 32 / 8; + if (cpu_is_omap44xx()) + burst_size_bytes = 8 * 128 / 8; /* OMAP4: highest + burst size is 8x128*/ + else + burst_size_bytes = 16 * 32 / 8; *fifo_high = fifo_size - 1; *fifo_low = fifo_size - burst_size_bytes; @@ -342,7 +402,7 @@ int omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev) return 16; case OMAP_DISPLAY_TYPE_VENC: case OMAP_DISPLAY_TYPE_SDI: - return 24; + case OMAP_DISPLAY_TYPE_HDMI: return 24; default: BUG(); @@ -365,6 +425,9 @@ bool dss_use_replication(struct omap_dss_device *dssdev, (dssdev->panel.config & OMAP_DSS_LCD_TFT) == 0) return false; + if (dssdev->type == OMAP_DISPLAY_TYPE_HDMI) + return false; + switch (dssdev->type) { case OMAP_DISPLAY_TYPE_DPI: bpp = dssdev->phy.dpi.data_lines; @@ -392,7 +455,9 @@ void dss_init_device(struct platform_device *pdev, int r; switch (dssdev->type) { +#ifdef CONFIG_OMAP2_DSS_DPI case OMAP_DISPLAY_TYPE_DPI: +#endif #ifdef CONFIG_OMAP2_DSS_RFBI case OMAP_DISPLAY_TYPE_DBI: #endif @@ -405,6 +470,9 @@ void dss_init_device(struct platform_device *pdev, #ifdef CONFIG_OMAP2_DSS_VENC case OMAP_DISPLAY_TYPE_VENC: #endif +#ifdef CONFIG_OMAP2_DSS_HDMI + case OMAP_DISPLAY_TYPE_HDMI: +#endif break; default: DSSERR("Support for display '%s' not compiled in.\n", @@ -413,9 +481,11 @@ void dss_init_device(struct platform_device *pdev, } switch (dssdev->type) { +#ifdef CONFIG_OMAP2_DSS_DPI case OMAP_DISPLAY_TYPE_DPI: r = dpi_init_display(dssdev); break; +#endif #ifdef CONFIG_OMAP2_DSS_RFBI case OMAP_DISPLAY_TYPE_DBI: r = rfbi_init_display(dssdev); @@ -436,6 +506,11 @@ void dss_init_device(struct platform_device *pdev, r = dsi_init_display(dssdev); break; #endif +#ifdef CONFIG_OMAP2_DSS_HDMI + case OMAP_DISPLAY_TYPE_HDMI: + r = hdmi_init_display(dssdev); + break; +#endif default: BUG(); } @@ -541,7 +616,10 @@ int dss_resume_all_devices(void) static int dss_disable_device(struct device *dev, void *data) { struct omap_dss_device *dssdev = to_dss_device(dev); - dssdev->driver->disable(dssdev); + + if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) + dssdev->driver->disable(dssdev); + return 0; } diff --git a/drivers/video/omap2/dss/dpi.c b/drivers/video/omap2/dss/dpi.c index 960e977a8bf0..971ee171a82d 100644 --- a/drivers/video/omap2/dss/dpi.c +++ b/drivers/video/omap2/dss/dpi.c @@ -40,25 +40,35 @@ static struct { } dpi; #ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL -static int dpi_set_dsi_clk(bool is_tft, unsigned long pck_req, +static int dpi_set_dsi_clk(int lcd_channel_ix, bool is_tft, unsigned long pck_req, unsigned long *fck, int *lck_div, int *pck_div) { struct dsi_clock_info dsi_cinfo; struct dispc_clock_info dispc_cinfo; int r; - - r = dsi_pll_calc_clock_div_pck(is_tft, pck_req, &dsi_cinfo, + if (!cpu_is_omap44xx()) { + r = dsi_pll_calc_clock_div_pck(lcd_channel_ix, is_tft, pck_req, &dsi_cinfo, &dispc_cinfo); if (r) return r; - - r = dsi_pll_set_clock_div(&dsi_cinfo); + } else { + dispc_cinfo.lck_div = 1; + dispc_cinfo.pck_div = 4; + dsi_cinfo.regn = 19; + dsi_cinfo.regm = 150; + dsi_cinfo.regm3 = 4; + dsi_cinfo.regm4 = 4; + dsi_cinfo.use_dss2_fck = true; + dsi_cinfo.highfreq = 0; + dsi_calc_clock_rates(&dsi_cinfo); + } + r = dsi_pll_set_clock_div(lcd_channel_ix, &dsi_cinfo); if (r) return r; dss_select_dispc_clk_source(DSS_SRC_DSI1_PLL_FCLK); - r = dispc_set_clock_div(&dispc_cinfo); + r = dispc_set_clock_div(lcd_channel_ix, &dispc_cinfo); if (r) return r; @@ -69,13 +79,15 @@ static int dpi_set_dsi_clk(bool is_tft, unsigned long pck_req, return 0; } #else -static int dpi_set_dispc_clk(bool is_tft, unsigned long pck_req, +static int dpi_set_dispc_clk(int lcd_channel_ix, bool is_tft, unsigned long pck_req, unsigned long *fck, int *lck_div, int *pck_div) { struct dss_clock_info dss_cinfo; struct dispc_clock_info dispc_cinfo; int r; + if (cpu_is_omap44xx()) /*TODO Check this */ + return 0; r = dss_calc_clock_div(is_tft, pck_req, &dss_cinfo, &dispc_cinfo); if (r) return r; @@ -84,7 +96,7 @@ static int dpi_set_dispc_clk(bool is_tft, unsigned long pck_req, if (r) return r; - r = dispc_set_clock_div(&dispc_cinfo); + r = dispc_set_clock_div(lcd_channel_ix, &dispc_cinfo); if (r) return r; @@ -103,20 +115,28 @@ static int dpi_set_mode(struct omap_dss_device *dssdev) unsigned long fck; unsigned long pck; bool is_tft; - int r = 0; + int r = 0, lcd_channel_ix = 0; + + if (dssdev->channel == OMAP_DSS_CHANNEL_LCD2) + lcd_channel_ix = 1; dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); - dispc_set_pol_freq(dssdev->panel.config, dssdev->panel.acbi, - dssdev->panel.acb); + if (dssdev->channel == OMAP_DSS_CHANNEL_LCD2) + dispc_set_pol_freq(OMAP_DSS_CHANNEL_LCD2, dssdev->panel.config, + dssdev->panel.acbi, dssdev->panel.acb); + else + dispc_set_pol_freq(OMAP_DSS_CHANNEL_LCD, dssdev->panel.config, + dssdev->panel.acbi, dssdev->panel.acb); + is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0; #ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL - r = dpi_set_dsi_clk(is_tft, t->pixel_clock * 1000, + r = dpi_set_dsi_clk(lcd_channel_ix, is_tft, t->pixel_clock * 1000, &fck, &lck_div, &pck_div); #else - r = dpi_set_dispc_clk(is_tft, t->pixel_clock * 1000, + r = dpi_set_dispc_clk(lcd_channel_ix, is_tft, t->pixel_clock * 1000, &fck, &lck_div, &pck_div); #endif if (r) @@ -132,8 +152,15 @@ static int dpi_set_mode(struct omap_dss_device *dssdev) t->pixel_clock = pck; } - dispc_set_lcd_timings(t); + if (dssdev->channel == OMAP_DSS_CHANNEL_LCD2) + dispc_set_lcd_timings(OMAP_DSS_CHANNEL_LCD2, t); + else + dispc_set_lcd_timings(OMAP_DSS_CHANNEL_LCD, t); +#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL +err1: + dsi_pll_uninit(lcd_channel_ix); +#endif err0: dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); return r; @@ -145,17 +172,51 @@ static int dpi_basic_init(struct omap_dss_device *dssdev) is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0; - dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_BYPASS); - dispc_set_lcd_display_type(is_tft ? OMAP_DSS_LCD_DISPLAY_TFT : - OMAP_DSS_LCD_DISPLAY_STN); - dispc_set_tft_data_lines(dssdev->phy.dpi.data_lines); - + if (dssdev->channel == OMAP_DSS_CHANNEL_LCD2) { + dispc_set_parallel_interface_mode(OMAP_DSS_CHANNEL_LCD2, + OMAP_DSS_PARALLELMODE_BYPASS); + dispc_set_lcd_display_type(OMAP_DSS_CHANNEL_LCD2, + is_tft ? OMAP_DSS_LCD_DISPLAY_TFT : OMAP_DSS_LCD_DISPLAY_STN); + dispc_set_tft_data_lines(OMAP_DSS_CHANNEL_LCD2, + dssdev->phy.dpi.data_lines); + + } else { + dispc_set_parallel_interface_mode(OMAP_DSS_CHANNEL_LCD, + OMAP_DSS_PARALLELMODE_BYPASS); + dispc_set_lcd_display_type(OMAP_DSS_CHANNEL_LCD, + is_tft ? OMAP_DSS_LCD_DISPLAY_TFT : OMAP_DSS_LCD_DISPLAY_STN); + dispc_set_tft_data_lines(OMAP_DSS_CHANNEL_LCD, + dssdev->phy.dpi.data_lines); + } return 0; } +/*This one needs to be to set the ovl info to dirty*/ +static void dpi_start_auto_update(struct omap_dss_device *dssdev) + +{ + int i; + DSSDBG("starting auto update\n"); + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { + struct omap_overlay *ovl; + ovl = omap_dss_get_overlay(i); + if (ovl->manager == dssdev->manager) + ovl->info_dirty = true; + printk(KERN_ERR "ovl[%d]->manager = %s", i, ovl->manager->name); + } + dssdev->manager->apply(dssdev->manager); +} + int omapdss_dpi_display_enable(struct omap_dss_device *dssdev) { int r; + int lcd_channel_ix = 1; + + if (dssdev->channel == OMAP_DSS_CHANNEL_LCD2) { + printk("Lcd channel index 1"); + lcd_channel_ix = 1; + } else + lcd_channel_ix = 0; r = omap_dss_start_device(dssdev); if (r) { @@ -187,13 +248,16 @@ int omapdss_dpi_display_enable(struct omap_dss_device *dssdev) mdelay(2); + if (cpu_is_omap44xx()) + dpi_start_auto_update(dssdev); + dssdev->manager->enable(dssdev->manager); return 0; err4: #ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL - dsi_pll_uninit(); + dsi_pll_uninit(lcd_channel_ix); err3: dss_clk_disable(DSS_CLK_FCK2); #endif @@ -210,11 +274,16 @@ EXPORT_SYMBOL(omapdss_dpi_display_enable); void omapdss_dpi_display_disable(struct omap_dss_device *dssdev) { + int lcd_channel_ix = 0; + + if (dssdev->channel == OMAP_DSS_CHANNEL_LCD2) + lcd_channel_ix = 1; + dssdev->manager->disable(dssdev->manager); #ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL dss_select_dispc_clk_source(DSS_SRC_DSS1_ALWON_FCLK); - dsi_pll_uninit(); + dsi_pll_uninit(lcd_channel_ix); dss_clk_disable(DSS_CLK_FCK2); #endif @@ -234,7 +303,10 @@ void dpi_set_timings(struct omap_dss_device *dssdev, dssdev->panel.timings = *timings; if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { dpi_set_mode(dssdev); - dispc_go(OMAP_DSS_CHANNEL_LCD); + if (dssdev->channel == OMAP_DSS_CHANNEL_LCD2) + dispc_go(OMAP_DSS_CHANNEL_LCD2); + else + dispc_go(OMAP_DSS_CHANNEL_LCD); } } EXPORT_SYMBOL(dpi_set_timings); @@ -243,11 +315,14 @@ int dpi_check_timings(struct omap_dss_device *dssdev, struct omap_video_timings *timings) { bool is_tft; - int r; + int r = 0, lcd_channel_ix = 0; int lck_div, pck_div; unsigned long fck; unsigned long pck; + if (dssdev->channel == OMAP_DSS_CHANNEL_LCD2) + lcd_channel_ix = 1; + if (!dispc_lcd_timings_ok(timings)) return -EINVAL; @@ -260,7 +335,7 @@ int dpi_check_timings(struct omap_dss_device *dssdev, { struct dsi_clock_info dsi_cinfo; struct dispc_clock_info dispc_cinfo; - r = dsi_pll_calc_clock_div_pck(is_tft, + r = dsi_pll_calc_clock_div_pck(lcd_channel_ix, is_tft, timings->pixel_clock * 1000, &dsi_cinfo, &dispc_cinfo); diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c index 3af207b2bde3..2602a6617e2f 100644 --- a/drivers/video/omap2/dss/dsi.c +++ b/drivers/video/omap2/dss/dsi.c @@ -42,7 +42,13 @@ /*#define VERBOSE_IRQ*/ #define DSI_CATCH_MISSING_TE +#ifndef CONFIG_ARCH_OMAP4 #define DSI_BASE 0x4804FC00 +#else +#define DSI_BASE 0x58004000 +#define DSI2_BASE 0x58005000 + +#endif struct dsi_reg { u16 idx; }; @@ -92,6 +98,13 @@ struct dsi_reg { u16 idx; }; #define DSI_DSIPHY_CFG2 DSI_REG(0x200 + 0x0008) #define DSI_DSIPHY_CFG5 DSI_REG(0x200 + 0x0014) +/* DSI Rev 3.0 Registers */ + +#define DSI_DSIPHY_CFG8 DSI_REG(0x200 + 0x0020) +#define DSI_DSIPHY_CFG9 DSI_REG(0x200 + 0x0024) +#define DSI_DSIPHY_CFG12 DSI_REG(0x200 + 0x0030) +#define DSI_DSIPHY_CFG14 DSI_REG(0x200 + 0x0038) + /* DSI_PLL_CTRL_SCP */ #define DSI_PLL_CONTROL DSI_REG(0x300 + 0x0000) @@ -100,11 +113,11 @@ struct dsi_reg { u16 idx; }; #define DSI_PLL_CONFIGURATION1 DSI_REG(0x300 + 0x000C) #define DSI_PLL_CONFIGURATION2 DSI_REG(0x300 + 0x0010) -#define REG_GET(idx, start, end) \ - FLD_GET(dsi_read_reg(idx), start, end) +#define REG_GET(no, idx, start, end) \ + FLD_GET(dsi_read_reg(no, idx), start, end) -#define REG_FLD_MOD(idx, val, start, end) \ - dsi_write_reg(idx, FLD_MOD(dsi_read_reg(idx), val, start, end)) +#define REG_FLD_MOD(no, idx, val, start, end) \ + dsi_write_reg(no, idx, FLD_MOD(dsi_read_reg(no, idx), val, start, end)) /* Global interrupts */ #define DSI_IRQ_VC0 (1 << 0) @@ -212,7 +225,7 @@ struct dsi_irq_stats { unsigned cio_irqs[32]; }; -static struct +static struct dsi_struct { void __iomem *base; @@ -224,6 +237,7 @@ static struct enum dsi_vc_mode mode; struct omap_dss_device *dssdev; enum fifo_size fifo_size; + int dest_per; } vc[4]; struct mutex lock; @@ -237,7 +251,7 @@ static struct struct dsi_update_region update_region; bool te_enabled; - + bool use_ext_te; struct work_struct framedone_work; void (*framedone_callback)(int, void *); void *framedone_data; @@ -265,21 +279,29 @@ static struct spinlock_t irq_stats_lock; struct dsi_irq_stats irq_stats; #endif -} dsi; +} dsi1, dsi2; #ifdef DEBUG static unsigned int dsi_perf; module_param_named(dsi_perf, dsi_perf, bool, 0644); #endif -static inline void dsi_write_reg(const struct dsi_reg idx, u32 val) +static inline void dsi_write_reg(enum omap_dsi_index ix, + const struct dsi_reg idx, u32 val) { - __raw_writel(val, dsi.base + idx.idx); + if (ix == DSI1) + __raw_writel(val, dsi1.base + idx.idx); + else + __raw_writel(val, dsi2.base + idx.idx); } -static inline u32 dsi_read_reg(const struct dsi_reg idx) +static inline u32 dsi_read_reg(enum omap_dsi_index ix, + const struct dsi_reg idx) { - return __raw_readl(dsi.base + idx.idx); + if (ix == DSI1) + return __raw_readl(dsi1.base + idx.idx); + else + return __raw_readl(dsi2.base + idx.idx); } @@ -291,29 +313,39 @@ void dsi_restore_context(void) { } -void dsi_bus_lock(void) +void dsi_bus_lock(enum omap_dsi_index ix) { - down(&dsi.bus_lock); + if (ix == DSI1) + down(&dsi1.bus_lock); + else + down(&dsi2.bus_lock); } EXPORT_SYMBOL(dsi_bus_lock); -void dsi_bus_unlock(void) +void dsi_bus_unlock(enum omap_dsi_index ix) { - up(&dsi.bus_lock); + if (ix == DSI1) + up(&dsi1.bus_lock); + else + up(&dsi2.bus_lock); } EXPORT_SYMBOL(dsi_bus_unlock); -static bool dsi_bus_is_locked(void) +static bool dsi_bus_is_locked(enum omap_dsi_index ix) { - return dsi.bus_lock.count == 0; + if (ix == DSI1) + return dsi1.bus_lock.count == 0; + else + return dsi2.bus_lock.count == 0; } -static inline int wait_for_bit_change(const struct dsi_reg idx, int bitnum, +static inline int wait_for_bit_change(enum omap_dsi_index ix, + const struct dsi_reg idx, int bitnum, int value) { int t = 100000; - while (REG_GET(idx, bitnum, bitnum) != value) { + while (REG_GET(ix, idx, bitnum, bitnum) != value) { if (--t == 0) return !value; } @@ -322,42 +354,49 @@ static inline int wait_for_bit_change(const struct dsi_reg idx, int bitnum, } #ifdef DEBUG -static void dsi_perf_mark_setup(void) +static void dsi_perf_mark_setup(enum omap_dsi_index ix) { - dsi.perf_setup_time = ktime_get(); + struct dsi_struct *p_dsi; + p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; + p_dsi->perf_setup_time = ktime_get(); } -static void dsi_perf_mark_start(void) +static void dsi_perf_mark_start(enum omap_dsi_index ix) { - dsi.perf_start_time = ktime_get(); + struct dsi_struct *p_dsi; + p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; + p_dsi->perf_start_time = ktime_get(); } -static void dsi_perf_show(const char *name) +static void dsi_perf_show(enum omap_dsi_index ix, + const char *name) { ktime_t t, setup_time, trans_time; u32 total_bytes; u32 setup_us, trans_us, total_us; + struct dsi_struct *p_dsi; + p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; if (!dsi_perf) return; t = ktime_get(); - setup_time = ktime_sub(dsi.perf_start_time, dsi.perf_setup_time); + setup_time = ktime_sub(p_dsi->perf_start_time, p_dsi->perf_setup_time); setup_us = (u32)ktime_to_us(setup_time); if (setup_us == 0) setup_us = 1; - trans_time = ktime_sub(t, dsi.perf_start_time); + trans_time = ktime_sub(t, p_dsi->perf_start_time); trans_us = (u32)ktime_to_us(trans_time); if (trans_us == 0) trans_us = 1; total_us = setup_us + trans_us; - total_bytes = dsi.update_region.w * - dsi.update_region.h * - dsi.update_region.device->ctrl.pixel_size / 8; + total_bytes = p_dsi->update_region.w * + p_dsi->update_region.h * + p_dsi->update_region.device->ctrl.pixel_size / 8; printk(KERN_INFO "DSI(%s): %u us + %u us = %u us (%uHz), " "%u bytes, %u kbytes/sec\n", @@ -370,9 +409,9 @@ static void dsi_perf_show(const char *name) total_bytes * 1000 / total_us); } #else -#define dsi_perf_mark_setup() -#define dsi_perf_mark_start() -#define dsi_perf_show(x) +#define dsi_perf_mark_setup(x) +#define dsi_perf_mark_start(x) +#define dsi_perf_show(x, y) #endif static void print_irq_status(u32 status) @@ -470,47 +509,56 @@ static void print_irq_status_cio(u32 status) static int debug_irq; -/* called from dss */ -void dsi_irq_handler(void) +/* called from dss in OMAP3, in OMAP4 there is a dedicated +* interrupt line for DSI */ +irqreturn_t dsi_irq_handler(int irq, void *arg) + { u32 irqstatus, vcstatus, ciostatus; int i; + enum omap_dsi_index ix = DSI1; + struct dsi_struct *p_dsi; + + if (cpu_is_omap44xx() && irq == OMAP44XX_IRQ_DSS_DSI2) + ix = DSI2; - irqstatus = dsi_read_reg(DSI_IRQSTATUS); + p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; + + irqstatus = dsi_read_reg(ix, DSI_IRQSTATUS); #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS - spin_lock(&dsi.irq_stats_lock); - dsi.irq_stats.irq_count++; - dss_collect_irq_stats(irqstatus, dsi.irq_stats.dsi_irqs); + spin_lock(&p_dsi->irq_stats_lock); + p_dsi->irq_stats.irq_count++; + dss_collect_irq_stats(irqstatus, p_dsi->irq_stats.dsi_irqs); #endif if (irqstatus & DSI_IRQ_ERROR_MASK) { DSSERR("DSI error, irqstatus %x\n", irqstatus); print_irq_status(irqstatus); - spin_lock(&dsi.errors_lock); - dsi.errors |= irqstatus & DSI_IRQ_ERROR_MASK; - spin_unlock(&dsi.errors_lock); + spin_lock(&p_dsi->errors_lock); + p_dsi->errors |= irqstatus & DSI_IRQ_ERROR_MASK; + spin_unlock(&p_dsi->errors_lock); } else if (debug_irq) { print_irq_status(irqstatus); } #ifdef DSI_CATCH_MISSING_TE if (irqstatus & DSI_IRQ_TE_TRIGGER) - del_timer(&dsi.te_timer); + del_timer(&p_dsi->te_timer); #endif for (i = 0; i < 4; ++i) { if ((irqstatus & (1<<i)) == 0) continue; - vcstatus = dsi_read_reg(DSI_VC_IRQSTATUS(i)); + vcstatus = dsi_read_reg(ix, DSI_VC_IRQSTATUS(i)); #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS - dss_collect_irq_stats(vcstatus, dsi.irq_stats.vc_irqs[i]); + dss_collect_irq_stats(vcstatus, p_dsi->irq_stats.vc_irqs[i]); #endif if (vcstatus & DSI_VC_IRQ_BTA) - complete(&dsi.bta_completion); + complete(&p_dsi->bta_completion); if (vcstatus & DSI_VC_IRQ_ERROR_MASK) { DSSERR("DSI VC(%d) error, vc irqstatus %x\n", @@ -520,106 +568,110 @@ void dsi_irq_handler(void) print_irq_status_vc(i, vcstatus); } - dsi_write_reg(DSI_VC_IRQSTATUS(i), vcstatus); + dsi_write_reg(ix, DSI_VC_IRQSTATUS(i), vcstatus); /* flush posted write */ - dsi_read_reg(DSI_VC_IRQSTATUS(i)); + dsi_read_reg(ix, DSI_VC_IRQSTATUS(i)); } if (irqstatus & DSI_IRQ_COMPLEXIO_ERR) { - ciostatus = dsi_read_reg(DSI_COMPLEXIO_IRQ_STATUS); + ciostatus = dsi_read_reg(ix, DSI_COMPLEXIO_IRQ_STATUS); #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS - dss_collect_irq_stats(ciostatus, dsi.irq_stats.cio_irqs); + dss_collect_irq_stats(ciostatus, p_dsi->irq_stats.cio_irqs); #endif - dsi_write_reg(DSI_COMPLEXIO_IRQ_STATUS, ciostatus); + dsi_write_reg(ix, DSI_COMPLEXIO_IRQ_STATUS, ciostatus); /* flush posted write */ - dsi_read_reg(DSI_COMPLEXIO_IRQ_STATUS); + dsi_read_reg(ix, DSI_COMPLEXIO_IRQ_STATUS); DSSERR("DSI CIO error, cio irqstatus %x\n", ciostatus); print_irq_status_cio(ciostatus); } - dsi_write_reg(DSI_IRQSTATUS, irqstatus & ~DSI_IRQ_CHANNEL_MASK); + dsi_write_reg(ix, DSI_IRQSTATUS, irqstatus & ~DSI_IRQ_CHANNEL_MASK); /* flush posted write */ - dsi_read_reg(DSI_IRQSTATUS); + dsi_read_reg(ix, DSI_IRQSTATUS); #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS - spin_unlock(&dsi.irq_stats_lock); + spin_unlock(&p_dsi->irq_stats_lock); #endif + return IRQ_HANDLED; } - -static void _dsi_initialize_irq(void) +static void _dsi_initialize_irq(enum omap_dsi_index ix) { u32 l; int i; /* disable all interrupts */ - dsi_write_reg(DSI_IRQENABLE, 0); + dsi_write_reg(ix, DSI_IRQENABLE, 0); for (i = 0; i < 4; ++i) - dsi_write_reg(DSI_VC_IRQENABLE(i), 0); - dsi_write_reg(DSI_COMPLEXIO_IRQ_ENABLE, 0); + dsi_write_reg(ix, DSI_VC_IRQENABLE(i), 0); + dsi_write_reg(ix, DSI_COMPLEXIO_IRQ_ENABLE, 0); /* clear interrupt status */ - l = dsi_read_reg(DSI_IRQSTATUS); - dsi_write_reg(DSI_IRQSTATUS, l & ~DSI_IRQ_CHANNEL_MASK); + l = dsi_read_reg(ix, DSI_IRQSTATUS); + dsi_write_reg(ix, DSI_IRQSTATUS, l & ~DSI_IRQ_CHANNEL_MASK); for (i = 0; i < 4; ++i) { - l = dsi_read_reg(DSI_VC_IRQSTATUS(i)); - dsi_write_reg(DSI_VC_IRQSTATUS(i), l); + l = dsi_read_reg(ix, DSI_VC_IRQSTATUS(i)); + dsi_write_reg(ix, DSI_VC_IRQSTATUS(i), l); } - l = dsi_read_reg(DSI_COMPLEXIO_IRQ_STATUS); - dsi_write_reg(DSI_COMPLEXIO_IRQ_STATUS, l); + l = dsi_read_reg(ix, DSI_COMPLEXIO_IRQ_STATUS); + dsi_write_reg(ix, DSI_COMPLEXIO_IRQ_STATUS, l); /* enable error irqs */ l = DSI_IRQ_ERROR_MASK; #ifdef DSI_CATCH_MISSING_TE l |= DSI_IRQ_TE_TRIGGER; #endif - dsi_write_reg(DSI_IRQENABLE, l); + dsi_write_reg(ix, DSI_IRQENABLE, l); l = DSI_VC_IRQ_ERROR_MASK; for (i = 0; i < 4; ++i) - dsi_write_reg(DSI_VC_IRQENABLE(i), l); + dsi_write_reg(ix, DSI_VC_IRQENABLE(i), l); /* XXX zonda responds incorrectly, causing control error: Exit from LP-ESC mode to LP11 uses wrong transition states on the data lines LP0 and LN0. */ - dsi_write_reg(DSI_COMPLEXIO_IRQ_ENABLE, + dsi_write_reg(ix, DSI_COMPLEXIO_IRQ_ENABLE, -1 & (~DSI_CIO_IRQ_ERRCONTROL2)); } -static u32 dsi_get_errors(void) +static u32 dsi_get_errors(enum omap_dsi_index ix) { unsigned long flags; u32 e; - spin_lock_irqsave(&dsi.errors_lock, flags); - e = dsi.errors; - dsi.errors = 0; - spin_unlock_irqrestore(&dsi.errors_lock, flags); + struct dsi_struct *p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; + + spin_lock_irqsave(&p_dsi->errors_lock, flags); + e = p_dsi->errors; + p_dsi->errors = 0; + spin_unlock_irqrestore(&p_dsi->errors_lock, flags); return e; } -static void dsi_vc_enable_bta_irq(int channel) +static void dsi_vc_enable_bta_irq(enum omap_dsi_index ix, + int channel) { u32 l; - dsi_write_reg(DSI_VC_IRQSTATUS(channel), DSI_VC_IRQ_BTA); + dsi_write_reg(ix, DSI_VC_IRQSTATUS(channel), DSI_VC_IRQ_BTA); - l = dsi_read_reg(DSI_VC_IRQENABLE(channel)); + l = dsi_read_reg(ix, DSI_VC_IRQENABLE(channel)); l |= DSI_VC_IRQ_BTA; - dsi_write_reg(DSI_VC_IRQENABLE(channel), l); + dsi_write_reg(ix, DSI_VC_IRQENABLE(channel), l); } -static void dsi_vc_disable_bta_irq(int channel) +static void dsi_vc_disable_bta_irq(enum omap_dsi_index ix, + int channel) { u32 l; - l = dsi_read_reg(DSI_VC_IRQENABLE(channel)); + l = dsi_read_reg(ix, DSI_VC_IRQENABLE(channel)); l &= ~DSI_VC_IRQ_BTA; - dsi_write_reg(DSI_VC_IRQENABLE(channel), l); + dsi_write_reg(ix, DSI_VC_IRQENABLE(channel), l); } /* DSI func clock. this could also be DSI2_PLL_FCLK */ @@ -632,21 +684,24 @@ static inline void enable_clocks(bool enable) } /* source clock for DSI PLL. this could also be PCLKFREE */ -static inline void dsi_enable_pll_clock(bool enable) +static inline void dsi_enable_pll_clock(enum omap_dsi_index ix, + bool enable) { + struct dsi_struct *p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; + if (enable) dss_clk_enable(DSS_CLK_FCK2); else dss_clk_disable(DSS_CLK_FCK2); - if (enable && dsi.pll_locked) { - if (wait_for_bit_change(DSI_PLL_STATUS, 1, 1) != 1) + if (enable && p_dsi->pll_locked) { + if (wait_for_bit_change(ix, DSI_PLL_STATUS, 1, 1) != 1) DSSERR("cannot lock PLL when enabling clocks\n"); } } #ifdef DEBUG -static void _dsi_print_reset_status(void) +static void _dsi_print_reset_status(enum omap_dsi_index ix) { u32 l; @@ -656,17 +711,17 @@ static void _dsi_print_reset_status(void) /* A dummy read using the SCP interface to any DSIPHY register is * required after DSIPHY reset to complete the reset of the DSI complex * I/O. */ - l = dsi_read_reg(DSI_DSIPHY_CFG5); + l = dsi_read_reg(ix, DSI_DSIPHY_CFG5); printk(KERN_DEBUG "DSI resets: "); - l = dsi_read_reg(DSI_PLL_STATUS); + l = dsi_read_reg(ix, DSI_PLL_STATUS); printk("PLL (%d) ", FLD_GET(l, 0, 0)); - l = dsi_read_reg(DSI_COMPLEXIO_CFG1); + l = dsi_read_reg(ix, DSI_COMPLEXIO_CFG1); printk("CIO (%d) ", FLD_GET(l, 29, 29)); - l = dsi_read_reg(DSI_DSIPHY_CFG5); + l = dsi_read_reg(ix, DSI_DSIPHY_CFG5); printk("PHY (%x, %d, %d, %d)\n", FLD_GET(l, 28, 26), FLD_GET(l, 29, 29), @@ -674,17 +729,18 @@ static void _dsi_print_reset_status(void) FLD_GET(l, 31, 31)); } #else -#define _dsi_print_reset_status() +#define _dsi_print_reset_status(ix) #endif -static inline int dsi_if_enable(bool enable) +static inline int dsi_if_enable(enum omap_dsi_index ix, + bool enable) { DSSDBG("dsi_if_enable(%d)\n", enable); enable = enable ? 1 : 0; - REG_FLD_MOD(DSI_CTRL, enable, 0, 0); /* IF_EN */ + REG_FLD_MOD(ix, DSI_CTRL, enable, 0, 0); /* IF_EN */ - if (wait_for_bit_change(DSI_CTRL, 0, enable) != enable) { + if (wait_for_bit_change(ix, DSI_CTRL, 0, enable) != enable) { DSSERR("Failed to set dsi_if_enable to %d\n", enable); return -EIO; } @@ -692,22 +748,25 @@ static inline int dsi_if_enable(bool enable) return 0; } -unsigned long dsi_get_dsi1_pll_rate(void) +unsigned long dsi_get_dsi1_pll_rate(enum omap_dsi_index ix) { - return dsi.current_cinfo.dsi1_pll_fclk; + struct dsi_struct *p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; + return p_dsi->current_cinfo.dsi1_pll_fclk; } -static unsigned long dsi_get_dsi2_pll_rate(void) +static unsigned long dsi_get_dsi2_pll_rate(enum omap_dsi_index ix) { - return dsi.current_cinfo.dsi2_pll_fclk; + struct dsi_struct *p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; + return p_dsi->current_cinfo.dsi2_pll_fclk; } -static unsigned long dsi_get_txbyteclkhs(void) +static unsigned long dsi_get_txbyteclkhs(enum omap_dsi_index ix) { - return dsi.current_cinfo.clkin4ddr / 16; + struct dsi_struct *p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; + return p_dsi->current_cinfo.clkin4ddr / 16; } -static unsigned long dsi_fclk_rate(void) +static unsigned long dsi_fclk_rate(enum omap_dsi_index ix) { unsigned long r; @@ -716,7 +775,7 @@ static unsigned long dsi_fclk_rate(void) r = dss_clk_get_rate(DSS_CLK_FCK1); } else { /* DSI FCLK source is DSI2_PLL_FCLK */ - r = dsi_get_dsi2_pll_rate(); + r = dsi_get_dsi2_pll_rate(ix); } return r; @@ -727,23 +786,28 @@ static int dsi_set_lp_clk_divisor(struct omap_dss_device *dssdev) unsigned long dsi_fclk; unsigned lp_clk_div; unsigned long lp_clk; + struct dsi_struct *p_dsi; + enum omap_dsi_index ix; + + ix = (dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? DSI1 : DSI2; + p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; lp_clk_div = dssdev->phy.dsi.div.lp_clk_div; if (lp_clk_div == 0 || lp_clk_div > LP_DIV_MAX) return -EINVAL; - dsi_fclk = dsi_fclk_rate(); + dsi_fclk = dsi_fclk_rate(ix); lp_clk = dsi_fclk / 2 / lp_clk_div; DSSDBG("LP_CLK_DIV %u, LP_CLK %lu\n", lp_clk_div, lp_clk); - dsi.current_cinfo.lp_clk = lp_clk; - dsi.current_cinfo.lp_clk_div = lp_clk_div; + p_dsi->current_cinfo.lp_clk = lp_clk; + p_dsi->current_cinfo.lp_clk_div = lp_clk_div; - REG_FLD_MOD(DSI_CLK_CTRL, lp_clk_div, 12, 0); /* LP_CLK_DIVISOR */ + REG_FLD_MOD(ix, DSI_CLK_CTRL, lp_clk_div, 12, 0); /* LP_CLK_DIVISOR */ - REG_FLD_MOD(DSI_CLK_CTRL, dsi_fclk > 30000000 ? 1 : 0, + REG_FLD_MOD(ix, DSI_CLK_CTRL, dsi_fclk > 30000000 ? 1 : 0, 21, 21); /* LP_RX_SYNCHRO_ENABLE */ return 0; @@ -757,14 +821,15 @@ enum dsi_pll_power_state { DSI_PLL_POWER_ON_DIV = 0x3, }; -static int dsi_pll_power(enum dsi_pll_power_state state) +static int dsi_pll_power(enum omap_dsi_index ix, + enum dsi_pll_power_state state) { int t = 0; - REG_FLD_MOD(DSI_CLK_CTRL, state, 31, 30); /* PLL_PWR_CMD */ + REG_FLD_MOD(ix, DSI_CLK_CTRL, state, 31, 30); /* PLL_PWR_CMD */ /* PLL_PWR_STATUS */ - while (FLD_GET(dsi_read_reg(DSI_CLK_CTRL), 29, 28) != state) { + while (FLD_GET(dsi_read_reg(ix, DSI_CLK_CTRL), 29, 28) != state) { if (++t > 1000) { DSSERR("Failed to set DSI PLL power mode to %d\n", state); @@ -777,7 +842,7 @@ static int dsi_pll_power(enum dsi_pll_power_state state) } /* calculate clock rates using dividers in cinfo */ -static int dsi_calc_clock_rates(struct dsi_clock_info *cinfo) +int dsi_calc_clock_rates(struct dsi_clock_info *cinfo) { if (cinfo->regn == 0 || cinfo->regn > REGN_MAX) return -EINVAL; @@ -793,11 +858,12 @@ static int dsi_calc_clock_rates(struct dsi_clock_info *cinfo) if (cinfo->use_dss2_fck) { cinfo->clkin = dss_clk_get_rate(DSS_CLK_FCK2); + cinfo->clkin = 38400000; /* XXX it is unclear if highfreq should be used * with DSS2_FCK source also */ cinfo->highfreq = 0; } else { - cinfo->clkin = dispc_pclk_rate(); + cinfo->clkin = dispc_pclk_rate(OMAP_DSS_CHANNEL_LCD); if (cinfo->clkin < 32000000) cinfo->highfreq = 0; @@ -828,7 +894,8 @@ static int dsi_calc_clock_rates(struct dsi_clock_info *cinfo) return 0; } -int dsi_pll_calc_clock_div_pck(bool is_tft, unsigned long req_pck, +int dsi_pll_calc_clock_div_pck(enum omap_dsi_index ix, + bool is_tft, unsigned long req_pck, struct dsi_clock_info *dsi_cinfo, struct dispc_clock_info *dispc_cinfo) { @@ -837,13 +904,14 @@ int dsi_pll_calc_clock_div_pck(bool is_tft, unsigned long req_pck, int min_fck_per_pck; int match = 0; unsigned long dss_clk_fck2; + struct dsi_struct *p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; dss_clk_fck2 = dss_clk_get_rate(DSS_CLK_FCK2); - if (req_pck == dsi.cache_req_pck && - dsi.cache_cinfo.clkin == dss_clk_fck2) { + if (req_pck == p_dsi->cache_req_pck && + p_dsi->cache_cinfo.clkin == dss_clk_fck2) { DSSDBG("DSI clock info found from cache\n"); - *dsi_cinfo = dsi.cache_cinfo; + *dsi_cinfo = p_dsi->cache_cinfo; dispc_find_clk_divs(is_tft, req_pck, dsi_cinfo->dsi1_pll_fclk, dispc_cinfo); return 0; @@ -954,30 +1022,32 @@ found: if (dispc_cinfo) *dispc_cinfo = best_dispc; - dsi.cache_req_pck = req_pck; - dsi.cache_clk_freq = 0; - dsi.cache_cinfo = best; + p_dsi->cache_req_pck = req_pck; + p_dsi->cache_clk_freq = 0; + p_dsi->cache_cinfo = best; return 0; } -int dsi_pll_set_clock_div(struct dsi_clock_info *cinfo) +int dsi_pll_set_clock_div(enum omap_dsi_index ix, + struct dsi_clock_info *cinfo) { int r = 0; u32 l; - int f; + int f = 0; + struct dsi_struct *p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; DSSDBGF(); - dsi.current_cinfo.fint = cinfo->fint; - dsi.current_cinfo.clkin4ddr = cinfo->clkin4ddr; - dsi.current_cinfo.dsi1_pll_fclk = cinfo->dsi1_pll_fclk; - dsi.current_cinfo.dsi2_pll_fclk = cinfo->dsi2_pll_fclk; + p_dsi->current_cinfo.fint = cinfo->fint; + p_dsi->current_cinfo.clkin4ddr = cinfo->clkin4ddr; + p_dsi->current_cinfo.dsi1_pll_fclk = cinfo->dsi1_pll_fclk; + p_dsi->current_cinfo.dsi2_pll_fclk = cinfo->dsi2_pll_fclk; - dsi.current_cinfo.regn = cinfo->regn; - dsi.current_cinfo.regm = cinfo->regm; - dsi.current_cinfo.regm3 = cinfo->regm3; - dsi.current_cinfo.regm4 = cinfo->regm4; + p_dsi->current_cinfo.regn = cinfo->regn; + p_dsi->current_cinfo.regm = cinfo->regm; + p_dsi->current_cinfo.regm3 = cinfo->regm3; + p_dsi->current_cinfo.regm4 = cinfo->regm4; DSSDBG("DSI Fint %ld\n", cinfo->fint); @@ -1004,32 +1074,39 @@ int dsi_pll_set_clock_div(struct dsi_clock_info *cinfo) DSSDBG("regm4 = %d, dsi2_pll_fclk = %lu\n", cinfo->regm4, cinfo->dsi2_pll_fclk); - REG_FLD_MOD(DSI_PLL_CONTROL, 0, 0, 0); /* DSI_PLL_AUTOMODE = manual */ + REG_FLD_MOD(ix, DSI_PLL_CONTROL, 0, + 0, 0); /* DSI_PLL_AUTOMODE = manual */ - l = dsi_read_reg(DSI_PLL_CONFIGURATION1); + l = dsi_read_reg(ix, DSI_PLL_CONFIGURATION1); l = FLD_MOD(l, 1, 0, 0); /* DSI_PLL_STOPMODE */ - l = FLD_MOD(l, cinfo->regn - 1, 7, 1); /* DSI_PLL_REGN */ - l = FLD_MOD(l, cinfo->regm, 18, 8); /* DSI_PLL_REGM */ + l = FLD_MOD(l, cinfo->regn - 1, (cpu_is_omap44xx()) ? 8 : 7, + 1); /* DSI_PLL_REGN */ + l = FLD_MOD(l, cinfo->regm, (cpu_is_omap44xx()) ? 20 : 18, + (cpu_is_omap44xx()) ? 9 : 8); /* DSI_PLL_REGM */ l = FLD_MOD(l, cinfo->regm3 > 0 ? cinfo->regm3 - 1 : 0, - 22, 19); /* DSI_CLOCK_DIV */ + (cpu_is_omap44xx()) ? 25 : 22, + (cpu_is_omap44xx()) ? 21 : 19); /* DSI_CLOCK_DIV */ l = FLD_MOD(l, cinfo->regm4 > 0 ? cinfo->regm4 - 1 : 0, - 26, 23); /* DSIPROTO_CLOCK_DIV */ - dsi_write_reg(DSI_PLL_CONFIGURATION1, l); + (cpu_is_omap44xx()) ? 30 : 26, + (cpu_is_omap44xx()) ? 26 : 23); /* DSIPROTO_CLOCK_DIV */ + dsi_write_reg(ix, DSI_PLL_CONFIGURATION1, l); BUG_ON(cinfo->fint < 750000 || cinfo->fint > 2100000); - if (cinfo->fint < 1000000) - f = 0x3; - else if (cinfo->fint < 1250000) - f = 0x4; - else if (cinfo->fint < 1500000) - f = 0x5; - else if (cinfo->fint < 1750000) - f = 0x6; - else - f = 0x7; - - l = dsi_read_reg(DSI_PLL_CONFIGURATION2); - l = FLD_MOD(l, f, 4, 1); /* DSI_PLL_FREQSEL */ + if (!cpu_is_omap44xx()) { + if (cinfo->fint < 1000000) + f = 0x3; + else if (cinfo->fint < 1250000) + f = 0x4; + else if (cinfo->fint < 1500000) + f = 0x5; + else if (cinfo->fint < 1750000) + f = 0x6; + else + f = 0x7; + } + l = dsi_read_reg(ix, DSI_PLL_CONFIGURATION2); + if (!cpu_is_omap44xx()) + l = FLD_MOD(l, f, 4, 1); /* DSI_PLL_FREQSEL */ l = FLD_MOD(l, cinfo->use_dss2_fck ? 0 : 1, 11, 11); /* DSI_PLL_CLKSEL */ l = FLD_MOD(l, cinfo->highfreq, @@ -1037,29 +1114,32 @@ int dsi_pll_set_clock_div(struct dsi_clock_info *cinfo) l = FLD_MOD(l, 1, 13, 13); /* DSI_PLL_REFEN */ l = FLD_MOD(l, 0, 14, 14); /* DSIPHY_CLKINEN */ l = FLD_MOD(l, 1, 20, 20); /* DSI_HSDIVBYPASS */ - dsi_write_reg(DSI_PLL_CONFIGURATION2, l); + if (cpu_is_omap44xx()) + l = FLD_MOD(l, 3, 22, 21); /* DSI_REF_SEL */ + dsi_write_reg(ix, DSI_PLL_CONFIGURATION2, l); - REG_FLD_MOD(DSI_PLL_GO, 1, 0, 0); /* DSI_PLL_GO */ + REG_FLD_MOD(ix, DSI_PLL_GO, 1, 0, 0); /* DSI_PLL_GO */ - if (wait_for_bit_change(DSI_PLL_GO, 0, 0) != 0) { + if (wait_for_bit_change(ix, DSI_PLL_GO, 0, 0) != 0) { DSSERR("dsi pll go bit not going down.\n"); r = -EIO; goto err; } - if (wait_for_bit_change(DSI_PLL_STATUS, 1, 1) != 1) { + if (wait_for_bit_change(ix, DSI_PLL_STATUS, 1, 1) != 1) { DSSERR("cannot lock PLL\n"); r = -EIO; goto err; } - dsi.pll_locked = 1; + p_dsi->pll_locked = 1; - l = dsi_read_reg(DSI_PLL_CONFIGURATION2); + l = dsi_read_reg(ix, DSI_PLL_CONFIGURATION2); l = FLD_MOD(l, 0, 0, 0); /* DSI_PLL_IDLE */ l = FLD_MOD(l, 0, 5, 5); /* DSI_PLL_PLLLPMODE */ l = FLD_MOD(l, 0, 6, 6); /* DSI_PLL_LOWCURRSTBY */ - l = FLD_MOD(l, 0, 7, 7); /* DSI_PLL_TIGHTPHASELOCK */ + if (!cpu_is_omap44xx()) + l = FLD_MOD(l, 0, 7, 7); /* DSI_PLL_TIGHTPHASELOCK */ l = FLD_MOD(l, 0, 8, 8); /* DSI_PLL_DRIFTGUARDEN */ l = FLD_MOD(l, 0, 10, 9); /* DSI_PLL_LOCKSEL */ l = FLD_MOD(l, 1, 13, 13); /* DSI_PLL_REFEN */ @@ -1070,7 +1150,11 @@ int dsi_pll_set_clock_div(struct dsi_clock_info *cinfo) l = FLD_MOD(l, 1, 18, 18); /* DSI_PROTO_CLOCK_EN */ l = FLD_MOD(l, 0, 19, 19); /* DSI_PROTO_CLOCK_PWDN */ l = FLD_MOD(l, 0, 20, 20); /* DSI_HSDIVBYPASS */ - dsi_write_reg(DSI_PLL_CONFIGURATION2, l); + if (cpu_is_omap44xx()) { + l = FLD_MOD(l, 0, 25, 25); /* M7_CLOCK_EN */ + l = FLD_MOD(l, 0, 26, 26); /* M7_CLOCK_PWDN */ + } + dsi_write_reg(ix, DSI_PLL_CONFIGURATION2, l); DSSDBG("PLL config done\n"); err: @@ -1082,20 +1166,30 @@ int dsi_pll_init(struct omap_dss_device *dssdev, bool enable_hsclk, { int r = 0; enum dsi_pll_power_state pwstate; + enum omap_dsi_index ix; + + ix = (dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? DSI1 : DSI2; DSSDBG("PLL init\n"); + /* The SCPClk is required for PLL registers in OMAP4 */ + if (cpu_is_omap44xx()) + REG_FLD_MOD(ix, DSI_CLK_CTRL, 1, 14, 14); + enable_clocks(1); - dsi_enable_pll_clock(1); + dsi_enable_pll_clock(ix, 1); - r = regulator_enable(dsi.vdds_dsi_reg); - if (r) - goto err0; + if (!cpu_is_omap44xx()) { + r = regulator_enable(dsi1.vdds_dsi_reg); + if (r) + goto err0; + } /* XXX PLL does not come out of reset without this... */ - dispc_pck_free_enable(1); + if (!cpu_is_omap44xx()) + dispc_pck_free_enable(1); - if (wait_for_bit_change(DSI_PLL_STATUS, 0, 1) != 1) { + if (wait_for_bit_change(ix, DSI_PLL_STATUS, 0, 1) != 1) { DSSERR("PLL not coming out of reset.\n"); r = -ENODEV; goto err1; @@ -1103,7 +1197,8 @@ int dsi_pll_init(struct omap_dss_device *dssdev, bool enable_hsclk, /* XXX ... but if left on, we get problems when planes do not * fill the whole display. No idea about this */ - dispc_pck_free_enable(0); + if (!cpu_is_omap44xx()) + dispc_pck_free_enable(0); if (enable_hsclk && enable_hsdiv) pwstate = DSI_PLL_POWER_ON_ALL; @@ -1114,7 +1209,7 @@ int dsi_pll_init(struct omap_dss_device *dssdev, bool enable_hsclk, else pwstate = DSI_PLL_POWER_OFF; - r = dsi_pll_power(pwstate); + r = dsi_pll_power(ix, pwstate); if (r) goto err1; @@ -1123,32 +1218,37 @@ int dsi_pll_init(struct omap_dss_device *dssdev, bool enable_hsclk, return 0; err1: - regulator_disable(dsi.vdds_dsi_reg); + if (!cpu_is_omap44xx()) + regulator_disable(dsi1.vdds_dsi_reg); err0: enable_clocks(0); - dsi_enable_pll_clock(0); + dsi_enable_pll_clock(ix, 0); return r; } -void dsi_pll_uninit(void) +void dsi_pll_uninit(enum omap_dsi_index ix) { + struct dsi_struct *p_dsi; + p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; + enable_clocks(0); - dsi_enable_pll_clock(0); + dsi_enable_pll_clock(ix, 0); - dsi.pll_locked = 0; - dsi_pll_power(DSI_PLL_POWER_OFF); - regulator_disable(dsi.vdds_dsi_reg); + p_dsi->pll_locked = 0; + dsi_pll_power(ix, DSI_PLL_POWER_OFF); + if (!cpu_is_omap44xx()) + regulator_disable(dsi1.vdds_dsi_reg); DSSDBG("PLL uninit done\n"); } -void dsi_dump_clocks(struct seq_file *s) +void dsi_dump_clocks(enum omap_dsi_index ix, struct seq_file *s) { int clksel; - struct dsi_clock_info *cinfo = &dsi.current_cinfo; - + struct dsi_struct *p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; + struct dsi_clock_info *cinfo = &p_dsi->current_cinfo; enable_clocks(1); - clksel = REG_GET(DSI_PLL_CONFIGURATION2, 11, 11); + clksel = REG_GET(ix, DSI_PLL_CONFIGURATION2, 11, 11); seq_printf(s, "- DSI PLL -\n"); @@ -1179,36 +1279,37 @@ void dsi_dump_clocks(struct seq_file *s) dss_get_dsi_clk_source() == DSS_SRC_DSS1_ALWON_FCLK ? "dss1_alwon_fclk" : "dsi2_pll_fclk"); - seq_printf(s, "DSI_FCLK\t%lu\n", dsi_fclk_rate()); + seq_printf(s, "DSI_FCLK\t%lu\n", dsi_fclk_rate(ix)); seq_printf(s, "DDR_CLK\t\t%lu\n", cinfo->clkin4ddr / 4); - seq_printf(s, "TxByteClkHS\t%lu\n", dsi_get_txbyteclkhs()); + seq_printf(s, "TxByteClkHS\t%lu\n", dsi_get_txbyteclkhs(ix)); seq_printf(s, "LP_CLK\t\t%lu\n", cinfo->lp_clk); seq_printf(s, "VP_CLK\t\t%lu\n" "VP_PCLK\t\t%lu\n", - dispc_lclk_rate(), - dispc_pclk_rate()); + dispc_lclk_rate(OMAP_DSS_CHANNEL_LCD), + dispc_pclk_rate(OMAP_DSS_CHANNEL_LCD)); enable_clocks(0); } #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS -void dsi_dump_irqs(struct seq_file *s) +void dsi_dump_irqs(enum omap_dsi_index ix, struct seq_file *s) { unsigned long flags; struct dsi_irq_stats stats; + struct dsi_struct *p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; - spin_lock_irqsave(&dsi.irq_stats_lock, flags); + spin_lock_irqsave(&p_dsi->irq_stats_lock, flags); - stats = dsi.irq_stats; - memset(&dsi.irq_stats, 0, sizeof(dsi.irq_stats)); - dsi.irq_stats.last_reset = jiffies; + stats = p_dsi->irq_stats; + memset(&p_dsi->irq_stats, 0, sizeof(p_dsi->irq_stats)); + p_dsi->irq_stats.last_reset = jiffies; - spin_unlock_irqrestore(&dsi.irq_stats_lock, flags); + spin_unlock_irqrestore(&p_dsi->irq_stats_lock, flags); seq_printf(s, "period %u ms\n", jiffies_to_msecs(jiffies - stats.last_reset)); @@ -1285,81 +1386,81 @@ void dsi_dump_irqs(struct seq_file *s) } #endif -void dsi_dump_regs(struct seq_file *s) +void dsi_dump_regs(enum omap_dsi_index ix, struct seq_file *s) { -#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dsi_read_reg(r)) +#define DUMPREG(ix, r) seq_printf(s, "%-35s %08x\n", #r, dsi_read_reg(ix, r)) dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); - DUMPREG(DSI_REVISION); - DUMPREG(DSI_SYSCONFIG); - DUMPREG(DSI_SYSSTATUS); - DUMPREG(DSI_IRQSTATUS); - DUMPREG(DSI_IRQENABLE); - DUMPREG(DSI_CTRL); - DUMPREG(DSI_COMPLEXIO_CFG1); - DUMPREG(DSI_COMPLEXIO_IRQ_STATUS); - DUMPREG(DSI_COMPLEXIO_IRQ_ENABLE); - DUMPREG(DSI_CLK_CTRL); - DUMPREG(DSI_TIMING1); - DUMPREG(DSI_TIMING2); - DUMPREG(DSI_VM_TIMING1); - DUMPREG(DSI_VM_TIMING2); - DUMPREG(DSI_VM_TIMING3); - DUMPREG(DSI_CLK_TIMING); - DUMPREG(DSI_TX_FIFO_VC_SIZE); - DUMPREG(DSI_RX_FIFO_VC_SIZE); - DUMPREG(DSI_COMPLEXIO_CFG2); - DUMPREG(DSI_RX_FIFO_VC_FULLNESS); - DUMPREG(DSI_VM_TIMING4); - DUMPREG(DSI_TX_FIFO_VC_EMPTINESS); - DUMPREG(DSI_VM_TIMING5); - DUMPREG(DSI_VM_TIMING6); - DUMPREG(DSI_VM_TIMING7); - DUMPREG(DSI_STOPCLK_TIMING); - - DUMPREG(DSI_VC_CTRL(0)); - DUMPREG(DSI_VC_TE(0)); - DUMPREG(DSI_VC_LONG_PACKET_HEADER(0)); - DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(0)); - DUMPREG(DSI_VC_SHORT_PACKET_HEADER(0)); - DUMPREG(DSI_VC_IRQSTATUS(0)); - DUMPREG(DSI_VC_IRQENABLE(0)); - - DUMPREG(DSI_VC_CTRL(1)); - DUMPREG(DSI_VC_TE(1)); - DUMPREG(DSI_VC_LONG_PACKET_HEADER(1)); - DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(1)); - DUMPREG(DSI_VC_SHORT_PACKET_HEADER(1)); - DUMPREG(DSI_VC_IRQSTATUS(1)); - DUMPREG(DSI_VC_IRQENABLE(1)); - - DUMPREG(DSI_VC_CTRL(2)); - DUMPREG(DSI_VC_TE(2)); - DUMPREG(DSI_VC_LONG_PACKET_HEADER(2)); - DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(2)); - DUMPREG(DSI_VC_SHORT_PACKET_HEADER(2)); - DUMPREG(DSI_VC_IRQSTATUS(2)); - DUMPREG(DSI_VC_IRQENABLE(2)); - - DUMPREG(DSI_VC_CTRL(3)); - DUMPREG(DSI_VC_TE(3)); - DUMPREG(DSI_VC_LONG_PACKET_HEADER(3)); - DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(3)); - DUMPREG(DSI_VC_SHORT_PACKET_HEADER(3)); - DUMPREG(DSI_VC_IRQSTATUS(3)); - DUMPREG(DSI_VC_IRQENABLE(3)); - - DUMPREG(DSI_DSIPHY_CFG0); - DUMPREG(DSI_DSIPHY_CFG1); - DUMPREG(DSI_DSIPHY_CFG2); - DUMPREG(DSI_DSIPHY_CFG5); - - DUMPREG(DSI_PLL_CONTROL); - DUMPREG(DSI_PLL_STATUS); - DUMPREG(DSI_PLL_GO); - DUMPREG(DSI_PLL_CONFIGURATION1); - DUMPREG(DSI_PLL_CONFIGURATION2); + DUMPREG(ix, DSI_REVISION); + DUMPREG(ix, DSI_SYSCONFIG); + DUMPREG(ix, DSI_SYSSTATUS); + DUMPREG(ix, DSI_IRQSTATUS); + DUMPREG(ix, DSI_IRQENABLE); + DUMPREG(ix, DSI_CTRL); + DUMPREG(ix, DSI_COMPLEXIO_CFG1); + DUMPREG(ix, DSI_COMPLEXIO_IRQ_STATUS); + DUMPREG(ix, DSI_COMPLEXIO_IRQ_ENABLE); + DUMPREG(ix, DSI_CLK_CTRL); + DUMPREG(ix, DSI_TIMING1); + DUMPREG(ix, DSI_TIMING2); + DUMPREG(ix, DSI_VM_TIMING1); + DUMPREG(ix, DSI_VM_TIMING2); + DUMPREG(ix, DSI_VM_TIMING3); + DUMPREG(ix, DSI_CLK_TIMING); + DUMPREG(ix, DSI_TX_FIFO_VC_SIZE); + DUMPREG(ix, DSI_RX_FIFO_VC_SIZE); + DUMPREG(ix, DSI_COMPLEXIO_CFG2); + DUMPREG(ix, DSI_RX_FIFO_VC_FULLNESS); + DUMPREG(ix, DSI_VM_TIMING4); + DUMPREG(ix, DSI_TX_FIFO_VC_EMPTINESS); + DUMPREG(ix, DSI_VM_TIMING5); + DUMPREG(ix, DSI_VM_TIMING6); + DUMPREG(ix, DSI_VM_TIMING7); + DUMPREG(ix, DSI_STOPCLK_TIMING); + + DUMPREG(ix, DSI_VC_CTRL(0)); + DUMPREG(ix, DSI_VC_TE(0)); + DUMPREG(ix, DSI_VC_LONG_PACKET_HEADER(0)); + DUMPREG(ix, DSI_VC_LONG_PACKET_PAYLOAD(0)); + DUMPREG(ix, DSI_VC_SHORT_PACKET_HEADER(0)); + DUMPREG(ix, DSI_VC_IRQSTATUS(0)); + DUMPREG(ix, DSI_VC_IRQENABLE(0)); + + DUMPREG(ix, DSI_VC_CTRL(1)); + DUMPREG(ix, DSI_VC_TE(1)); + DUMPREG(ix, DSI_VC_LONG_PACKET_HEADER(1)); + DUMPREG(ix, DSI_VC_LONG_PACKET_PAYLOAD(1)); + DUMPREG(ix, DSI_VC_SHORT_PACKET_HEADER(1)); + DUMPREG(ix, DSI_VC_IRQSTATUS(1)); + DUMPREG(ix, DSI_VC_IRQENABLE(1)); + + DUMPREG(ix, DSI_VC_CTRL(2)); + DUMPREG(ix, DSI_VC_TE(2)); + DUMPREG(ix, DSI_VC_LONG_PACKET_HEADER(2)); + DUMPREG(ix, DSI_VC_LONG_PACKET_PAYLOAD(2)); + DUMPREG(ix, DSI_VC_SHORT_PACKET_HEADER(2)); + DUMPREG(ix, DSI_VC_IRQSTATUS(2)); + DUMPREG(ix, DSI_VC_IRQENABLE(2)); + + DUMPREG(ix, DSI_VC_CTRL(3)); + DUMPREG(ix, DSI_VC_TE(3)); + DUMPREG(ix, DSI_VC_LONG_PACKET_HEADER(3)); + DUMPREG(ix, DSI_VC_LONG_PACKET_PAYLOAD(3)); + DUMPREG(ix, DSI_VC_SHORT_PACKET_HEADER(3)); + DUMPREG(ix, DSI_VC_IRQSTATUS(3)); + DUMPREG(ix, DSI_VC_IRQENABLE(3)); + + DUMPREG(ix, DSI_DSIPHY_CFG0); + DUMPREG(ix, DSI_DSIPHY_CFG1); + DUMPREG(ix, DSI_DSIPHY_CFG2); + DUMPREG(ix, DSI_DSIPHY_CFG5); + + DUMPREG(ix, DSI_PLL_CONTROL); + DUMPREG(ix, DSI_PLL_STATUS); + DUMPREG(ix, DSI_PLL_GO); + DUMPREG(ix, DSI_PLL_CONFIGURATION1); + DUMPREG(ix, DSI_PLL_CONFIGURATION2); dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); #undef DUMPREG @@ -1371,15 +1472,20 @@ enum dsi_complexio_power_state { DSI_COMPLEXIO_POWER_ULPS = 0x2, }; -static int dsi_complexio_power(enum dsi_complexio_power_state state) +static int dsi_complexio_power(enum omap_dsi_index ix, + enum dsi_complexio_power_state state) { int t = 0; /* PWR_CMD */ - REG_FLD_MOD(DSI_COMPLEXIO_CFG1, state, 28, 27); + REG_FLD_MOD(ix, DSI_COMPLEXIO_CFG1, state, 28, 27); + + if (cpu_is_omap44xx()) + /*bit 30 has to be set to 1 to GO in omap4*/ + REG_FLD_MOD(ix, DSI_COMPLEXIO_CFG1, 1, 30, 30); /* PWR_STATUS */ - while (FLD_GET(dsi_read_reg(DSI_COMPLEXIO_CFG1), 26, 25) != state) { + while (FLD_GET(dsi_read_reg(ix, DSI_COMPLEXIO_CFG1), 26, 25) != state) { if (++t > 1000) { DSSERR("failed to set complexio power state to " "%d\n", state); @@ -1394,6 +1500,8 @@ static int dsi_complexio_power(enum dsi_complexio_power_state state) static void dsi_complexio_config(struct omap_dss_device *dssdev) { u32 r; + enum omap_dsi_index ix; + ix = (dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? DSI1 : DSI2; int clk_lane = dssdev->phy.dsi.clk_lane; int data1_lane = dssdev->phy.dsi.data1_lane; @@ -1402,14 +1510,14 @@ static void dsi_complexio_config(struct omap_dss_device *dssdev) int data1_pol = dssdev->phy.dsi.data1_pol; int data2_pol = dssdev->phy.dsi.data2_pol; - r = dsi_read_reg(DSI_COMPLEXIO_CFG1); + r = dsi_read_reg(ix, DSI_COMPLEXIO_CFG1); r = FLD_MOD(r, clk_lane, 2, 0); r = FLD_MOD(r, clk_pol, 3, 3); r = FLD_MOD(r, data1_lane, 6, 4); r = FLD_MOD(r, data1_pol, 7, 7); r = FLD_MOD(r, data2_lane, 10, 8); r = FLD_MOD(r, data2_pol, 11, 11); - dsi_write_reg(DSI_COMPLEXIO_CFG1, r); + dsi_write_reg(ix, DSI_COMPLEXIO_CFG1, r); /* The configuration of the DSI complex I/O (number of data lanes, position, differential order) should not be changed while @@ -1430,20 +1538,22 @@ static void dsi_complexio_config(struct omap_dss_device *dssdev) */ } -static inline unsigned ns2ddr(unsigned ns) +static inline unsigned ns2ddr(enum omap_dsi_index ix, unsigned ns) { /* convert time in ns to ddr ticks, rounding up */ - unsigned long ddr_clk = dsi.current_cinfo.clkin4ddr / 4; + struct dsi_struct *p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; + unsigned long ddr_clk = p_dsi->current_cinfo.clkin4ddr / 4; return (ns * (ddr_clk / 1000 / 1000) + 999) / 1000; } -static inline unsigned ddr2ns(unsigned ddr) +static inline unsigned ddr2ns(enum omap_dsi_index ix, unsigned ddr) { - unsigned long ddr_clk = dsi.current_cinfo.clkin4ddr / 4; + struct dsi_struct *p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; + unsigned long ddr_clk = p_dsi->current_cinfo.clkin4ddr / 4; return ddr * 1000 * 1000 / (ddr_clk / 1000); } -static void dsi_complexio_timings(void) +static void dsi_complexio_timings(enum omap_dsi_index ix) { u32 r; u32 ths_prepare, ths_prepare_ths_zero, ths_trail, ths_exit; @@ -1455,80 +1565,85 @@ static void dsi_complexio_timings(void) /* 1 * DDR_CLK = 2 * UI */ /* min 40ns + 4*UI max 85ns + 6*UI */ - ths_prepare = ns2ddr(70) + 2; + ths_prepare = ns2ddr(ix, 70) + 2; /* min 145ns + 10*UI */ - ths_prepare_ths_zero = ns2ddr(175) + 2; + ths_prepare_ths_zero = ns2ddr(ix, 175) + 2; /* min max(8*UI, 60ns+4*UI) */ - ths_trail = ns2ddr(60) + 5; + ths_trail = ns2ddr(ix, 60) + 5; /* min 100ns */ - ths_exit = ns2ddr(145); + ths_exit = ns2ddr(ix, 145); /* tlpx min 50n */ - tlpx_half = ns2ddr(25); + tlpx_half = ns2ddr(ix, 25); /* min 60ns */ - tclk_trail = ns2ddr(60) + 2; + tclk_trail = ns2ddr(ix, 60) + 2; /* min 38ns, max 95ns */ - tclk_prepare = ns2ddr(65); + tclk_prepare = ns2ddr(ix, 65); /* min tclk-prepare + tclk-zero = 300ns */ - tclk_zero = ns2ddr(260); + tclk_zero = ns2ddr(ix, 260); DSSDBG("ths_prepare %u (%uns), ths_prepare_ths_zero %u (%uns)\n", - ths_prepare, ddr2ns(ths_prepare), - ths_prepare_ths_zero, ddr2ns(ths_prepare_ths_zero)); + ths_prepare, ddr2ns(ix, ths_prepare), + ths_prepare_ths_zero, ddr2ns(ix, ths_prepare_ths_zero)); DSSDBG("ths_trail %u (%uns), ths_exit %u (%uns)\n", - ths_trail, ddr2ns(ths_trail), - ths_exit, ddr2ns(ths_exit)); + ths_trail, ddr2ns(ix, ths_trail), + ths_exit, ddr2ns(ix, ths_exit)); DSSDBG("tlpx_half %u (%uns), tclk_trail %u (%uns), " "tclk_zero %u (%uns)\n", - tlpx_half, ddr2ns(tlpx_half), - tclk_trail, ddr2ns(tclk_trail), - tclk_zero, ddr2ns(tclk_zero)); + tlpx_half, ddr2ns(ix, tlpx_half), + tclk_trail, ddr2ns(ix, tclk_trail), + tclk_zero, ddr2ns(ix, tclk_zero)); DSSDBG("tclk_prepare %u (%uns)\n", - tclk_prepare, ddr2ns(tclk_prepare)); + tclk_prepare, ddr2ns(ix, tclk_prepare)); /* program timings */ - r = dsi_read_reg(DSI_DSIPHY_CFG0); + r = dsi_read_reg(ix, DSI_DSIPHY_CFG0); r = FLD_MOD(r, ths_prepare, 31, 24); r = FLD_MOD(r, ths_prepare_ths_zero, 23, 16); r = FLD_MOD(r, ths_trail, 15, 8); r = FLD_MOD(r, ths_exit, 7, 0); - dsi_write_reg(DSI_DSIPHY_CFG0, r); + dsi_write_reg(ix, DSI_DSIPHY_CFG0, r); - r = dsi_read_reg(DSI_DSIPHY_CFG1); + r = dsi_read_reg(ix, DSI_DSIPHY_CFG1); r = FLD_MOD(r, tlpx_half, 22, 16); r = FLD_MOD(r, tclk_trail, 15, 8); r = FLD_MOD(r, tclk_zero, 7, 0); - dsi_write_reg(DSI_DSIPHY_CFG1, r); + dsi_write_reg(ix, DSI_DSIPHY_CFG1, r); - r = dsi_read_reg(DSI_DSIPHY_CFG2); + r = dsi_read_reg(ix, DSI_DSIPHY_CFG2); r = FLD_MOD(r, tclk_prepare, 7, 0); - dsi_write_reg(DSI_DSIPHY_CFG2, r); + dsi_write_reg(ix, DSI_DSIPHY_CFG2, r); } static int dsi_complexio_init(struct omap_dss_device *dssdev) { int r = 0; + enum omap_dsi_index ix; + ix = (dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? DSI1 : DSI2; DSSDBG("dsi_complexio_init\n"); /* CIO_CLK_ICG, enable L3 clk to CIO */ - REG_FLD_MOD(DSI_CLK_CTRL, 1, 14, 14); + REG_FLD_MOD(ix, DSI_CLK_CTRL, 1, 14, 14); + + if (cpu_is_omap44xx()) + REG_FLD_MOD(ix, DSI_CLK_CTRL, 1, 13, 13); /* A dummy read using the SCP interface to any DSIPHY register is * required after DSIPHY reset to complete the reset of the DSI complex * I/O. */ - dsi_read_reg(DSI_DSIPHY_CFG5); + dsi_read_reg(ix, DSI_DSIPHY_CFG5); - if (wait_for_bit_change(DSI_DSIPHY_CFG5, 30, 1) != 1) { + if (wait_for_bit_change(ix, DSI_DSIPHY_CFG5, 30, 1) != 1) { DSSERR("ComplexIO PHY not coming out of reset.\n"); r = -ENODEV; goto err; @@ -1536,24 +1651,26 @@ static int dsi_complexio_init(struct omap_dss_device *dssdev) dsi_complexio_config(dssdev); - r = dsi_complexio_power(DSI_COMPLEXIO_POWER_ON); + r = dsi_complexio_power(ix, DSI_COMPLEXIO_POWER_ON); if (r) goto err; - if (wait_for_bit_change(DSI_COMPLEXIO_CFG1, 29, 1) != 1) { + if (wait_for_bit_change(ix, DSI_COMPLEXIO_CFG1, 29, 1) != 1) { DSSERR("ComplexIO not coming out of reset.\n"); r = -ENODEV; goto err; } - if (wait_for_bit_change(DSI_COMPLEXIO_CFG1, 21, 1) != 1) { - DSSERR("ComplexIO LDO power down.\n"); - r = -ENODEV; - goto err; + if (!cpu_is_omap44xx()) { + if (wait_for_bit_change(ix, DSI_COMPLEXIO_CFG1, 21, 1) != 1) { + DSSERR("ComplexIO LDO power down.\n"); + r = -ENODEV; + goto err; + } } - dsi_complexio_timings(); + dsi_complexio_timings(ix); /* The configuration of the DSI complex I/O (number of data lanes, @@ -1567,27 +1684,27 @@ static int dsi_complexio_init(struct omap_dss_device *dssdev) bit to 1. If the sequence is not followed, the DSi complex I/O configuration is undetermined. */ - dsi_if_enable(1); - dsi_if_enable(0); - REG_FLD_MOD(DSI_CLK_CTRL, 1, 20, 20); /* LP_CLK_ENABLE */ - dsi_if_enable(1); - dsi_if_enable(0); + dsi_if_enable(ix, 1); + dsi_if_enable(ix, 0); + REG_FLD_MOD(ix, DSI_CLK_CTRL, 1, 20, 20); /* LP_CLK_ENABLE */ + dsi_if_enable(ix, 1); + dsi_if_enable(ix, 0); DSSDBG("CIO init done\n"); err: return r; } -static void dsi_complexio_uninit(void) +static void dsi_complexio_uninit(enum omap_dsi_index ix) { - dsi_complexio_power(DSI_COMPLEXIO_POWER_OFF); + dsi_complexio_power(ix, DSI_COMPLEXIO_POWER_OFF); } -static int _dsi_wait_reset(void) +static int _dsi_wait_reset(enum omap_dsi_index ix) { int t = 0; - while (REG_GET(DSI_SYSSTATUS, 0, 0) == 0) { + while (REG_GET(ix, DSI_SYSSTATUS, 0, 0) == 0) { if (++t > 5) { DSSERR("soft reset failed\n"); return -ENODEV; @@ -1598,42 +1715,44 @@ static int _dsi_wait_reset(void) return 0; } -static int _dsi_reset(void) +static int _dsi_reset(enum omap_dsi_index ix) { /* Soft reset */ - REG_FLD_MOD(DSI_SYSCONFIG, 1, 1, 1); - return _dsi_wait_reset(); + REG_FLD_MOD(ix, DSI_SYSCONFIG, 1, 1, 1); + return _dsi_wait_reset(ix); } -static void dsi_reset_tx_fifo(int channel) +static void dsi_reset_tx_fifo(enum omap_dsi_index ix, int channel) { u32 mask; u32 l; /* set fifosize of the channel to 0, then return the old size */ - l = dsi_read_reg(DSI_TX_FIFO_VC_SIZE); + l = dsi_read_reg(ix, DSI_TX_FIFO_VC_SIZE); mask = FLD_MASK((8 * channel) + 7, (8 * channel) + 4); - dsi_write_reg(DSI_TX_FIFO_VC_SIZE, l & ~mask); + dsi_write_reg(ix, DSI_TX_FIFO_VC_SIZE, l & ~mask); - dsi_write_reg(DSI_TX_FIFO_VC_SIZE, l); + dsi_write_reg(ix, DSI_TX_FIFO_VC_SIZE, l); } -static void dsi_config_tx_fifo(enum fifo_size size1, enum fifo_size size2, - enum fifo_size size3, enum fifo_size size4) +static void dsi_config_tx_fifo(enum omap_dsi_index ix, + enum fifo_size size1, enum fifo_size size2, + enum fifo_size size3, enum fifo_size size4) { u32 r = 0; int add = 0; int i; + struct dsi_struct *p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; - dsi.vc[0].fifo_size = size1; - dsi.vc[1].fifo_size = size2; - dsi.vc[2].fifo_size = size3; - dsi.vc[3].fifo_size = size4; + p_dsi->vc[0].fifo_size = size1; + p_dsi->vc[1].fifo_size = size2; + p_dsi->vc[2].fifo_size = size3; + p_dsi->vc[3].fifo_size = size4; for (i = 0; i < 4; i++) { u8 v; - int size = dsi.vc[i].fifo_size; + int size = p_dsi->vc[i].fifo_size; if (add + size > 4) { DSSERR("Illegal FIFO configuration\n"); @@ -1646,24 +1765,26 @@ static void dsi_config_tx_fifo(enum fifo_size size1, enum fifo_size size2, add += size; } - dsi_write_reg(DSI_TX_FIFO_VC_SIZE, r); + dsi_write_reg(ix, DSI_TX_FIFO_VC_SIZE, r); } -static void dsi_config_rx_fifo(enum fifo_size size1, enum fifo_size size2, - enum fifo_size size3, enum fifo_size size4) +static void dsi_config_rx_fifo(enum omap_dsi_index ix, + enum fifo_size size1, enum fifo_size size2, + enum fifo_size size3, enum fifo_size size4) { u32 r = 0; int add = 0; int i; + struct dsi_struct *p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; - dsi.vc[0].fifo_size = size1; - dsi.vc[1].fifo_size = size2; - dsi.vc[2].fifo_size = size3; - dsi.vc[3].fifo_size = size4; + p_dsi->vc[0].fifo_size = size1; + p_dsi->vc[1].fifo_size = size2; + p_dsi->vc[2].fifo_size = size3; + p_dsi->vc[3].fifo_size = size4; for (i = 0; i < 4; i++) { u8 v; - int size = dsi.vc[i].fifo_size; + int size = p_dsi->vc[i].fifo_size; if (add + size > 4) { DSSERR("Illegal FIFO configuration\n"); @@ -1676,18 +1797,18 @@ static void dsi_config_rx_fifo(enum fifo_size size1, enum fifo_size size2, add += size; } - dsi_write_reg(DSI_RX_FIFO_VC_SIZE, r); + dsi_write_reg(ix, DSI_RX_FIFO_VC_SIZE, r); } -static int dsi_force_tx_stop_mode_io(void) +static int dsi_force_tx_stop_mode_io(enum omap_dsi_index ix) { u32 r; - r = dsi_read_reg(DSI_TIMING1); + r = dsi_read_reg(ix, DSI_TIMING1); r = FLD_MOD(r, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */ - dsi_write_reg(DSI_TIMING1, r); + dsi_write_reg(ix, DSI_TIMING1, r); - if (wait_for_bit_change(DSI_TIMING1, 15, 0) != 0) { + if (wait_for_bit_change(ix, DSI_TIMING1, 15, 0) != 0) { DSSERR("TX_STOP bit not going down\n"); return -EIO; } @@ -1695,16 +1816,18 @@ static int dsi_force_tx_stop_mode_io(void) return 0; } -static int dsi_vc_enable(int channel, bool enable) +static int dsi_vc_enable(enum omap_dsi_index ix, + int channel, bool enable) { DSSDBG("dsi_vc_enable channel %d, enable %d\n", channel, enable); enable = enable ? 1 : 0; - REG_FLD_MOD(DSI_VC_CTRL(channel), enable, 0, 0); + REG_FLD_MOD(ix, DSI_VC_CTRL(channel), enable, 0, 0); - if (wait_for_bit_change(DSI_VC_CTRL(channel), 0, enable) != enable) { + if (wait_for_bit_change(ix, DSI_VC_CTRL(channel), + 0, enable) != enable) { DSSERR("Failed to set dsi_vc_enable to %d\n", enable); return -EIO; } @@ -1712,13 +1835,14 @@ static int dsi_vc_enable(int channel, bool enable) return 0; } -static void dsi_vc_initial_config(int channel) +static void dsi_vc_initial_config(enum omap_dsi_index ix, int channel) { u32 r; + struct dsi_struct *p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; DSSDBGF("%d", channel); - r = dsi_read_reg(DSI_VC_CTRL(channel)); + r = dsi_read_reg(ix, DSI_VC_CTRL(channel)); if (FLD_GET(r, 15, 15)) /* VC_BUSY */ DSSERR("VC(%d) busy when trying to configure it!\n", @@ -1731,77 +1855,112 @@ static void dsi_vc_initial_config(int channel) r = FLD_MOD(r, 1, 7, 7); /* CS_TX_EN */ r = FLD_MOD(r, 1, 8, 8); /* ECC_TX_EN */ r = FLD_MOD(r, 0, 9, 9); /* MODE_SPEED, high speed on/off */ - + if (cpu_is_omap44xx()) { + r = FLD_MOD(r, 3, 11, 10); + r = FLD_MOD(r, 3, 18, 17); + } r = FLD_MOD(r, 4, 29, 27); /* DMA_RX_REQ_NB = no dma */ r = FLD_MOD(r, 4, 23, 21); /* DMA_TX_REQ_NB = no dma */ - dsi_write_reg(DSI_VC_CTRL(channel), r); + dsi_write_reg(ix, DSI_VC_CTRL(channel), r); - dsi.vc[channel].mode = DSI_VC_MODE_L4; + p_dsi->vc[channel].mode = DSI_VC_MODE_L4; } -static void dsi_vc_config_l4(int channel) +static void dsi_vc_initial_config_vp(enum omap_dsi_index ix, int channel) { - if (dsi.vc[channel].mode == DSI_VC_MODE_L4) + u32 r; + struct dsi_struct *p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; + + DSSDBGF("%d", channel); + + r = dsi_read_reg(ix, DSI_VC_CTRL(channel)); + r = FLD_MOD(r, 1, 1, 1); /* SOURCE, 1 = video port */ + r = FLD_MOD(r, 0, 2, 2); /* BTA_SHORT_EN */ + r = FLD_MOD(r, 0, 3, 3); /* BTA_LONG_EN */ + r = FLD_MOD(r, 0, 4, 4); /* MODE, 0 = command */ + r = FLD_MOD(r, 1, 7, 7); /* CS_TX_EN */ + r = FLD_MOD(r, 1, 8, 8); /* ECC_TX_EN */ + r = FLD_MOD(r, 1, 9, 9); /* MODE_SPEED, high speed on/off */ + r = FLD_MOD(r, 1, 12, 12); /*RGB565_ORDER*/ + r = FLD_MOD(r, 4, 29, 27); /* DMA_RX_REQ_NB = no dma */ + r = FLD_MOD(r, 4, 23, 21); /* DMA_TX_REQ_NB = no dma */ + r = FLD_MOD(r, 1, 30, 30); /* DCS_CMD_ENABLE*/ + r = FLD_MOD(r, 0, 31, 31); /* DCS_CMD_CODE*/ + dsi_write_reg(ix, DSI_VC_CTRL(channel), r); + + p_dsi->vc[channel].mode = DSI_VC_MODE_VP; +} + +static void dsi_vc_config_l4(enum omap_dsi_index ix, int channel) +{ + struct dsi_struct *p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; + + if (p_dsi->vc[channel].mode == DSI_VC_MODE_L4) return; DSSDBGF("%d", channel); - dsi_vc_enable(channel, 0); + dsi_vc_enable(ix, channel, 0); - if (REG_GET(DSI_VC_CTRL(channel), 15, 15)) /* VC_BUSY */ + if (REG_GET(ix, DSI_VC_CTRL(channel), 15, 15)) /* VC_BUSY */ DSSERR("vc(%d) busy when trying to config for L4\n", channel); - REG_FLD_MOD(DSI_VC_CTRL(channel), 0, 1, 1); /* SOURCE, 0 = L4 */ + REG_FLD_MOD(ix, DSI_VC_CTRL(channel), 0, 1, 1); /* SOURCE, 0 = L4 */ - dsi_vc_enable(channel, 1); + dsi_vc_enable(ix, channel, 1); - dsi.vc[channel].mode = DSI_VC_MODE_L4; + p_dsi->vc[channel].mode = DSI_VC_MODE_L4; } -static void dsi_vc_config_vp(int channel) +static void dsi_vc_config_vp(enum omap_dsi_index ix, int channel) { - if (dsi.vc[channel].mode == DSI_VC_MODE_VP) + struct dsi_struct *p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; + + if (p_dsi->vc[channel].mode == DSI_VC_MODE_VP) return; DSSDBGF("%d", channel); - dsi_vc_enable(channel, 0); + dsi_vc_enable(ix, channel, 0); - if (REG_GET(DSI_VC_CTRL(channel), 15, 15)) /* VC_BUSY */ + if (REG_GET(ix, DSI_VC_CTRL(channel), 15, 15)) /* VC_BUSY */ DSSERR("vc(%d) busy when trying to config for VP\n", channel); - REG_FLD_MOD(DSI_VC_CTRL(channel), 1, 1, 1); /* SOURCE, 1 = video port */ + REG_FLD_MOD(ix, DSI_VC_CTRL(channel), + 1, 1, 1); /* SOURCE, 1 = video port */ - dsi_vc_enable(channel, 1); + dsi_vc_enable(ix, channel, 1); - dsi.vc[channel].mode = DSI_VC_MODE_VP; + p_dsi->vc[channel].mode = DSI_VC_MODE_VP; } -void omapdss_dsi_vc_enable_hs(int channel, bool enable) +void omapdss_dsi_vc_enable_hs(enum omap_dsi_index ix, + int channel, bool enable) { DSSDBG("dsi_vc_enable_hs(%d, %d)\n", channel, enable); - WARN_ON(!dsi_bus_is_locked()); + WARN_ON(!dsi_bus_is_locked(ix)); - dsi_vc_enable(channel, 0); - dsi_if_enable(0); + dsi_vc_enable(ix, channel, 0); + dsi_if_enable(ix, 0); - REG_FLD_MOD(DSI_VC_CTRL(channel), enable, 9, 9); + REG_FLD_MOD(ix, DSI_VC_CTRL(channel), enable, 9, 9); - dsi_vc_enable(channel, 1); - dsi_if_enable(1); + dsi_vc_enable(ix, channel, 1); + dsi_if_enable(ix, 1); - dsi_force_tx_stop_mode_io(); + dsi_force_tx_stop_mode_io(ix); } EXPORT_SYMBOL(omapdss_dsi_vc_enable_hs); -static void dsi_vc_flush_long_data(int channel) +static void dsi_vc_flush_long_data(enum omap_dsi_index ix, + int channel) { - while (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { + while (REG_GET(ix, DSI_VC_CTRL(channel), 20, 20)) { u32 val; - val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel)); + val = dsi_read_reg(ix, DSI_VC_SHORT_PACKET_HEADER(channel)); DSSDBG("\t\tb1 %#02x b2 %#02x b3 %#02x b4 %#02x\n", (val >> 0) & 0xff, (val >> 8) & 0xff, @@ -1847,18 +2006,20 @@ static void dsi_show_rx_ack_with_err(u16 err) DSSERR("\t\tDSI Protocol Violation\n"); } -static u16 dsi_vc_flush_receive_data(int channel) +static u16 dsi_vc_flush_receive_data(enum omap_dsi_index ix, + int channel) { /* RX_FIFO_NOT_EMPTY */ - while (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { + while (REG_GET(ix, DSI_VC_CTRL(channel), 20, 20)) { u32 val; u8 dt; - val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel)); + val = dsi_read_reg(ix, DSI_VC_SHORT_PACKET_HEADER(channel)); DSSDBG("\trawval %#08x\n", val); dt = FLD_GET(val, 5, 0); if (dt == DSI_DT_RX_ACK_WITH_ERR) { u16 err = FLD_GET(val, 23, 8); - dsi_show_rx_ack_with_err(err); + if (!cpu_is_omap44xx()) + dsi_show_rx_ack_with_err(err); } else if (dt == DSI_DT_RX_SHORT_READ_1) { DSSDBG("\tDCS short response, 1 byte: %#x\n", FLD_GET(val, 23, 8)); @@ -1868,7 +2029,7 @@ static u16 dsi_vc_flush_receive_data(int channel) } else if (dt == DSI_DT_RX_DCS_LONG_READ) { DSSDBG("\tDCS long response, len %d\n", FLD_GET(val, 23, 8)); - dsi_vc_flush_long_data(channel); + dsi_vc_flush_long_data(ix, channel); } else { DSSERR("\tunknown datatype 0x%02x\n", dt); } @@ -1876,74 +2037,84 @@ static u16 dsi_vc_flush_receive_data(int channel) return 0; } -static int dsi_vc_send_bta(int channel) +static int dsi_vc_send_bta(enum omap_dsi_index ix, int channel) { - if (dsi.debug_write || dsi.debug_read) + struct dsi_struct *p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; + + if (p_dsi->debug_write || p_dsi->debug_read) DSSDBG("dsi_vc_send_bta %d\n", channel); - WARN_ON(!dsi_bus_is_locked()); + WARN_ON(!dsi_bus_is_locked(ix)); - if (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { /* RX_FIFO_NOT_EMPTY */ - DSSERR("rx fifo not empty when sending BTA, dumping data:\n"); - dsi_vc_flush_receive_data(channel); + /* RX_FIFO_NOT_EMPTY */ + if (REG_GET(ix, DSI_VC_CTRL(channel), 20, 20)) { + if (!cpu_is_omap44xx()) + DSSERR("rx fifo not empty when sending BTA, dumping data:\n"); + dsi_vc_flush_receive_data(ix, channel); } - REG_FLD_MOD(DSI_VC_CTRL(channel), 1, 6, 6); /* BTA_EN */ + REG_FLD_MOD(ix, DSI_VC_CTRL(channel), 1, 6, 6); /* BTA_EN */ return 0; } -int dsi_vc_send_bta_sync(int channel) +int dsi_vc_send_bta_sync(enum omap_dsi_index ix, + int channel) { int r = 0; u32 err; + struct dsi_struct *p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; - INIT_COMPLETION(dsi.bta_completion); + INIT_COMPLETION(p_dsi->bta_completion); - dsi_vc_enable_bta_irq(channel); + dsi_vc_enable_bta_irq(ix, channel); - r = dsi_vc_send_bta(channel); + r = dsi_vc_send_bta(ix, channel); if (r) goto err; - if (wait_for_completion_timeout(&dsi.bta_completion, + if (wait_for_completion_timeout(&p_dsi->bta_completion, msecs_to_jiffies(500)) == 0) { DSSERR("Failed to receive BTA\n"); r = -EIO; goto err; } - err = dsi_get_errors(); + err = dsi_get_errors(ix); if (err) { DSSERR("Error while sending BTA: %x\n", err); r = -EIO; goto err; } err: - dsi_vc_disable_bta_irq(channel); + dsi_vc_disable_bta_irq(ix, channel); return r; } EXPORT_SYMBOL(dsi_vc_send_bta_sync); -static inline void dsi_vc_write_long_header(int channel, u8 data_type, +static inline void dsi_vc_write_long_header(enum omap_dsi_index ix, + int channel, u8 data_type, u16 len, u8 ecc) { u32 val; u8 data_id; + struct dsi_struct *p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; + WARN_ON(!dsi_bus_is_locked(ix)); - WARN_ON(!dsi_bus_is_locked()); - - data_id = data_type | channel << 6; + if (cpu_is_omap44xx()) + data_id = data_type | p_dsi->vc[channel].dest_per << 6; + else + data_id = data_type | channel << 6; val = FLD_VAL(data_id, 7, 0) | FLD_VAL(len, 23, 8) | FLD_VAL(ecc, 31, 24); - dsi_write_reg(DSI_VC_LONG_PACKET_HEADER(channel), val); + dsi_write_reg(ix, DSI_VC_LONG_PACKET_HEADER(channel), val); } -static inline void dsi_vc_write_long_payload(int channel, - u8 b1, u8 b2, u8 b3, u8 b4) +static inline void dsi_vc_write_long_payload(enum omap_dsi_index ix, + int channel, u8 b1, u8 b2, u8 b3, u8 b4) { u32 val; @@ -1952,10 +2123,11 @@ static inline void dsi_vc_write_long_payload(int channel, /* DSSDBG("\twriting %02x, %02x, %02x, %02x (%#010x)\n", b1, b2, b3, b4, val); */ - dsi_write_reg(DSI_VC_LONG_PACKET_PAYLOAD(channel), val); + dsi_write_reg(ix, DSI_VC_LONG_PACKET_PAYLOAD(channel), val); } -static int dsi_vc_send_long(int channel, u8 data_type, u8 *data, u16 len, +static int dsi_vc_send_long(enum omap_dsi_index ix, int channel, + u8 data_type, u8 *data, u16 len, u8 ecc) { /*u32 val; */ @@ -1963,23 +2135,24 @@ static int dsi_vc_send_long(int channel, u8 data_type, u8 *data, u16 len, u8 *p; int r = 0; u8 b1, b2, b3, b4; + struct dsi_struct *p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; - if (dsi.debug_write) + if (p_dsi->debug_write) DSSDBG("dsi_vc_send_long, %d bytes\n", len); /* len + header */ - if (dsi.vc[channel].fifo_size * 32 * 4 < len + 4) { + if (p_dsi->vc[channel].fifo_size * 32 * 4 < len + 4) { DSSERR("unable to send long packet: packet too long.\n"); return -EINVAL; } - dsi_vc_config_l4(channel); + dsi_vc_config_l4(ix, channel); - dsi_vc_write_long_header(channel, data_type, len, ecc); + dsi_vc_write_long_header(ix, channel, data_type, len, ecc); p = data; for (i = 0; i < len >> 2; i++) { - if (dsi.debug_write) + if (p_dsi->debug_write) DSSDBG("\tsending full packet %d\n", i); b1 = *p++; @@ -1987,14 +2160,14 @@ static int dsi_vc_send_long(int channel, u8 data_type, u8 *data, u16 len, b3 = *p++; b4 = *p++; - dsi_vc_write_long_payload(channel, b1, b2, b3, b4); + dsi_vc_write_long_payload(ix, channel, b1, b2, b3, b4); } i = len % 4; if (i) { b1 = 0; b2 = 0; b3 = 0; - if (dsi.debug_write) + if (p_dsi->debug_write) DSSDBG("\tsending remainder bytes %d\n", i); switch (i) { @@ -2012,62 +2185,68 @@ static int dsi_vc_send_long(int channel, u8 data_type, u8 *data, u16 len, break; } - dsi_vc_write_long_payload(channel, b1, b2, b3, 0); + dsi_vc_write_long_payload(ix, channel, b1, b2, b3, 0); } return r; } -static int dsi_vc_send_short(int channel, u8 data_type, u16 data, u8 ecc) +static int dsi_vc_send_short(enum omap_dsi_index ix, + int channel, u8 data_type, u16 data, u8 ecc) { u32 r; u8 data_id; + struct dsi_struct *p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; - WARN_ON(!dsi_bus_is_locked()); + WARN_ON(!dsi_bus_is_locked(ix)); - if (dsi.debug_write) + if (p_dsi->debug_write) DSSDBG("dsi_vc_send_short(ch%d, dt %#x, b1 %#x, b2 %#x)\n", channel, data_type, data & 0xff, (data >> 8) & 0xff); - dsi_vc_config_l4(channel); + dsi_vc_config_l4(ix, channel); - if (FLD_GET(dsi_read_reg(DSI_VC_CTRL(channel)), 16, 16)) { + if (FLD_GET(dsi_read_reg(ix, DSI_VC_CTRL(channel)), 16, 16)) { DSSERR("ERROR FIFO FULL, aborting transfer\n"); return -EINVAL; } - data_id = data_type | channel << 6; + if (cpu_is_omap44xx()) + data_id = data_type | p_dsi->vc[channel].dest_per << 6; + else + data_id = data_type | channel << 6; r = (data_id << 0) | (data << 8) | (ecc << 24); - dsi_write_reg(DSI_VC_SHORT_PACKET_HEADER(channel), r); + dsi_write_reg(ix, DSI_VC_SHORT_PACKET_HEADER(channel), r); return 0; } -int dsi_vc_send_null(int channel) +int dsi_vc_send_null(enum omap_dsi_index ix, int channel) { u8 nullpkg[] = {0, 0, 0, 0}; - return dsi_vc_send_long(channel, DSI_DT_NULL_PACKET, nullpkg, 4, 0); + return dsi_vc_send_long(ix, channel, DSI_DT_NULL_PACKET, nullpkg, 4, 0); } EXPORT_SYMBOL(dsi_vc_send_null); -int dsi_vc_dcs_write_nosync(int channel, u8 *data, int len) +int dsi_vc_dcs_write_nosync(enum omap_dsi_index ix, int channel, + u8 *data, int len) { int r; BUG_ON(len == 0); if (len == 1) { - r = dsi_vc_send_short(channel, DSI_DT_DCS_SHORT_WRITE_0, + r = dsi_vc_send_short(ix, channel, DSI_DT_DCS_SHORT_WRITE_0, data[0], 0); } else if (len == 2) { - r = dsi_vc_send_short(channel, DSI_DT_DCS_SHORT_WRITE_1, + r = dsi_vc_send_short(ix, channel, DSI_DT_DCS_SHORT_WRITE_1, data[0] | (data[1] << 8), 0); } else { /* 0x39 = DCS Long Write */ - r = dsi_vc_send_long(channel, DSI_DT_DCS_LONG_WRITE, + r = dsi_vc_send_long(ix, channel, DSI_DT_DCS_LONG_WRITE, data, len, 0); } @@ -2075,15 +2254,16 @@ int dsi_vc_dcs_write_nosync(int channel, u8 *data, int len) } EXPORT_SYMBOL(dsi_vc_dcs_write_nosync); -int dsi_vc_dcs_write(int channel, u8 *data, int len) +int dsi_vc_dcs_write(enum omap_dsi_index ix, int channel, + u8 *data, int len) { int r; - r = dsi_vc_dcs_write_nosync(channel, data, len); + r = dsi_vc_dcs_write_nosync(ix, channel, data, len); if (r) goto err; - r = dsi_vc_send_bta_sync(channel); + r = dsi_vc_send_bta_sync(ix, channel); if (r) goto err; @@ -2095,47 +2275,51 @@ err: } EXPORT_SYMBOL(dsi_vc_dcs_write); -int dsi_vc_dcs_write_0(int channel, u8 dcs_cmd) +int dsi_vc_dcs_write_0(enum omap_dsi_index ix, int channel, + u8 dcs_cmd) { - return dsi_vc_dcs_write(channel, &dcs_cmd, 1); + return dsi_vc_dcs_write(ix, channel, &dcs_cmd, 1); } EXPORT_SYMBOL(dsi_vc_dcs_write_0); -int dsi_vc_dcs_write_1(int channel, u8 dcs_cmd, u8 param) +int dsi_vc_dcs_write_1(enum omap_dsi_index ix, int channel, + u8 dcs_cmd, u8 param) { u8 buf[2]; buf[0] = dcs_cmd; buf[1] = param; - return dsi_vc_dcs_write(channel, buf, 2); + return dsi_vc_dcs_write(ix, channel, buf, 2); } EXPORT_SYMBOL(dsi_vc_dcs_write_1); -int dsi_vc_dcs_read(int channel, u8 dcs_cmd, u8 *buf, int buflen) +int dsi_vc_dcs_read(enum omap_dsi_index ix, int channel, + u8 dcs_cmd, u8 *buf, int buflen) { u32 val; u8 dt; int r; + struct dsi_struct *p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; - if (dsi.debug_read) + if (p_dsi->debug_read) DSSDBG("dsi_vc_dcs_read(ch%d, dcs_cmd %x)\n", channel, dcs_cmd); - r = dsi_vc_send_short(channel, DSI_DT_DCS_READ, dcs_cmd, 0); + r = dsi_vc_send_short(ix, channel, DSI_DT_DCS_READ, dcs_cmd, 0); if (r) goto err; - r = dsi_vc_send_bta_sync(channel); + r = dsi_vc_send_bta_sync(ix, channel); if (r) goto err; /* RX_FIFO_NOT_EMPTY */ - if (REG_GET(DSI_VC_CTRL(channel), 20, 20) == 0) { + if (REG_GET(ix, DSI_VC_CTRL(channel), 20, 20) == 0) { DSSERR("RX fifo empty when trying to read.\n"); r = -EIO; goto err; } - val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel)); - if (dsi.debug_read) + val = dsi_read_reg(ix, DSI_VC_SHORT_PACKET_HEADER(channel)); + if (p_dsi->debug_read) DSSDBG("\theader: %08x\n", val); dt = FLD_GET(val, 5, 0); if (dt == DSI_DT_RX_ACK_WITH_ERR) { @@ -2146,7 +2330,7 @@ int dsi_vc_dcs_read(int channel, u8 dcs_cmd, u8 *buf, int buflen) } else if (dt == DSI_DT_RX_SHORT_READ_1) { u8 data = FLD_GET(val, 15, 8); - if (dsi.debug_read) + if (p_dsi->debug_read) DSSDBG("\tDCS short response, 1 byte: %02x\n", data); if (buflen < 1) { @@ -2159,7 +2343,7 @@ int dsi_vc_dcs_read(int channel, u8 dcs_cmd, u8 *buf, int buflen) return 1; } else if (dt == DSI_DT_RX_SHORT_READ_2) { u16 data = FLD_GET(val, 23, 8); - if (dsi.debug_read) + if (p_dsi->debug_read) DSSDBG("\tDCS short response, 2 byte: %04x\n", data); if (buflen < 2) { @@ -2174,7 +2358,7 @@ int dsi_vc_dcs_read(int channel, u8 dcs_cmd, u8 *buf, int buflen) } else if (dt == DSI_DT_RX_DCS_LONG_READ) { int w; int len = FLD_GET(val, 23, 8); - if (dsi.debug_read) + if (p_dsi->debug_read) DSSDBG("\tDCS long response, len %d\n", len); if (len > buflen) { @@ -2185,8 +2369,8 @@ int dsi_vc_dcs_read(int channel, u8 dcs_cmd, u8 *buf, int buflen) /* two byte checksum ends the packet, not included in len */ for (w = 0; w < len + 2;) { int b; - val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel)); - if (dsi.debug_read) + val = dsi_read_reg(ix, DSI_VC_SHORT_PACKET_HEADER(channel)); + if (p_dsi->debug_read) DSSDBG("\t\t%02x %02x %02x %02x\n", (val >> 0) & 0xff, (val >> 8) & 0xff, @@ -2217,11 +2401,12 @@ err: } EXPORT_SYMBOL(dsi_vc_dcs_read); -int dsi_vc_dcs_read_1(int channel, u8 dcs_cmd, u8 *data) +int dsi_vc_dcs_read_1(enum omap_dsi_index ix, int channel, + u8 dcs_cmd, u8 *data) { int r; - r = dsi_vc_dcs_read(channel, dcs_cmd, data, 1); + r = dsi_vc_dcs_read(ix, channel, dcs_cmd, data, 1); if (r < 0) return r; @@ -2233,11 +2418,12 @@ int dsi_vc_dcs_read_1(int channel, u8 dcs_cmd, u8 *data) } EXPORT_SYMBOL(dsi_vc_dcs_read_1); -int dsi_vc_dcs_read_2(int channel, u8 dcs_cmd, u16 *data) +int dsi_vc_dcs_read_2(enum omap_dsi_index ix, int channel, + u8 dcs_cmd, u16 *data) { int r; - r = dsi_vc_dcs_read(channel, dcs_cmd, (u8 *)data, 2); + r = dsi_vc_dcs_read(ix, channel, dcs_cmd, (u8 *)data, 2); if (r < 0) return r; @@ -2249,22 +2435,24 @@ int dsi_vc_dcs_read_2(int channel, u8 dcs_cmd, u16 *data) } EXPORT_SYMBOL(dsi_vc_dcs_read_2); -int dsi_vc_set_max_rx_packet_size(int channel, u16 len) +int dsi_vc_set_max_rx_packet_size(enum omap_dsi_index ix, + int channel, u16 len) { int r; - r = dsi_vc_send_short(channel, DSI_DT_SET_MAX_RET_PKG_SIZE, + r = dsi_vc_send_short(ix, channel, DSI_DT_SET_MAX_RET_PKG_SIZE, len, 0); if (r) return r; - r = dsi_vc_send_bta_sync(channel); + r = dsi_vc_send_bta_sync(ix, channel); return r; } EXPORT_SYMBOL(dsi_vc_set_max_rx_packet_size); -static void dsi_set_lp_rx_timeout(unsigned long ns) +static void dsi_set_lp_rx_timeout(enum omap_dsi_index ix, + unsigned long ns) { u32 r; unsigned x4, x16; @@ -2273,7 +2461,7 @@ static void dsi_set_lp_rx_timeout(unsigned long ns) /* ticks in DSI_FCK */ - fck = dsi_fclk_rate(); + fck = dsi_fclk_rate(ix); ticks = (fck / 1000 / 1000) * ns / 1000; x4 = 0; x16 = 0; @@ -2303,12 +2491,12 @@ static void dsi_set_lp_rx_timeout(unsigned long ns) x16 = 1; } - r = dsi_read_reg(DSI_TIMING2); + r = dsi_read_reg(ix, DSI_TIMING2); r = FLD_MOD(r, 1, 15, 15); /* LP_RX_TO */ r = FLD_MOD(r, x16, 14, 14); /* LP_RX_TO_X16 */ r = FLD_MOD(r, x4, 13, 13); /* LP_RX_TO_X4 */ r = FLD_MOD(r, ticks, 12, 0); /* LP_RX_COUNTER */ - dsi_write_reg(DSI_TIMING2, r); + dsi_write_reg(ix, DSI_TIMING2, r); DSSDBG("LP_RX_TO %lu ns (%#lx ticks%s%s)\n", (ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1) * 1000) / @@ -2316,7 +2504,8 @@ static void dsi_set_lp_rx_timeout(unsigned long ns) ticks, x4 ? " x4" : "", x16 ? " x16" : ""); } -static void dsi_set_ta_timeout(unsigned long ns) +static void dsi_set_ta_timeout(enum omap_dsi_index ix, + unsigned long ns) { u32 r; unsigned x8, x16; @@ -2324,7 +2513,7 @@ static void dsi_set_ta_timeout(unsigned long ns) unsigned long ticks; /* ticks in DSI_FCK */ - fck = dsi_fclk_rate(); + fck = dsi_fclk_rate(ix); ticks = (fck / 1000 / 1000) * ns / 1000; x8 = 0; x16 = 0; @@ -2354,12 +2543,12 @@ static void dsi_set_ta_timeout(unsigned long ns) x16 = 1; } - r = dsi_read_reg(DSI_TIMING1); + r = dsi_read_reg(ix, DSI_TIMING1); r = FLD_MOD(r, 1, 31, 31); /* TA_TO */ r = FLD_MOD(r, x16, 30, 30); /* TA_TO_X16 */ r = FLD_MOD(r, x8, 29, 29); /* TA_TO_X8 */ r = FLD_MOD(r, ticks, 28, 16); /* TA_TO_COUNTER */ - dsi_write_reg(DSI_TIMING1, r); + dsi_write_reg(ix, DSI_TIMING1, r); DSSDBG("TA_TO %lu ns (%#lx ticks%s%s)\n", (ticks * (x16 ? 16 : 1) * (x8 ? 8 : 1) * 1000) / @@ -2367,7 +2556,8 @@ static void dsi_set_ta_timeout(unsigned long ns) ticks, x8 ? " x8" : "", x16 ? " x16" : ""); } -static void dsi_set_stop_state_counter(unsigned long ns) +static void dsi_set_stop_state_counter(enum omap_dsi_index ix, + unsigned long ns) { u32 r; unsigned x4, x16; @@ -2376,7 +2566,7 @@ static void dsi_set_stop_state_counter(unsigned long ns) /* ticks in DSI_FCK */ - fck = dsi_fclk_rate(); + fck = dsi_fclk_rate(ix); ticks = (fck / 1000 / 1000) * ns / 1000; x4 = 0; x16 = 0; @@ -2407,12 +2597,12 @@ static void dsi_set_stop_state_counter(unsigned long ns) x16 = 1; } - r = dsi_read_reg(DSI_TIMING1); + r = dsi_read_reg(ix, DSI_TIMING1); r = FLD_MOD(r, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */ r = FLD_MOD(r, x16, 14, 14); /* STOP_STATE_X16_IO */ r = FLD_MOD(r, x4, 13, 13); /* STOP_STATE_X4_IO */ r = FLD_MOD(r, ticks, 12, 0); /* STOP_STATE_COUNTER_IO */ - dsi_write_reg(DSI_TIMING1, r); + dsi_write_reg(ix, DSI_TIMING1, r); DSSDBG("STOP_STATE_COUNTER %lu ns (%#lx ticks%s%s)\n", (ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1) * 1000) / @@ -2420,7 +2610,8 @@ static void dsi_set_stop_state_counter(unsigned long ns) ticks, x4 ? " x4" : "", x16 ? " x16" : ""); } -static void dsi_set_hs_tx_timeout(unsigned long ns) +static void dsi_set_hs_tx_timeout(enum omap_dsi_index ix, + unsigned long ns) { u32 r; unsigned x4, x16; @@ -2429,7 +2620,7 @@ static void dsi_set_hs_tx_timeout(unsigned long ns) /* ticks in TxByteClkHS */ - fck = dsi_get_txbyteclkhs(); + fck = dsi_get_txbyteclkhs(ix); ticks = (fck / 1000 / 1000) * ns / 1000; x4 = 0; x16 = 0; @@ -2459,12 +2650,12 @@ static void dsi_set_hs_tx_timeout(unsigned long ns) x16 = 1; } - r = dsi_read_reg(DSI_TIMING2); + r = dsi_read_reg(ix, DSI_TIMING2); r = FLD_MOD(r, 1, 31, 31); /* HS_TX_TO */ r = FLD_MOD(r, x16, 30, 30); /* HS_TX_TO_X16 */ r = FLD_MOD(r, x4, 29, 29); /* HS_TX_TO_X8 (4 really) */ r = FLD_MOD(r, ticks, 28, 16); /* HS_TX_TO_COUNTER */ - dsi_write_reg(DSI_TIMING2, r); + dsi_write_reg(ix, DSI_TIMING2, r); DSSDBG("HS_TX_TO %lu ns (%#lx ticks%s%s)\n", (ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1) * 1000) / @@ -2475,22 +2666,27 @@ static int dsi_proto_config(struct omap_dss_device *dssdev) { u32 r; int buswidth = 0; + struct dsi_struct *p_dsi; + enum omap_dsi_index ix; + + ix = (dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? DSI1 : DSI2; + p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; - dsi_config_tx_fifo(DSI_FIFO_SIZE_32, + dsi_config_tx_fifo(ix, DSI_FIFO_SIZE_32, DSI_FIFO_SIZE_32, DSI_FIFO_SIZE_32, DSI_FIFO_SIZE_32); - dsi_config_rx_fifo(DSI_FIFO_SIZE_32, + dsi_config_rx_fifo(ix, DSI_FIFO_SIZE_32, DSI_FIFO_SIZE_32, DSI_FIFO_SIZE_32, DSI_FIFO_SIZE_32); /* XXX what values for the timeouts? */ - dsi_set_stop_state_counter(1000); - dsi_set_ta_timeout(6400000); - dsi_set_lp_rx_timeout(48000); - dsi_set_hs_tx_timeout(1000000); + dsi_set_stop_state_counter(ix, 1000); + dsi_set_ta_timeout(ix, 6400000); + dsi_set_lp_rx_timeout(ix, 48000); + dsi_set_hs_tx_timeout(ix, 1000000); switch (dssdev->ctrl.pixel_size) { case 16: @@ -2506,26 +2702,45 @@ static int dsi_proto_config(struct omap_dss_device *dssdev) BUG(); } - r = dsi_read_reg(DSI_CTRL); - r = FLD_MOD(r, 1, 1, 1); /* CS_RX_EN */ - r = FLD_MOD(r, 1, 2, 2); /* ECC_RX_EN */ + r = dsi_read_reg(ix, DSI_CTRL); + r = FLD_MOD(r, (cpu_is_omap44xx()) ? 0 : 1, + 1, 1); /* CS_RX_EN */ + r = FLD_MOD(r, (cpu_is_omap44xx()) ? 0 : 1, + 2, 2); /* ECC_RX_EN */ r = FLD_MOD(r, 1, 3, 3); /* TX_FIFO_ARBITRATION */ r = FLD_MOD(r, 1, 4, 4); /* VP_CLK_RATIO, always 1, see errata*/ r = FLD_MOD(r, buswidth, 7, 6); /* VP_DATA_BUS_WIDTH */ r = FLD_MOD(r, 0, 8, 8); /* VP_CLK_POL */ + r = FLD_MOD(r, (cpu_is_omap44xx()) ? 1 : 0, + 9, 9); /*VP_DE_POL */ + r = FLD_MOD(r, (cpu_is_omap44xx()) ? 1 : 0, + 11, 11); /*VP_VSYNC_POL */ r = FLD_MOD(r, 2, 13, 12); /* LINE_BUFFER, 2 lines */ r = FLD_MOD(r, 1, 14, 14); /* TRIGGER_RESET_MODE */ - r = FLD_MOD(r, 1, 19, 19); /* EOT_ENABLE */ - r = FLD_MOD(r, 1, 24, 24); /* DCS_CMD_ENABLE */ - r = FLD_MOD(r, 0, 25, 25); /* DCS_CMD_CODE, 1=start, 0=continue */ - - dsi_write_reg(DSI_CTRL, r); - - dsi_vc_initial_config(0); - dsi_vc_initial_config(1); - dsi_vc_initial_config(2); - dsi_vc_initial_config(3); + r = FLD_MOD(r, (cpu_is_omap44xx()) ? 0 : 1, + 19, 19); /* EOT_ENABLE */ + if (cpu_is_omap34xx()) { + r = FLD_MOD(r, 1, 24, 24); /* DCS_CMD_ENABLE */ + r = FLD_MOD(r, 0, 25, 25); /* DCS_CMD_CODE */ + } + dsi_write_reg(ix, DSI_CTRL, r); + dsi_vc_initial_config(ix, 0); + if (cpu_is_omap44xx()) + dsi_vc_initial_config_vp(ix, 1); + else + dsi_vc_initial_config(ix, 1); + dsi_vc_initial_config(ix, 2); + dsi_vc_initial_config(ix, 3); + + /* In Present OMAP4 configuration, 2 VC's send data + * to the same peripheral */ + if (cpu_is_omap44xx()) { + p_dsi->vc[0].dest_per = 0; + p_dsi->vc[1].dest_per = 0; + p_dsi->vc[2].dest_per = 0; + p_dsi->vc[3].dest_per = 0; + } return 0; } @@ -2539,26 +2754,29 @@ static void dsi_proto_timings(struct omap_dss_device *dssdev) unsigned enter_hs_mode_lat, exit_hs_mode_lat; unsigned ths_eot; u32 r; + enum omap_dsi_index ix; + + ix = (dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? DSI1 : DSI2; - r = dsi_read_reg(DSI_DSIPHY_CFG0); + r = dsi_read_reg(ix, DSI_DSIPHY_CFG0); ths_prepare = FLD_GET(r, 31, 24); ths_prepare_ths_zero = FLD_GET(r, 23, 16); ths_zero = ths_prepare_ths_zero - ths_prepare; ths_trail = FLD_GET(r, 15, 8); ths_exit = FLD_GET(r, 7, 0); - r = dsi_read_reg(DSI_DSIPHY_CFG1); + r = dsi_read_reg(ix, DSI_DSIPHY_CFG1); tlpx = FLD_GET(r, 22, 16) * 2; tclk_trail = FLD_GET(r, 15, 8); tclk_zero = FLD_GET(r, 7, 0); - r = dsi_read_reg(DSI_DSIPHY_CFG2); + r = dsi_read_reg(ix, DSI_DSIPHY_CFG2); tclk_prepare = FLD_GET(r, 7, 0); /* min 8*UI */ tclk_pre = 20; /* min 60ns + 52*UI */ - tclk_post = ns2ddr(60) + 26; + tclk_post = ns2ddr(ix, 60) + 26; /* ths_eot is 2 for 2 datalanes and 4 for 1 datalane */ if (dssdev->phy.dsi.data1_lane != 0 && @@ -2574,10 +2792,10 @@ static void dsi_proto_timings(struct omap_dss_device *dssdev) BUG_ON(ddr_clk_pre == 0 || ddr_clk_pre > 255); BUG_ON(ddr_clk_post == 0 || ddr_clk_post > 255); - r = dsi_read_reg(DSI_CLK_TIMING); + r = dsi_read_reg(ix, DSI_CLK_TIMING); r = FLD_MOD(r, ddr_clk_pre, 15, 8); r = FLD_MOD(r, ddr_clk_post, 7, 0); - dsi_write_reg(DSI_CLK_TIMING, r); + dsi_write_reg(ix, DSI_CLK_TIMING, r); DSSDBG("ddr_clk_pre %u, ddr_clk_post %u\n", ddr_clk_pre, @@ -2591,7 +2809,7 @@ static void dsi_proto_timings(struct omap_dss_device *dssdev) r = FLD_VAL(enter_hs_mode_lat, 31, 16) | FLD_VAL(exit_hs_mode_lat, 15, 0); - dsi_write_reg(DSI_VM_TIMING7, r); + dsi_write_reg(ix, DSI_VM_TIMING7, r); DSSDBG("enter_hs_mode_lat %u, exit_hs_mode_lat %u\n", enter_hs_mode_lat, exit_hs_mode_lat); @@ -2601,19 +2819,19 @@ static void dsi_proto_timings(struct omap_dss_device *dssdev) #define DSI_DECL_VARS \ int __dsi_cb = 0; u32 __dsi_cv = 0; -#define DSI_FLUSH(ch) \ +#define DSI_FLUSH(ix, ch) \ if (__dsi_cb > 0) { \ /*DSSDBG("sending long packet %#010x\n", __dsi_cv);*/ \ - dsi_write_reg(DSI_VC_LONG_PACKET_PAYLOAD(ch), __dsi_cv); \ + dsi_write_reg(ix, DSI_VC_LONG_PACKET_PAYLOAD(ch), __dsi_cv); \ __dsi_cb = __dsi_cv = 0; \ } -#define DSI_PUSH(ch, data) \ +#define DSI_PUSH(ix, ch, data) \ do { \ __dsi_cv |= (data) << (__dsi_cb * 8); \ /*DSSDBG("cv = %#010x, cb = %d\n", __dsi_cv, __dsi_cb);*/ \ if (++__dsi_cb > 3) \ - DSI_FLUSH(ch); \ + DSI_FLUSH(ix, ch); \ } while (0) static int dsi_update_screen_l4(struct omap_dss_device *dssdev, @@ -2633,9 +2851,14 @@ static int dsi_update_screen_l4(struct omap_dss_device *dssdev, int horiz_inc; int current_x; struct omap_overlay *ovl; + struct dsi_struct *p_dsi; + enum omap_dsi_index ix; debug_irq = 0; + ix = (dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? DSI1 : DSI2; + p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; + DSSDBG("dsi_update_screen_l4 (%d,%d %dx%d)\n", x, y, w, h); @@ -2658,7 +2881,7 @@ static int dsi_update_screen_l4(struct omap_dss_device *dssdev, * in fifo */ /* When using CPU, max long packet size is TX buffer size */ - max_dsi_packet_size = dsi.vc[0].fifo_size * 32 * 4; + max_dsi_packet_size = p_dsi->vc[0].fifo_size * 32 * 4; /* we seem to get better perf if we divide the tx fifo to half, and while the other half is being sent, we fill the other half @@ -2687,12 +2910,12 @@ static int dsi_update_screen_l4(struct omap_dss_device *dssdev, #if 1 /* using fifo not empty */ /* TX_FIFO_NOT_EMPTY */ - while (FLD_GET(dsi_read_reg(DSI_VC_CTRL(0)), 5, 5)) { + while (FLD_GET(dsi_read_reg(ix, DSI_VC_CTRL(0)), 5, 5)) { fifo_stalls++; if (fifo_stalls > 0xfffff) { DSSERR("fifo stalls overflow, pixels left %d\n", pixels_left); - dsi_if_enable(0); + dsi_if_enable(ix, 0); return -EIO; } udelay(1); @@ -2724,17 +2947,17 @@ static int dsi_update_screen_l4(struct omap_dss_device *dssdev, pixels_left -= pixels; - dsi_vc_write_long_header(0, DSI_DT_DCS_LONG_WRITE, + dsi_vc_write_long_header(ix, 0, DSI_DT_DCS_LONG_WRITE, 1 + pixels * bytespp, 0); - DSI_PUSH(0, dcs_cmd); + DSI_PUSH(ix, 0, dcs_cmd); while (pixels-- > 0) { u32 pix = __raw_readl(data++); - DSI_PUSH(0, (pix >> 16) & 0xff); - DSI_PUSH(0, (pix >> 8) & 0xff); - DSI_PUSH(0, (pix >> 0) & 0xff); + DSI_PUSH(ix, 0, (pix >> 16) & 0xff); + DSI_PUSH(ix, 0, (pix >> 8) & 0xff); + DSI_PUSH(ix, 0, (pix >> 0) & 0xff); current_x++; if (current_x == x+w) { @@ -2743,7 +2966,7 @@ static int dsi_update_screen_l4(struct omap_dss_device *dssdev, } } - DSI_FLUSH(0); + DSI_FLUSH(ix, 0); } return 0; @@ -2759,16 +2982,26 @@ static void dsi_update_screen_dispc(struct omap_dss_device *dssdev, unsigned packet_payload; unsigned packet_len; u32 l; - const unsigned channel = dsi.update_channel; + struct dsi_struct *p_dsi; + enum omap_dsi_index ix; + unsigned channel; + bool use_te_trigger; /* line buffer is 1024 x 24bits */ /* XXX: for some reason using full buffer size causes considerable TX * slowdown with update sizes that fill the whole buffer */ const unsigned line_buf_size = 1023 * 3; + ix = (dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? DSI1 : DSI2; + p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; + + channel = p_dsi->update_channel; + + use_te_trigger = p_dsi->te_enabled && !p_dsi->use_ext_te; + DSSDBG("dsi_update_screen_dispc(%d,%d %dx%d)\n", x, y, w, h); - dsi_vc_config_vp(channel); + dsi_vc_config_vp(ix, channel); bytespp = dssdev->ctrl.pixel_size / 8; bytespl = w * bytespp; @@ -2789,15 +3022,16 @@ static void dsi_update_screen_dispc(struct omap_dss_device *dssdev, total_len += (bytespf % packet_payload) + 1; l = FLD_VAL(total_len, 23, 0); /* TE_SIZE */ - dsi_write_reg(DSI_VC_TE(channel), l); + dsi_write_reg(ix, DSI_VC_TE(channel), l); - dsi_vc_write_long_header(channel, DSI_DT_DCS_LONG_WRITE, packet_len, 0); + dsi_vc_write_long_header(ix, channel, DSI_DT_DCS_LONG_WRITE, + packet_len, 0); - if (dsi.te_enabled) + if (use_te_trigger) l = FLD_MOD(l, 1, 30, 30); /* TE_EN */ else l = FLD_MOD(l, 1, 31, 31); /* TE_START */ - dsi_write_reg(DSI_VC_TE(channel), l); + dsi_write_reg(ix, DSI_VC_TE(channel), l); /* We put SIDLEMODE to no-idle for the duration of the transfer, * because DSS interrupts are not capable of waking up the CPU and the @@ -2807,22 +3041,24 @@ static void dsi_update_screen_dispc(struct omap_dss_device *dssdev, */ dispc_disable_sidle(); - dsi_perf_mark_start(); + dsi_perf_mark_start(ix); - schedule_delayed_work(&dsi.framedone_timeout_work, + schedule_delayed_work(&p_dsi->framedone_timeout_work, msecs_to_jiffies(250)); dss_start_update(dssdev); - if (dsi.te_enabled) { + if (use_te_trigger) { /* disable LP_RX_TO, so that we can receive TE. Time to wait * for TE is longer than the timer allows */ - REG_FLD_MOD(DSI_TIMING2, 0, 15, 15); /* LP_RX_TO */ - - dsi_vc_send_bta(channel); + REG_FLD_MOD(ix, DSI_TIMING2, 0, 15, 15); /* LP_RX_TO */ + if (cpu_is_omap44xx()) + dsi_vc_send_bta(ix, 0); + else + dsi_vc_send_bta(ix, channel); #ifdef DSI_CATCH_MISSING_TE - mod_timer(&dsi.te_timer, jiffies + msecs_to_jiffies(250)); + mod_timer(&p_dsi->te_timer, jiffies + msecs_to_jiffies(250)); #endif } } @@ -2830,23 +3066,37 @@ static void dsi_update_screen_dispc(struct omap_dss_device *dssdev, #ifdef DSI_CATCH_MISSING_TE static void dsi_te_timeout(unsigned long arg) { - DSSERR("TE not received for 250ms!\n"); + DSSERR("DSI TE not received for 250ms!\n"); +} + +static void dsi2_te_timeout(unsigned long arg) +{ + DSSERR("DSI2 TE not received for 250ms!\n"); } #endif static void dsi_framedone_timeout_work_callback(struct work_struct *work) { int r; - const int channel = dsi.update_channel; + int channel; + + if (!cpu_is_omap44xx()) + channel = dsi1.update_channel; + else + channel = 0; DSSERR("Framedone not received for 250ms!\n"); /* SIDLEMODE back to smart-idle */ dispc_enable_sidle(); - if (dsi.te_enabled) { + if (cpu_is_omap44xx()) + /* Ensures recovery of DISPC after a failed lcd_enable*/ + dispc_enable_lcd_out(OMAP_DSS_CHANNEL_LCD, 0); + + if (dsi1.te_enabled) { /* enable LP_RX_TO again after the TE */ - REG_FLD_MOD(DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */ + REG_FLD_MOD(DSI1, DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */ } /* Send BTA after the frame. We need this for the TE to work, as TE @@ -2858,17 +3108,63 @@ static void dsi_framedone_timeout_work_callback(struct work_struct *work) * make sure that the transfer has been completed. It would be more * optimal, but more complex, to wait only just before starting next * transfer. */ - r = dsi_vc_send_bta_sync(channel); + r = dsi_vc_send_bta_sync(DSI1, channel); if (r) DSSERR("BTA after framedone failed\n"); /* RX_FIFO_NOT_EMPTY */ - if (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { + if (REG_GET(DSI1, DSI_VC_CTRL(channel), 20, 20)) { + DSSERR("Received error during frame transfer:\n"); + dsi_vc_flush_receive_data(DSI1, channel); + } + + dsi1.framedone_callback(-ETIMEDOUT, dsi1.framedone_data); +} + +static void dsi2_framedone_timeout_work_callback(struct work_struct *work) +{ + int r; + int channel; + + if (!cpu_is_omap44xx()) + channel = dsi2.update_channel; + else + channel = 0; + + DSSERR("Framedone2 not received for 250ms!\n"); + + /* SIDLEMODE back to smart-idle */ + dispc_enable_sidle(); + + if (cpu_is_omap44xx()) + /* Ensures recovery of DISPC after a failed lcd_enable*/ + dispc_enable_lcd_out(OMAP_DSS_CHANNEL_LCD2, 0); + + if (dsi2.te_enabled) { + /* enable LP_RX_TO again after the TE */ + REG_FLD_MOD(DSI2, DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */ + } + + /* Send BTA after the frame. We need this for the TE to work, as TE + * trigger is only sent for BTAs without preceding packet. Thus we need + * to BTA after the pixel packets so that next BTA will cause TE + * trigger. + * + * This is not needed when TE is not in use, but we do it anyway to + * make sure that the transfer has been completed. It would be more + * optimal, but more complex, to wait only just before starting next + * transfer. */ + r = dsi_vc_send_bta_sync(DSI2, channel); + if (r) + DSSERR("BTA after framedone2 failed\n"); + + /* RX_FIFO_NOT_EMPTY */ + if (REG_GET(DSI2, DSI_VC_CTRL(channel), 20, 20)) { DSSERR("Received error during frame transfer:\n"); - dsi_vc_flush_receive_data(channel); + dsi_vc_flush_receive_data(DSI2, channel); } - dsi.framedone_callback(-ETIMEDOUT, dsi.framedone_data); + dsi2.framedone_callback(-ETIMEDOUT, dsi2.framedone_data); } static void dsi_framedone_irq_callback(void *data, u32 mask) @@ -2880,20 +3176,41 @@ static void dsi_framedone_irq_callback(void *data, u32 mask) /* SIDLEMODE back to smart-idle */ dispc_enable_sidle(); + schedule_work(&dsi1.framedone_work); +} - schedule_work(&dsi.framedone_work); +static void dsi2_framedone_irq_callback(void *data, u32 mask) +{ + /* Note: We get FRAMEDONE when DISPC has finished sending pixels and + * turns itself off. However, DSI still has the pixels in its buffers, + * and is sending the data. + */ + + /* SIDLEMODE back to smart-idle */ + dispc_enable_sidle(); + schedule_work(&dsi2.framedone_work); } -static void dsi_handle_framedone(void) +static void dsi_handle_framedone(enum omap_dsi_index ix) { int r; - const int channel = dsi.update_channel; + int channel; + struct dsi_struct *p_dsi; + bool use_te_trigger; + p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; + + use_te_trigger = p_dsi->te_enabled && !p_dsi->use_ext_te; + + if (!cpu_is_omap44xx()) + channel = p_dsi->update_channel; + else + channel = 0; DSSDBG("FRAMEDONE\n"); - if (dsi.te_enabled) { + if (use_te_trigger) { /* enable LP_RX_TO again after the TE */ - REG_FLD_MOD(DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */ + REG_FLD_MOD(ix, DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */ } /* Send BTA after the frame. We need this for the TE to work, as TE @@ -2905,18 +3222,19 @@ static void dsi_handle_framedone(void) * make sure that the transfer has been completed. It would be more * optimal, but more complex, to wait only just before starting next * transfer. */ - r = dsi_vc_send_bta_sync(channel); + r = dsi_vc_send_bta_sync(ix, channel); if (r) DSSERR("BTA after framedone failed\n"); /* RX_FIFO_NOT_EMPTY */ - if (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { - DSSERR("Received error during frame transfer:\n"); - dsi_vc_flush_receive_data(channel); + if (REG_GET(ix, DSI_VC_CTRL(channel), 20, 20)) { + if (!cpu_is_omap44xx()) + DSSERR("Received error during frame transfer:\n"); + dsi_vc_flush_receive_data(ix, channel); } #ifdef CONFIG_OMAP2_DSS_FAKE_VSYNC - dispc_fake_vsync_irq(); + dispc_fake_vsync_irq(ix); #endif } @@ -2924,19 +3242,35 @@ static void dsi_framedone_work_callback(struct work_struct *work) { DSSDBGF(); - cancel_delayed_work_sync(&dsi.framedone_timeout_work); + cancel_delayed_work_sync(&dsi1.framedone_timeout_work); - dsi_handle_framedone(); + dsi_handle_framedone(DSI1); - dsi_perf_show("DISPC"); + dsi_perf_show(DSI1, "DISPC"); - dsi.framedone_callback(0, dsi.framedone_data); + dsi1.framedone_callback(0, dsi1.framedone_data); +} + +static void dsi2_framedone_work_callback(struct work_struct *work) +{ + DSSDBGF(); + + cancel_delayed_work_sync(&dsi2.framedone_timeout_work); + + dsi_handle_framedone(DSI2); + + dsi_perf_show(DSI2, "DISPC"); + + dsi2.framedone_callback(0, dsi2.framedone_data); } int omap_dsi_prepare_update(struct omap_dss_device *dssdev, u16 *x, u16 *y, u16 *w, u16 *h) { u16 dw, dh; + enum omap_dsi_index ix; + + ix = (dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? DSI1 : DSI2; dssdev->driver->get_resolution(dssdev, &dw, &dh); @@ -2955,11 +3289,11 @@ int omap_dsi_prepare_update(struct omap_dss_device *dssdev, if (*w == 0 || *h == 0) return -EINVAL; - dsi_perf_mark_setup(); + dsi_perf_mark_setup(ix); if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) { dss_setup_partial_planes(dssdev, x, y, w, h); - dispc_set_lcd_size(*w, *h); + dispc_set_lcd_size(dssdev->channel, *w, *h); } return 0; @@ -2971,22 +3305,28 @@ int omap_dsi_update(struct omap_dss_device *dssdev, u16 x, u16 y, u16 w, u16 h, void (*callback)(int, void *), void *data) { - dsi.update_channel = channel; + struct dsi_struct *p_dsi; + enum omap_dsi_index ix; + + ix = (dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? DSI1 : DSI2; + p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; + + p_dsi->update_channel = channel; if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) { - dsi.framedone_callback = callback; - dsi.framedone_data = data; + p_dsi->framedone_callback = callback; + p_dsi->framedone_data = data; - dsi.update_region.x = x; - dsi.update_region.y = y; - dsi.update_region.w = w; - dsi.update_region.h = h; - dsi.update_region.device = dssdev; + p_dsi->update_region.x = x; + p_dsi->update_region.y = y; + p_dsi->update_region.w = w; + p_dsi->update_region.h = h; + p_dsi->update_region.device = dssdev; dsi_update_screen_dispc(dssdev, x, y, w, h); } else { dsi_update_screen_l4(dssdev, x, y, w, h); - dsi_perf_show("L4"); + dsi_perf_show(ix, "L4"); callback(0, data); } @@ -3000,21 +3340,42 @@ static int dsi_display_init_dispc(struct omap_dss_device *dssdev) { int r; - r = omap_dispc_register_isr(dsi_framedone_irq_callback, NULL, - DISPC_IRQ_FRAMEDONE); + r = omap_dispc_register_isr((dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? + dsi_framedone_irq_callback : dsi2_framedone_irq_callback, + NULL, (dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? + DISPC_IRQ_FRAMEDONE : DISPC_IRQ_FRAMEDONE2); if (r) { DSSERR("can't get FRAMEDONE irq\n"); return r; } - dispc_set_lcd_display_type(OMAP_DSS_LCD_DISPLAY_TFT); + dispc_set_lcd_display_type(dssdev->channel, + OMAP_DSS_LCD_DISPLAY_TFT); - dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_DSI); + dispc_set_parallel_interface_mode(dssdev->channel, + OMAP_DSS_PARALLELMODE_DSI); dispc_enable_fifohandcheck(1); - dispc_set_tft_data_lines(dssdev->ctrl.pixel_size); + dispc_set_tft_data_lines(dssdev->channel, dssdev->ctrl.pixel_size); + + if (cpu_is_omap44xx()) + dispc_set_pol_freq(dssdev->channel, dssdev->panel.config, + dssdev->panel.acbi, dssdev->panel.acb); - { + if (cpu_is_omap44xx()) { + struct omap_video_timings timings = { + .hsw = 4+1, + .hfp = 4+1, + .hbp = 4+1, + .vsw = 0+1, + .vfp = 0, + .vbp = 1, + .x_res = 864, + .y_res = 480, + }; + + dispc_set_lcd_timings(dssdev->channel, &timings); + } else { struct omap_video_timings timings = { .hsw = 1, .hfp = 1, @@ -3024,7 +3385,7 @@ static int dsi_display_init_dispc(struct omap_dss_device *dssdev) .vbp = 0, }; - dispc_set_lcd_timings(&timings); + dispc_set_lcd_timings(dssdev->channel, &timings); } return 0; @@ -3032,14 +3393,19 @@ static int dsi_display_init_dispc(struct omap_dss_device *dssdev) static void dsi_display_uninit_dispc(struct omap_dss_device *dssdev) { - omap_dispc_unregister_isr(dsi_framedone_irq_callback, NULL, - DISPC_IRQ_FRAMEDONE); + omap_dispc_unregister_isr((dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? + dsi_framedone_irq_callback : dsi2_framedone_irq_callback, + NULL, (dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? + DISPC_IRQ_FRAMEDONE : DISPC_IRQ_FRAMEDONE2); } static int dsi_configure_dsi_clocks(struct omap_dss_device *dssdev) { struct dsi_clock_info cinfo; int r; + enum omap_dsi_index ix; + + ix = (dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? DSI1 : DSI2; /* we always use DSS2_FCK as input clock */ cinfo.use_dss2_fck = true; @@ -3051,7 +3417,7 @@ static int dsi_configure_dsi_clocks(struct omap_dss_device *dssdev) if (r) return r; - r = dsi_pll_set_clock_div(&cinfo); + r = dsi_pll_set_clock_div(ix, &cinfo); if (r) { DSSERR("Failed to set dsi clocks\n"); return r; @@ -3065,8 +3431,11 @@ static int dsi_configure_dispc_clocks(struct omap_dss_device *dssdev) struct dispc_clock_info dispc_cinfo; int r; unsigned long long fck; + enum omap_dsi_index ix; - fck = dsi_get_dsi1_pll_rate(); + ix = (dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? DSI1 : DSI2; + + fck = dsi_get_dsi1_pll_rate(ix); dispc_cinfo.lck_div = dssdev->phy.dsi.div.lck_div; dispc_cinfo.pck_div = dssdev->phy.dsi.div.pck_div; @@ -3077,7 +3446,7 @@ static int dsi_configure_dispc_clocks(struct omap_dss_device *dssdev) return r; } - r = dispc_set_clock_div(&dispc_cinfo); + r = dispc_set_clock_div(dssdev->channel, &dispc_cinfo); if (r) { DSSERR("Failed to set dispc clocks\n"); return r; @@ -3088,9 +3457,15 @@ static int dsi_configure_dispc_clocks(struct omap_dss_device *dssdev) static int dsi_display_init_dsi(struct omap_dss_device *dssdev) { - int r; + int r, l = 0; + struct dsi_struct *p_dsi; + enum omap_dsi_index ix; + + ix = (dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? DSI1 : DSI2; - _dsi_print_reset_status(); + p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; + + _dsi_print_reset_status(ix); r = dsi_pll_init(dssdev, true, true); if (r) @@ -3100,8 +3475,11 @@ static int dsi_display_init_dsi(struct omap_dss_device *dssdev) if (r) goto err1; - dss_select_dispc_clk_source(DSS_SRC_DSI1_PLL_FCLK); - dss_select_dsi_clk_source(DSS_SRC_DSI2_PLL_FCLK); + if (cpu_is_omap44xx()) + dss_select_dispc_clk_source(DSS_SRC_DSS1_ALWON_FCLK); + else + dss_select_dispc_clk_source(DSS_SRC_DSI1_PLL_FCLK); + dss_select_dsi_clk_source(ix, DSS_SRC_DSI2_PLL_FCLK); DSSDBG("PLL OK\n"); @@ -3113,58 +3491,81 @@ static int dsi_display_init_dsi(struct omap_dss_device *dssdev) if (r) goto err2; - _dsi_print_reset_status(); + _dsi_print_reset_status(ix); dsi_proto_timings(dssdev); dsi_set_lp_clk_divisor(dssdev); if (1) - _dsi_print_reset_status(); + _dsi_print_reset_status(ix); r = dsi_proto_config(dssdev); if (r) goto err3; /* enable interface */ - dsi_vc_enable(0, 1); - dsi_vc_enable(1, 1); - dsi_vc_enable(2, 1); - dsi_vc_enable(3, 1); - dsi_if_enable(1); - dsi_force_tx_stop_mode_io(); + dsi_vc_enable(ix, 0, 1); + dsi_vc_enable(ix, 1, 1); + dsi_vc_enable(ix, 2, 1); + dsi_vc_enable(ix, 3, 1); + dsi_if_enable(ix, 1); + dsi_force_tx_stop_mode_io(ix); + + /* magic OMAP4 registers */ + dsi_write_reg(ix, DSI_DSIPHY_CFG12, 0x58); + + l = dsi_read_reg(ix, DSI_DSIPHY_CFG14); + l = FLD_MOD(l, 1, 31, 31); + l = FLD_MOD(l, 0x54, 30, 23); + l = FLD_MOD(l, 1, 19, 19); + l = FLD_MOD(l, 1, 18, 18); + l = FLD_MOD(l, 7, 17, 14); + l = FLD_MOD(l, 1, 11, 11); + dsi_write_reg(ix, DSI_DSIPHY_CFG14, l); + + l = 0; + l = dsi_read_reg(ix, DSI_DSIPHY_CFG8); + l = FLD_MOD(l, 1, 11, 11); + l = FLD_MOD(l, 0x10, 10, 6); + l = FLD_MOD(l, 1, 5, 5); + l = FLD_MOD(l, 0xE, 3, 0); + dsi_write_reg(ix, DSI_DSIPHY_CFG8, l); return 0; err3: - dsi_complexio_uninit(); + dsi_complexio_uninit(ix); err2: dss_select_dispc_clk_source(DSS_SRC_DSS1_ALWON_FCLK); - dss_select_dsi_clk_source(DSS_SRC_DSS1_ALWON_FCLK); + dss_select_dsi_clk_source(ix, DSS_SRC_DSS1_ALWON_FCLK); err1: - dsi_pll_uninit(); + dsi_pll_uninit(ix); err0: return r; } static void dsi_display_uninit_dsi(struct omap_dss_device *dssdev) { + enum omap_dsi_index ix; + ix = (dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? DSI1 : DSI2; + dss_select_dispc_clk_source(DSS_SRC_DSS1_ALWON_FCLK); - dss_select_dsi_clk_source(DSS_SRC_DSS1_ALWON_FCLK); - dsi_complexio_uninit(); - dsi_pll_uninit(); + dss_select_dsi_clk_source(ix, DSS_SRC_DSS1_ALWON_FCLK); + dsi_complexio_uninit(ix); + dsi_pll_uninit(ix); } -static int dsi_core_init(void) +static int dsi_core_init(enum omap_dsi_index ix) { /* Autoidle */ - REG_FLD_MOD(DSI_SYSCONFIG, 1, 0, 0); + REG_FLD_MOD(ix, DSI_SYSCONFIG, 1, 0, 0); /* ENWAKEUP */ - REG_FLD_MOD(DSI_SYSCONFIG, 1, 2, 2); + REG_FLD_MOD(ix, DSI_SYSCONFIG, 1, 2, 2); /* SIDLEMODE smart-idle */ - REG_FLD_MOD(DSI_SYSCONFIG, 2, 4, 3); + REG_FLD_MOD(ix, DSI_SYSCONFIG, 2, 4, 3); - _dsi_initialize_irq(); + _dsi_initialize_irq(ix); return 0; } @@ -3172,12 +3573,18 @@ static int dsi_core_init(void) int omapdss_dsi_display_enable(struct omap_dss_device *dssdev) { int r = 0; + struct dsi_struct *p_dsi; + enum omap_dsi_index ix; + + ix = (dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? DSI1 : DSI2; + + p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; DSSDBG("dsi_display_enable\n"); - WARN_ON(!dsi_bus_is_locked()); + WARN_ON(!dsi_bus_is_locked(ix)); - mutex_lock(&dsi.lock); + mutex_lock(&p_dsi->lock); r = omap_dss_start_device(dssdev); if (r) { @@ -3186,13 +3593,17 @@ int omapdss_dsi_display_enable(struct omap_dss_device *dssdev) } enable_clocks(1); - dsi_enable_pll_clock(1); + dsi_enable_pll_clock(ix, 1); + + /* DSS_PWR_DSS_DSS_CTRL */ + if (cpu_is_omap44xx()) + omap_writel(0x00030007, 0x4A307100); - r = _dsi_reset(); + r = _dsi_reset(ix); if (r) goto err1; - dsi_core_init(); + dsi_core_init(ix); r = dsi_display_init_dispc(dssdev); if (r) @@ -3202,7 +3613,9 @@ int omapdss_dsi_display_enable(struct omap_dss_device *dssdev) if (r) goto err2; - mutex_unlock(&dsi.lock); + p_dsi->use_ext_te = dssdev->phy.dsi.ext_te; + + mutex_unlock(&p_dsi->lock); return 0; @@ -3210,10 +3623,10 @@ err2: dsi_display_uninit_dispc(dssdev); err1: enable_clocks(0); - dsi_enable_pll_clock(0); + dsi_enable_pll_clock(ix, 0); omap_dss_stop_device(dssdev); err0: - mutex_unlock(&dsi.lock); + mutex_unlock(&p_dsi->lock); DSSDBG("dsi_display_enable FAILED\n"); return r; } @@ -3221,28 +3634,37 @@ EXPORT_SYMBOL(omapdss_dsi_display_enable); void omapdss_dsi_display_disable(struct omap_dss_device *dssdev) { + struct dsi_struct *p_dsi; + enum omap_dsi_index ix; + + ix = (dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? DSI1 : DSI2; + p_dsi = (ix == DSI1) ? &dsi1 : &dsi2; + DSSDBG("dsi_display_disable\n"); - WARN_ON(!dsi_bus_is_locked()); + WARN_ON(!dsi_bus_is_locked(ix)); - mutex_lock(&dsi.lock); + mutex_lock(&p_dsi->lock); dsi_display_uninit_dispc(dssdev); dsi_display_uninit_dsi(dssdev); enable_clocks(0); - dsi_enable_pll_clock(0); + dsi_enable_pll_clock(ix, 0); omap_dss_stop_device(dssdev); - mutex_unlock(&dsi.lock); + mutex_unlock(&p_dsi->lock); } EXPORT_SYMBOL(omapdss_dsi_display_disable); int omapdss_dsi_enable_te(struct omap_dss_device *dssdev, bool enable) { - dsi.te_enabled = enable; + struct dsi_struct *p_dsi; + p_dsi = (dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? &dsi1 : &dsi2; + + p_dsi->te_enabled = enable; return 0; } EXPORT_SYMBOL(omapdss_dsi_enable_te); @@ -3262,14 +3684,18 @@ void dsi_get_overlay_fifo_thresholds(enum omap_plane plane, int dsi_init_display(struct omap_dss_device *dssdev) { + struct dsi_struct *p_dsi; + DSSDBG("DSI init\n"); + p_dsi = (dssdev->channel == OMAP_DSS_CHANNEL_LCD) ? &dsi1 : &dsi2; + /* XXX these should be figured out dynamically */ dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE | OMAP_DSS_DISPLAY_CAP_TEAR_ELIM; - dsi.vc[0].dssdev = dssdev; - dsi.vc[1].dssdev = dssdev; + p_dsi->vc[0].dssdev = dssdev; + p_dsi->vc[1].dssdev = dssdev; return 0; } @@ -3278,47 +3704,56 @@ int dsi_init(struct platform_device *pdev) { u32 rev; int r; + enum omap_dsi_index ix = DSI1; - spin_lock_init(&dsi.errors_lock); - dsi.errors = 0; + spin_lock_init(&dsi1.errors_lock); + dsi1.errors = 0; #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS - spin_lock_init(&dsi.irq_stats_lock); - dsi.irq_stats.last_reset = jiffies; + spin_lock_init(&dsi1.irq_stats_lock); + dsi1.irq_stats.last_reset = jiffies; #endif - init_completion(&dsi.bta_completion); + init_completion(&dsi1.bta_completion); - mutex_init(&dsi.lock); - sema_init(&dsi.bus_lock, 1); + mutex_init(&dsi1.lock); + sema_init(&dsi1.bus_lock, 1); - INIT_WORK(&dsi.framedone_work, dsi_framedone_work_callback); - INIT_DELAYED_WORK_DEFERRABLE(&dsi.framedone_timeout_work, + INIT_WORK(&dsi1.framedone_work, dsi_framedone_work_callback); + INIT_DELAYED_WORK_DEFERRABLE(&dsi1.framedone_timeout_work, dsi_framedone_timeout_work_callback); + if (cpu_is_omap44xx()) { + r = request_irq(OMAP44XX_IRQ_DSS_DSI1, dsi_irq_handler, + 0, "OMAP DSI", (void *)0); + if (r) + goto err2; + } #ifdef DSI_CATCH_MISSING_TE - init_timer(&dsi.te_timer); - dsi.te_timer.function = dsi_te_timeout; - dsi.te_timer.data = 0; + init_timer(&dsi1.te_timer); + dsi1.te_timer.function = dsi_te_timeout; + dsi1.te_timer.data = 0; #endif - dsi.base = ioremap(DSI_BASE, DSI_SZ_REGS); - if (!dsi.base) { + dsi1.base = ioremap(DSI_BASE, DSI_SZ_REGS); + if (!dsi1.base) { DSSERR("can't ioremap DSI\n"); r = -ENOMEM; goto err1; } - dsi.vdds_dsi_reg = dss_get_vdds_dsi(); - if (IS_ERR(dsi.vdds_dsi_reg)) { - iounmap(dsi.base); - DSSERR("can't get VDDS_DSI regulator\n"); - r = PTR_ERR(dsi.vdds_dsi_reg); - goto err2; + if (!cpu_is_omap44xx()) { + dsi1.vdds_dsi_reg = dss_get_vdds_dsi(); + if (IS_ERR(dsi1.vdds_dsi_reg)) { + iounmap(dsi1.base); + DSSERR("can't get VDDS_DSI regulator\n"); + r = PTR_ERR(dsi1.vdds_dsi_reg); + goto err2; + } } enable_clocks(1); - rev = dsi_read_reg(DSI_REVISION); + rev = dsi_read_reg(ix, DSI_REVISION); printk(KERN_INFO "OMAP DSI rev %d.%d\n", FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); @@ -3326,15 +3761,81 @@ int dsi_init(struct platform_device *pdev) return 0; err2: - iounmap(dsi.base); + iounmap(dsi1.base); + if (cpu_is_omap44xx()) + free_irq(OMAP44XX_IRQ_DSS_DSI1, (void *)0); +err1: + return r; +} + +int dsi2_init(struct platform_device *pdev) +{ + u32 rev; + int r; + enum omap_dsi_index ix = DSI2; + + spin_lock_init(&dsi2.errors_lock); + dsi2.errors = 0; + +#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS + spin_lock_init(&dsi2.irq_stats_lock); + dsi2.irq_stats.last_reset = jiffies; +#endif + + init_completion(&dsi2.bta_completion); + + mutex_init(&dsi2.lock); + sema_init(&dsi2.bus_lock, 1); + + INIT_WORK(&dsi2.framedone_work, dsi2_framedone_work_callback); + INIT_DELAYED_WORK_DEFERRABLE(&dsi2.framedone_timeout_work, + dsi2_framedone_timeout_work_callback); + + r = request_irq(OMAP44XX_IRQ_DSS_DSI2, dsi_irq_handler, + 0, "OMAP DSI2", (void *)0); + if (r) + goto err2; + +#ifdef DSI_CATCH_MISSING_TE + init_timer(&dsi2.te_timer); + dsi2.te_timer.function = dsi2_te_timeout; + dsi2.te_timer.data = 0; +#endif + dsi2.te_enabled = true; + + dsi2.base = ioremap(DSI2_BASE, DSI_SZ_REGS); + if (!dsi2.base) { + DSSERR("can't ioremap DSI2\n"); + r = -ENOMEM; + goto err1; + } + + enable_clocks(1); + + rev = dsi_read_reg(ix, DSI_REVISION); + printk(KERN_INFO "OMAP DSI2 rev %d.%d\n", + FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); + + enable_clocks(0); + + return 0; +err2: + iounmap(dsi2.base); + free_irq(OMAP44XX_IRQ_DSS_DSI2, (void *)0); err1: return r; } void dsi_exit(void) { - iounmap(dsi.base); + iounmap(dsi1.base); DSSDBG("omap_dsi_exit\n"); } +void dsi2_exit(void) +{ + iounmap(dsi2.base); + + DSSDBG("omap_dsi2_exit\n"); +} diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c index 54344184dd73..db8bc715c564 100644 --- a/drivers/video/omap2/dss/dss.c +++ b/drivers/video/omap2/dss/dss.c @@ -33,7 +33,11 @@ #include <plat/display.h> #include "dss.h" +#ifndef CONFIG_ARCH_OMAP4 #define DSS_BASE 0x48050000 +#else +#define DSS_BASE 0x58000000 +#endif #define DSS_SZ_REGS SZ_512 @@ -223,7 +227,13 @@ void dss_dump_clocks(struct seq_file *s) seq_printf(s, "dpll4_ck %lu\n", dpll4_ck_rate); - seq_printf(s, "dss1_alwon_fclk = %lu / %lu * 2 = %lu\n", + if (cpu_is_omap3630()) + seq_printf(s, "dss1_alwon_fclk = %lu / %lu = %lu\n", + dpll4_ck_rate, + dpll4_ck_rate / dpll4_m4_ck_rate, + dss_clk_get_rate(DSS_CLK_FCK1)); + else + seq_printf(s, "dss1_alwon_fclk = %lu / %lu * 2 = %lu\n", dpll4_ck_rate, dpll4_ck_rate / dpll4_m4_ck_rate, dss_clk_get_rate(DSS_CLK_FCK1)); @@ -259,12 +269,17 @@ void dss_select_dispc_clk_source(enum dss_clk_source clk_src) b = clk_src == DSS_SRC_DSS1_ALWON_FCLK ? 0 : 1; - REG_FLD_MOD(DSS_CONTROL, b, 0, 0); /* DISPC_CLK_SWITCH */ + if (!cpu_is_omap44xx()) { + REG_FLD_MOD(DSS_CONTROL, b, 0, 0); /* DISPC_CLK_SWITCH */ + } else { + REG_FLD_MOD(DSS_CONTROL, b, 9, 8); /* FCK_CLK_SWITCH */ + } dss.dispc_clk_source = clk_src; } -void dss_select_dsi_clk_source(enum dss_clk_source clk_src) +void dss_select_dsi_clk_source(enum omap_dsi_index ix, + enum dss_clk_source clk_src) { int b; @@ -273,7 +288,14 @@ void dss_select_dsi_clk_source(enum dss_clk_source clk_src) b = clk_src == DSS_SRC_DSS1_ALWON_FCLK ? 0 : 1; - REG_FLD_MOD(DSS_CONTROL, b, 1, 1); /* DSI_CLK_SWITCH */ + if (ix == DSI1) { + REG_FLD_MOD(DSS_CONTROL, b, 1, 1); /* DSI_CLK_SWITCH */ + if (cpu_is_omap44xx()) + REG_FLD_MOD(DSS_CONTROL, b, 0, 0); /* LCD1_CLK_SWITCH */ + } else { + REG_FLD_MOD(DSS_CONTROL, b, 10, 10); /* DSI2_CLK_SWITCH */ + REG_FLD_MOD(DSS_CONTROL, b, 12, 12); /* LCD2_CLK_SWITCH */ + } dss.dsi_clk_source = clk_src; } @@ -293,7 +315,8 @@ int dss_calc_clock_rates(struct dss_clock_info *cinfo) { unsigned long prate; - if (cinfo->fck_div > 16 || cinfo->fck_div == 0) + if (cinfo->fck_div > (cpu_is_omap3630() ? 32 : 16) || + cinfo->fck_div == 0) return -EINVAL; prate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck)); @@ -329,7 +352,10 @@ int dss_get_clock_div(struct dss_clock_info *cinfo) if (cpu_is_omap34xx()) { unsigned long prate; prate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck)); - cinfo->fck_div = prate / (cinfo->fck / 2); + if (cpu_is_omap3630()) + cinfo->fck_div = prate / (cinfo->fck); + else + cinfo->fck_div = prate / (cinfo->fck / 2); } else { cinfo->fck_div = 0; } @@ -402,10 +428,14 @@ retry: goto found; } else if (cpu_is_omap34xx()) { - for (fck_div = 16; fck_div > 0; --fck_div) { + for (fck_div = (cpu_is_omap3630() ? 32 : 16); + fck_div > 0; --fck_div) { struct dispc_clock_info cur_dispc; - fck = prate / fck_div * 2; + if (cpu_is_omap3630()) + fck = prate / fck_div; + else + fck = prate / fck_div * 2; if (fck > DISPC_MAX_FCK) continue; @@ -480,8 +510,8 @@ static irqreturn_t dss_irq_handler_omap3(int irq, void *arg) if (irqstatus & (1<<0)) /* DISPC_IRQ */ dispc_irq_handler(); #ifdef CONFIG_OMAP2_DSS_DSI - if (irqstatus & (1<<1)) /* DSI_IRQ */ - dsi_irq_handler(); + if (!cpu_is_omap44xx() && (irqstatus & (1<<1))) /* DSI_IRQ */ + dsi_irq_handler(0, NULL); #endif return IRQ_HANDLED; @@ -529,6 +559,13 @@ void dss_set_dac_pwrdn_bgz(bool enable) REG_FLD_MOD(DSS_CONTROL, enable, 5, 5); /* DAC Power-Down Control */ } +void dss_switch_tv_hdmi(int hdmi) +{ + REG_FLD_MOD(DSS_CONTROL, hdmi, 15, 15); /* 0x1 for HDMI, 0x0 TV */ + if (hdmi) + REG_FLD_MOD(DSS_CONTROL, 0, 9, 8); +} + int dss_init(bool skip_init) { int r; @@ -570,11 +607,16 @@ int dss_init(bool skip_init) REG_FLD_MOD(DSS_CONTROL, 0, 2, 2); /* venc clock mode = normal */ #endif - r = request_irq(INT_24XX_DSS_IRQ, - cpu_is_omap24xx() - ? dss_irq_handler_omap2 - : dss_irq_handler_omap3, - 0, "OMAP DSS", NULL); + if (!cpu_is_omap44xx()) + r = request_irq(INT_24XX_DSS_IRQ, + cpu_is_omap24xx() + ? dss_irq_handler_omap2 + : dss_irq_handler_omap3, + 0, "OMAP DSS", NULL); + else + r = request_irq(OMAP44XX_IRQ_DSS_DISPC, + dss_irq_handler_omap2, + 0, "OMAP DSS", NULL); if (r < 0) { DSSERR("omap2 dss: request_irq failed\n"); @@ -602,7 +644,8 @@ int dss_init(bool skip_init) return 0; fail2: - free_irq(INT_24XX_DSS_IRQ, NULL); + if (!cpu_is_omap44xx()) + free_irq(INT_24XX_DSS_IRQ, NULL); fail1: iounmap(dss.base); fail0: @@ -614,8 +657,8 @@ void dss_exit(void) if (cpu_is_omap34xx()) clk_put(dss.dpll4_m4_ck); - free_irq(INT_24XX_DSS_IRQ, NULL); - + if (!cpu_is_omap44xx()) + free_irq(INT_24XX_DSS_IRQ, NULL); iounmap(dss.base); } diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h index 24326a5fd292..02cd5aed5c87 100644 --- a/drivers/video/omap2/dss/dss.h +++ b/drivers/video/omap2/dss/dss.h @@ -20,9 +20,12 @@ * this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <plat/display.h> #ifndef __OMAP2_DSS_H #define __OMAP2_DSS_H +#include <linux/interrupt.h> + #ifdef CONFIG_OMAP2_DSS_DEBUG_SUPPORT #define DEBUG #endif @@ -165,6 +168,38 @@ struct dsi_clock_info { bool use_dss2_fck; }; +/*TODO: Move this structure to manager.c*/ +struct writeback_cache_data { + /* If true, cache changed, but not written to shadow registers. Set + * in apply(), cleared when registers written. */ + bool dirty; + /* If true, shadow registers contain changed values not yet in real + * registers. Set when writing to shadow registers, cleared at + * VSYNC/EVSYNC */ + bool shadow_dirty; + + bool enabled; + + u32 paddr; + u32 puv_addr; /* relevant for NV12 format only */ + + u16 width; + u16 height; + u16 input_width; + u16 input_height; + + enum omap_color_mode color_mode; + enum omap_color_mode input_color_mode; + enum omap_writeback_capturemode capturemode; + enum omap_writeback_source_type source_type; + enum omap_writeback_source source; + + enum omap_burst_size burst_size; + u32 fifo_low; + u32 fifo_high; + +}; + struct seq_file; struct platform_device; @@ -211,11 +246,16 @@ void dss_overlay_setup_dispc_manager(struct omap_overlay_manager *mgr); void dss_overlay_setup_l4_manager(struct omap_overlay_manager *mgr); #endif void dss_recheck_connections(struct omap_dss_device *dssdev, bool force); +/* Write back */ +void dss_init_writeback(struct platform_device *pdev); +bool omap_dss_check_wb(struct writeback_cache_data *wb, int overlayId, int managerId); /* DSS */ int dss_init(bool skip_init); void dss_exit(void); +void dss_switch_tv_hdmi(int hdmi); + void dss_save_context(void); void dss_restore_context(void); @@ -226,7 +266,8 @@ int dss_sdi_enable(void); void dss_sdi_disable(void); void dss_select_dispc_clk_source(enum dss_clk_source clk_src); -void dss_select_dsi_clk_source(enum dss_clk_source clk_src); +void dss_select_dsi_clk_source(enum omap_dsi_index ix, + enum dss_clk_source clk_src); enum dss_clk_source dss_get_dispc_clk_source(void); enum dss_clk_source dss_get_dsi_clk_source(void); @@ -249,27 +290,32 @@ int sdi_init_display(struct omap_dss_device *display); /* DSI */ int dsi_init(struct platform_device *pdev); void dsi_exit(void); +int dsi2_init(struct platform_device *pdev); +void dsi2_exit(void); -void dsi_dump_clocks(struct seq_file *s); -void dsi_dump_irqs(struct seq_file *s); -void dsi_dump_regs(struct seq_file *s); +void dsi_dump_clocks(enum omap_dsi_index ix, struct seq_file *s); +void dsi_dump_irqs(enum omap_dsi_index ix, struct seq_file *s); +void dsi_dump_regs(enum omap_dsi_index ix, struct seq_file *s); void dsi_save_context(void); void dsi_restore_context(void); int dsi_init_display(struct omap_dss_device *display); -void dsi_irq_handler(void); -unsigned long dsi_get_dsi1_pll_rate(void); -int dsi_pll_set_clock_div(struct dsi_clock_info *cinfo); -int dsi_pll_calc_clock_div_pck(bool is_tft, unsigned long req_pck, +irqreturn_t dsi_irq_handler(int irq, void *arg); +unsigned long dsi_get_dsi1_pll_rate(enum omap_dsi_index ix); +int dsi_pll_set_clock_div(enum omap_dsi_index ix, + struct dsi_clock_info *cinfo); +int dsi_pll_calc_clock_div_pck(enum omap_dsi_index ix, + bool is_tft, unsigned long req_pck, struct dsi_clock_info *cinfo, struct dispc_clock_info *dispc_cinfo); int dsi_pll_init(struct omap_dss_device *dssdev, bool enable_hsclk, bool enable_hsdiv); -void dsi_pll_uninit(void); +void dsi_pll_uninit(enum omap_dsi_index ix); void dsi_get_overlay_fifo_thresholds(enum omap_plane plane, u32 fifo_size, enum omap_burst_size *burst_size, u32 *fifo_low, u32 *fifo_high); +int dsi_calc_clock_rates(struct dsi_clock_info *cinfo); /* DPI */ int dpi_init(struct platform_device *pdev); @@ -283,7 +329,7 @@ void dispc_dump_clocks(struct seq_file *s); void dispc_dump_irqs(struct seq_file *s); void dispc_dump_regs(struct seq_file *s); void dispc_irq_handler(void); -void dispc_fake_vsync_irq(void); +void dispc_fake_vsync_irq(enum omap_dsi_index ix); void dispc_save_context(void); void dispc_restore_context(void); @@ -295,17 +341,24 @@ void dispc_lcd_enable_signal_polarity(bool act_high); void dispc_lcd_enable_signal(bool enable); void dispc_pck_free_enable(bool enable); void dispc_enable_fifohandcheck(bool enable); +void dispc_enable_lcd_out(enum omap_channel channel, bool enable); -void dispc_set_lcd_size(u16 width, u16 height); +void dispc_set_lcd_size(enum omap_channel channel, u16 width, u16 height); void dispc_set_digit_size(u16 width, u16 height); u32 dispc_get_plane_fifo_size(enum omap_plane plane); void dispc_setup_plane_fifo(enum omap_plane plane, u32 low, u32 high); void dispc_enable_fifomerge(bool enable); void dispc_set_burst_size(enum omap_plane plane, enum omap_burst_size burst_size); +void dispc_set_zorder(enum omap_plane plane, + enum omap_overlay_zorder zorder); +void dispc_enable_zorder(enum omap_plane plane, bool enable); void dispc_set_plane_ba0(enum omap_plane plane, u32 paddr); void dispc_set_plane_ba1(enum omap_plane plane, u32 paddr); +void dispc_enable_gamma_table(bool enable); +void dispc_set_idle_mode(void); + void dispc_set_plane_pos(enum omap_plane plane, u16 x, u16 y); void dispc_set_plane_size(enum omap_plane plane, u16 width, u16 height); void dispc_set_channel_out(enum omap_plane plane, @@ -320,18 +373,23 @@ int dispc_setup_plane(enum omap_plane plane, bool ilace, enum omap_dss_rotation_type rotation_type, u8 rotation, bool mirror, - u8 global_alpha); + u8 global_alpha, enum omap_channel channel, + u32 puv_addr); bool dispc_go_busy(enum omap_channel channel); void dispc_go(enum omap_channel channel); +void dispc_enable_digit_out(bool enable); void dispc_enable_channel(enum omap_channel channel, bool enable); bool dispc_is_channel_enabled(enum omap_channel channel); int dispc_enable_plane(enum omap_plane plane, bool enable); void dispc_enable_replication(enum omap_plane plane, bool enable); -void dispc_set_parallel_interface_mode(enum omap_parallel_interface_mode mode); -void dispc_set_tft_data_lines(u8 data_lines); -void dispc_set_lcd_display_type(enum omap_lcd_display_type type); +void dispc_set_parallel_interface_mode(enum omap_channel channel, + enum omap_parallel_interface_mode mode); +void dispc_set_tft_data_lines(enum omap_channel channel, + u8 data_lines); +void dispc_set_lcd_display_type(enum omap_channel channel, + enum omap_lcd_display_type type); void dispc_set_loadmode(enum omap_dss_load_mode mode); void dispc_set_default_color(enum omap_channel channel, u32 color); @@ -348,18 +406,23 @@ bool dispc_trans_key_enabled(enum omap_channel ch); bool dispc_alpha_blending_enabled(enum omap_channel ch); bool dispc_lcd_timings_ok(struct omap_video_timings *timings); -void dispc_set_lcd_timings(struct omap_video_timings *timings); +void dispc_set_lcd_timings(enum omap_channel channel, + struct omap_video_timings *timings); unsigned long dispc_fclk_rate(void); -unsigned long dispc_lclk_rate(void); -unsigned long dispc_pclk_rate(void); -void dispc_set_pol_freq(enum omap_panel_config config, u8 acbi, u8 acb); +unsigned long dispc_lclk_rate(enum omap_channel channel); +unsigned long dispc_pclk_rate(enum omap_channel channel); +void dispc_set_pol_freq(enum omap_channel channel, + enum omap_panel_config config, u8 acbi, u8 acb); void dispc_find_clk_divs(bool is_tft, unsigned long req_pck, unsigned long fck, struct dispc_clock_info *cinfo); int dispc_calc_clock_rates(unsigned long dispc_fclk_rate, struct dispc_clock_info *cinfo); -int dispc_set_clock_div(struct dispc_clock_info *cinfo); -int dispc_get_clock_div(struct dispc_clock_info *cinfo); - +int dispc_set_clock_div(enum omap_channel channel, + struct dispc_clock_info *cinfo); +int dispc_get_clock_div(enum omap_channel channel, + struct dispc_clock_info *cinfo); +void dispc_go_wb(void); +int dispc_setup_wb(struct writeback_cache_data *wb); /* VENC */ int venc_init(struct platform_device *pdev); @@ -380,6 +443,12 @@ void rfbi_set_timings(int rfbi_module, struct rfbi_timings *t); unsigned long rfbi_get_max_tx_rate(void); int rfbi_init_display(struct omap_dss_device *display); +#ifdef CONFIG_OMAP2_DSS_HDMI +int hdmi_init(struct platform_device *pdev, int code, int mode); +void hdmi_exit(void); +void hdmi_dump_regs(struct seq_file *s); +int hdmi_init_display(struct omap_dss_device *display); +#endif #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS static inline void dss_collect_irq_stats(u32 irqstatus, unsigned *irq_arr) diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c new file mode 100644 index 000000000000..6a248cce7ec8 --- /dev/null +++ b/drivers/video/omap2/dss/hdmi.c @@ -0,0 +1,1373 @@ +/* + * linux/drivers/video/omap2/dss/hdmi.c + * + * Copyright (C) 2009 Texas Instruments + * Author: Yong Zhi + * + * HDMI settings from TI's DSS driver + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + * History: + * Mythripk <mythripk@ti.com> Apr 2010 Modified for EDID reading and adding OMAP + * related timing + * May 2010 Added support of Hot Plug Detect + * + */ + +#define DSS_SUBSYS_NAME "HDMI" + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/mutex.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/platform_device.h> +#include <plat/display.h> +#include <plat/cpu.h> +#include <plat/hdmi_lib.h> +#include <plat/gpio.h> + +#include "dss.h" +#include "hdmi.h" + +#define HDMI_PLLCTRL 0x58006200 +#define HDMI_PHY 0x58006300 + +u16 current_descriptor_addrs; +u8 edid[HDMI_EDID_MAX_LENGTH] = {0}; +u8 edid_set = 0; +u8 header[8] = {0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0}; +u8 hpd_mode = 0; +/* PLL */ +#define PLLCTRL_PLL_CONTROL 0x0ul +#define PLLCTRL_PLL_STATUS 0x4ul +#define PLLCTRL_PLL_GO 0x8ul +#define PLLCTRL_CFG1 0xCul +#define PLLCTRL_CFG2 0x10ul +#define PLLCTRL_CFG3 0x14ul +#define PLLCTRL_CFG4 0x20ul + +/* HDMI PHY */ +#define HDMI_TXPHY_TX_CTRL 0x0ul +#define HDMI_TXPHY_DIGITAL_CTRL 0x4ul +#define HDMI_TXPHY_POWER_CTRL 0x8ul +#define HDMI_TXPHY_PAD_CFG_CTRL 0xCul + +/*This is the structure which has all supported timing values that OMAP4 supports*/ +struct omap_video_timings all_timings_direct[31] = { {640, 480, 25200, 96, 16, 48, 2, 10, 33}, + {1280, 720, 74250, 40, 440, 220, 5, 5, 20}, + {1280, 720, 74250, 40, 110, 220, 5, 5, 20}, + {720, 480, 27000, 62, 16, 60, 6, 9, 30}, + {2880, 576, 108000, 256, 48, 272, 5, 5, 39}, + {1440, 480, 27000, 124, 38, 114, 3, 4, 15}, + {1440, 576, 27000, 126, 24, 138, 3, 2, 19}, + {1920, 1080, 74250, 44, 528, 148, 2, 5, 15}, + {1920, 1080, 74250, 44, 88, 148, 2, 5, 15}, + {1920, 1080, 148500, 44, 88, 148, 5, 4, 36}, + {720, 576, 27000, 64, 12, 68, 5, 5, 39}, + {1440, 576, 54000, 128, 24, 136, 5, 5, 39}, + {1920, 1080, 148500, 44, 528, 148, 5, 4, 36}, + {2880, 480, 108000, 248, 64, 240, 6, 9, 30}, + /*Vesa frome here*/ + {640, 480, 25175, 96, 16, 48, 2 , 11, 31}, + {800, 600, 40000, 128, 40, 88, 4 , 1, 23}, + {848, 480, 33750, 112, 16, 112, 8 , 6, 23}, + {1280, 768, 71000, 128, 64, 192, 7 , 3, 20}, + {1280, 800, 83500, 128, 72, 200, 6 , 3, 22}, {1360, 768, 85500, 112, 64, 256, 6 , 3, 18}, + {1280, 960, 108000, 112, 96, 312, 3 , 1, 36}, + {1280, 1024, 108000, 112, 48, 248, 3 , 1, 38}, + {1024, 768, 65000, 136, 24, 160, 6, 3, 29}, + {1400, 1050, 121750, 144, 88, 232, 4, 3, 32}, + {1440, 900, 106500, 152, 80, 232, 6, 3, 25}, + {1680, 1050, 146250, 176 , 104, 280, 6, 3, 30}, + {1366, 768, 85500, 143, 70, 213, 3, 3, 24}, + {1920, 1080, 148500, 44, 88, 80, 5, 4, 36}, + {1280, 768, 68250, 32, 48, 80, 7, 3, 12}, + {1400, 1050, 101000, 32, 48, 80, 4, 3, 23}, {1680, 1050, 119000, 32, 48, 80, 6, 3, 21} } ; + +/*This is a static Mapping array which maps the timing values with corresponding CEA / VESA code*/ +int code_index[31] = {1, 19, 4, 2, 37, 6, 21, 20, 5, 16, 17, 29, 31, 35, + /* <--14 CEA 17--> vesa*/ + 4, 9, 0xE, 0x17, 0x1C, 0x27, 0x20, 0x23, 0x10, 0x2A, + 0X2F, 0x3A, 0X51, 0X52, 0x16, 0x29, 0x39}; + +/*This is revere static mapping which maps the CEA / VESA code to the corresponding timing values*/ +int code_cea[39] = {-1, 0, 3, 3, 2, 8, 5, 5, -1, -1, -1, -1, -1, -1, -1, -1, 9, + 10, 10, 1, 7, 6, 6 , -1, -1, -1, -1, -1, -1, 11, 11, + 12, -1, -1, -1, 13, 13, 4, 4}; + +int code_vesa[83] = {-1, -1, -1, -1, 14, -1, -1, -1, -1, 15, -1, -1, -1, -1, 16, + -1, 22, -1, -1, -1, -1, -1, 28, 17, -1, -1, -1, -1, 18, + -1, -1, -1, 20, -1, -1, 21, -1, -1, -1, 19, -1, 29, 23, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 30, 25, -1, -1, -1, -1, -1, -1, -1 , -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, 27}; + + + +static struct { + void __iomem *base_phy; + void __iomem *base_pll; + struct mutex lock; + int code; + int mode; + HDMI_Timing_t ti; +} hdmi; + +static struct hdmi_cm { + int code; + int mode; +}; +struct omap_video_timings edid_timings; + +static inline void hdmi_write_reg(u32 base, u16 idx, u32 val) +{ + void __iomem *b; + + switch (base) { + case HDMI_PHY: + b = hdmi.base_phy; + break; + case HDMI_PLLCTRL: + b = hdmi.base_pll; + break; + default: + BUG(); + } + __raw_writel(val, b + idx); + /* DBG("write = 0x%x idx =0x%x\r\n", val, idx); */ +} + +static inline u32 hdmi_read_reg(u32 base, u16 idx) +{ + void __iomem *b; + u32 l; + + switch (base) { + case HDMI_PHY: + b = hdmi.base_phy; + break; + case HDMI_PLLCTRL: + b = hdmi.base_pll; + break; + default: + BUG(); + } + l = __raw_readl(b + idx); + + /* DBG("addr = 0x%p rd = 0x%x idx = 0x%x\r\n", (b+idx), l, idx); */ + return l; +} + +#define FLD_GET(val, start, end) (((val) & FLD_MASK(start, end)) >> (end)) +#define FLD_MOD(orig, val, start, end) \ + (((orig) & ~FLD_MASK(start, end)) | FLD_VAL(val, start, end)) + +#define REG_FLD_MOD(b, i, v, s, e) \ + hdmi_write_reg(b, i, FLD_MOD(hdmi_read_reg(b, i), v, s, e)) + +/* + * refclk = (sys_clk/(highfreq+1))/(n+1) + * so refclk = 38.4/2/(n+1) = 19.2/(n+1) + * choose n = 15, makes refclk = 1.2 + * + * m = tclk/cpf*refclk = tclk/2*1.2 + * + * for clkin = 38.2/2 = 192 + * phy = 2520 + * + * m = 2520*16/2* 192 = 105; + * + * for clkin = 38.4 + * phy = 2520 + * + */ + +#define CPF 2 + +struct hdmi_pll_info { + u16 regn; + u16 regm; + u32 regmf; + u16 regm4; /* M4_CLOCK_DIV */ + u16 regm2; + u16 regsd; + u16 dcofreq; +}; + +static void compute_pll(int clkin, int phy, + int n, struct hdmi_pll_info *pi) +{ + int refclk; + u32 temp, mf; + + if (clkin > 3200) /* 32 mHz */ + refclk = clkin / (2 * (n + 1)); + else + refclk = clkin / (n + 1); + + temp = phy * 100/(CPF * refclk); + + pi->regn = n; + pi->regm = temp/100; + pi->regm2 = 1; + + mf = (phy - pi->regm * CPF * refclk) * 262144; + pi->regmf = mf/(CPF * refclk); + + if (phy > 1000 * 100) { + pi->regm4 = phy / 10000; + pi->dcofreq = 1; + pi->regsd = ((pi->regm * 384)/((n + 1) * 250) + 5)/10; + } else { + pi->regm4 = 1; + pi->dcofreq = 0; + pi->regsd = 0; + } + + DSSDBG("M = %d Mf = %d, m4= %d\n", pi->regm, pi->regmf, pi->regm4); + DSSDBG("range = %d sd = %d\n", pi->dcofreq, pi->regsd); +} + +static int hdmi_pll_init(int refsel, int dcofreq, struct hdmi_pll_info *fmt, u16 sd) +{ + u32 r; + unsigned t = 500000; + u32 pll = HDMI_PLLCTRL; + + /* PLL start always use manual mode */ + REG_FLD_MOD(pll, PLLCTRL_PLL_CONTROL, 0x0, 0, 0); + + r = hdmi_read_reg(pll, PLLCTRL_CFG1); + r = FLD_MOD(r, fmt->regm, 20, 9); /* CFG1__PLL_REGM */ + r = FLD_MOD(r, fmt->regn, 8, 1); /* CFG1__PLL_REGN */ + r = FLD_MOD(r, fmt->regm4, 25, 21); /* M4_CLOCK_DIV */ + + hdmi_write_reg(pll, PLLCTRL_CFG1, r); + + r = hdmi_read_reg(pll, PLLCTRL_CFG2); + + /* SYS w/o divide by 2 [22:21] = donot care [11:11] = 0x0 */ + /* SYS divide by 2 [22:21] = 0x3 [11:11] = 0x1 */ + /* PCLK, REF1 or REF2 [22:21] = 0x0, 0x 1 or 0x2 [11:11] = 0x1 */ + r = FLD_MOD(r, 0x0, 11, 11); /* PLL_CLKSEL 1: PLL 0: SYS*/ + r = FLD_MOD(r, 0x0, 12, 12); /* PLL_HIGHFREQ divide by 2 */ + r = FLD_MOD(r, 0x1, 13, 13); /* PLL_REFEN */ + r = FLD_MOD(r, 0x0, 14, 14); /* PHY_CLKINEN de-assert during locking */ + r = FLD_MOD(r, 0x1, 20, 20); /* HSDIVBYPASS assert during locking */ + r = FLD_MOD(r, refsel, 22, 21); /* REFSEL */ + /* DPLL3 used by DISPC or HDMI itself*/ + r = FLD_MOD(r, 0x0, 17, 17); /* M4_CLOCK_PWDN */ + r = FLD_MOD(r, 0x1, 16, 16); /* M4_CLOCK_EN */ + + if (dcofreq) { + /* divider programming for 1080p */ + REG_FLD_MOD(pll, PLLCTRL_CFG3, sd, 17, 10); + r = FLD_MOD(r, 0x4, 3, 1); /* 1000MHz and 2000MHz */ + } else + r = FLD_MOD(r, 0x2, 3, 1); /* 500MHz and 1000MHz */ + + hdmi_write_reg(pll, PLLCTRL_CFG2, r); + + r = hdmi_read_reg(pll, PLLCTRL_CFG4); + r = FLD_MOD(r, 0, 24, 18); /* todo: M2 */ + r = FLD_MOD(r, fmt->regmf, 17, 0); + + /* go now */ + REG_FLD_MOD(pll, PLLCTRL_PLL_GO, 0x1ul, 0, 0); + + /* wait for bit change */ + while (FLD_GET(hdmi_read_reg(pll, PLLCTRL_PLL_GO), 0, 0)) + + /* Wait till the lock bit is set */ + /* read PLL status */ + while (0 == FLD_GET(hdmi_read_reg(pll, PLLCTRL_PLL_STATUS), 1, 1)) { + udelay(1); + if (!--t) { + printk(KERN_WARNING "HDMI: cannot lock PLL\n"); + DSSDBG("CFG1 0x%x\n", hdmi_read_reg(pll, PLLCTRL_CFG1)); + DSSDBG("CFG2 0x%x\n", hdmi_read_reg(pll, PLLCTRL_CFG2)); + DSSDBG("CFG4 0x%x\n", hdmi_read_reg(pll, PLLCTRL_CFG4)); + return -EIO; + } + } + + DSSDBG("PLL locked!\n"); + + r = hdmi_read_reg(pll, PLLCTRL_CFG2); + r = FLD_MOD(r, 0, 0, 0); /* PLL_IDLE */ + r = FLD_MOD(r, 0, 5, 5); /* PLL_PLLLPMODE */ + r = FLD_MOD(r, 0, 6, 6); /* PLL_LOWCURRSTBY */ + r = FLD_MOD(r, 0, 8, 8); /* PLL_DRIFTGUARDEN */ + r = FLD_MOD(r, 0, 10, 9); /* PLL_LOCKSEL */ + r = FLD_MOD(r, 1, 13, 13); /* PLL_REFEN */ + r = FLD_MOD(r, 1, 14, 14); /* PHY_CLKINEN */ + r = FLD_MOD(r, 0, 15, 15); /* BYPASSEN */ + r = FLD_MOD(r, 0, 20, 20); /* HSDIVBYPASS */ + hdmi_write_reg(pll, PLLCTRL_CFG2, r); + + return 0; +} + +static int hdmi_pll_reset(void) +{ + int t = 0; + + /* SYSREEST controled by power FSM*/ + REG_FLD_MOD(HDMI_PLLCTRL, PLLCTRL_PLL_CONTROL, 0x0, 3, 3); + + /* READ 0x0 reset is in progress */ + while (!FLD_GET(hdmi_read_reg(HDMI_PLLCTRL, + PLLCTRL_PLL_STATUS), 0, 0)) { + udelay(1); + if (t++ > 1000) { + ERR("Failed to sysrest PLL\n"); + return -ENODEV; + } + } + return 0; +} + +int hdmi_pll_program(struct hdmi_pll_info *fmt) +{ + u32 r; + int refsel; + + HDMI_PllPwr_t PllPwrWaitParam; + + /* wait for wrapper rest */ + HDMI_W1_SetWaitSoftReset(); + + /* power off PLL */ + PllPwrWaitParam = HDMI_PLLPWRCMD_ALLOFF; + r = HDMI_W1_SetWaitPllPwrState(HDMI_WP, + PllPwrWaitParam); + if (r) + return r; + + /* power on PLL */ + PllPwrWaitParam = HDMI_PLLPWRCMD_BOTHON_ALLCLKS; + r = HDMI_W1_SetWaitPllPwrState(HDMI_WP, + PllPwrWaitParam); + if (r) + return r; + + hdmi_pll_reset(); + + refsel = 0x3; /* select SYSCLK reference */ + + r = hdmi_pll_init(refsel, fmt->dcofreq, fmt, fmt->regsd); + + return r; +} + +/* double check the order */ +static int hdmi_phy_init(u32 w1, + u32 phy) +{ + u32 count; + int r; + + /* wait till PHY_PWR_STATUS=LDOON */ + /* HDMI_PHYPWRCMD_LDOON = 1 */ + r = HDMI_W1_SetWaitPhyPwrState(w1, 1); + if (r) + return r; + + /* wait till PHY_PWR_STATUS=TXON */ + r = HDMI_W1_SetWaitPhyPwrState(w1, 2); + if (r) + return r; + + /* read address 0 in order to get the SCPreset done completed */ + /* Dummy access performed to solve resetdone issue */ + hdmi_read_reg(phy, HDMI_TXPHY_TX_CTRL); + + /* write to phy address 0 to configure the clock */ + /* use HFBITCLK write HDMI_TXPHY_TX_CONTROL__FREQOUT field */ + REG_FLD_MOD(phy, HDMI_TXPHY_TX_CTRL, 0x1, 31, 30); + + /* write to phy address 1 to start HDMI line (TXVALID and TMDSCLKEN) */ + hdmi_write_reg(phy, HDMI_TXPHY_DIGITAL_CTRL, + 0xF0000000); + + /* setup max LDO voltage */ + REG_FLD_MOD(phy, HDMI_TXPHY_POWER_CTRL, 0xB, 3, 0); + /* write to phy address 3 to change the polarity control */ + REG_FLD_MOD(phy, HDMI_TXPHY_PAD_CFG_CTRL, 0x1, 27, 27); + + count = 0; + while (count++ < 1000) + ; + + return 0; +} + +static int hdmi_phy_off(u32 name) +{ + int r = 0; + u32 count; + + /* wait till PHY_PWR_STATUS=OFF */ + /* HDMI_PHYPWRCMD_OFF = 0 */ + r = HDMI_W1_SetWaitPhyPwrState(name, 0); + if (r) + return r; + + count = 0; + while (count++ < 200) + ; + + return 0; +} + +/* driver */ +static int hdmi_panel_probe(struct omap_dss_device *dssdev) +{ + int code; + DSSDBG("ENTER hdmi_panel_probe()\n"); + + dssdev->panel.config = OMAP_DSS_LCD_TFT | + OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS; + + if (hdmi.mode == 0) + code = code_vesa[hdmi.code]; + else + code = code_cea[hdmi.code]; + if (code == -1) { + code = 9; + hdmi.code = 16; + hdmi.mode = 1; + } + + dssdev->panel.timings = all_timings_direct[code]; + DSSDBG("hdmi_panel_probe x_res= %d y_res = %d", dssdev->panel.timings.x_res, + dssdev->panel.timings.y_res); + + return 0; +} + +static void hdmi_panel_remove(struct omap_dss_device *dssdev) +{ + +} + +static int hdmi_panel_enable(struct omap_dss_device *dssdev) +{ + hdmi_enable_display(dssdev); + return 0; +} + +static void hdmi_panel_disable(struct omap_dss_device *dssdev) +{ + hdmi_disable_display(dssdev); +} + +static int hdmi_panel_suspend(struct omap_dss_device *dssdev) +{ + hdmi_display_suspend(dssdev); + return 0; +} + +static int hdmi_panel_resume(struct omap_dss_device *dssdev) +{ + hdmi_display_resume(dssdev); + return 0; +} + +static void hdmi_enable_clocks(int enable) +{ + if (enable) + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_54M | + DSS_CLK_96M); + else + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_54M | + DSS_CLK_96M); +} + +static struct omap_dss_driver hdmi_driver = { + .probe = hdmi_panel_probe, + .remove = hdmi_panel_remove, + + .enable = hdmi_panel_enable, + .disable = hdmi_panel_disable, + .suspend = hdmi_panel_suspend, + .resume = hdmi_panel_resume, + .get_timings = hdmi_get_timings, + .set_timings = hdmi_set_timings, + .check_timings = hdmi_check_timings, + .get_edid = hdmi_get_edid, + .set_custom_edid_timing_code = hdmi_set_custom_edid_timing_code, + .hpd_enable = hdmi_enable_hpd, + .driver = { + .name = "hdmi_panel", + .owner = THIS_MODULE, + }, +}; +/* driver end */ + +int hdmi_init(struct platform_device *pdev, int code, int mode) +{ + int r = 0; + DSSDBG("Enter hdmi_init()\n"); + + mutex_init(&hdmi.lock); + + hdmi.base_pll = ioremap(HDMI_PLLCTRL, 64); + if (!hdmi.base_pll) { + ERR("can't ioremap pll\n"); + return -ENOMEM; + } + hdmi.base_phy = ioremap(HDMI_PHY, 64); + + if (!hdmi.base_phy) { + ERR("can't ioremap phy\n"); + return -ENOMEM; + } + hdmi.code = code; + hdmi.mode = mode; + DSSDBG("%d code %d mode", hdmi.code, hdmi.mode); + hdmi_enable_clocks(1); + + hdmi_lib_init(); + + hdmi_enable_clocks(0); + r = request_irq(OMAP44XX_IRQ_DSS_HDMI, hdmi_irq_handler, + 0, "OMAP HDMI", (void *)0); + + + return omap_dss_register_driver(&hdmi_driver); + +} + +void hdmi_exit(void) +{ + hdmi_lib_exit(); + free_irq(OMAP44XX_IRQ_DSS_HDMI, NULL); + iounmap(hdmi.base_pll); + iounmap(hdmi.base_phy); +} + +static int hdmi_power_on(struct omap_dss_device *dssdev) +{ + int r = 0; + int code = 0; + struct omap_video_timings *p; + struct hdmi_pll_info pll_data; + + int clkin, n, phy; + + if (hdmi.mode == 0) + code = code_vesa[hdmi.code]; + else + code = code_cea[hdmi.code]; + if (code == -1) { + code = 9; + hdmi.code = 16; + hdmi.mode = 1; + } + + dssdev->panel.timings = all_timings_direct[code]; + DSSDBG("hdmi_panel_probe x_res= %d y_res = %d", dssdev->panel.timings.x_res, + dssdev->panel.timings.y_res); + + hdmi_enable_clocks(1); + + p = &dssdev->panel.timings; + + r = hdmi_read_edid(p); + if (r) { + r = -EIO; + goto err; + } + + clkin = 3840; /* 38.4 mHz */ + n = 15; /* this is a constant for our math */ + phy = p->pixel_clock; + compute_pll(clkin, phy, n, &pll_data); + + HDMI_W1_StopVideoFrame(HDMI_WP); + + dispc_enable_digit_out(0); + + /* config the PLL and PHY first */ + r = hdmi_pll_program(&pll_data); + if (r) { + DSSERR("Failed to lock PLL\n"); + r = -EIO; + goto err; + } + + r = hdmi_phy_init(HDMI_WP, HDMI_PHY); + if (r) { + DSSERR("Failed to start PHY\n"); + r = -EIO; + goto err; + } + + DSS_HDMI_CONFIG(hdmi.ti, hdmi.code, hdmi.mode); + + /* these settings are independent of overlays */ + dss_switch_tv_hdmi(1); + + /* bypass TV gamma table*/ + dispc_enable_gamma_table(0); + + /* do not fall into any sort of idle */ + dispc_set_idle_mode(); + + /* tv size */ + dispc_set_digit_size(dssdev->panel.timings.x_res, + dssdev->panel.timings.y_res); + + HDMI_W1_StartVideoFrame(HDMI_WP); + + dispc_enable_digit_out(1); + + return 0; +err: + return r; +} + +int hdmi_min_enable(void) +{ + int r; + DSSDBG("hdmi_min_enable"); + r = hdmi_phy_init(HDMI_WP, HDMI_PHY); + if (r) { + DSSERR("Failed to start PHY\n"); + } + DSS_HDMI_CONFIG(hdmi.ti, hdmi.code, hdmi.mode); + return 0; +} + +static irqreturn_t hdmi_irq_handler(int irq, void *arg) +{ + int r = 0; + struct omap_dss_device *dssdev = NULL; + const char *buf = "hdmi"; + int match(struct omap_dss_device *dssdev2 , void *data) + { + const char *str = data; + return sysfs_streq(dssdev2->name , str); + } + dssdev = omap_dss_find_device((void *)buf , match); + DSSDBG("found hdmi handle %s" , dssdev->name); + HDMI_W1_HPD_handler(&r); + DSSDBG("r = %d", r); + + if ((r == 4 || r == 2) && (hpd_mode == 1)) { + hdmi_phy_off(HDMI_WP); + hdmi_enable_clocks(1); + hdmi_power_on(dssdev); + mdelay(1000); + printk(KERN_INFO "Display enabled"); + } + if (r == 1 || r == 4) { + hpd_mode = 0; + } + if ((r == 3) && (dssdev->state != OMAP_DSS_DISPLAY_DISABLED)) { + printk(KERN_INFO "Display disabled"); + hdmi_power_off(dssdev); + hpd_mode = 1; + + if (dssdev->platform_disable) + dssdev->platform_disable(dssdev); + + hdmi_min_enable(); + } + + return IRQ_HANDLED; + +} + +static void hdmi_power_off(struct omap_dss_device *dssdev) +{ + HDMI_W1_StopVideoFrame(HDMI_WP); + + dispc_enable_digit_out(0); + + hdmi_phy_off(HDMI_WP); + + HDMI_W1_SetWaitPllPwrState(HDMI_WP, HDMI_PLLPWRCMD_ALLOFF); + + if (dssdev->platform_disable) + dssdev->platform_disable(dssdev); + + edid_set = 0; + hdmi_enable_clocks(0); + + /* reset to default */ + +} + +static int hdmi_enable_display(struct omap_dss_device *dssdev) +{ + int r = 0; + DSSDBG("ENTER hdmi_enable_display()\n"); + + mutex_lock(&hdmi.lock); + + /* the tv overlay manager is shared*/ + r = omap_dss_start_device(dssdev); + if (r) { + DSSERR("failed to start device\n"); + goto err; + } + + if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) { + r = -EINVAL; + goto err; + } + + free_irq(OMAP44XX_IRQ_DSS_HDMI, NULL); + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + /* PAD0_HDMI_HPD_PAD1_HDMI_CEC */ + omap_writel(0x01180118, 0x4A100098); + /* PAD0_HDMI_DDC_SCL_PAD1_HDMI_DDC_SDA */ + omap_writel(0x01180118 , 0x4A10009C); + /* CONTROL_HDMI_TX_PHY */ + omap_writel(0x10000000, 0x4A100610); + + if (dssdev->platform_enable) + dssdev->platform_enable(dssdev); + + r = hdmi_power_on(dssdev); + if (r) { + DSSERR("failed to power on device\n"); + goto err; + } + r = request_irq(OMAP44XX_IRQ_DSS_HDMI, hdmi_irq_handler, + 0, "OMAP HDMI", (void *)0); + +err: + mutex_unlock(&hdmi.lock); + return r; + +} + +static int hdmi_enable_hpd(struct omap_dss_device *dssdev) +{ + int r = 0; + DSSDBG("ENTER hdmi_enable_hpd()\n"); + + mutex_lock(&hdmi.lock); + + /* the tv overlay manager is shared*/ + r = omap_dss_start_device(dssdev); + if (r) { + DSSERR("failed to start device\n"); + goto err; + } + + if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) { + r = -EINVAL; + goto err; + } + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + /* PAD0_HDMI_HPD_PAD1_HDMI_CEC */ + omap_writel(0x01180118, 0x4A100098); + /* PAD0_HDMI_DDC_SCL_PAD1_HDMI_DDC_SDA */ + omap_writel(0x01180118 , 0x4A10009C); + /* CONTROL_HDMI_TX_PHY */ + omap_writel(0x10000000, 0x4A100610); + + if (dssdev->platform_enable) + dssdev->platform_enable(dssdev); + + hpd_mode = 1; + r = hdmi_min_enable(); + if (r) { + DSSERR("failed to power on device\n"); + goto err; + } + +err: + mutex_unlock(&hdmi.lock); + return r; + +} + +static void hdmi_disable_display(struct omap_dss_device *dssdev) +{ + DSSDBG("Enter hdmi_disable_display()\n"); + + mutex_lock(&hdmi.lock); + if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED) + goto end; + + if (dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) { + /* suspended is the same as disabled with venc */ + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; + goto end; + } + + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; + omap_dss_stop_device(dssdev); + + hdmi_power_off(dssdev); + + hdmi.code = 16; + hdmi.mode = 1 ; /*setting to default only in case of disable and not suspend*/ +end: + mutex_unlock(&hdmi.lock); +} + +static int hdmi_display_suspend(struct omap_dss_device *dssdev) +{ + int r = 0; + + DSSDBG("hdmi_display_suspend\n"); + mutex_lock(&hdmi.lock); + if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED) + goto end; + + if (dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) + goto end; + + dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; + + omap_dss_stop_device(dssdev); + + hdmi_power_off(dssdev); +end: + mutex_unlock(&hdmi.lock); + return r; +} + +static int hdmi_display_resume(struct omap_dss_device *dssdev) +{ + int r = 0; + + DSSDBG("hdmi_display_resume\n"); + mutex_lock(&hdmi.lock); + + /* the tv overlay manager is shared*/ + r = omap_dss_start_device(dssdev); + if (r) { + DSSERR("failed to start device\n"); + goto err; + } + + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { + r = -EINVAL; + goto err; + } + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + /* PAD0_HDMI_HPD_PAD1_HDMI_CEC */ + omap_writel(0x01180118, 0x4A100098); + /* PAD0_HDMI_DDC_SCL_PAD1_HDMI_DDC_SDA */ + omap_writel(0x01180118 , 0x4A10009C); + /* CONTROL_HDMI_TX_PHY */ + omap_writel(0x10000000, 0x4A100610); + + if (dssdev->platform_enable) + dssdev->platform_enable(dssdev); + + r = hdmi_power_on(dssdev); + if (r) { + DSSERR("failed to power on device\n"); + goto err; + } + +err: + mutex_unlock(&hdmi.lock); + + return r; +} + +static void hdmi_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + *timings = dssdev->panel.timings; +} + +static void hdmi_set_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + DSSDBG("hdmi_set_timings\n"); + + dssdev->panel.timings = *timings; + + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { + /* turn the hdmi off and on to get new timings to use */ + hdmi_disable_display(dssdev); + hdmi_enable_display(dssdev); + } +} + +static void hdmi_set_custom_edid_timing_code(struct omap_dss_device *dssdev, int code , int mode) +{ + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { + /* turn the hdmi off and on to get new timings to use */ + hdmi_disable_display(dssdev); + hdmi.code = code; + hdmi.mode = mode; + hdmi_enable_display(dssdev); + } +} + +static struct hdmi_cm hdmi_get_code(struct omap_video_timings *timing) +{ + int i = 0, code = -1; + struct omap_video_timings temp; + struct hdmi_cm cm = {-1}; + DSSDBG("hdmi_get_code"); + + for (i = 0; i < 31; i++) { + temp = all_timings_direct[i]; + + if (!memcmp(&temp, timing, sizeof(struct omap_video_timings))) { + + code = i; + cm.code = code_index[i]; + if (code < 14) + cm.mode = 1; + else + cm.mode = 0; + printk(KERN_INFO "Hdmi_code = %d mode = %d ", cm.code, cm.mode); + printk(KERN_INFO "Timing Info " + "pixel_clk = %d\n" + "Xresolution =%d\n" + "yresolution =%d\n" + "hfp = %d\n" + "hsw = %d\n" + "hbp = %d\n" + "vfp = %d\n" + "vsw = %d\n" + "vbp = %d\n", + temp.pixel_clock, + temp.x_res, + temp.y_res, + temp.hfp, + temp.hsw, + temp.hbp, + temp.vfp, + temp.vsw, + temp.vbp); + break; + } + + } + return cm; +} + +static void hdmi_get_edid(struct omap_dss_device *dssdev) +{ + u8 i = 0, flag = 0; + int count, offset, effective_addrs; + if (edid_set != 1) { + printk(KERN_WARNING "Display doesnt seem to be enabled invalid read\n"); + if (HDMI_CORE_DDC_READEDID(HDMI_CORE_SYS, edid) != 0) { + printk(KERN_WARNING "HDMI failed to read E-EDID\n"); + } + for (i = 0x00; i < 0x08; i++) { + if (edid[i] == header[i]) + continue; + else { + flag = 1; + break; + } + } + if (flag == 0) + edid_set = 1; + } + + mdelay(1000); + + printk("\nHeader:\n"); + for (i = 0x00; i < 0x08; i++) + printk("%02x ", edid[i]); + printk("\nVendor & Product:\n"); + for (i = 0x08; i < 0x12; i++) + printk("%02x ", edid[i]); + printk("\nEDID Structure:\n"); + for (i = 0x12; i < 0x14; i++) + printk("%02x ", edid[i]); + printk("\nBasic Display Parameter:\n"); + for (i = 0x14; i < 0x19; i++) + printk("%02x ", edid[i]); + printk("\nColor Characteristics:\n"); + for (i = 0x19; i < 0x23; i++) + printk("%02x ", edid[i]); + printk("\nEstablished timings:\n"); + for (i = 0x23; i < 0x26; i++) + printk("%02x ", edid[i]); + printk("\nStandard timings:\n"); + for (i = 0x26; i < 0x36; i++) + printk("%02x ", edid[i]); + + for (count = 0; count < EDID_SIZE_BLOCK0_TIMING_DESCRIPTOR; count++) { + current_descriptor_addrs = + EDID_DESCRIPTOR_BLOCK0_ADDRESS + + count * EDID_TIMING_DESCRIPTOR_SIZE; + show_horz_vert_timing_info(edid); + } + if (edid[0x7e] != 0x00) { + offset = edid[EDID_DESCRIPTOR_BLOCK1_ADDRESS + 2]; + printk("\n offset %x\n", offset); + if (offset != 0) { + effective_addrs = EDID_DESCRIPTOR_BLOCK1_ADDRESS + + offset; + /*to determine the number of descriptor blocks */ + for (count = 0; + count < EDID_SIZE_BLOCK1_TIMING_DESCRIPTOR; + count++) { + current_descriptor_addrs = effective_addrs + + count * EDID_TIMING_DESCRIPTOR_SIZE; + show_horz_vert_timing_info(edid); + } + } + + + } + hdmi_get_image_format(); + hdmi_get_audio_format(); + +} +void show_horz_vert_timing_info(u8 *edid) +{ + struct omap_video_timings timings_value; + + printk(KERN_INFO + "EDID DTD block address = 0x%x\n", + current_descriptor_addrs + ); + /*X and Y resolution */ + timings_value.x_res = (((edid[current_descriptor_addrs + 4] & 0xF0) << 4) | + edid[current_descriptor_addrs + 2]); + timings_value.y_res = (((edid[current_descriptor_addrs + 7] & 0xF0) << 4) | + edid[current_descriptor_addrs + 5]); + timings_value.pixel_clock = ((edid[current_descriptor_addrs + 1] << 8) | + edid[current_descriptor_addrs]); + + timings_value.pixel_clock = 10 * timings_value.pixel_clock; + + /*HORIZONTAL FRONT PORCH */ + timings_value.hfp = edid[current_descriptor_addrs + 8]; + /*HORIZONTAL SYNC WIDTH */ + timings_value.hsw = edid[current_descriptor_addrs + 9]; + /*HORIZONTAL BACK PORCH */ + timings_value.hbp = (((edid[current_descriptor_addrs + 4] + & 0x0F) << 8) | + edid[current_descriptor_addrs + 3]) - + (timings_value.hfp + timings_value.hsw); + /*VERTICAL FRONT PORCH */ + timings_value.vfp = ((edid[current_descriptor_addrs + 10] & + 0xF0) >> 4); + /*VERTICAL SYNC WIDTH */ + timings_value.vsw = (edid[current_descriptor_addrs + 10] & + 0x0F); + /*VERTICAL BACK PORCH */ + timings_value.vbp = (((edid[current_descriptor_addrs + 7] & + 0x0F) << 8) | + edid[current_descriptor_addrs + 6]) - + (timings_value.vfp + timings_value.vsw); + + hdmi_get_code(&timings_value); + +} + +static int hdmi_check_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + DSSDBG("hdmi_check_timings\n"); + + if (memcmp(&dssdev->panel.timings, timings, sizeof(*timings)) == 0) + return 0; + + return -EINVAL; +} + +int hdmi_init_display(struct omap_dss_device *dssdev) +{ + DSSDBG("init_display\n"); + + return 0; +} + +static int hdmi_read_edid(struct omap_video_timings *dp) +{ + int r = 0, i = 0 , flag = 0 , ret; + + u16 horizontal_res; + u16 vertical_res; + u16 pixel_clk; + struct hdmi_cm cm; + struct omap_video_timings *tp; + + memset(edid, 0, HDMI_EDID_MAX_LENGTH); + tp = dp; + + if (edid_set != 1) { + ret = HDMI_CORE_DDC_READEDID(HDMI_CORE_SYS, edid); + } + if (ret != 0) { + printk(KERN_WARNING "HDMI failed to read E-EDID\n"); + + } else { + for (i = 0x00; i < 0x08; i++) { + if (edid[i] == header[i]) + continue; + else { + flag = 1; + break; + } + } + if (flag == 0) + edid_set = 1; + edid_timings.pixel_clock = dp->pixel_clock; + edid_timings.x_res = dp->x_res; + edid_timings.y_res = dp->y_res; + /* search for timings of default resolution */ + if (get_edid_timing_data(edid, &pixel_clk, + &horizontal_res, &vertical_res)) { + dp->pixel_clock = pixel_clk * 10; /* be careful */ + tp = &edid_timings; + } else { + edid_timings.pixel_clock = + all_timings_direct[2].pixel_clock; + edid_timings.x_res = all_timings_direct[2].x_res; + edid_timings.y_res = all_timings_direct[2].y_res; + if (get_edid_timing_data(edid, + &pixel_clk, &horizontal_res, + &vertical_res)) { + dp->pixel_clock = pixel_clk * 10; + dp->x_res = horizontal_res; + dp->y_res = vertical_res; + tp = &edid_timings; + + } + } + + } + cm = hdmi_get_code(tp); + hdmi.code = cm.code; + hdmi.mode = cm.mode; + hdmi.ti.pixelPerLine = tp->x_res; + hdmi.ti.linePerPanel = tp->y_res; + hdmi.ti.horizontalBackPorch = tp->hbp; + hdmi.ti.horizontalFrontPorch = tp->hfp; + hdmi.ti.horizontalSyncPulse = tp->hsw; + hdmi.ti.verticalBackPorch = tp->vbp; + hdmi.ti.verticalFrontPorch = tp->vfp; + hdmi.ti.verticalSyncPulse = tp->vsw; + + return r; +} + +u16 current_descriptor_addrs; + +void get_horz_vert_timing_info(u8 *edid) +{ + /*HORIZONTAL FRONT PORCH */ + edid_timings.hfp = edid[current_descriptor_addrs + 8]; + /*HORIZONTAL SYNC WIDTH */ + edid_timings.hsw = edid[current_descriptor_addrs + 9]; + /*HORIZONTAL BACK PORCH */ + edid_timings.hbp = (((edid[current_descriptor_addrs + 4] + & 0x0F) << 8) | + edid[current_descriptor_addrs + 3]) - + (edid_timings.hfp + edid_timings.hsw); + /*VERTICAL FRONT PORCH */ + edid_timings.vfp = ((edid[current_descriptor_addrs + 10] & + 0xF0) >> 4); + /*VERTICAL SYNC WIDTH */ + edid_timings.vsw = (edid[current_descriptor_addrs + 10] & + 0x0F); + /*VERTICAL BACK PORCH */ + edid_timings.vbp = (((edid[current_descriptor_addrs + 7] & + 0x0F) << 8) | + edid[current_descriptor_addrs + 6]) - + (edid_timings.vfp + edid_timings.vsw); + + DSSDBG(KERN_INFO "hfp = %d\n" + "hsw = %d\n" + "hbp = %d\n" + "vfp = %d\n" + "vsw = %d\n" + "vbp = %d\n", + edid_timings.hfp, + edid_timings.hsw, + edid_timings.hbp, + edid_timings.vfp, + edid_timings.vsw, + edid_timings.vbp); + +} + +/*------------------------------------------------------------------------------ + | Function : get_edid_timing_data + +------------------------------------------------------------------------------ + | Description : This function gets the resolution information from EDID + | + | Parameters : void + | + | Returns : void + +----------------------------------------------------------------------------*/ +static int get_edid_timing_data(u8 *edid, u16 *pixel_clk, u16 *horizontal_res, + u16 *vertical_res) +{ + u8 offset, effective_addrs; + u8 count; + u8 flag = false; + /* Seach block 0, there are 4 DTDs arranged in priority order */ + for (count = 0; count < EDID_SIZE_BLOCK0_TIMING_DESCRIPTOR; count++) { + current_descriptor_addrs = + EDID_DESCRIPTOR_BLOCK0_ADDRESS + + count * EDID_TIMING_DESCRIPTOR_SIZE; + *horizontal_res = + (((edid[EDID_DESCRIPTOR_BLOCK0_ADDRESS + 4 + + count * EDID_TIMING_DESCRIPTOR_SIZE] & 0xF0) << 4) | + edid[EDID_DESCRIPTOR_BLOCK0_ADDRESS + 2 + + count * EDID_TIMING_DESCRIPTOR_SIZE]); + *vertical_res = + (((edid[EDID_DESCRIPTOR_BLOCK0_ADDRESS + 7 + + count * EDID_TIMING_DESCRIPTOR_SIZE] & 0xF0) << 4) | + edid[EDID_DESCRIPTOR_BLOCK0_ADDRESS + 5 + + count * EDID_TIMING_DESCRIPTOR_SIZE]); + DSSDBG("***Block-0-Timing-descriptor[%d]***\n", count); +#ifdef EDID_DEBUG + for (i = current_descriptor_addrs; + i < + (current_descriptor_addrs+EDID_TIMING_DESCRIPTOR_SIZE); + i++) + DSSDBG("%d ==> %x\n", i, edid[i]); + + DSSDBG("E-EDID Buffer Index = 0x%x\n" + "horizontal_res = %d\n" + "vertical_res = %d\n", + current_descriptor_addrs, + *horizontal_res, + *vertical_res + ); +#endif + if (*horizontal_res == edid_timings.x_res && + *vertical_res == edid_timings.y_res) { + DSSDBG("Found EDID Data for %d x %dp\n", + *horizontal_res, *vertical_res); + flag = true; + break; + } + } + + /*check for the 1080p in extended block CEA DTDs*/ + if (flag != true) { + offset = edid[EDID_DESCRIPTOR_BLOCK1_ADDRESS + 2]; + if (offset != 0) { + effective_addrs = EDID_DESCRIPTOR_BLOCK1_ADDRESS + + offset; + /*to determine the number of descriptor blocks */ + for (count = 0; + count < EDID_SIZE_BLOCK1_TIMING_DESCRIPTOR; + count++) { + current_descriptor_addrs = effective_addrs + + count * EDID_TIMING_DESCRIPTOR_SIZE; + *horizontal_res = + (((edid[effective_addrs + 4 + + count*EDID_TIMING_DESCRIPTOR_SIZE] & + 0xF0) << 4) | + edid[effective_addrs + 2 + + count * EDID_TIMING_DESCRIPTOR_SIZE]); + *vertical_res = + (((edid[effective_addrs + 7 + + count*EDID_TIMING_DESCRIPTOR_SIZE] & + 0xF0) << 4) | + edid[effective_addrs + 5 + + count * EDID_TIMING_DESCRIPTOR_SIZE]); + + DSSDBG("Block1-Timing-descriptor[%d]\n", count); +#ifdef EDID_DEBUG + for (i = current_descriptor_addrs; + i < (current_descriptor_addrs+ + EDID_TIMING_DESCRIPTOR_SIZE); i++) + DSSDBG("%x ==> %x\n", + i, edid[i]); + + DSSDBG("current_descriptor = 0x%x\n" + "horizontal_res = %d\n" + "vertical_res = %d\n", + current_descriptor_addrs, + *horizontal_res, *vertical_res); +#endif + if (*horizontal_res == edid_timings.x_res && + *vertical_res == edid_timings.y_res) { + DSSDBG("Found EDID Data for " + "%d x %dp\n", + *horizontal_res, + *vertical_res + ); + flag = true; + break; + } + } + } + } + + if (flag == true) { + *pixel_clk = ((edid[current_descriptor_addrs + 1] << 8) | + edid[current_descriptor_addrs]); + + edid_timings.x_res = *horizontal_res; + edid_timings.y_res = *vertical_res; + edid_timings.pixel_clock = *pixel_clk*10; + printk(KERN_INFO "EDID TIMING DATA FOUND\n"); + DSSDBG("EDID DTD block address = 0x%x\n" + "pixel_clk = %d\n" + "horizontal res = %d\n" + "vertical res = %d\n", + current_descriptor_addrs, + edid_timings.pixel_clock, + edid_timings.x_res, + edid_timings.y_res + ); + + get_horz_vert_timing_info(edid); + } else { + + printk(KERN_INFO "EDID TIMING DATA supported NOT FOUND\n"); + DSSDBG("setting default timing values\n" + "pixel_clk = %d\n" + "horizontal res = %d\n" + "vertical res = %d\n", + edid_timings.pixel_clock, + edid_timings.x_res, + edid_timings.y_res + ); + + *pixel_clk = edid_timings.pixel_clock; + *horizontal_res = edid_timings.x_res; + *vertical_res = edid_timings.y_res; + } + + return flag; +} + +void hdmi_dump_regs(struct seq_file *s) +{ + DSSDBG("0x4a100060 x%x\n", omap_readl(0x4A100060)); + DSSDBG("0x4A100088 x%x\n", omap_readl(0x4A100088)); + DSSDBG("0x48055134 x%x\n", omap_readl(0x48055134)); + DSSDBG("0x48055194 x%x\n", omap_readl(0x48055194)); +} diff --git a/drivers/video/omap2/dss/hdmi.h b/drivers/video/omap2/dss/hdmi.h new file mode 100644 index 000000000000..ea2228c6cdb9 --- /dev/null +++ b/drivers/video/omap2/dss/hdmi.h @@ -0,0 +1,245 @@ +/* + * drivers/media/video/omap2/dss/hdmi.h + * + * Copyright (C) 2009 Texas Instruments + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + * hdmi driver + */ +#ifndef _OMAP4_HDMI_H_ +#define _OMAP4_HDMI_H_ + +#define HDMI_EDID_DETAILED_TIMING_OFFSET 0x36 /*EDID Detailed Timing + Info 0 begin offset*/ +#define HDMI_EDID_PIX_CLK_OFFSET 0 +#define HDMI_EDID_H_ACTIVE_OFFSET 2 +#define HDMI_EDID_H_BLANKING_OFFSET 3 +#define HDMI_EDID_V_ACTIVE_OFFSET 5 +#define HDMI_EDID_V_BLANKING_OFFSET 6 +#define HDMI_EDID_H_SYNC_OFFSET 8 +#define HDMI_EDID_H_SYNC_PW_OFFSET 9 +#define HDMI_EDID_V_SYNC_OFFSET 10 +#define HDMI_EDID_V_SYNC_PW_OFFSET 10 +#define HDMI_EDID_H_IMAGE_SIZE_OFFSET 12 +#define HDMI_EDID_V_IMAGE_SIZE_OFFSET 13 +#define HDMI_EDID_H_BORDER_OFFSET 15 +#define HDMI_EDID_V_BORDER_OFFSET 16 +#define HDMI_EDID_FLAGS_OFFSET 17 + + +#define EDID_TIMING_DESCRIPTOR_SIZE 0x12 +#define EDID_DESCRIPTOR_BLOCK0_ADDRESS 0x36 +#define EDID_DESCRIPTOR_BLOCK1_ADDRESS 0x80 +#define EDID_SIZE_BLOCK0_TIMING_DESCRIPTOR 4 +#define EDID_SIZE_BLOCK1_TIMING_DESCRIPTOR 4 + +/* HDMI Connected States */ +#define HDMI_STATE_NOMONITOR 0 /* No HDMI monitor connected*/ +#define HDMI_STATE_CONNECTED 1 /* HDMI monitor connected but powered off*/ +#define HDMI_STATE_ON 2 /* HDMI monitor connected and powered on*/ + + +/* HDMI EDID Length */ +#define HDMI_EDID_MAX_LENGTH 256 + +/* HDMI EDID DTDs */ +#define HDMI_EDID_MAX_DTDS 4 + +/* HDMI EDID DTD Tags */ +#define HDMI_EDID_DTD_TAG_MONITOR_NAME 0xFC +#define HDMI_EDID_DTD_TAG_MONITOR_SERIALNUM 0xFF +#define HDMI_EDID_DTD_TAG_MONITOR_LIMITS 0xFD + + +/* HDMI EDID Extension Data Block Tags */ +#define HDMI_EDID_EX_DATABLOCK_TAG_MASK 0xE0 +#define HDMI_EDID_EX_DATABLOCK_LEN_MASK 0x1F + +#define HDMI_EDID_EX_DATABLOCK_AUDIO 0x20 +#define HDMI_EDID_EX_DATABLOCK_VIDEO 0x40 +#define HDMI_EDID_EX_DATABLOCK_VENDOR 0x60 +#define HDMI_EDID_EX_DATABLOCK_SPEAKERS 0x80 + +/* HDMI EDID Extenion Data Block Values: Video */ +#define HDMI_EDID_EX_VIDEO_NATIVE 0x80 +#define HDMI_EDID_EX_VIDEO_MASK 0x7F +#define HDMI_EDID_EX_VIDEO_MAX 35 + +#define HDMI_EDID_EX_VIDEO_640x480p_60Hz_4_3 1 +#define HDMI_EDID_EX_VIDEO_720x480p_60Hz_4_3 2 +#define HDMI_EDID_EX_VIDEO_720x480p_60Hz_16_9 3 +#define HDMI_EDID_EX_VIDEO_1280x720p_60Hz_16_9 4 +#define HDMI_EDID_EX_VIDEO_1920x1080i_60Hz_16_9 5 +#define HDMI_EDID_EX_VIDEO_720x480i_60Hz_4_3 6 +#define HDMI_EDID_EX_VIDEO_720x480i_60Hz_16_9 7 +#define HDMI_EDID_EX_VIDEO_720x240p_60Hz_4_3 8 +#define HDMI_EDID_EX_VIDEO_720x240p_60Hz_16_9 9 +#define HDMI_EDID_EX_VIDEO_2880x480i_60Hz_4_3 10 +#define HDMI_EDID_EX_VIDEO_2880x480i_60Hz_16_9 11 +#define HDMI_EDID_EX_VIDEO_2880x480p_60Hz_4_3 12 +#define HDMI_EDID_EX_VIDEO_2880x480p_60Hz_16_9 13 +#define HDMI_EDID_EX_VIDEO_1440x480p_60Hz_4_3 14 +#define HDMI_EDID_EX_VIDEO_1440x480p_60Hz_16_9 15 +#define HDMI_EDID_EX_VIDEO_1920x1080p_60Hz_16_9 16 +#define HDMI_EDID_EX_VIDEO_720x576p_50Hz_4_3 17 +#define HDMI_EDID_EX_VIDEO_720x576p_50Hz_16_9 18 +#define HDMI_EDID_EX_VIDEO_1280x720p_50Hz_16_9 19 +#define HDMI_EDID_EX_VIDEO_1920x1080i_50Hz_16_9 20 +#define HDMI_EDID_EX_VIDEO_720x576i_50Hz_4_3 21 +#define HDMI_EDID_EX_VIDEO_720x576i_50Hz_16_9 22 +#define HDMI_EDID_EX_VIDEO_720x288p_50Hz_4_3 23 +#define HDMI_EDID_EX_VIDEO_720x288p_50Hz_16_9 24 +#define HDMI_EDID_EX_VIDEO_2880x576i_50Hz_4_3 25 +#define HDMI_EDID_EX_VIDEO_2880x576i_50Hz_16_9 26 +#define HDMI_EDID_EX_VIDEO_2880x288p_50Hz_4_3 27 +#define HDMI_EDID_EX_VIDEO_2880x288p_50Hz_16_9 28 +#define HDMI_EDID_EX_VIDEO_1440x576p_50Hz_4_3 29 +#define HDMI_EDID_EX_VIDEO_1440x576p_50Hz_16_9 30 +#define HDMI_EDID_EX_VIDEO_1920x1080p_50Hz_16_9 31 +#define HDMI_EDID_EX_VIDEO_1920x1080p_24Hz_16_9 32 +#define HDMI_EDID_EX_VIDEO_1920x1080p_25Hz_16_9 33 +#define HDMI_EDID_EX_VIDEO_1920x1080p_30Hz_16_9 34 + +/*--------------------------------------------------------------------- */ + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Video Descriptor Block */ +typedef struct { + u8 pixel_clock[2]; /* 54-55 */ + u8 horiz_active; /* 56 */ + u8 horiz_blanking; /* 57 */ + u8 horiz_high; /* 58 */ + u8 vert_active; /* 59 */ + u8 vert_blanking; /* 60 */ + u8 vert_high; /* 61 */ + u8 horiz_sync_offset; /* 62 */ + u8 horiz_sync_pulse; /* 63 */ + u8 vert_sync_pulse; /* 64 */ + u8 sync_pulse_high; /* 65 */ + u8 horiz_image_size; /* 66 */ + u8 vert_image_size; /* 67 */ + u8 image_size_high; /* 68 */ + u8 horiz_border; /* 69 */ + u8 vert_border; /* 70 */ + u8 misc_settings; /* 71 */ +} +HDMI_EDID_DTD_VIDEO; + + +/* Monitor Limits Descriptor Block */ +typedef struct { + u8 pixel_clock[2]; /* 54-55*/ + u8 _reserved1; /* 56 */ + u8 block_type; /* 57 */ + u8 _reserved2; /* 58 */ + u8 min_vert_freq; /* 59 */ + u8 max_vert_freq; /* 60 */ + u8 min_horiz_freq; /* 61 */ + u8 max_horiz_freq; /* 62 */ + u8 pixel_clock_mhz; /* 63 */ + + u8 GTF[2]; /* 64 -65 */ + u8 start_horiz_freq; /* 66 */ + u8 C; /* 67 */ + u8 M[2]; /* 68-69 */ + u8 K; /* 70 */ + u8 J; /* 71 */ +} +HDMI_EDID_DTD_MONITOR; + + +/* Text Descriptor Block */ +typedef struct { + u8 pixel_clock[2]; /* 54-55 */ + u8 _reserved1; /* 56 */ + u8 block_type; /* 57 */ + u8 _reserved2; /* 58 */ + + u8 text[13]; /* 59-71 */ +} +HDMI_EDID_DTD_TEXT; + + +/* DTD Union */ +typedef union { + HDMI_EDID_DTD_VIDEO video; + HDMI_EDID_DTD_TEXT monitor_name; + HDMI_EDID_DTD_TEXT monitor_serial_number; + HDMI_EDID_DTD_MONITOR monitor_limits; +} +HDMI_EDID_DTD; + + +/* EDID struct */ +typedef struct { + u8 header[8]; /* 00-07 */ + u8 manufacturerID[2]; /* 08-09 */ + u8 product_id[2]; /* 10-11 */ + u8 serial_number[4]; /* 12-15 */ + u8 week_manufactured; /* 16 */ + u8 year_manufactured; /* 17 */ + u8 edid_version; /* 18 */ + u8 edid_revision; /* 19 */ + + u8 video_in_definition; /* 20 */ + u8 max_horiz_image_size; /* 21 */ + u8 max_vert_image_size; /* 22 */ + u8 display_gamma; /* 23 */ + u8 power_features; /* 24 */ + u8 chroma_info[10]; /* 25-34 */ + u8 timing_1; /* 35 */ + u8 timing_2; /* 36 */ + u8 timing_3; /* 37 */ + u8 std_timings[16]; /* 38-53 */ + + HDMI_EDID_DTD DTD[4]; /* 72-125 */ + + u8 extension_edid; /* 126 */ + u8 checksum; /* 127 */ + + u8 extension_tag; /* 00 (extensions follow EDID) */ + u8 extention_rev; /* 01 */ + u8 offset_dtd; /* 02 */ + u8 num_dtd; /* 03 */ + + u8 data_block[123]; /* 04 - 126 */ + u8 extension_checksum; /* 127 */ + } +HDMI_EDID; + +static int hdmi_enable_display(struct omap_dss_device *dssdev); +static void hdmi_disable_display(struct omap_dss_device *dssdev); +static int hdmi_display_suspend(struct omap_dss_device *dssdev); +static int hdmi_display_resume(struct omap_dss_device *dssdev); +static void hdmi_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings); +static void hdmi_set_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings); +static void hdmi_set_custom_edid_timing_code(struct omap_dss_device *dssdev, int code , int mode); +static void hdmi_get_edid(struct omap_dss_device *dssdev); +static int hdmi_check_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings); +static int hdmi_read_edid(struct omap_video_timings *); +static int get_edid_timing_data(u8 *edid, u16 *pixel_clk, u16 *horizontal_res, + u16 *vertical_res); +void show_horz_vert_timing_info(u8 *edid); +int hdmi_get_image_format(void); +int hdmi_get_audio_format(void); +static irqreturn_t hdmi_irq_handler(int irq, void *arg); +static int hdmi_enable_hpd(struct omap_dss_device *dssdev); +static void hdmi_power_off(struct omap_dss_device *dssdev); + +#ifdef __cplusplus +}; +#endif + +#endif + + diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c index 0820986d4a68..2f32160c8375 100644 --- a/drivers/video/omap2/dss/manager.c +++ b/drivers/video/omap2/dss/manager.c @@ -34,6 +34,8 @@ #include "dss.h" +#define MAX_DSS_MANAGERS (cpu_is_omap44xx() ? 3 : 2) + static int num_managers; static struct list_head manager_list; @@ -415,6 +417,8 @@ struct overlay_cache_data { u32 fifo_high; bool manual_update; + enum omap_overlay_zorder zorder; + u32 p_uv_addr; /* relevent for NV12 format only */ }; struct manager_cache_data { @@ -444,8 +448,9 @@ struct manager_cache_data { static struct { spinlock_t lock; - struct overlay_cache_data overlay_cache[3]; - struct manager_cache_data manager_cache[2]; + struct overlay_cache_data overlay_cache[4]; + struct manager_cache_data manager_cache[3]; + struct writeback_cache_data writeback_cache; bool irq_enabled; } dss_cache; @@ -509,9 +514,20 @@ static int dss_mgr_wait_for_vsync(struct omap_overlay_manager *mgr) if (mgr->device->type == OMAP_DISPLAY_TYPE_VENC) irq = DISPC_IRQ_EVSYNC_ODD; - else - irq = DISPC_IRQ_VSYNC; - + else if (mgr->device->type == OMAP_DISPLAY_TYPE_HDMI) + irq = DISPC_IRQ_EVSYNC_EVEN; + else if ((mgr->device->type == OMAP_DISPLAY_TYPE_DSI) + && (mgr->device->channel == OMAP_DSS_CHANNEL_LCD)) + irq = DISPC_IRQ_FRAMEDONE; + else if ((mgr->device->type == OMAP_DISPLAY_TYPE_DSI) + && (mgr->device->channel == OMAP_DSS_CHANNEL_LCD2)) + irq = DISPC_IRQ_FRAMEDONE2; + else if ((mgr->device->type == OMAP_DISPLAY_TYPE_DPI) + && (mgr->device->channel == OMAP_DSS_CHANNEL_LCD)) + irq = DISPC_IRQ_VSYNC; + else if ((mgr->device->type == OMAP_DISPLAY_TYPE_DPI) + && (mgr->device->channel == OMAP_DSS_CHANNEL_LCD2)) + irq = DISPC_IRQ_VSYNC2; return omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout); } @@ -527,10 +543,10 @@ static int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr) if (!dssdev) return 0; - - if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) { + channel = mgr->device->channel; + if (dssdev->type == OMAP_DISPLAY_TYPE_VENC + || dssdev->type == OMAP_DISPLAY_TYPE_HDMI) { irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN; - channel = OMAP_DSS_CHANNEL_DIGIT; } else { if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { enum omap_dss_update_mode mode; @@ -538,11 +554,16 @@ static int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr) if (mode != OMAP_DSS_UPDATE_AUTO) return 0; - irq = DISPC_IRQ_FRAMEDONE; + irq = (channel == OMAP_DSS_CHANNEL_LCD) ? + DISPC_IRQ_FRAMEDONE + : DISPC_IRQ_FRAMEDONE2; + } else { - irq = DISPC_IRQ_VSYNC; + irq = (channel == OMAP_DSS_CHANNEL_LCD) ? + DISPC_IRQ_VSYNC + : DISPC_IRQ_VSYNC2; + } - channel = OMAP_DSS_CHANNEL_LCD; } mc = &dss_cache.manager_cache[mgr->id]; @@ -600,10 +621,11 @@ int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl) return 0; dssdev = ovl->manager->device; + channel = dssdev->channel; - if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) { + if (dssdev->type == OMAP_DISPLAY_TYPE_VENC + || dssdev->type == OMAP_DISPLAY_TYPE_HDMI) { irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN; - channel = OMAP_DSS_CHANNEL_DIGIT; } else { if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { enum omap_dss_update_mode mode; @@ -611,11 +633,14 @@ int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl) if (mode != OMAP_DSS_UPDATE_AUTO) return 0; - irq = DISPC_IRQ_FRAMEDONE; + irq = (channel == OMAP_DSS_CHANNEL_LCD) ? + DISPC_IRQ_FRAMEDONE + : DISPC_IRQ_FRAMEDONE2; } else { - irq = DISPC_IRQ_VSYNC; + irq = (channel == OMAP_DSS_CHANNEL_LCD) ? + DISPC_IRQ_VSYNC + : DISPC_IRQ_VSYNC2; } - channel = OMAP_DSS_CHANNEL_LCD; } oc = &dss_cache.overlay_cache[ovl->id]; @@ -816,7 +841,9 @@ static int configure_overlay(enum omap_plane plane) c->rotation_type, c->rotation, c->mirror, - c->global_alpha); + c->global_alpha, + c->channel, + c->p_uv_addr); if (r) { /* this shouldn't happen */ @@ -828,8 +855,22 @@ static int configure_overlay(enum omap_plane plane) dispc_enable_replication(plane, c->replication); dispc_set_burst_size(plane, c->burst_size); + dispc_set_zorder(plane, c->zorder); + dispc_enable_zorder(plane, 1); dispc_setup_plane_fifo(plane, c->fifo_low, c->fifo_high); - + if (cpu_is_omap44xx()) { + struct writeback_cache_data *wb; + wb = &dss_cache.writeback_cache; + /*if writeback is enabled and input source is the current overlay + set writeback values and enable wb plane before source plane*/ + if ((wb->enabled) && + (omap_dss_check_wb(wb, plane, c->channel))) { + /* writeback is enabled for this plane - set accordingly */ + dispc_setup_wb(wb); + wb->dirty = false; + wb->shadow_dirty = true; + } + } dispc_enable_plane(plane, 1); return 0; @@ -856,21 +897,25 @@ static int configure_dispc(void) { struct overlay_cache_data *oc; struct manager_cache_data *mc; + struct writeback_cache_data *wb; const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache); - const int num_mgrs = ARRAY_SIZE(dss_cache.manager_cache); + const int num_mgrs = MAX_DSS_MANAGERS; int i; int r; - bool mgr_busy[2]; - bool mgr_go[2]; + bool mgr_busy[MAX_DSS_MANAGERS]; + bool mgr_go[MAX_DSS_MANAGERS]; bool busy; r = 0; busy = false; - mgr_busy[0] = dispc_go_busy(0); - mgr_busy[1] = dispc_go_busy(1); - mgr_go[0] = false; - mgr_go[1] = false; + for (i = 0; i < num_mgrs; i++) { + mgr_busy[i] = dispc_go_busy(i); + mgr_go[i] = false; + } + + if (cpu_is_omap44xx()) + wb = &dss_cache.writeback_cache; /* Commit overlay settings */ for (i = 0; i < num_ovls; ++i) { @@ -894,7 +939,12 @@ static int configure_dispc(void) oc->dirty = false; oc->shadow_dirty = true; + if (!cpu_is_omap44xx()) mgr_go[oc->channel] = true; + else + if (!omap_dss_check_wb(wb, i, -1)) + /*skip manager go if WB enabled*/ + mgr_go[oc->channel] = true; } /* Commit manager settings */ @@ -917,7 +967,28 @@ static int configure_dispc(void) mc->shadow_dirty = true; mgr_go[i] = true; } - + if (cpu_is_omap44xx()) { + /*Enable WB plane and source plane */ + DSSDBG("configure manager wb->shadow_dirty = %d", wb->shadow_dirty); + if (wb->shadow_dirty && wb->enabled) { + DSSDBG("dispc_go_wb_is called after enabling input plane and then WB"); + switch (wb->source) { + case OMAP_WB_OVERLAY0: + case OMAP_WB_OVERLAY1: + case OMAP_WB_OVERLAY2: + case OMAP_WB_OVERLAY3: + dispc_enable_plane(wb->source - 3, 1); + break; + case OMAP_WB_LCD_1_MANAGER: + case OMAP_WB_LCD_2_MANAGER: + case OMAP_WB_TV_MANAGER: + ;/*Do nothing As of now as we dont support Manager yet with WB*/ + } + dispc_go_wb(); + wb->shadow_dirty = false; + dispc_enable_plane(OMAP_DSS_WB, 1); + } + } /* set GO */ for (i = 0; i < num_mgrs; ++i) { mc = &dss_cache.manager_cache[i]; @@ -940,6 +1011,22 @@ static int configure_dispc(void) return r; } +/* Make the coordinates even. There are some strange problems with OMAP and + * partial DSI update when the update widths are odd. */ +static void make_even(u16 *x, u16 *w) +{ + u16 x1, x2; + + x1 = *x; + x2 = *x + *w; + + x1 &= ~1; + x2 = ALIGN(x2, 2); + + *x = x1; + *w = x2 - x1; +} + /* Configure dispc for partial update. Return possibly modified update * area */ void dss_setup_partial_planes(struct omap_dss_device *dssdev, @@ -968,6 +1055,8 @@ void dss_setup_partial_planes(struct omap_dss_device *dssdev, return; } + make_even(&x, &w); + spin_lock_irqsave(&dss_cache.lock, flags); /* We need to show the whole overlay if it is scaled. So look for @@ -1029,6 +1118,8 @@ void dss_setup_partial_planes(struct omap_dss_device *dssdev, w = x2 - x1; h = y2 - y1; + make_even(&x, &w); + DSSDBG("changing upd area due to ovl(%d) scaling %d,%d %dx%d\n", i, x, y, w, h); } @@ -1057,7 +1148,7 @@ void dss_start_update(struct omap_dss_device *dssdev) struct manager_cache_data *mc; struct overlay_cache_data *oc; const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache); - const int num_mgrs = ARRAY_SIZE(dss_cache.manager_cache); + const int num_mgrs = MAX_DSS_MANAGERS; struct omap_overlay_manager *mgr; int i; @@ -1089,10 +1180,10 @@ static void dss_apply_irq_handler(void *data, u32 mask) const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache); const int num_mgrs = ARRAY_SIZE(dss_cache.manager_cache); int i, r; - bool mgr_busy[2]; + bool mgr_busy[MAX_DSS_MANAGERS]; - mgr_busy[0] = dispc_go_busy(0); - mgr_busy[1] = dispc_go_busy(1); + for (i = 0; i < num_mgrs; i++) + mgr_busy[i] = dispc_go_busy(i); spin_lock(&dss_cache.lock); @@ -1125,7 +1216,7 @@ static void dss_apply_irq_handler(void *data, u32 mask) omap_dispc_unregister_isr(dss_apply_irq_handler, NULL, DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD | - DISPC_IRQ_EVSYNC_EVEN); + DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_VSYNC2); dss_cache.irq_enabled = false; end: @@ -1186,6 +1277,7 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr) oc->dirty = true; oc->paddr = ovl->info.paddr; + oc->p_uv_addr = ovl->info.p_uv_addr; oc->vaddr = ovl->info.vaddr; oc->screen_width = ovl->info.screen_width; oc->width = ovl->info.width; @@ -1199,6 +1291,7 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr) oc->out_width = ovl->info.out_width; oc->out_height = ovl->info.out_height; oc->global_alpha = ovl->info.global_alpha; + oc->zorder = ovl->info.zorder; oc->replication = dss_use_replication(dssdev, ovl->info.color_mode); @@ -1298,6 +1391,7 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr) case OMAP_DISPLAY_TYPE_DBI: case OMAP_DISPLAY_TYPE_SDI: case OMAP_DISPLAY_TYPE_VENC: + case OMAP_DISPLAY_TYPE_HDMI: default_get_overlay_fifo_thresholds(ovl->id, size, &oc->burst_size, &oc->fifo_low, &oc->fifo_high); @@ -1319,7 +1413,7 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr) if (!dss_cache.irq_enabled) { r = omap_dispc_register_isr(dss_apply_irq_handler, NULL, DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD | - DISPC_IRQ_EVSYNC_EVEN); + DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_VSYNC2); dss_cache.irq_enabled = true; } configure_dispc(); @@ -1330,6 +1424,274 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr) return r; } +int count_wb_manager = 0; +void wb_irq_handler(void *data, u32 mask) +{ + count_wb_manager++; + DSSDBG("Framedone wb count = %d", count_wb_manager); +} + +int omap_dss_wb_apply(struct omap_overlay_manager *mgr, struct omap_writeback *wb) +{ + struct overlay_cache_data *oc; + struct manager_cache_data *mc; + int i, j; + struct omap_overlay *ovl; + int num_planes_enabled = 0; + bool use_fifomerge; + unsigned long flags; + int r; + struct writeback_cache_data *wbc; + + DSSDBG("omap_dss_wb_apply(%s)\n", mgr->name); + + spin_lock_irqsave(&dss_cache.lock, flags); + + /* Configure overlays */ + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { + struct omap_dss_device *dssdev; + + ovl = omap_dss_get_overlay(i); + + if (ovl == NULL) + break; + + if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) + continue; + + oc = &dss_cache.overlay_cache[ovl->id]; + + if (!overlay_enabled(ovl)) { + if (oc->enabled) { + oc->enabled = false; + oc->dirty = true; + } + continue; + } + oc->enabled = true; + + if (!ovl->info_dirty) { + if (oc->enabled) + ++num_planes_enabled; + continue; + } + + dssdev = ovl->manager->device; + + if (dss_check_overlay(ovl, dssdev)) { + if (oc->enabled) { + oc->enabled = false; + oc->dirty = true; + } + continue; + } + + ovl->info_dirty = false; + oc->dirty = true; + + oc->paddr = ovl->info.paddr; + oc->p_uv_addr = ovl->info.p_uv_addr; + oc->zorder = ovl->info.zorder; + oc->vaddr = ovl->info.vaddr; + oc->screen_width = ovl->info.screen_width; + oc->width = ovl->info.width; + oc->height = ovl->info.height; + oc->color_mode = ovl->info.color_mode; + oc->rotation = ovl->info.rotation; + oc->rotation_type = ovl->info.rotation_type; + oc->mirror = ovl->info.mirror; + oc->pos_x = ovl->info.pos_x; + oc->pos_y = ovl->info.pos_y; + oc->out_width = ovl->info.out_width; + oc->out_height = ovl->info.out_height; + oc->global_alpha = ovl->info.global_alpha; + + oc->replication = + dss_use_replication(dssdev, ovl->info.color_mode); + + oc->ilace = dssdev->type == OMAP_DISPLAY_TYPE_VENC; + + oc->channel = ovl->manager->id; + /* TODO: to change with dssdev->channel? */ + + oc->enabled = true; + + oc->manual_update = + dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE && + dssdev->driver->get_update_mode(dssdev) != OMAP_DSS_UPDATE_AUTO; + + ++num_planes_enabled; + wbc = &dss_cache.writeback_cache; + + if (!wb->first_time) { + DSSDBG("entered wb_first time\n"); + wbc->enabled = true; + wbc->source = wb->info.source; + wbc->source_type = wb->info.source_type; + wb->first_time = true; + } + DSSDBG("dss_wb_apply %d\n", wbc->enabled); + /* Configure Write-back - check for connect with this overlay*/ + if ((wbc->enabled) && + (omap_dss_check_wb(wbc, ovl->id , ovl->manager->id))) { + + DSSDBG("dss_mgr_apply paddr = %lx", wb->info.paddr); + for (j = 0 ; j < 1 ; j++) { + if (!wb->enabled) { + if (wbc->enabled) { + wbc->enabled = false; + wbc->dirty = true; + } + continue; + } + if (!wb->info_dirty) + continue; + + wb->info_dirty = false; + wbc->dirty = true; + + wbc->color_mode = wb->info.dss_mode; + wbc->input_color_mode = oc->color_mode; + /*OMAP_DSS_COLOR_ARGB32; */ + wbc->width = wb->info.width; + wbc->height = wb->info.height; + wbc->input_width = ovl->info.width; + wbc->input_height = ovl->info.height; + + wbc->paddr = wb->info.paddr; + wbc->puv_addr = wb->info.puv_addr; + + wbc->enabled = true; + + wbc->capturemode = wb->info.capturemode; + wbc->burst_size = OMAP_DSS_BURST_16x32; /* 8x128 - min. for OMAP4 */ + wbc->source = wb->info.source; + + /* TODO: Set fifo high, fifo low values ? */ + wbc->fifo_high = 0x28A; + wbc->fifo_low = 0XFA; + } + } + } + if (count_wb_manager == 0) + omap_dispc_register_isr(wb_irq_handler, NULL, + DISPC_IRQ_FRAMEDONE_WB); + /* Configure managers */ + list_for_each_entry(mgr, &manager_list, list) { + struct omap_dss_device *dssdev; + + if (!(mgr->caps & OMAP_DSS_OVL_MGR_CAP_DISPC)) + continue; + + mc = &dss_cache.manager_cache[mgr->id]; + + if (mgr->device_changed) { + mgr->device_changed = false; + mgr->info_dirty = true; + } + + if (!mgr->info_dirty) + continue; + + if (!mgr->device) + continue; + + dssdev = mgr->device; + + mgr->info_dirty = false; + mc->dirty = true; + + mc->default_color = mgr->info.default_color; + mc->trans_key_type = mgr->info.trans_key_type; + mc->trans_key = mgr->info.trans_key; + mc->trans_enabled = mgr->info.trans_enabled; + mc->alpha_enabled = mgr->info.alpha_enabled; + + mc->manual_upd_display = + dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE; + + mc->manual_update = + dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE && + dssdev->driver->get_update_mode(dssdev) != OMAP_DSS_UPDATE_AUTO; + } + + /* XXX TODO: Try to get fifomerge working. The problem is that it + * affects both managers, not individually but at the same time. This + * means the change has to be well synchronized. I guess the proper way + * is to have a two step process for fifo merge: + * fifomerge enable: + * 1. disable other planes, leaving one plane enabled + * 2. wait until the planes are disabled on HW + * 3. config merged fifo thresholds, enable fifomerge + * fifomerge disable: + * 1. config unmerged fifo thresholds, disable fifomerge + * 2. wait until fifo changes are in HW + * 3. enable planes + */ + use_fifomerge = false; + + /* Configure overlay fifos */ + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { + struct omap_dss_device *dssdev; + u32 size; + + ovl = omap_dss_get_overlay(i); + + if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) + continue; + + oc = &dss_cache.overlay_cache[ovl->id]; + + if (!oc->enabled) + continue; + + dssdev = ovl->manager->device; + + size = dispc_get_plane_fifo_size(ovl->id); + if (use_fifomerge) + size *= 3; + + switch (dssdev->type) { + case OMAP_DISPLAY_TYPE_DPI: + case OMAP_DISPLAY_TYPE_DBI: + case OMAP_DISPLAY_TYPE_SDI: + case OMAP_DISPLAY_TYPE_VENC: + case OMAP_DISPLAY_TYPE_HDMI: + default_get_overlay_fifo_thresholds(ovl->id, size, + &oc->burst_size, &oc->fifo_low, + &oc->fifo_high); + break; +#ifdef CONFIG_OMAP2_DSS_DSI + case OMAP_DISPLAY_TYPE_DSI: + dsi_get_overlay_fifo_thresholds(ovl->id, size, + &oc->burst_size, &oc->fifo_low, + &oc->fifo_high); + break; +#endif + default: + BUG(); + } + } + + r = 0; + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + if (!dss_cache.irq_enabled) { + r = omap_dispc_register_isr(dss_apply_irq_handler, NULL, + DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD | + DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_VSYNC2); + + dss_cache.irq_enabled = true; + } + configure_dispc(); + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + + spin_unlock_irqrestore(&dss_cache.lock, flags); + + return r; +} + +EXPORT_SYMBOL(omap_dss_wb_apply); + static int dss_check_manager(struct omap_overlay_manager *mgr) { /* OMAP supports only graphics source transparency color key and alpha @@ -1396,7 +1758,7 @@ int dss_init_overlay_managers(struct platform_device *pdev) num_managers = 0; - for (i = 0; i < 2; ++i) { + for (i = 0; i < MAX_DSS_MANAGERS; ++i) { struct omap_overlay_manager *mgr; mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); @@ -1413,8 +1775,17 @@ int dss_init_overlay_managers(struct platform_device *pdev) case 1: mgr->name = "tv"; mgr->id = OMAP_DSS_CHANNEL_DIGIT; - mgr->supported_displays = OMAP_DISPLAY_TYPE_VENC; + mgr->supported_displays = + OMAP_DISPLAY_TYPE_VENC | OMAP_DISPLAY_TYPE_HDMI; break; + case 2: + mgr->name = "2lcd"; + mgr->id = OMAP_DSS_CHANNEL_LCD2; + mgr->supported_displays = + OMAP_DISPLAY_TYPE_DBI | OMAP_DISPLAY_TYPE_SDI | + OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DSI; + break; + } mgr->set_device = &omap_dss_set_device; diff --git a/drivers/video/omap2/dss/overlay.c b/drivers/video/omap2/dss/overlay.c index 82336583adef..2462f51a6900 100644 --- a/drivers/video/omap2/dss/overlay.c +++ b/drivers/video/omap2/dss/overlay.c @@ -36,6 +36,8 @@ #include "dss.h" +#define MAX_DSS_OVERLAYS (cpu_is_omap44xx() ? 4 : 3) + static int num_overlays; static struct list_head overlay_list; @@ -56,6 +58,7 @@ static ssize_t overlay_manager_store(struct omap_overlay *ovl, const char *buf, int i, r; struct omap_overlay_manager *mgr = NULL; struct omap_overlay_manager *old_mgr; + struct omap_overlay_info info; int len = size; if (buf[size-1] == '\n') @@ -81,6 +84,16 @@ static ssize_t overlay_manager_store(struct omap_overlay *ovl, const char *buf, if (mgr == ovl->manager) return size; + if (sysfs_streq(mgr->name, "tv")) { + ovl->get_overlay_info(ovl, &info); + if (mgr->device->panel.timings.x_res < info.width || + mgr->device->panel.timings.y_res < info.height) { + printk(KERN_ERR"TV does not support downscaling" + "Please configure overlay to supported format"); + return -EINVAL; + } + } + old_mgr = ovl->manager; /* detach old manager */ @@ -166,18 +179,28 @@ static ssize_t overlay_output_size_show(struct omap_overlay *ovl, char *buf) static ssize_t overlay_output_size_store(struct omap_overlay *ovl, const char *buf, size_t size) { - int r; + int r, out_width, out_height; char *last; struct omap_overlay_info info; ovl->get_overlay_info(ovl, &info); - info.out_width = simple_strtoul(buf, &last, 10); + out_width = simple_strtoul(buf, &last, 10); ++last; if (last - buf >= size) return -EINVAL; - info.out_height = simple_strtoul(last, &last, 10); + out_height = simple_strtoul(last, &last, 10); + + if (sysfs_streq(ovl->manager->name, "tv")) { + if (ovl->manager->device->panel.timings.x_res < out_width || + ovl->manager->device->panel.timings.y_res < out_height) + printk(KERN_ERR"TV does not support downscaling , Wrong output size"); + return -EINVAL; + } + + info.out_width = out_width; + info.out_height = out_height; r = ovl->set_overlay_info(ovl, &info); if (r) @@ -234,10 +257,10 @@ static ssize_t overlay_global_alpha_store(struct omap_overlay *ovl, ovl->get_overlay_info(ovl, &info); - /* Video1 plane does not support global alpha + /* In OMAP2/3 Video1 plane does not support global alpha * to always make it 255 completely opaque */ - if (ovl->id == OMAP_DSS_VIDEO1) + if ((!cpu_is_omap44xx()) && (ovl->id == OMAP_DSS_VIDEO1)) info.global_alpha = 255; else info.global_alpha = simple_strtoul(buf, NULL, 10); @@ -255,6 +278,35 @@ static ssize_t overlay_global_alpha_store(struct omap_overlay *ovl, return size; } +static ssize_t overlay_zorder_show(struct omap_overlay *ovl, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", + ovl->info.zorder); +} + +static ssize_t overlay_zorder_store(struct omap_overlay *ovl, + const char *buf, size_t size) +{ + int r; + struct omap_overlay_info info; + + ovl->get_overlay_info(ovl, &info); + + info.zorder = simple_strtoul(buf, NULL, 10); + + r = ovl->set_overlay_info(ovl, &info); + if (r) + return r; + + if (ovl->manager) { + r = ovl->manager->apply(ovl->manager); + if (r) + return r; + } + + return size; +} + struct overlay_attribute { struct attribute attr; ssize_t (*show)(struct omap_overlay *, char *); @@ -278,6 +330,9 @@ static OVERLAY_ATTR(enabled, S_IRUGO|S_IWUSR, overlay_enabled_show, overlay_enabled_store); static OVERLAY_ATTR(global_alpha, S_IRUGO|S_IWUSR, overlay_global_alpha_show, overlay_global_alpha_store); +static OVERLAY_ATTR(zorder, S_IRUGO|S_IWUSR, + overlay_zorder_show, overlay_zorder_store); + static struct attribute *overlay_sysfs_attrs[] = { &overlay_attr_name.attr, @@ -288,6 +343,7 @@ static struct attribute *overlay_sysfs_attrs[] = { &overlay_attr_output_size.attr, &overlay_attr_enabled.attr, &overlay_attr_global_alpha.attr, + &overlay_attr_zorder.attr, NULL }; @@ -392,6 +448,12 @@ int dss_check_overlay(struct omap_overlay *ovl, struct omap_dss_device *dssdev) return -EINVAL; } + if ((info->zorder < OMAP_DSS_OVL_ZORDER_0) || + (info->zorder > OMAP_DSS_OVL_ZORDER_3)) { + DSSERR("overlay doesn't support zorder %d\n", info->zorder); + return -EINVAL; + } + return 0; } @@ -510,11 +572,11 @@ static void omap_dss_add_overlay(struct omap_overlay *overlay) list_add_tail(&overlay->list, &overlay_list); } -static struct omap_overlay *dispc_overlays[3]; +static struct omap_overlay *dispc_overlays[4]; void dss_overlay_setup_dispc_manager(struct omap_overlay_manager *mgr) { - mgr->num_overlays = 3; + mgr->num_overlays = MAX_DSS_OVERLAYS; mgr->overlays = dispc_overlays; } @@ -535,7 +597,7 @@ void dss_init_overlays(struct platform_device *pdev) num_overlays = 0; - for (i = 0; i < 3; ++i) { + for (i = 0; i < MAX_DSS_OVERLAYS; ++i) { struct omap_overlay *ovl; ovl = kzalloc(sizeof(*ovl), GFP_KERNEL); @@ -545,32 +607,48 @@ void dss_init_overlays(struct platform_device *pdev) case 0: ovl->name = "gfx"; ovl->id = OMAP_DSS_GFX; - ovl->supported_modes = cpu_is_omap34xx() ? + ovl->supported_modes = (cpu_is_omap44xx() | + cpu_is_omap34xx()) ? OMAP_DSS_COLOR_GFX_OMAP3 : OMAP_DSS_COLOR_GFX_OMAP2; ovl->caps = OMAP_DSS_OVL_CAP_DISPC; ovl->info.global_alpha = 255; + ovl->info.zorder = OMAP_DSS_OVL_ZORDER_0; break; case 1: ovl->name = "vid1"; ovl->id = OMAP_DSS_VIDEO1; - ovl->supported_modes = cpu_is_omap34xx() ? + ovl->supported_modes = (cpu_is_omap44xx() | + cpu_is_omap34xx()) ? OMAP_DSS_COLOR_VID1_OMAP3 : OMAP_DSS_COLOR_VID_OMAP2; ovl->caps = OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_DISPC; ovl->info.global_alpha = 255; + ovl->info.zorder = OMAP_DSS_OVL_ZORDER_3; break; case 2: ovl->name = "vid2"; ovl->id = OMAP_DSS_VIDEO2; - ovl->supported_modes = cpu_is_omap34xx() ? + ovl->supported_modes = (cpu_is_omap44xx() | + cpu_is_omap34xx()) ? OMAP_DSS_COLOR_VID2_OMAP3 : OMAP_DSS_COLOR_VID_OMAP2; ovl->caps = OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_DISPC; ovl->info.global_alpha = 255; + ovl->info.zorder = OMAP_DSS_OVL_ZORDER_2; break; + case 3: + ovl->name = "vid3"; + ovl->id = OMAP_DSS_VIDEO3; + ovl->supported_modes = OMAP_DSS_COLOR_VID3_OMAP3; + ovl->caps = OMAP_DSS_OVL_CAP_SCALE | + OMAP_DSS_OVL_CAP_DISPC; + ovl->info.global_alpha = 255; + ovl->info.zorder = OMAP_DSS_OVL_ZORDER_1; + break; + } ovl->set_manager = &omap_dss_set_manager; @@ -627,21 +705,35 @@ void dss_recheck_connections(struct omap_dss_device *dssdev, bool force) int i; struct omap_overlay_manager *lcd_mgr; struct omap_overlay_manager *tv_mgr; + struct omap_overlay_manager *lcd2_mgr = NULL; struct omap_overlay_manager *mgr = NULL; lcd_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_LCD); tv_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_TV); - - if (dssdev->type != OMAP_DISPLAY_TYPE_VENC) { - if (!lcd_mgr->device || force) { - if (lcd_mgr->device) - lcd_mgr->unset_device(lcd_mgr); - lcd_mgr->set_device(lcd_mgr, dssdev); - mgr = lcd_mgr; + if (cpu_is_omap44xx()) + lcd2_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_LCD2); + + if (dssdev->channel == OMAP_DSS_CHANNEL_LCD2) { + if (!lcd2_mgr->device || force || sysfs_streq(dssdev->name, "2lcd")) { + if (lcd2_mgr->device) + lcd2_mgr->unset_device(lcd2_mgr); + lcd2_mgr->set_device(lcd2_mgr, dssdev); + mgr = lcd2_mgr; + } + } else if (dssdev->type != OMAP_DISPLAY_TYPE_VENC + && dssdev->type != OMAP_DISPLAY_TYPE_HDMI) { + if (lcd2_mgr) { + if (!lcd_mgr->device || force) { + if (lcd_mgr->device) + lcd_mgr->unset_device(lcd_mgr); + lcd_mgr->set_device(lcd_mgr, dssdev); + mgr = lcd_mgr; + } } } - if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) { + if (dssdev->type == OMAP_DISPLAY_TYPE_VENC + || dssdev->type == OMAP_DISPLAY_TYPE_HDMI) { if (!tv_mgr->device || force) { if (tv_mgr->device) tv_mgr->unset_device(tv_mgr); @@ -651,7 +743,7 @@ void dss_recheck_connections(struct omap_dss_device *dssdev, bool force) } if (mgr) { - for (i = 0; i < 3; i++) { + for (i = 0; i < MAX_DSS_OVERLAYS; i++) { struct omap_overlay *ovl; ovl = omap_dss_get_overlay(i); if (!ovl->manager || force) { diff --git a/drivers/video/omap2/dss/rfbi.c b/drivers/video/omap2/dss/rfbi.c index cc23f53cc62d..a9fbb45ca52f 100644 --- a/drivers/video/omap2/dss/rfbi.c +++ b/drivers/video/omap2/dss/rfbi.c @@ -311,7 +311,7 @@ void rfbi_transfer_area(u16 width, u16 height, DSSDBG("rfbi_transfer_area %dx%d\n", width, height); - dispc_set_lcd_size(width, height); + dispc_set_lcd_size(OMAP_DSS_CHANNEL_LCD, width, height); dispc_enable_channel(OMAP_DSS_CHANNEL_LCD, true); @@ -1018,11 +1018,14 @@ int omapdss_rfbi_display_enable(struct omap_dss_device *dssdev) goto err1; } - dispc_set_lcd_display_type(OMAP_DSS_LCD_DISPLAY_TFT); + dispc_set_lcd_display_type(OMAP_DSS_CHANNEL_LCD, + OMAP_DSS_LCD_DISPLAY_TFT); - dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_RFBI); + dispc_set_parallel_interface_mode(OMAP_DSS_CHANNEL_LCD, + OMAP_DSS_PARALLELMODE_RFBI); - dispc_set_tft_data_lines(dssdev->ctrl.pixel_size); + dispc_set_tft_data_lines(OMAP_DSS_CHANNEL_LCD, + dssdev->ctrl.pixel_size); rfbi_configure(dssdev->phy.rfbi.channel, dssdev->ctrl.pixel_size, diff --git a/drivers/video/omap2/dss/sdi.c b/drivers/video/omap2/dss/sdi.c index 12eb4042dd82..95e6a53df75f 100644 --- a/drivers/video/omap2/dss/sdi.c +++ b/drivers/video/omap2/dss/sdi.c @@ -23,21 +23,26 @@ #include <linux/clk.h> #include <linux/delay.h> #include <linux/err.h> +#include <linux/regulator/consumer.h> #include <plat/display.h> +#include <plat/cpu.h> #include "dss.h" static struct { bool skip_init; bool update_enabled; + struct regulator *vdds_sdi_reg; } sdi; static void sdi_basic_init(void) { - dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_BYPASS); + dispc_set_parallel_interface_mode(OMAP_DSS_CHANNEL_LCD, + OMAP_DSS_PARALLELMODE_BYPASS); - dispc_set_lcd_display_type(OMAP_DSS_LCD_DISPLAY_TFT); - dispc_set_tft_data_lines(24); + dispc_set_lcd_display_type(OMAP_DSS_CHANNEL_LCD, + OMAP_DSS_LCD_DISPLAY_TFT); + dispc_set_tft_data_lines(OMAP_DSS_CHANNEL_LCD, 24); dispc_lcd_enable_signal_polarity(1); } @@ -57,6 +62,10 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev) goto err0; } + r = regulator_enable(sdi.vdds_sdi_reg); + if (r) + goto err1; + /* In case of skip_init sdi_init has already enabled the clocks */ if (!sdi.skip_init) dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); @@ -66,15 +75,15 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev) /* 15.5.9.1.2 */ dssdev->panel.config |= OMAP_DSS_LCD_RF | OMAP_DSS_LCD_ONOFF; - dispc_set_pol_freq(dssdev->panel.config, dssdev->panel.acbi, - dssdev->panel.acb); + dispc_set_pol_freq(OMAP_DSS_CHANNEL_LCD, dssdev->panel.config, + dssdev->panel.acbi, dssdev->panel.acb); if (!sdi.skip_init) { r = dss_calc_clock_div(1, t->pixel_clock * 1000, &dss_cinfo, &dispc_cinfo); } else { r = dss_get_clock_div(&dss_cinfo); - r = dispc_get_clock_div(&dispc_cinfo); + r = dispc_get_clock_div(OMAP_DSS_CHANNEL_LCD, &dispc_cinfo); } if (r) @@ -95,7 +104,7 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev) } - dispc_set_lcd_timings(t); + dispc_set_lcd_timings(OMAP_DSS_CHANNEL_LCD, t); r = dss_set_clock_div(&dss_cinfo); if (r) @@ -115,19 +124,12 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev) dssdev->manager->enable(dssdev->manager); - if (dssdev->driver->enable) { - r = dssdev->driver->enable(dssdev); - if (r) - goto err3; - } - sdi.skip_init = 0; return 0; -err3: - dssdev->manager->disable(dssdev->manager); err2: dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + regulator_disable(sdi.vdds_sdi_reg); err1: omap_dss_stop_device(dssdev); err0: @@ -137,15 +139,14 @@ EXPORT_SYMBOL(omapdss_sdi_display_enable); void omapdss_sdi_display_disable(struct omap_dss_device *dssdev) { - if (dssdev->driver->disable) - dssdev->driver->disable(dssdev); - dssdev->manager->disable(dssdev->manager); dss_sdi_disable(); dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + regulator_disable(sdi.vdds_sdi_reg); + omap_dss_stop_device(dssdev); } EXPORT_SYMBOL(omapdss_sdi_display_disable); @@ -162,6 +163,11 @@ int sdi_init(bool skip_init) /* we store this for first display enable, then clear it */ sdi.skip_init = skip_init; + sdi.vdds_sdi_reg = dss_get_vdds_sdi(); + if (IS_ERR(sdi.vdds_sdi_reg)) { + DSSERR("can't get VDDS_SDI regulator\n"); + return PTR_ERR(sdi.vdds_sdi_reg); + } /* * Enable clocks already here, otherwise there would be a toggle * of them until sdi_display_enable is called. diff --git a/drivers/video/omap2/dss/venc.c b/drivers/video/omap2/dss/venc.c index f0ba5732d84a..eff35050e28a 100644 --- a/drivers/video/omap2/dss/venc.c +++ b/drivers/video/omap2/dss/venc.c @@ -479,12 +479,6 @@ static int venc_panel_enable(struct omap_dss_device *dssdev) goto err1; } - if (dssdev->platform_enable) { - r = dssdev->platform_enable(dssdev); - if (r) - goto err2; - } - venc_power_on(dssdev); venc.wss_data = 0; @@ -494,13 +488,9 @@ static int venc_panel_enable(struct omap_dss_device *dssdev) /* wait couple of vsyncs until enabling the LCD */ msleep(50); - mutex_unlock(&venc.venc_lock); - - return r; -err2: - venc_power_off(dssdev); err1: mutex_unlock(&venc.venc_lock); + return r; } @@ -524,9 +514,6 @@ static void venc_panel_disable(struct omap_dss_device *dssdev) /* wait at least 5 vsyncs after disabling the LCD */ msleep(100); - if (dssdev->platform_disable) - dssdev->platform_disable(dssdev); - dssdev->state = OMAP_DSS_DISPLAY_DISABLED; end: mutex_unlock(&venc.venc_lock); diff --git a/drivers/video/omap2/dss/wb.c b/drivers/video/omap2/dss/wb.c new file mode 100644 index 000000000000..02f9a7a18df1 --- /dev/null +++ b/drivers/video/omap2/dss/wb.c @@ -0,0 +1,179 @@ +/* + * linux/drivers/video/omap2/dss/wb.c + * Copyright (C) 2009 Texas Instruments + * Author: mythripk <mythripk@ti.com> + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#define DSS_SUBSYS_NAME "WRITEBACK" + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/sysfs.h> +#include <linux/kobject.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <plat/display.h> +#include <plat/cpu.h> + +#include "dss.h" + +static struct list_head wb_list; + +static struct attribute *writeback_sysfs_attrs[] = { + NULL +}; + +static ssize_t writeback_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ +return 0; +} + +static ssize_t writeback_attr_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t size) +{ +return 0; +} + +static struct sysfs_ops writeback_sysfs_ops = { + .show = writeback_attr_show, + .store = writeback_attr_store, +}; + +static struct kobj_type writeback_ktype = { + .sysfs_ops = &writeback_sysfs_ops, + .default_attrs = writeback_sysfs_attrs, +}; + +bool omap_dss_check_wb(struct writeback_cache_data *wb, int overlayId, int managerId) +{ + bool result = false; + DSSDBG("ovl=%d,mgr=%d,srcty=%d(%s),src=%d", + overlayId, managerId, wb->source_type, + wb->source_type == OMAP_WB_SOURCE_OVERLAY ? "OMAP_WB_SOURCE_OVERLAY" : + wb->source_type == OMAP_WB_SOURCE_MANAGER ? "OMAP_WB_SOURCE_MANAGER" : "???", + wb->source); + + if ((wb->source_type == OMAP_WB_SOURCE_OVERLAY) && + ((wb->source - 3) == overlayId)) + result = true; + else if (wb->source_type == OMAP_WB_SOURCE_MANAGER) { + switch (wb->source) { + case OMAP_WB_LCD_1_MANAGER: + if (managerId == OMAP_DSS_CHANNEL_LCD) + result = true; + case OMAP_WB_LCD_2_MANAGER: + if (managerId == OMAP_DSS_CHANNEL_LCD2) + result = true; + case OMAP_WB_TV_MANAGER: + if (managerId == OMAP_DSS_CHANNEL_DIGIT) + result = true; + case OMAP_WB_OVERLAY0: + case OMAP_WB_OVERLAY1: + case OMAP_WB_OVERLAY2: + case OMAP_WB_OVERLAY3: + break; + } + } + + return result; + +} + +static bool dss_check_wb(struct omap_writeback *wb) +{ + DSSDBG("srcty=%d(%s),src=%d", wb->info.source_type, + wb->info.source_type == OMAP_WB_SOURCE_OVERLAY ? "OMAP_WB_SOURCE_OVERLAY" : + wb->info.source_type == OMAP_WB_SOURCE_MANAGER ? "OMAP_WB_SOURCE_MANAGER" : "???", + wb->info.source); + + return 0; +} + +static int omap_dss_wb_set_info(struct omap_writeback *wb, + struct omap_writeback_info *info) +{ + int r; + struct omap_writeback_info old_info; + old_info = wb->info; + wb->info = *info; + + r = dss_check_wb(wb); + if (r) { + wb->info = old_info; + return r; + } + + wb->info_dirty = true; + + return 0; +} + +static void omap_dss_wb_get_info(struct omap_writeback *wb, + struct omap_writeback_info *info) +{ + *info = wb->info; +} + +struct omap_writeback *omap_dss_get_wb(int num) +{ + int i = 0; + struct omap_writeback *wb; + + list_for_each_entry(wb, &wb_list, list) { + if (i++ == num) + return wb; + } + + return NULL; +} +EXPORT_SYMBOL(omap_dss_get_wb); + + +static void omap_dss_add_wb(struct omap_writeback *wb) +{ + list_add_tail(&wb->list, &wb_list); +} + + +void dss_init_writeback(struct platform_device *pdev) +{ + int r; + struct omap_writeback *wb; + INIT_LIST_HEAD(&wb_list); + + wb = kzalloc(sizeof(*wb), GFP_KERNEL); + + BUG_ON(wb == NULL); + + wb->check_wb = &dss_check_wb; + wb->set_wb_info = &omap_dss_wb_set_info; + wb->get_wb_info = &omap_dss_wb_get_info; + mutex_init(&wb->lock); + + omap_dss_add_wb(wb); + + r = kobject_init_and_add(&wb->kobj, &writeback_ktype, + &pdev->dev.kobj, "writeback", 0); + + if (r) { + DSSERR("failed to create sysfs file\n"); + } + +} diff --git a/drivers/video/omap2/omapfb/Kconfig b/drivers/video/omap2/omapfb/Kconfig index 43496d6c377f..f186c2b6509b 100644 --- a/drivers/video/omap2/omapfb/Kconfig +++ b/drivers/video/omap2/omapfb/Kconfig @@ -1,17 +1,16 @@ menuconfig FB_OMAP2 - tristate "OMAP2/3 frame buffer support (EXPERIMENTAL)" + tristate "OMAP2/3/4 frame buffer support (EXPERIMENTAL)" depends on FB && OMAP2_DSS select OMAP2_VRAM - select OMAP2_VRFB select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT help - Frame buffer driver for OMAP2/3 based boards. + Frame buffer driver for OMAP2/3/4 based boards. config FB_OMAP2_DEBUG_SUPPORT - bool "Debug support for OMAP2/3 FB" + bool "Debug support for OMAP2/3/4 FB" default y depends on FB_OMAP2 help diff --git a/drivers/video/omap2/omapfb/omapfb-ioctl.c b/drivers/video/omap2/omapfb/omapfb-ioctl.c index 1ffa760b8545..9c7361871d78 100644 --- a/drivers/video/omap2/omapfb/omapfb-ioctl.c +++ b/drivers/video/omap2/omapfb/omapfb-ioctl.c @@ -183,13 +183,14 @@ int omapfb_update_window(struct fb_info *fbi, struct omapfb2_device *fbdev = ofbi->fbdev; int r; + if (!lock_fb_info(fbi)) + return -ENODEV; omapfb_lock(fbdev); - lock_fb_info(fbi); r = omapfb_update_window_nolock(fbi, x, y, w, h); - unlock_fb_info(fbi); omapfb_unlock(fbdev); + unlock_fb_info(fbi); return r; } diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c index 4b4506da96da..34bacdd75742 100644 --- a/drivers/video/omap2/omapfb/omapfb-main.c +++ b/drivers/video/omap2/omapfb/omapfb-main.c @@ -33,6 +33,7 @@ #include <plat/display.h> #include <plat/vram.h> #include <plat/vrfb.h> +#include <mach/tiler.h> #include "omapfb.h" @@ -44,6 +45,7 @@ static char *def_mode; static char *def_vram; static int def_vrfb; +static int def_tiler; static int def_rotate; static int def_mirror; @@ -621,13 +623,16 @@ void set_fb_fix(struct fb_info *fbi) } fix->smem_len = var->yres_virtual * fix->line_length; - } else { + } else if (ofbi->rotation_type != OMAP_DSS_ROT_TILER) { fix->line_length = (var->xres_virtual * var->bits_per_pixel) >> 3; - fix->smem_len = rg->size; + + /* tiler line length is set during allocation, and cannot + be changed */ } fix->smem_start = omapfb_get_region_paddr(ofbi); + fix->smem_len = rg->size; fix->type = FB_TYPE_PACKED_PIXELS; @@ -853,14 +858,21 @@ static int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl, DBG("setup_overlay %d, posx %d, posy %d, outw %d, outh %d\n", ofbi->id, posx, posy, outw, outh); + if (ofbi->rotation_type == OMAP_DSS_ROT_TILER) { + xres = var->xres; + yres = var->yres; + } else { if (rotation == FB_ROTATE_CW || rotation == FB_ROTATE_CCW) { xres = var->yres; yres = var->xres; } else { xres = var->xres; yres = var->yres; + } } + offset = ((var->yoffset * var->xres_virtual + + var->xoffset) * var->bits_per_pixel) >> 3; if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { data_start_p = omapfb_get_region_rot_paddr(ofbi, rotation); @@ -913,7 +925,12 @@ static int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl, info.paddr = data_start_p; info.vaddr = data_start_v; info.screen_width = screen_width; + if (ofbi->rotation_type == OMAP_DSS_ROT_TILER) { + info.width = + ((rotation == 1) | (rotation == 3)) ? yres : xres; + } else { info.width = xres; + } info.height = yres; info.color_mode = mode; info.rotation_type = ofbi->rotation_type; @@ -922,7 +939,12 @@ static int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl, info.pos_x = posx; info.pos_y = posy; + if (ofbi->rotation_type == OMAP_DSS_ROT_TILER) { + info.out_width = + ((rotation == 1) | (rotation == 3)) ? outh : outw; + } else { info.out_width = outw; + } info.out_height = outh; r = ovl->set_overlay_info(ovl, &info); @@ -969,6 +991,10 @@ int omapfb_apply_changes(struct fb_info *fbi, int init) if (init || (ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) { int rotation = (var->rotate + ofbi->rotation[i]) % 4; + if (ofbi->rotation_type == OMAP_DSS_ROT_TILER) { + outw = var->xres; + outh = var->yres; + } else { if (rotation == FB_ROTATE_CW || rotation == FB_ROTATE_CCW) { outw = var->yres; @@ -977,9 +1003,27 @@ int omapfb_apply_changes(struct fb_info *fbi, int init) outw = var->xres; outh = var->yres; } + } } else { - outw = ovl->info.out_width; - outh = ovl->info.out_height; + /*sv it comes here for vid1 on fb */ + DBG("its vid pipeline so sclaing is enabled, still\ + we will not scale for output size,\ + just maintain the input size"); + int rotation = (var->rotate + ofbi->rotation[i]) % 4; + if (rotation == FB_ROTATE_CW || + rotation == FB_ROTATE_CCW) { + outw = var->yres; + outh = var->xres; + } else { + DBG("info.out_width = %d, info.out_height = %d\ + take care of this for vid pipeline", + ovl->info.out_width, + ovl->info.out_height); + /*svoutw = ovl->info.out_width; + outh = ovl->info.out_height;*/ + outw = var->xres; + outh = var->yres; + } } if (init) { @@ -1038,7 +1082,8 @@ static int omapfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *fbi) { struct fb_var_screeninfo new_var; - int r; + int r = 0; + struct omap_dss_device *display = fb2display(fbi); DBG("pan_display(%d)\n", FB2OFB(fbi)->id); @@ -1053,6 +1098,9 @@ static int omapfb_pan_display(struct fb_var_screeninfo *var, fbi->var = new_var; r = omapfb_apply_changes(fbi, 0); + /* don't call panel update for OMAP4 */ + if (!cpu_is_omap44xx() && display && display->driver->update) + display->driver->update(display, 0, 0, var->xres, var->yres); return r; } @@ -1101,7 +1149,24 @@ static int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma) DBG("user mmap region start %lx, len %d, off %lx\n", start, len, off); - vma->vm_pgoff = off >> PAGE_SHIFT; + vma->vm_private_data = ofbi; + if (ofbi->rotation_type == OMAP_DSS_ROT_TILER) { + int k = 0, p = fix->line_length; + + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + vma->vm_ops = &mmap_user_ops; /* &dmm_remap_vm_ops; */ + + /* we need to figure out the height of the block. */ + for (k = 0; k < len; k += p) { + /* map each page of the line */ + vma->vm_pgoff = off >> PAGE_SHIFT; + if (remap_pfn_range(vma, vma->vm_start + k, + off >> PAGE_SHIFT, p, vma->vm_page_prot)) + return -EAGAIN; + off += 2*64*TILER_WIDTH; + } + } else { + vma->vm_pgoff = off >> PAGE_SHIFT; vma->vm_flags |= VM_IO | VM_RESERVED; vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); vma->vm_ops = &mmap_user_ops; @@ -1109,6 +1174,7 @@ static int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma) if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, vma->vm_end - vma->vm_start, vma->vm_page_prot)) return -EAGAIN; + } /* vm_ops.open won't be called for mmap itself. */ atomic_inc(&ofbi->map_count); return 0; @@ -1302,9 +1368,13 @@ static void omapfb_free_fbmem(struct fb_info *fbi) rg = &ofbi->region; + if (ofbi->rotation_type == OMAP_DSS_ROT_TILER) { + tiler_free(rg->paddr); + } else { if (rg->paddr) if (omap_vram_free(rg->paddr, rg->size)) dev_err(fbdev->dev, "VRAM FREE failed\n"); + } if (rg->vaddr) iounmap(rg->vaddr); @@ -1354,6 +1424,9 @@ static int omapfb_alloc_fbmem(struct fb_info *fbi, unsigned long size, struct omapfb2_mem_region *rg; void __iomem *vaddr; int r; + u16 h = 0, w = 0; + unsigned long pstride; + size_t psize; rg = &ofbi->region; memset(rg, 0, sizeof(*rg)); @@ -1362,7 +1435,19 @@ static int omapfb_alloc_fbmem(struct fb_info *fbi, unsigned long size, if (!paddr) { DBG("allocating %lu bytes for fb %d\n", size, ofbi->id); + if (ofbi->rotation_type == OMAP_DSS_ROT_TILER) { + int err = 0xFFFFFFFF; + /* get width & height from line length & size */ + w = fbi->fix.line_length / + (fbi->var.bits_per_pixel >> 3); + h = size / fbi->fix.line_length; + err = tiler_alloc(TILFMT_32BIT, w, h, &paddr); + if (err != 0x0) + return -ENOMEM; + r = 0; + } else { r = omap_vram_alloc(OMAP_VRAM_MEMTYPE_SDRAM, size, &paddr); + } } else { DBG("reserving %lu bytes at %lx for fb %d\n", size, paddr, ofbi->id); @@ -1374,7 +1459,7 @@ static int omapfb_alloc_fbmem(struct fb_info *fbi, unsigned long size, return -ENOMEM; } - if (ofbi->rotation_type != OMAP_DSS_ROT_VRFB) { + if (ofbi->rotation_type == OMAP_DSS_ROT_DMA) { vaddr = ioremap_wc(paddr, size); if (!vaddr) { @@ -1382,9 +1467,17 @@ static int omapfb_alloc_fbmem(struct fb_info *fbi, unsigned long size, omap_vram_free(paddr, size); return -ENOMEM; } + } else if (ofbi->rotation_type == OMAP_DSS_ROT_TILER) { + pstride = tiler_stride(tiler_get_natural_addr(paddr)); + psize = h * pstride; + vaddr = __arm_multi_strided_ioremap(1, &paddr, &psize, + &pstride, (unsigned long *) &fbi->fix.line_length, + MT_DEVICE_WC); + if (vaddr == NULL) + return -ENOMEM; DBG("allocated VRAM paddr %lx, vaddr %p\n", paddr, vaddr); - } else { + } else if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { r = omap_vrfb_request_ctx(&rg->vrfb); if (r) { dev_err(fbdev->dev, "vrfb create ctx failed\n"); @@ -1428,6 +1521,10 @@ static int omapfb_alloc_fbmem_display(struct fb_info *fbi, unsigned long size, break; } + if (ofbi->rotation_type == OMAP_DSS_ROT_TILER) { + fbi->var.bits_per_pixel = 32; /* always 32-bit for tiler */ + bytespp = fbi->var.bits_per_pixel >> 3; + } if (!size) { u16 w, h; @@ -1439,9 +1536,13 @@ static int omapfb_alloc_fbmem_display(struct fb_info *fbi, unsigned long size, DBG("adjusting fb mem size for VRFB, %u -> %lu\n", w * h * bytespp, size); - } else { - size = w * h * bytespp; + } else if (ofbi->rotation_type == OMAP_DSS_ROT_TILER) { + /* round up width to tiler size */ + w = ALIGN(w, PAGE_SIZE / bytespp); + fbi->fix.line_length = w * bytespp; } + size = w * h * bytespp; + } if (!size) @@ -1595,9 +1696,9 @@ static int omapfb_allocate_all_fbs(struct omapfb2_device *fbdev) } for (i = 0; i < fbdev->num_fbs; i++) { - /* allocate memory automatically only for fb0, or if + /* allocate memory automatically only for fb0, fb1 and fb2 , or if * excplicitly defined with vram or plat data option */ - if (i == 0 || vram_sizes[i] != 0) { + if (i == 0 || i == 1 || i == 2 || vram_sizes[i] != 0) { r = omapfb_alloc_fbmem_display(fbdev->fbs[i], vram_sizes[i], vram_paddrs[i]); @@ -1748,9 +1849,12 @@ static int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi) if (display) { u16 w, h; int rotation = (var->rotate + ofbi->rotation[0]) % 4; - display->driver->get_resolution(display, &w, &h); + if (ofbi->rotation_type == OMAP_DSS_ROT_TILER) { + var->xres = w; + var->yres = h; + } else { if (rotation == FB_ROTATE_CW || rotation == FB_ROTATE_CCW) { var->xres = h; @@ -1759,7 +1863,7 @@ static int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi) var->xres = w; var->yres = h; } - + } var->xres_virtual = var->xres; var->yres_virtual = var->yres; @@ -1872,8 +1976,14 @@ static int omapfb_create_framebuffers(struct omapfb2_device *fbdev) ofbi->id = i; /* assign these early, so that fb alloc can use them */ - ofbi->rotation_type = def_vrfb ? OMAP_DSS_ROT_VRFB : - OMAP_DSS_ROT_DMA; + if (def_vrfb == 1) + ofbi->rotation_type = OMAP_DSS_ROT_VRFB; + else if (def_tiler == 1) + ofbi->rotation_type = OMAP_DSS_ROT_TILER; + else + ofbi->rotation_type = OMAP_DSS_ROT_DMA; + + ofbi->mirror = def_mirror; fbdev->num_fbs++; @@ -2210,9 +2320,11 @@ static int omapfb_probe(struct platform_device *pdev) if (dssdrv->set_update_mode) dssdrv->set_update_mode(def_display, OMAP_DSS_UPDATE_MANUAL); - - dssdrv->get_resolution(def_display, &w, &h); - def_display->driver->update(def_display, 0, 0, w, h); + /* don't call panel update for OMAP4 */ + if (!cpu_is_omap44xx()) { + dssdrv->get_resolution(def_display, &w, &h); + def_display->driver->update(def_display, 0, 0, w, h); + } } else { if (dssdrv->set_update_mode) dssdrv->set_update_mode(def_display, @@ -2273,6 +2385,7 @@ module_param_named(mode, def_mode, charp, 0); module_param_named(vram, def_vram, charp, 0); module_param_named(rotate, def_rotate, int, 0); module_param_named(vrfb, def_vrfb, bool, 0); +module_param_named(tiler, def_tiler, bool, 0); module_param_named(mirror, def_mirror, bool, 0); /* late_initcall to let panel/ctrl drivers loaded first. diff --git a/drivers/video/omap2/omapfb/omapfb-sysfs.c b/drivers/video/omap2/omapfb/omapfb-sysfs.c index 62bb88f5c192..5179219128bd 100644 --- a/drivers/video/omap2/omapfb/omapfb-sysfs.c +++ b/drivers/video/omap2/omapfb/omapfb-sysfs.c @@ -57,7 +57,8 @@ static ssize_t store_rotate_type(struct device *dev, if (rot_type != OMAP_DSS_ROT_DMA && rot_type != OMAP_DSS_ROT_VRFB) return -EINVAL; - lock_fb_info(fbi); + if (!lock_fb_info(fbi)) + return -ENODEV; r = 0; if (rot_type == ofbi->rotation_type) @@ -105,7 +106,8 @@ static ssize_t store_mirror(struct device *dev, if (mirror != 0 && mirror != 1) return -EINVAL; - lock_fb_info(fbi); + if (!lock_fb_info(fbi)) + return -ENODEV; ofbi->mirror = mirror; @@ -137,8 +139,9 @@ static ssize_t show_overlays(struct device *dev, ssize_t l = 0; int t; + if (!lock_fb_info(fbi)) + return -ENODEV; omapfb_lock(fbdev); - lock_fb_info(fbi); for (t = 0; t < ofbi->num_overlays; t++) { struct omap_overlay *ovl = ofbi->overlays[t]; @@ -154,8 +157,8 @@ static ssize_t show_overlays(struct device *dev, l += snprintf(buf + l, PAGE_SIZE - l, "\n"); - unlock_fb_info(fbi); omapfb_unlock(fbdev); + unlock_fb_info(fbi); return l; } @@ -195,8 +198,9 @@ static ssize_t store_overlays(struct device *dev, struct device_attribute *attr, if (buf[len - 1] == '\n') len = len - 1; + if (!lock_fb_info(fbi)) + return -ENODEV; omapfb_lock(fbdev); - lock_fb_info(fbi); if (len > 0) { char *p = (char *)buf; @@ -303,8 +307,8 @@ static ssize_t store_overlays(struct device *dev, struct device_attribute *attr, r = count; out: - unlock_fb_info(fbi); omapfb_unlock(fbdev); + unlock_fb_info(fbi); return r; } @@ -317,7 +321,8 @@ static ssize_t show_overlays_rotate(struct device *dev, ssize_t l = 0; int t; - lock_fb_info(fbi); + if (!lock_fb_info(fbi)) + return -ENODEV; for (t = 0; t < ofbi->num_overlays; t++) { l += snprintf(buf + l, PAGE_SIZE - l, "%s%d", @@ -345,7 +350,8 @@ static ssize_t store_overlays_rotate(struct device *dev, if (buf[len - 1] == '\n') len = len - 1; - lock_fb_info(fbi); + if (!lock_fb_info(fbi)) + return -ENODEV; if (len > 0) { char *p = (char *)buf; @@ -416,7 +422,8 @@ static ssize_t store_size(struct device *dev, struct device_attribute *attr, size = PAGE_ALIGN(simple_strtoul(buf, NULL, 0)); - lock_fb_info(fbi); + if (!lock_fb_info(fbi)) + return -ENODEV; for (i = 0; i < ofbi->num_overlays; i++) { if (ofbi->overlays[i]->info.enabled) { |