diff options
Diffstat (limited to 'meta/recipes-kernel/linux/linux-netbook-2.6.33.2/linux-2.6.35-OMAP-DSS2-Add-ACX565AKM-Panel-Driver.patch')
-rw-r--r-- | meta/recipes-kernel/linux/linux-netbook-2.6.33.2/linux-2.6.35-OMAP-DSS2-Add-ACX565AKM-Panel-Driver.patch | 813 |
1 files changed, 813 insertions, 0 deletions
diff --git a/meta/recipes-kernel/linux/linux-netbook-2.6.33.2/linux-2.6.35-OMAP-DSS2-Add-ACX565AKM-Panel-Driver.patch b/meta/recipes-kernel/linux/linux-netbook-2.6.33.2/linux-2.6.35-OMAP-DSS2-Add-ACX565AKM-Panel-Driver.patch new file mode 100644 index 000000000..0b3b50129 --- /dev/null +++ b/meta/recipes-kernel/linux/linux-netbook-2.6.33.2/linux-2.6.35-OMAP-DSS2-Add-ACX565AKM-Panel-Driver.patch @@ -0,0 +1,813 @@ +From 635f44cfde6c057a2ecbb8c9d9a67225e53b6545 Mon Sep 17 00:00:00 2001 +From: Roger Quadros <roger.quadros@nokia.com> +Date: Wed, 10 Mar 2010 17:32:44 +0200 +Subject: [PATCH 3/10] OMAP: DSS2: Add ACX565AKM Panel Driver + +From: Roger Quadros <roger.quadros@nokia.com> + +Patch-mainline: 2.6.35? +Git-repo: http://www.gitorious.org/linux-omap-dss2/linux/commit/4f2308f3be2fe631412ea85a80c91414c3bfe730 + +This is the panel used on Nokia N900 + +Signed-off-by: Roger Quadros <roger.quadros@nokia.com> +--- + drivers/video/omap2/displays/Kconfig | 6 + + drivers/video/omap2/displays/Makefile | 1 + + drivers/video/omap2/displays/panel-acx565akm.c | 760 ++++++++++++++++++++++++ + 3 files changed, 767 insertions(+), 0 deletions(-) + create mode 100644 drivers/video/omap2/displays/panel-acx565akm.c + +diff --git a/drivers/video/omap2/displays/Kconfig b/drivers/video/omap2/displays/Kconfig +index b12a59c..1f5b7d1 100644 +--- a/drivers/video/omap2/displays/Kconfig ++++ b/drivers/video/omap2/displays/Kconfig +@@ -19,4 +19,10 @@ config PANEL_TAAL + help + Taal DSI command mode panel from TPO. + ++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 9556464..0af16b7 100644 +--- a/drivers/video/omap2/displays/Makefile ++++ b/drivers/video/omap2/displays/Makefile +@@ -2,3 +2,4 @@ obj-$(CONFIG_PANEL_GENERIC) += panel-generic.o + obj-$(CONFIG_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o + + obj-$(CONFIG_PANEL_TAAL) += panel-taal.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 0000000..27e9847 +--- /dev/null ++++ b/drivers/video/omap2/displays/panel-acx565akm.c +@@ -0,0 +1,760 @@ ++/* ++ * 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 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 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; ++ ++ 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; ++ dssdev->panel.recommended_bpp = 16; ++ ++ 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 --------*/ ++ ++ bldev = backlight_device_register("acx565akm", &md->spi->dev, ++ md, &acx565akm_bl_ops); ++ 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); ++ } ++ ++ bldev->props.fb_blank = FB_BLANK_UNBLANK; ++ bldev->props.power = FB_BLANK_UNBLANK; ++ ++ 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); ++ ++ if (dssdev->platform_enable) { ++ r = dssdev->platform_enable(dssdev); ++ if (r) ++ return r; ++ } ++ ++ 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); ++} ++ ++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); ++ ++ 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 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, ++ ++ .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"); +-- +1.6.0.4 + |