M7350v1_en_gpl

This commit is contained in:
T
2024-09-09 08:52:07 +00:00
commit f9cc65cfda
65988 changed files with 26357421 additions and 0 deletions
@@ -0,0 +1,75 @@
menu "OMAP2/3 Display Device Drivers"
depends on OMAP2_DSS
config PANEL_GENERIC_DPI
tristate "Generic DPI Panel"
depends on OMAP2_DSS_DPI
help
Generic DPI panel driver.
Supports DVI output for Beagle and OMAP3 SDP.
Supports LCD Panel used in TI SDP3430 and EVM boards,
OMAP3517 EVM boards and CM-T35.
config PANEL_DVI
tristate "DVI output"
depends on OMAP2_DSS_DPI && I2C
help
Driver for external monitors, connected via DVI. The driver uses i2c
to read EDID information from the monitor.
config PANEL_LGPHILIPS_LB035Q02
tristate "LG.Philips LB035Q02 LCD Panel"
depends on OMAP2_DSS_DPI && SPI
help
LCD Panel used on the Gumstix Overo Palo35
config PANEL_SHARP_LS037V7DW01
tristate "Sharp LS037V7DW01 LCD Panel"
depends on OMAP2_DSS_DPI
depends on BACKLIGHT_CLASS_DEVICE
help
LCD Panel used in TI's SDP3430 and EVM boards
config PANEL_NEC_NL8048HL11_01B
tristate "NEC NL8048HL11-01B Panel"
depends on OMAP2_DSS_DPI
depends on SPI
depends on BACKLIGHT_CLASS_DEVICE
help
This NEC NL8048HL11-01B panel is TFT LCD
used in the Zoom2/3/3630 sdp boards.
config PANEL_PICODLP
tristate "TI PICO DLP mini-projector"
depends on OMAP2_DSS_DPI && I2C
help
A mini-projector used in TI's SDP4430 and EVM boards
For more info please visit http://www.dlp.com/projector/
config PANEL_TAAL
tristate "Taal DSI Panel"
depends on OMAP2_DSS_DSI
depends on BACKLIGHT_CLASS_DEVICE
help
Taal DSI command mode panel from TPO.
config PANEL_TPO_TD043MTEA1
tristate "TPO TD043MTEA1 LCD Panel"
depends on OMAP2_DSS_DPI && SPI
help
LCD Panel used in OMAP3 Pandora
config PANEL_ACX565AKM
tristate "ACX565AKM Panel"
depends on OMAP2_DSS_SDI && SPI
depends on BACKLIGHT_CLASS_DEVICE
help
This is the LCD panel used on Nokia N900
config PANEL_N8X0
tristate "N8X0 Panel"
depends on OMAP2_DSS_RFBI && SPI
depends on BACKLIGHT_CLASS_DEVICE
help
This is the LCD panel used on Nokia N8x0
endmenu
@@ -0,0 +1,11 @@
obj-$(CONFIG_PANEL_GENERIC_DPI) += panel-generic-dpi.o
obj-$(CONFIG_PANEL_DVI) += panel-dvi.o
obj-$(CONFIG_PANEL_LGPHILIPS_LB035Q02) += panel-lgphilips-lb035q02.o
obj-$(CONFIG_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o
obj-$(CONFIG_PANEL_NEC_NL8048HL11_01B) += panel-nec-nl8048hl11-01b.o
obj-$(CONFIG_PANEL_TAAL) += panel-taal.o
obj-$(CONFIG_PANEL_PICODLP) += panel-picodlp.o
obj-$(CONFIG_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o
obj-$(CONFIG_PANEL_ACX565AKM) += panel-acx565akm.o
obj-$(CONFIG_PANEL_N8X0) += panel-n8x0.o
@@ -0,0 +1,816 @@
/*
* 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 <video/omapdss.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;
props.type = BACKLIGHT_RAW;
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__);
if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
return 0;
mutex_lock(&md->mutex);
r = omapdss_sdi_display_enable(dssdev);
if (r) {
pr_err("%s sdi enable failed\n", __func__);
goto fail_unlock;
}
/*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);
fail_unlock:
mutex_unlock(&md->mutex);
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__);
if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
return;
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",
.owner = THIS_MODULE,
},
.probe = acx565akm_spi_probe,
.remove = __devexit_p(acx565akm_spi_remove),
};
module_spi_driver(acx565akm_spi_driver);
MODULE_AUTHOR("Nokia Corporation");
MODULE_DESCRIPTION("acx565akm LCD Driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,363 @@
/*
* DVI output support
*
* Copyright (C) 2011 Texas Instruments Inc
* Author: Tomi Valkeinen <tomi.valkeinen@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/module.h>
#include <linux/slab.h>
#include <video/omapdss.h>
#include <linux/i2c.h>
#include <drm/drm_edid.h>
#include <video/omap-panel-dvi.h>
static const struct omap_video_timings panel_dvi_default_timings = {
.x_res = 640,
.y_res = 480,
.pixel_clock = 23500,
.hfp = 48,
.hsw = 32,
.hbp = 80,
.vfp = 3,
.vsw = 4,
.vbp = 7,
};
struct panel_drv_data {
struct omap_dss_device *dssdev;
struct mutex lock;
};
static inline struct panel_dvi_platform_data
*get_pdata(const struct omap_dss_device *dssdev)
{
return dssdev->data;
}
static int panel_dvi_power_on(struct omap_dss_device *dssdev)
{
struct panel_dvi_platform_data *pdata = get_pdata(dssdev);
int r;
if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
return 0;
r = omapdss_dpi_display_enable(dssdev);
if (r)
goto err0;
if (pdata->platform_enable) {
r = pdata->platform_enable(dssdev);
if (r)
goto err1;
}
return 0;
err1:
omapdss_dpi_display_disable(dssdev);
err0:
return r;
}
static void panel_dvi_power_off(struct omap_dss_device *dssdev)
{
struct panel_dvi_platform_data *pdata = get_pdata(dssdev);
if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
return;
if (pdata->platform_disable)
pdata->platform_disable(dssdev);
omapdss_dpi_display_disable(dssdev);
}
static int panel_dvi_probe(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata;
ddata = kzalloc(sizeof(*ddata), GFP_KERNEL);
if (!ddata)
return -ENOMEM;
dssdev->panel.timings = panel_dvi_default_timings;
dssdev->panel.config = OMAP_DSS_LCD_TFT;
ddata->dssdev = dssdev;
mutex_init(&ddata->lock);
dev_set_drvdata(&dssdev->dev, ddata);
return 0;
}
static void __exit panel_dvi_remove(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
mutex_lock(&ddata->lock);
dev_set_drvdata(&dssdev->dev, NULL);
mutex_unlock(&ddata->lock);
kfree(ddata);
}
static int panel_dvi_enable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
int r;
mutex_lock(&ddata->lock);
r = panel_dvi_power_on(dssdev);
if (r == 0)
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
mutex_unlock(&ddata->lock);
return r;
}
static void panel_dvi_disable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
mutex_lock(&ddata->lock);
panel_dvi_power_off(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
mutex_unlock(&ddata->lock);
}
static int panel_dvi_suspend(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
mutex_lock(&ddata->lock);
panel_dvi_power_off(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
mutex_unlock(&ddata->lock);
return 0;
}
static int panel_dvi_resume(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
int r;
mutex_lock(&ddata->lock);
r = panel_dvi_power_on(dssdev);
if (r == 0)
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
mutex_unlock(&ddata->lock);
return r;
}
static void panel_dvi_set_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
mutex_lock(&ddata->lock);
dpi_set_timings(dssdev, timings);
mutex_unlock(&ddata->lock);
}
static void panel_dvi_get_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
mutex_lock(&ddata->lock);
*timings = dssdev->panel.timings;
mutex_unlock(&ddata->lock);
}
static int panel_dvi_check_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
int r;
mutex_lock(&ddata->lock);
r = dpi_check_timings(dssdev, timings);
mutex_unlock(&ddata->lock);
return r;
}
static int panel_dvi_ddc_read(struct i2c_adapter *adapter,
unsigned char *buf, u16 count, u8 offset)
{
int r, retries;
for (retries = 3; retries > 0; retries--) {
struct i2c_msg msgs[] = {
{
.addr = DDC_ADDR,
.flags = 0,
.len = 1,
.buf = &offset,
}, {
.addr = DDC_ADDR,
.flags = I2C_M_RD,
.len = count,
.buf = buf,
}
};
r = i2c_transfer(adapter, msgs, 2);
if (r == 2)
return 0;
if (r != -EAGAIN)
break;
}
return r < 0 ? r : -EIO;
}
static int panel_dvi_read_edid(struct omap_dss_device *dssdev,
u8 *edid, int len)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
struct panel_dvi_platform_data *pdata = get_pdata(dssdev);
struct i2c_adapter *adapter;
int r, l, bytes_read;
mutex_lock(&ddata->lock);
if (pdata->i2c_bus_num == 0) {
r = -ENODEV;
goto err;
}
adapter = i2c_get_adapter(pdata->i2c_bus_num);
if (!adapter) {
dev_err(&dssdev->dev, "Failed to get I2C adapter, bus %d\n",
pdata->i2c_bus_num);
r = -EINVAL;
goto err;
}
l = min(EDID_LENGTH, len);
r = panel_dvi_ddc_read(adapter, edid, l, 0);
if (r)
goto err;
bytes_read = l;
/* if there are extensions, read second block */
if (len > EDID_LENGTH && edid[0x7e] > 0) {
l = min(EDID_LENGTH, len - EDID_LENGTH);
r = panel_dvi_ddc_read(adapter, edid + EDID_LENGTH,
l, EDID_LENGTH);
if (r)
goto err;
bytes_read += l;
}
mutex_unlock(&ddata->lock);
return bytes_read;
err:
mutex_unlock(&ddata->lock);
return r;
}
static bool panel_dvi_detect(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
struct panel_dvi_platform_data *pdata = get_pdata(dssdev);
struct i2c_adapter *adapter;
unsigned char out;
int r;
mutex_lock(&ddata->lock);
if (pdata->i2c_bus_num == 0)
goto out;
adapter = i2c_get_adapter(pdata->i2c_bus_num);
if (!adapter)
goto out;
r = panel_dvi_ddc_read(adapter, &out, 1, 0);
mutex_unlock(&ddata->lock);
return r == 0;
out:
mutex_unlock(&ddata->lock);
return true;
}
static struct omap_dss_driver panel_dvi_driver = {
.probe = panel_dvi_probe,
.remove = __exit_p(panel_dvi_remove),
.enable = panel_dvi_enable,
.disable = panel_dvi_disable,
.suspend = panel_dvi_suspend,
.resume = panel_dvi_resume,
.set_timings = panel_dvi_set_timings,
.get_timings = panel_dvi_get_timings,
.check_timings = panel_dvi_check_timings,
.read_edid = panel_dvi_read_edid,
.detect = panel_dvi_detect,
.driver = {
.name = "dvi",
.owner = THIS_MODULE,
},
};
static int __init panel_dvi_init(void)
{
return omap_dss_register_driver(&panel_dvi_driver);
}
static void __exit panel_dvi_exit(void)
{
omap_dss_unregister_driver(&panel_dvi_driver);
}
module_init(panel_dvi_init);
module_exit(panel_dvi_exit);
MODULE_LICENSE("GPL");
@@ -0,0 +1,595 @@
/*
* Generic DPI Panels support
*
* Copyright (C) 2010 Canonical Ltd.
* Author: Bryan Wu <bryan.wu@canonical.com>
*
* LCD panel driver for Sharp LQ043T1DG01
*
* Copyright (C) 2009 Texas Instruments Inc
* Author: Vaibhav Hiremath <hvaibhav@ti.com>
*
* LCD panel driver for Toppoly TDO35S
*
* Copyright (C) 2009 CompuLab, Ltd.
* Author: Mike Rapoport <mike@compulab.co.il>
*
* Copyright (C) 2008 Nokia Corporation
* Author: Tomi Valkeinen <tomi.valkeinen@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/module.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <video/omapdss.h>
#include <video/omap-panel-generic-dpi.h>
struct panel_config {
struct omap_video_timings timings;
int acbi; /* ac-bias pin transitions per interrupt */
/* Unit: line clocks */
int acb; /* ac-bias pin frequency */
enum omap_panel_config config;
int power_on_delay;
int power_off_delay;
/*
* Used to match device to panel configuration
* when use generic panel driver
*/
const char *name;
};
/* Panel configurations */
static struct panel_config generic_dpi_panels[] = {
/* Sharp LQ043T1DG01 */
{
{
.x_res = 480,
.y_res = 272,
.pixel_clock = 9000,
.hsw = 42,
.hfp = 3,
.hbp = 2,
.vsw = 11,
.vfp = 3,
.vbp = 2,
},
.acbi = 0x0,
.acb = 0x0,
.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IEO,
.power_on_delay = 50,
.power_off_delay = 100,
.name = "sharp_lq",
},
/* Sharp LS037V7DW01 */
{
{
.x_res = 480,
.y_res = 640,
.pixel_clock = 19200,
.hsw = 2,
.hfp = 1,
.hbp = 28,
.vsw = 1,
.vfp = 1,
.vbp = 1,
},
.acbi = 0x0,
.acb = 0x28,
.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
OMAP_DSS_LCD_IHS,
.power_on_delay = 50,
.power_off_delay = 100,
.name = "sharp_ls",
},
/* Toppoly TDO35S */
{
{
.x_res = 480,
.y_res = 640,
.pixel_clock = 26000,
.hfp = 104,
.hsw = 8,
.hbp = 8,
.vfp = 4,
.vsw = 2,
.vbp = 2,
},
.acbi = 0x0,
.acb = 0x0,
.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC |
OMAP_DSS_LCD_ONOFF,
.power_on_delay = 0,
.power_off_delay = 0,
.name = "toppoly_tdo35s",
},
/* Samsung LTE430WQ-F0C */
{
{
.x_res = 480,
.y_res = 272,
.pixel_clock = 9200,
.hfp = 8,
.hsw = 41,
.hbp = 45 - 41,
.vfp = 4,
.vsw = 10,
.vbp = 12 - 10,
},
.acbi = 0x0,
.acb = 0x0,
.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
OMAP_DSS_LCD_IHS,
.power_on_delay = 0,
.power_off_delay = 0,
.name = "samsung_lte430wq_f0c",
},
/* Seiko 70WVW1TZ3Z3 */
{
{
.x_res = 800,
.y_res = 480,
.pixel_clock = 33000,
.hsw = 128,
.hfp = 10,
.hbp = 10,
.vsw = 2,
.vfp = 4,
.vbp = 11,
},
.acbi = 0x0,
.acb = 0x0,
.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
OMAP_DSS_LCD_IHS,
.power_on_delay = 0,
.power_off_delay = 0,
.name = "seiko_70wvw1tz3",
},
/* Powertip PH480272T */
{
{
.x_res = 480,
.y_res = 272,
.pixel_clock = 9000,
.hsw = 40,
.hfp = 2,
.hbp = 2,
.vsw = 10,
.vfp = 2,
.vbp = 2,
},
.acbi = 0x0,
.acb = 0x0,
.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IEO,
.power_on_delay = 0,
.power_off_delay = 0,
.name = "powertip_ph480272t",
},
/* Innolux AT070TN83 */
{
{
.x_res = 800,
.y_res = 480,
.pixel_clock = 40000,
.hsw = 48,
.hfp = 1,
.hbp = 1,
.vsw = 3,
.vfp = 12,
.vbp = 25,
},
.acbi = 0x0,
.acb = 0x28,
.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
OMAP_DSS_LCD_IHS,
.power_on_delay = 0,
.power_off_delay = 0,
.name = "innolux_at070tn83",
},
/* NEC NL2432DR22-11B */
{
{
.x_res = 240,
.y_res = 320,
.pixel_clock = 5400,
.hsw = 3,
.hfp = 3,
.hbp = 39,
.vsw = 1,
.vfp = 2,
.vbp = 7,
},
.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
OMAP_DSS_LCD_IHS,
.name = "nec_nl2432dr22-11b",
},
/* Unknown panel used in OMAP H4 */
{
{
.x_res = 240,
.y_res = 320,
.pixel_clock = 6250,
.hsw = 15,
.hfp = 15,
.hbp = 60,
.vsw = 1,
.vfp = 1,
.vbp = 1,
},
.config = OMAP_DSS_LCD_TFT,
.name = "h4",
},
/* Unknown panel used in Samsung OMAP2 Apollon */
{
{
.x_res = 480,
.y_res = 272,
.pixel_clock = 6250,
.hsw = 41,
.hfp = 2,
.hbp = 2,
.vsw = 10,
.vfp = 2,
.vbp = 2,
},
.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
OMAP_DSS_LCD_IHS,
.name = "apollon",
},
/* FocalTech ETM070003DH6 */
{
{
.x_res = 800,
.y_res = 480,
.pixel_clock = 28000,
.hsw = 48,
.hfp = 40,
.hbp = 40,
.vsw = 3,
.vfp = 13,
.vbp = 29,
},
.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
OMAP_DSS_LCD_IHS,
.name = "focaltech_etm070003dh6",
},
/* Microtips Technologies - UMSH-8173MD */
{
{
.x_res = 800,
.y_res = 480,
.pixel_clock = 34560,
.hsw = 13,
.hfp = 101,
.hbp = 101,
.vsw = 23,
.vfp = 1,
.vbp = 1,
},
.acbi = 0x0,
.acb = 0x0,
.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC,
.power_on_delay = 0,
.power_off_delay = 0,
.name = "microtips_umsh_8173md",
},
/* OrtusTech COM43H4M10XTC */
{
{
.x_res = 480,
.y_res = 272,
.pixel_clock = 8000,
.hsw = 41,
.hfp = 8,
.hbp = 4,
.vsw = 10,
.vfp = 4,
.vbp = 2,
},
.config = OMAP_DSS_LCD_TFT,
.name = "ortustech_com43h4m10xtc",
},
/* Innolux AT080TN52 */
{
{
.x_res = 800,
.y_res = 600,
.pixel_clock = 41142,
.hsw = 20,
.hfp = 210,
.hbp = 46,
.vsw = 10,
.vfp = 12,
.vbp = 23,
},
.acb = 0x0,
.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IEO,
.name = "innolux_at080tn52",
},
};
struct panel_drv_data {
struct omap_dss_device *dssdev;
struct panel_config *panel_config;
};
static inline struct panel_generic_dpi_data
*get_panel_data(const struct omap_dss_device *dssdev)
{
return (struct panel_generic_dpi_data *) dssdev->data;
}
static int generic_dpi_panel_power_on(struct omap_dss_device *dssdev)
{
int r;
struct panel_generic_dpi_data *panel_data = get_panel_data(dssdev);
struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
struct panel_config *panel_config = drv_data->panel_config;
if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
return 0;
r = omapdss_dpi_display_enable(dssdev);
if (r)
goto err0;
/* wait couple of vsyncs until enabling the LCD */
if (panel_config->power_on_delay)
msleep(panel_config->power_on_delay);
if (panel_data->platform_enable) {
r = panel_data->platform_enable(dssdev);
if (r)
goto err1;
}
return 0;
err1:
omapdss_dpi_display_disable(dssdev);
err0:
return r;
}
static void generic_dpi_panel_power_off(struct omap_dss_device *dssdev)
{
struct panel_generic_dpi_data *panel_data = get_panel_data(dssdev);
struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
struct panel_config *panel_config = drv_data->panel_config;
if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
return;
if (panel_data->platform_disable)
panel_data->platform_disable(dssdev);
/* wait couple of vsyncs after disabling the LCD */
if (panel_config->power_off_delay)
msleep(panel_config->power_off_delay);
omapdss_dpi_display_disable(dssdev);
}
static int generic_dpi_panel_probe(struct omap_dss_device *dssdev)
{
struct panel_generic_dpi_data *panel_data = get_panel_data(dssdev);
struct panel_config *panel_config = NULL;
struct panel_drv_data *drv_data = NULL;
int i;
dev_dbg(&dssdev->dev, "probe\n");
if (!panel_data || !panel_data->name)
return -EINVAL;
for (i = 0; i < ARRAY_SIZE(generic_dpi_panels); i++) {
if (strcmp(panel_data->name, generic_dpi_panels[i].name) == 0) {
panel_config = &generic_dpi_panels[i];
break;
}
}
if (!panel_config)
return -EINVAL;
dssdev->panel.config = panel_config->config;
dssdev->panel.timings = panel_config->timings;
dssdev->panel.acb = panel_config->acb;
dssdev->panel.acbi = panel_config->acbi;
drv_data = kzalloc(sizeof(*drv_data), GFP_KERNEL);
if (!drv_data)
return -ENOMEM;
drv_data->dssdev = dssdev;
drv_data->panel_config = panel_config;
dev_set_drvdata(&dssdev->dev, drv_data);
return 0;
}
static void __exit generic_dpi_panel_remove(struct omap_dss_device *dssdev)
{
struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
dev_dbg(&dssdev->dev, "remove\n");
kfree(drv_data);
dev_set_drvdata(&dssdev->dev, NULL);
}
static int generic_dpi_panel_enable(struct omap_dss_device *dssdev)
{
int r = 0;
r = generic_dpi_panel_power_on(dssdev);
if (r)
return r;
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return 0;
}
static void generic_dpi_panel_disable(struct omap_dss_device *dssdev)
{
generic_dpi_panel_power_off(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
}
static int generic_dpi_panel_suspend(struct omap_dss_device *dssdev)
{
generic_dpi_panel_power_off(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
return 0;
}
static int generic_dpi_panel_resume(struct omap_dss_device *dssdev)
{
int r = 0;
r = generic_dpi_panel_power_on(dssdev);
if (r)
return r;
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return 0;
}
static void generic_dpi_panel_set_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
dpi_set_timings(dssdev, timings);
}
static void generic_dpi_panel_get_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
*timings = dssdev->panel.timings;
}
static int generic_dpi_panel_check_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
return dpi_check_timings(dssdev, timings);
}
static struct omap_dss_driver dpi_driver = {
.probe = generic_dpi_panel_probe,
.remove = __exit_p(generic_dpi_panel_remove),
.enable = generic_dpi_panel_enable,
.disable = generic_dpi_panel_disable,
.suspend = generic_dpi_panel_suspend,
.resume = generic_dpi_panel_resume,
.set_timings = generic_dpi_panel_set_timings,
.get_timings = generic_dpi_panel_get_timings,
.check_timings = generic_dpi_panel_check_timings,
.driver = {
.name = "generic_dpi_panel",
.owner = THIS_MODULE,
},
};
static int __init generic_dpi_panel_drv_init(void)
{
return omap_dss_register_driver(&dpi_driver);
}
static void __exit generic_dpi_panel_drv_exit(void)
{
omap_dss_unregister_driver(&dpi_driver);
}
module_init(generic_dpi_panel_drv_init);
module_exit(generic_dpi_panel_drv_exit);
MODULE_LICENSE("GPL");
@@ -0,0 +1,269 @@
/*
* LCD panel driver for LG.Philips LB035Q02
*
* Author: Steve Sakoman <steve@sakoman.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/module.h>
#include <linux/delay.h>
#include <linux/spi/spi.h>
#include <linux/mutex.h>
#include <video/omapdss.h>
struct lb035q02_data {
struct mutex lock;
};
static struct omap_video_timings lb035q02_timings = {
.x_res = 320,
.y_res = 240,
.pixel_clock = 6500,
.hsw = 2,
.hfp = 20,
.hbp = 68,
.vsw = 2,
.vfp = 4,
.vbp = 18,
};
static int lb035q02_panel_power_on(struct omap_dss_device *dssdev)
{
int r;
if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
return 0;
r = omapdss_dpi_display_enable(dssdev);
if (r)
goto err0;
if (dssdev->platform_enable) {
r = dssdev->platform_enable(dssdev);
if (r)
goto err1;
}
return 0;
err1:
omapdss_dpi_display_disable(dssdev);
err0:
return r;
}
static void lb035q02_panel_power_off(struct omap_dss_device *dssdev)
{
if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
return;
if (dssdev->platform_disable)
dssdev->platform_disable(dssdev);
omapdss_dpi_display_disable(dssdev);
}
static int lb035q02_panel_probe(struct omap_dss_device *dssdev)
{
struct lb035q02_data *ld;
int r;
dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
OMAP_DSS_LCD_IHS;
dssdev->panel.timings = lb035q02_timings;
ld = kzalloc(sizeof(*ld), GFP_KERNEL);
if (!ld) {
r = -ENOMEM;
goto err;
}
mutex_init(&ld->lock);
dev_set_drvdata(&dssdev->dev, ld);
return 0;
err:
return r;
}
static void lb035q02_panel_remove(struct omap_dss_device *dssdev)
{
struct lb035q02_data *ld = dev_get_drvdata(&dssdev->dev);
kfree(ld);
}
static int lb035q02_panel_enable(struct omap_dss_device *dssdev)
{
struct lb035q02_data *ld = dev_get_drvdata(&dssdev->dev);
int r;
mutex_lock(&ld->lock);
r = lb035q02_panel_power_on(dssdev);
if (r)
goto err;
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
mutex_unlock(&ld->lock);
return 0;
err:
mutex_unlock(&ld->lock);
return r;
}
static void lb035q02_panel_disable(struct omap_dss_device *dssdev)
{
struct lb035q02_data *ld = dev_get_drvdata(&dssdev->dev);
mutex_lock(&ld->lock);
lb035q02_panel_power_off(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
mutex_unlock(&ld->lock);
}
static int lb035q02_panel_suspend(struct omap_dss_device *dssdev)
{
struct lb035q02_data *ld = dev_get_drvdata(&dssdev->dev);
mutex_lock(&ld->lock);
lb035q02_panel_power_off(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
mutex_unlock(&ld->lock);
return 0;
}
static int lb035q02_panel_resume(struct omap_dss_device *dssdev)
{
struct lb035q02_data *ld = dev_get_drvdata(&dssdev->dev);
int r;
mutex_lock(&ld->lock);
r = lb035q02_panel_power_on(dssdev);
if (r)
goto err;
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
mutex_unlock(&ld->lock);
return 0;
err:
mutex_unlock(&ld->lock);
return r;
}
static struct omap_dss_driver lb035q02_driver = {
.probe = lb035q02_panel_probe,
.remove = lb035q02_panel_remove,
.enable = lb035q02_panel_enable,
.disable = lb035q02_panel_disable,
.suspend = lb035q02_panel_suspend,
.resume = lb035q02_panel_resume,
.driver = {
.name = "lgphilips_lb035q02_panel",
.owner = THIS_MODULE,
},
};
static int lb035q02_write_reg(struct spi_device *spi, u8 reg, u16 val)
{
struct spi_message msg;
struct spi_transfer index_xfer = {
.len = 3,
.cs_change = 1,
};
struct spi_transfer value_xfer = {
.len = 3,
};
u8 buffer[16];
spi_message_init(&msg);
/* register index */
buffer[0] = 0x70;
buffer[1] = 0x00;
buffer[2] = reg & 0x7f;
index_xfer.tx_buf = buffer;
spi_message_add_tail(&index_xfer, &msg);
/* register value */
buffer[4] = 0x72;
buffer[5] = val >> 8;
buffer[6] = val;
value_xfer.tx_buf = buffer + 4;
spi_message_add_tail(&value_xfer, &msg);
return spi_sync(spi, &msg);
}
static void init_lb035q02_panel(struct spi_device *spi)
{
/* Init sequence from page 28 of the lb035q02 spec */
lb035q02_write_reg(spi, 0x01, 0x6300);
lb035q02_write_reg(spi, 0x02, 0x0200);
lb035q02_write_reg(spi, 0x03, 0x0177);
lb035q02_write_reg(spi, 0x04, 0x04c7);
lb035q02_write_reg(spi, 0x05, 0xffc0);
lb035q02_write_reg(spi, 0x06, 0xe806);
lb035q02_write_reg(spi, 0x0a, 0x4008);
lb035q02_write_reg(spi, 0x0b, 0x0000);
lb035q02_write_reg(spi, 0x0d, 0x0030);
lb035q02_write_reg(spi, 0x0e, 0x2800);
lb035q02_write_reg(spi, 0x0f, 0x0000);
lb035q02_write_reg(spi, 0x16, 0x9f80);
lb035q02_write_reg(spi, 0x17, 0x0a0f);
lb035q02_write_reg(spi, 0x1e, 0x00c1);
lb035q02_write_reg(spi, 0x30, 0x0300);
lb035q02_write_reg(spi, 0x31, 0x0007);
lb035q02_write_reg(spi, 0x32, 0x0000);
lb035q02_write_reg(spi, 0x33, 0x0000);
lb035q02_write_reg(spi, 0x34, 0x0707);
lb035q02_write_reg(spi, 0x35, 0x0004);
lb035q02_write_reg(spi, 0x36, 0x0302);
lb035q02_write_reg(spi, 0x37, 0x0202);
lb035q02_write_reg(spi, 0x3a, 0x0a0d);
lb035q02_write_reg(spi, 0x3b, 0x0806);
}
static int __devinit lb035q02_panel_spi_probe(struct spi_device *spi)
{
init_lb035q02_panel(spi);
return omap_dss_register_driver(&lb035q02_driver);
}
static int __devexit lb035q02_panel_spi_remove(struct spi_device *spi)
{
omap_dss_unregister_driver(&lb035q02_driver);
return 0;
}
static struct spi_driver lb035q02_spi_driver = {
.driver = {
.name = "lgphilips_lb035q02_panel-spi",
.owner = THIS_MODULE,
},
.probe = lb035q02_panel_spi_probe,
.remove = __devexit_p(lb035q02_panel_spi_remove),
};
module_spi_driver(lb035q02_spi_driver);
MODULE_LICENSE("GPL");
@@ -0,0 +1,746 @@
/* #define DEBUG */
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/spi/spi.h>
#include <linux/backlight.h>
#include <linux/fb.h>
#include <video/omapdss.h>
#include <video/omap-panel-n8x0.h>
#define BLIZZARD_REV_CODE 0x00
#define BLIZZARD_CONFIG 0x02
#define BLIZZARD_PLL_DIV 0x04
#define BLIZZARD_PLL_LOCK_RANGE 0x06
#define BLIZZARD_PLL_CLOCK_SYNTH_0 0x08
#define BLIZZARD_PLL_CLOCK_SYNTH_1 0x0a
#define BLIZZARD_PLL_MODE 0x0c
#define BLIZZARD_CLK_SRC 0x0e
#define BLIZZARD_MEM_BANK0_ACTIVATE 0x10
#define BLIZZARD_MEM_BANK0_STATUS 0x14
#define BLIZZARD_PANEL_CONFIGURATION 0x28
#define BLIZZARD_HDISP 0x2a
#define BLIZZARD_HNDP 0x2c
#define BLIZZARD_VDISP0 0x2e
#define BLIZZARD_VDISP1 0x30
#define BLIZZARD_VNDP 0x32
#define BLIZZARD_HSW 0x34
#define BLIZZARD_VSW 0x38
#define BLIZZARD_DISPLAY_MODE 0x68
#define BLIZZARD_INPUT_WIN_X_START_0 0x6c
#define BLIZZARD_DATA_SOURCE_SELECT 0x8e
#define BLIZZARD_DISP_MEM_DATA_PORT 0x90
#define BLIZZARD_DISP_MEM_READ_ADDR0 0x92
#define BLIZZARD_POWER_SAVE 0xE6
#define BLIZZARD_NDISP_CTRL_STATUS 0xE8
/* Data source select */
/* For S1D13745 */
#define BLIZZARD_SRC_WRITE_LCD_BACKGROUND 0x00
#define BLIZZARD_SRC_WRITE_LCD_DESTRUCTIVE 0x01
#define BLIZZARD_SRC_WRITE_OVERLAY_ENABLE 0x04
#define BLIZZARD_SRC_DISABLE_OVERLAY 0x05
/* For S1D13744 */
#define BLIZZARD_SRC_WRITE_LCD 0x00
#define BLIZZARD_SRC_BLT_LCD 0x06
#define BLIZZARD_COLOR_RGB565 0x01
#define BLIZZARD_COLOR_YUV420 0x09
#define BLIZZARD_VERSION_S1D13745 0x01 /* Hailstorm */
#define BLIZZARD_VERSION_S1D13744 0x02 /* Blizzard */
#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
static struct panel_drv_data {
struct mutex lock;
struct omap_dss_device *dssdev;
struct spi_device *spidev;
struct backlight_device *bldev;
int blizzard_ver;
} s_drv_data;
static inline
struct panel_n8x0_data *get_board_data(const struct omap_dss_device *dssdev)
{
return dssdev->data;
}
static inline
struct panel_drv_data *get_drv_data(const struct omap_dss_device *dssdev)
{
return &s_drv_data;
}
static inline void blizzard_cmd(u8 cmd)
{
omap_rfbi_write_command(&cmd, 1);
}
static inline void blizzard_write(u8 cmd, const u8 *buf, int len)
{
omap_rfbi_write_command(&cmd, 1);
omap_rfbi_write_data(buf, len);
}
static inline void blizzard_read(u8 cmd, u8 *buf, int len)
{
omap_rfbi_write_command(&cmd, 1);
omap_rfbi_read_data(buf, len);
}
static u8 blizzard_read_reg(u8 cmd)
{
u8 data;
blizzard_read(cmd, &data, 1);
return data;
}
static void blizzard_ctrl_setup_update(struct omap_dss_device *dssdev,
int x, int y, int w, int h)
{
struct panel_drv_data *ddata = get_drv_data(dssdev);
u8 tmp[18];
int x_end, y_end;
x_end = x + w - 1;
y_end = y + h - 1;
tmp[0] = x;
tmp[1] = x >> 8;
tmp[2] = y;
tmp[3] = y >> 8;
tmp[4] = x_end;
tmp[5] = x_end >> 8;
tmp[6] = y_end;
tmp[7] = y_end >> 8;
/* scaling? */
tmp[8] = x;
tmp[9] = x >> 8;
tmp[10] = y;
tmp[11] = y >> 8;
tmp[12] = x_end;
tmp[13] = x_end >> 8;
tmp[14] = y_end;
tmp[15] = y_end >> 8;
tmp[16] = BLIZZARD_COLOR_RGB565;
if (ddata->blizzard_ver == BLIZZARD_VERSION_S1D13745)
tmp[17] = BLIZZARD_SRC_WRITE_LCD_BACKGROUND;
else
tmp[17] = ddata->blizzard_ver == BLIZZARD_VERSION_S1D13744 ?
BLIZZARD_SRC_WRITE_LCD :
BLIZZARD_SRC_WRITE_LCD_DESTRUCTIVE;
omap_rfbi_configure(dssdev, 16, 8);
blizzard_write(BLIZZARD_INPUT_WIN_X_START_0, tmp, 18);
omap_rfbi_configure(dssdev, 16, 16);
}
static void mipid_transfer(struct spi_device *spi, int cmd, const u8 *wbuf,
int wlen, u8 *rbuf, int rlen)
{
struct spi_message m;
struct spi_transfer *x, xfer[4];
u16 w;
int r;
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;
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 = &w;
x->len = 1;
spi_message_add_tail(x, &m);
if (rlen > 1) {
/* Arrange for the extra clock before the first
* data bit.
*/
x->bits_per_word = 9;
x->len = 2;
x++;
x->rx_buf = &rbuf[1];
x->len = rlen - 1;
spi_message_add_tail(x, &m);
}
}
r = spi_sync(spi, &m);
if (r < 0)
dev_dbg(&spi->dev, "spi_sync %d\n", r);
if (rlen)
rbuf[0] = w & 0xff;
}
static inline void mipid_cmd(struct spi_device *spi, int cmd)
{
mipid_transfer(spi, cmd, NULL, 0, NULL, 0);
}
static inline void mipid_write(struct spi_device *spi,
int reg, const u8 *buf, int len)
{
mipid_transfer(spi, reg, buf, len, NULL, 0);
}
static inline void mipid_read(struct spi_device *spi,
int reg, u8 *buf, int len)
{
mipid_transfer(spi, reg, NULL, 0, buf, len);
}
static void set_data_lines(struct spi_device *spi, int data_lines)
{
u16 par;
switch (data_lines) {
case 16:
par = 0x150;
break;
case 18:
par = 0x160;
break;
case 24:
par = 0x170;
break;
}
mipid_write(spi, 0x3a, (u8 *)&par, 2);
}
static void send_init_string(struct spi_device *spi)
{
u16 initpar[] = { 0x0102, 0x0100, 0x0100 };
mipid_write(spi, 0xc2, (u8 *)initpar, sizeof(initpar));
}
static void send_display_on(struct spi_device *spi)
{
mipid_cmd(spi, MIPID_CMD_DISP_ON);
}
static void send_display_off(struct spi_device *spi)
{
mipid_cmd(spi, MIPID_CMD_DISP_OFF);
}
static void send_sleep_out(struct spi_device *spi)
{
mipid_cmd(spi, MIPID_CMD_SLEEP_OUT);
msleep(120);
}
static void send_sleep_in(struct spi_device *spi)
{
mipid_cmd(spi, MIPID_CMD_SLEEP_IN);
msleep(50);
}
static int n8x0_panel_power_on(struct omap_dss_device *dssdev)
{
int r;
struct panel_n8x0_data *bdata = get_board_data(dssdev);
struct panel_drv_data *ddata = get_drv_data(dssdev);
struct spi_device *spi = ddata->spidev;
u8 rev, conf;
u8 display_id[3];
const char *panel_name;
if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
return 0;
gpio_direction_output(bdata->ctrl_pwrdown, 1);
if (bdata->platform_enable) {
r = bdata->platform_enable(dssdev);
if (r)
goto err_plat_en;
}
r = omapdss_rfbi_display_enable(dssdev);
if (r)
goto err_rfbi_en;
rev = blizzard_read_reg(BLIZZARD_REV_CODE);
conf = blizzard_read_reg(BLIZZARD_CONFIG);
switch (rev & 0xfc) {
case 0x9c:
ddata->blizzard_ver = BLIZZARD_VERSION_S1D13744;
dev_info(&dssdev->dev, "s1d13744 LCD controller rev %d "
"initialized (CNF pins %x)\n", rev & 0x03, conf & 0x07);
break;
case 0xa4:
ddata->blizzard_ver = BLIZZARD_VERSION_S1D13745;
dev_info(&dssdev->dev, "s1d13745 LCD controller rev %d "
"initialized (CNF pins %x)\n", rev & 0x03, conf & 0x07);
break;
default:
dev_err(&dssdev->dev, "invalid s1d1374x revision %02x\n", rev);
r = -ENODEV;
goto err_inv_chip;
}
/* panel */
gpio_direction_output(bdata->panel_reset, 1);
mipid_read(spi, MIPID_CMD_READ_DISP_ID, display_id, 3);
dev_dbg(&spi->dev, "MIPI display ID: %02x%02x%02x\n",
display_id[0], display_id[1], display_id[2]);
switch (display_id[0]) {
case 0x45:
panel_name = "lph8923";
break;
case 0x83:
panel_name = "ls041y3";
break;
default:
dev_err(&dssdev->dev, "invalid display ID 0x%x\n",
display_id[0]);
r = -ENODEV;
goto err_inv_panel;
}
dev_info(&dssdev->dev, "%s rev %02x LCD detected\n",
panel_name, display_id[1]);
send_sleep_out(spi);
send_init_string(spi);
set_data_lines(spi, 24);
send_display_on(spi);
return 0;
err_inv_panel:
/*
* HACK: we should turn off the panel here, but there is some problem
* with the initialization sequence, and we fail to init the panel if we
* have turned it off
*/
/* gpio_direction_output(bdata->panel_reset, 0); */
err_inv_chip:
omapdss_rfbi_display_disable(dssdev);
err_rfbi_en:
if (bdata->platform_disable)
bdata->platform_disable(dssdev);
err_plat_en:
gpio_direction_output(bdata->ctrl_pwrdown, 0);
return r;
}
static void n8x0_panel_power_off(struct omap_dss_device *dssdev)
{
struct panel_n8x0_data *bdata = get_board_data(dssdev);
struct panel_drv_data *ddata = get_drv_data(dssdev);
struct spi_device *spi = ddata->spidev;
if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
return;
send_display_off(spi);
send_sleep_in(spi);
if (bdata->platform_disable)
bdata->platform_disable(dssdev);
/*
* HACK: we should turn off the panel here, but there is some problem
* with the initialization sequence, and we fail to init the panel if we
* have turned it off
*/
/* gpio_direction_output(bdata->panel_reset, 0); */
gpio_direction_output(bdata->ctrl_pwrdown, 0);
omapdss_rfbi_display_disable(dssdev);
}
static const struct rfbi_timings n8x0_panel_timings = {
.cs_on_time = 0,
.we_on_time = 9000,
.we_off_time = 18000,
.we_cycle_time = 36000,
.re_on_time = 9000,
.re_off_time = 27000,
.re_cycle_time = 36000,
.access_time = 27000,
.cs_off_time = 36000,
.cs_pulse_width = 0,
};
static int n8x0_bl_update_status(struct backlight_device *dev)
{
struct omap_dss_device *dssdev = dev_get_drvdata(&dev->dev);
struct panel_n8x0_data *bdata = get_board_data(dssdev);
struct panel_drv_data *ddata = get_drv_data(dssdev);
int r;
int level;
mutex_lock(&ddata->lock);
if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
dev->props.power == FB_BLANK_UNBLANK)
level = dev->props.brightness;
else
level = 0;
dev_dbg(&dssdev->dev, "update brightness to %d\n", level);
if (!bdata->set_backlight)
r = -EINVAL;
else
r = bdata->set_backlight(dssdev, level);
mutex_unlock(&ddata->lock);
return r;
}
static int n8x0_bl_get_intensity(struct backlight_device *dev)
{
if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
dev->props.power == FB_BLANK_UNBLANK)
return dev->props.brightness;
return 0;
}
static const struct backlight_ops n8x0_bl_ops = {
.get_brightness = n8x0_bl_get_intensity,
.update_status = n8x0_bl_update_status,
};
static int n8x0_panel_probe(struct omap_dss_device *dssdev)
{
struct panel_n8x0_data *bdata = get_board_data(dssdev);
struct panel_drv_data *ddata;
struct backlight_device *bldev;
struct backlight_properties props;
int r;
dev_dbg(&dssdev->dev, "probe\n");
if (!bdata)
return -EINVAL;
s_drv_data.dssdev = dssdev;
ddata = &s_drv_data;
mutex_init(&ddata->lock);
dssdev->panel.config = OMAP_DSS_LCD_TFT;
dssdev->panel.timings.x_res = 800;
dssdev->panel.timings.y_res = 480;
dssdev->ctrl.pixel_size = 16;
dssdev->ctrl.rfbi_timings = n8x0_panel_timings;
memset(&props, 0, sizeof(props));
props.max_brightness = 127;
props.type = BACKLIGHT_PLATFORM;
bldev = backlight_device_register(dev_name(&dssdev->dev), &dssdev->dev,
dssdev, &n8x0_bl_ops, &props);
if (IS_ERR(bldev)) {
r = PTR_ERR(bldev);
dev_err(&dssdev->dev, "register backlight failed\n");
return r;
}
ddata->bldev = bldev;
bldev->props.fb_blank = FB_BLANK_UNBLANK;
bldev->props.power = FB_BLANK_UNBLANK;
bldev->props.brightness = 127;
n8x0_bl_update_status(bldev);
return 0;
}
static void n8x0_panel_remove(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = get_drv_data(dssdev);
struct backlight_device *bldev;
dev_dbg(&dssdev->dev, "remove\n");
bldev = ddata->bldev;
bldev->props.power = FB_BLANK_POWERDOWN;
n8x0_bl_update_status(bldev);
backlight_device_unregister(bldev);
dev_set_drvdata(&dssdev->dev, NULL);
}
static int n8x0_panel_enable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = get_drv_data(dssdev);
int r;
dev_dbg(&dssdev->dev, "enable\n");
mutex_lock(&ddata->lock);
rfbi_bus_lock();
r = n8x0_panel_power_on(dssdev);
rfbi_bus_unlock();
if (r) {
mutex_unlock(&ddata->lock);
return r;
}
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
mutex_unlock(&ddata->lock);
return 0;
}
static void n8x0_panel_disable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = get_drv_data(dssdev);
dev_dbg(&dssdev->dev, "disable\n");
mutex_lock(&ddata->lock);
rfbi_bus_lock();
n8x0_panel_power_off(dssdev);
rfbi_bus_unlock();
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
mutex_unlock(&ddata->lock);
}
static int n8x0_panel_suspend(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = get_drv_data(dssdev);
dev_dbg(&dssdev->dev, "suspend\n");
mutex_lock(&ddata->lock);
rfbi_bus_lock();
n8x0_panel_power_off(dssdev);
rfbi_bus_unlock();
dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
mutex_unlock(&ddata->lock);
return 0;
}
static int n8x0_panel_resume(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = get_drv_data(dssdev);
int r;
dev_dbg(&dssdev->dev, "resume\n");
mutex_lock(&ddata->lock);
rfbi_bus_lock();
r = n8x0_panel_power_on(dssdev);
rfbi_bus_unlock();
if (r) {
mutex_unlock(&ddata->lock);
return r;
}
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
mutex_unlock(&ddata->lock);
return 0;
}
static void n8x0_panel_get_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
*timings = dssdev->panel.timings;
}
static void n8x0_panel_get_resolution(struct omap_dss_device *dssdev,
u16 *xres, u16 *yres)
{
*xres = dssdev->panel.timings.x_res;
*yres = dssdev->panel.timings.y_res;
}
static void update_done(void *data)
{
rfbi_bus_unlock();
}
static int n8x0_panel_update(struct omap_dss_device *dssdev,
u16 x, u16 y, u16 w, u16 h)
{
struct panel_drv_data *ddata = get_drv_data(dssdev);
dev_dbg(&dssdev->dev, "update\n");
mutex_lock(&ddata->lock);
rfbi_bus_lock();
omap_rfbi_prepare_update(dssdev, &x, &y, &w, &h);
blizzard_ctrl_setup_update(dssdev, x, y, w, h);
omap_rfbi_update(dssdev, x, y, w, h, update_done, NULL);
mutex_unlock(&ddata->lock);
return 0;
}
static int n8x0_panel_sync(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = get_drv_data(dssdev);
dev_dbg(&dssdev->dev, "sync\n");
mutex_lock(&ddata->lock);
rfbi_bus_lock();
rfbi_bus_unlock();
mutex_unlock(&ddata->lock);
return 0;
}
static struct omap_dss_driver n8x0_panel_driver = {
.probe = n8x0_panel_probe,
.remove = n8x0_panel_remove,
.enable = n8x0_panel_enable,
.disable = n8x0_panel_disable,
.suspend = n8x0_panel_suspend,
.resume = n8x0_panel_resume,
.update = n8x0_panel_update,
.sync = n8x0_panel_sync,
.get_resolution = n8x0_panel_get_resolution,
.get_recommended_bpp = omapdss_default_get_recommended_bpp,
.get_timings = n8x0_panel_get_timings,
.driver = {
.name = "n8x0_panel",
.owner = THIS_MODULE,
},
};
/* PANEL */
static int mipid_spi_probe(struct spi_device *spi)
{
dev_dbg(&spi->dev, "mipid_spi_probe\n");
spi->mode = SPI_MODE_0;
s_drv_data.spidev = spi;
return 0;
}
static int mipid_spi_remove(struct spi_device *spi)
{
dev_dbg(&spi->dev, "mipid_spi_remove\n");
return 0;
}
static struct spi_driver mipid_spi_driver = {
.driver = {
.name = "lcd_mipid",
.owner = THIS_MODULE,
},
.probe = mipid_spi_probe,
.remove = __devexit_p(mipid_spi_remove),
};
static int __init n8x0_panel_drv_init(void)
{
int r;
r = spi_register_driver(&mipid_spi_driver);
if (r) {
pr_err("n8x0_panel: spi driver registration failed\n");
return r;
}
r = omap_dss_register_driver(&n8x0_panel_driver);
if (r) {
pr_err("n8x0_panel: dss driver registration failed\n");
spi_unregister_driver(&mipid_spi_driver);
return r;
}
return 0;
}
static void __exit n8x0_panel_drv_exit(void)
{
spi_unregister_driver(&mipid_spi_driver);
omap_dss_unregister_driver(&n8x0_panel_driver);
}
module_init(n8x0_panel_drv_init);
module_exit(n8x0_panel_drv_exit);
MODULE_LICENSE("GPL");
@@ -0,0 +1,357 @@
/*
* Support for NEC-nl8048hl11-01b panel driver
*
* Copyright (C) 2010 Texas Instruments Inc.
* Author: Erik Gilling <konkers@android.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/module.h>
#include <linux/delay.h>
#include <linux/spi/spi.h>
#include <linux/backlight.h>
#include <linux/fb.h>
#include <video/omapdss.h>
#define LCD_XRES 800
#define LCD_YRES 480
/*
* NEC PIX Clock Ratings
* MIN:21.8MHz TYP:23.8MHz MAX:25.7MHz
*/
#define LCD_PIXEL_CLOCK 23800
struct nec_8048_data {
struct backlight_device *bl;
};
static const struct {
unsigned char addr;
unsigned char dat;
} nec_8048_init_seq[] = {
{ 3, 0x01 }, { 0, 0x00 }, { 1, 0x01 }, { 4, 0x00 }, { 5, 0x14 },
{ 6, 0x24 }, { 16, 0xD7 }, { 17, 0x00 }, { 18, 0x00 }, { 19, 0x55 },
{ 20, 0x01 }, { 21, 0x70 }, { 22, 0x1E }, { 23, 0x25 }, { 24, 0x25 },
{ 25, 0x02 }, { 26, 0x02 }, { 27, 0xA0 }, { 32, 0x2F }, { 33, 0x0F },
{ 34, 0x0F }, { 35, 0x0F }, { 36, 0x0F }, { 37, 0x0F }, { 38, 0x0F },
{ 39, 0x00 }, { 40, 0x02 }, { 41, 0x02 }, { 42, 0x02 }, { 43, 0x0F },
{ 44, 0x0F }, { 45, 0x0F }, { 46, 0x0F }, { 47, 0x0F }, { 48, 0x0F },
{ 49, 0x0F }, { 50, 0x00 }, { 51, 0x02 }, { 52, 0x02 }, { 53, 0x02 },
{ 80, 0x0C }, { 83, 0x42 }, { 84, 0x42 }, { 85, 0x41 }, { 86, 0x14 },
{ 89, 0x88 }, { 90, 0x01 }, { 91, 0x00 }, { 92, 0x02 }, { 93, 0x0C },
{ 94, 0x1C }, { 95, 0x27 }, { 98, 0x49 }, { 99, 0x27 }, { 102, 0x76 },
{ 103, 0x27 }, { 112, 0x01 }, { 113, 0x0E }, { 114, 0x02 },
{ 115, 0x0C }, { 118, 0x0C }, { 121, 0x30 }, { 130, 0x00 },
{ 131, 0x00 }, { 132, 0xFC }, { 134, 0x00 }, { 136, 0x00 },
{ 138, 0x00 }, { 139, 0x00 }, { 140, 0x00 }, { 141, 0xFC },
{ 143, 0x00 }, { 145, 0x00 }, { 147, 0x00 }, { 148, 0x00 },
{ 149, 0x00 }, { 150, 0xFC }, { 152, 0x00 }, { 154, 0x00 },
{ 156, 0x00 }, { 157, 0x00 }, { 2, 0x00 },
};
/*
* NEC NL8048HL11-01B Manual
* defines HFB, HSW, HBP, VFP, VSW, VBP as shown below
*/
static struct omap_video_timings nec_8048_panel_timings = {
/* 800 x 480 @ 60 Hz Reduced blanking VESA CVT 0.31M3-R */
.x_res = LCD_XRES,
.y_res = LCD_YRES,
.pixel_clock = LCD_PIXEL_CLOCK,
.hfp = 6,
.hsw = 1,
.hbp = 4,
.vfp = 3,
.vsw = 1,
.vbp = 4,
};
static int nec_8048_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 nec_8048_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 nec_8048_bl_ops = {
.get_brightness = nec_8048_bl_get_brightness,
.update_status = nec_8048_bl_update_status,
};
static int nec_8048_panel_probe(struct omap_dss_device *dssdev)
{
struct backlight_device *bl;
struct nec_8048_data *necd;
struct backlight_properties props;
int r;
dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_RF |
OMAP_DSS_LCD_ONOFF;
dssdev->panel.timings = nec_8048_panel_timings;
necd = kzalloc(sizeof(*necd), GFP_KERNEL);
if (!necd)
return -ENOMEM;
dev_set_drvdata(&dssdev->dev, necd);
memset(&props, 0, sizeof(struct backlight_properties));
props.max_brightness = 255;
bl = backlight_device_register("nec-8048", &dssdev->dev, dssdev,
&nec_8048_bl_ops, &props);
if (IS_ERR(bl)) {
r = PTR_ERR(bl);
kfree(necd);
return r;
}
necd->bl = bl;
bl->props.fb_blank = FB_BLANK_UNBLANK;
bl->props.power = FB_BLANK_UNBLANK;
bl->props.max_brightness = dssdev->max_backlight_level;
bl->props.brightness = dssdev->max_backlight_level;
r = nec_8048_bl_update_status(bl);
if (r < 0)
dev_err(&dssdev->dev, "failed to set lcd brightness\n");
return 0;
}
static void nec_8048_panel_remove(struct omap_dss_device *dssdev)
{
struct nec_8048_data *necd = dev_get_drvdata(&dssdev->dev);
struct backlight_device *bl = necd->bl;
bl->props.power = FB_BLANK_POWERDOWN;
nec_8048_bl_update_status(bl);
backlight_device_unregister(bl);
kfree(necd);
}
static int nec_8048_panel_power_on(struct omap_dss_device *dssdev)
{
int r;
struct nec_8048_data *necd = dev_get_drvdata(&dssdev->dev);
struct backlight_device *bl = necd->bl;
if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
return 0;
r = omapdss_dpi_display_enable(dssdev);
if (r)
goto err0;
if (dssdev->platform_enable) {
r = dssdev->platform_enable(dssdev);
if (r)
goto err1;
}
r = nec_8048_bl_update_status(bl);
if (r < 0)
dev_err(&dssdev->dev, "failed to set lcd brightness\n");
return 0;
err1:
omapdss_dpi_display_disable(dssdev);
err0:
return r;
}
static void nec_8048_panel_power_off(struct omap_dss_device *dssdev)
{
struct nec_8048_data *necd = dev_get_drvdata(&dssdev->dev);
struct backlight_device *bl = necd->bl;
if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
return;
bl->props.brightness = 0;
nec_8048_bl_update_status(bl);
if (dssdev->platform_disable)
dssdev->platform_disable(dssdev);
omapdss_dpi_display_disable(dssdev);
}
static int nec_8048_panel_enable(struct omap_dss_device *dssdev)
{
int r;
r = nec_8048_panel_power_on(dssdev);
if (r)
return r;
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return 0;
}
static void nec_8048_panel_disable(struct omap_dss_device *dssdev)
{
nec_8048_panel_power_off(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
}
static int nec_8048_panel_suspend(struct omap_dss_device *dssdev)
{
nec_8048_panel_power_off(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
return 0;
}
static int nec_8048_panel_resume(struct omap_dss_device *dssdev)
{
int r;
r = nec_8048_panel_power_on(dssdev);
if (r)
return r;
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return 0;
}
static int nec_8048_recommended_bpp(struct omap_dss_device *dssdev)
{
return 16;
}
static struct omap_dss_driver nec_8048_driver = {
.probe = nec_8048_panel_probe,
.remove = nec_8048_panel_remove,
.enable = nec_8048_panel_enable,
.disable = nec_8048_panel_disable,
.suspend = nec_8048_panel_suspend,
.resume = nec_8048_panel_resume,
.get_recommended_bpp = nec_8048_recommended_bpp,
.driver = {
.name = "NEC_8048_panel",
.owner = THIS_MODULE,
},
};
static int nec_8048_spi_send(struct spi_device *spi, unsigned char reg_addr,
unsigned char reg_data)
{
int ret = 0;
unsigned int cmd = 0, data = 0;
cmd = 0x0000 | reg_addr; /* register address write */
data = 0x0100 | reg_data ; /* register data write */
data = (cmd << 16) | data;
ret = spi_write(spi, (unsigned char *)&data, 4);
if (ret)
pr_err("error in spi_write %x\n", data);
return ret;
}
static int init_nec_8048_wvga_lcd(struct spi_device *spi)
{
unsigned int i;
/* Initialization Sequence */
/* nec_8048_spi_send(spi, REG, VAL) */
for (i = 0; i < (ARRAY_SIZE(nec_8048_init_seq) - 1); i++)
nec_8048_spi_send(spi, nec_8048_init_seq[i].addr,
nec_8048_init_seq[i].dat);
udelay(20);
nec_8048_spi_send(spi, nec_8048_init_seq[i].addr,
nec_8048_init_seq[i].dat);
return 0;
}
static int nec_8048_spi_probe(struct spi_device *spi)
{
spi->mode = SPI_MODE_0;
spi->bits_per_word = 32;
spi_setup(spi);
init_nec_8048_wvga_lcd(spi);
return omap_dss_register_driver(&nec_8048_driver);
}
static int nec_8048_spi_remove(struct spi_device *spi)
{
omap_dss_unregister_driver(&nec_8048_driver);
return 0;
}
static int nec_8048_spi_suspend(struct spi_device *spi, pm_message_t mesg)
{
nec_8048_spi_send(spi, 2, 0x01);
mdelay(40);
return 0;
}
static int nec_8048_spi_resume(struct spi_device *spi)
{
/* reinitialize the panel */
spi_setup(spi);
nec_8048_spi_send(spi, 2, 0x00);
init_nec_8048_wvga_lcd(spi);
return 0;
}
static struct spi_driver nec_8048_spi_driver = {
.probe = nec_8048_spi_probe,
.remove = __devexit_p(nec_8048_spi_remove),
.suspend = nec_8048_spi_suspend,
.resume = nec_8048_spi_resume,
.driver = {
.name = "nec_8048_spi",
.owner = THIS_MODULE,
},
};
module_spi_driver(nec_8048_spi_driver);
MODULE_AUTHOR("Erik Gilling <konkers@android.com>");
MODULE_DESCRIPTION("NEC-nl8048hl11-01b Driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,594 @@
/*
* picodlp panel driver
* picodlp_i2c_driver: i2c_client driver
*
* Copyright (C) 2009-2011 Texas Instruments
* Author: Mythri P K <mythripk@ti.com>
* Mayuresh Janorkar <mayur@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/module.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/firmware.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <video/omapdss.h>
#include <video/omap-panel-picodlp.h>
#include "panel-picodlp.h"
struct picodlp_data {
struct mutex lock;
struct i2c_client *picodlp_i2c_client;
};
static struct i2c_board_info picodlp_i2c_board_info = {
I2C_BOARD_INFO("picodlp_i2c_driver", 0x1b),
};
struct picodlp_i2c_data {
struct mutex xfer_lock;
};
static struct i2c_device_id picodlp_i2c_id[] = {
{ "picodlp_i2c_driver", 0 },
};
struct picodlp_i2c_command {
u8 reg;
u32 value;
};
static struct omap_video_timings pico_ls_timings = {
.x_res = 864,
.y_res = 480,
.hsw = 7,
.hfp = 11,
.hbp = 7,
.pixel_clock = 19200,
.vsw = 2,
.vfp = 3,
.vbp = 14,
};
static inline struct picodlp_panel_data
*get_panel_data(const struct omap_dss_device *dssdev)
{
return (struct picodlp_panel_data *) dssdev->data;
}
static u32 picodlp_i2c_read(struct i2c_client *client, u8 reg)
{
u8 read_cmd[] = {READ_REG_SELECT, reg}, data[4];
struct picodlp_i2c_data *picodlp_i2c_data = i2c_get_clientdata(client);
struct i2c_msg msg[2];
mutex_lock(&picodlp_i2c_data->xfer_lock);
msg[0].addr = client->addr;
msg[0].flags = 0;
msg[0].len = 2;
msg[0].buf = read_cmd;
msg[1].addr = client->addr;
msg[1].flags = I2C_M_RD;
msg[1].len = 4;
msg[1].buf = data;
i2c_transfer(client->adapter, msg, 2);
mutex_unlock(&picodlp_i2c_data->xfer_lock);
return (data[3] | (data[2] << 8) | (data[1] << 16) | (data[0] << 24));
}
static int picodlp_i2c_write_block(struct i2c_client *client,
u8 *data, int len)
{
struct i2c_msg msg;
int i, r, msg_count = 1;
struct picodlp_i2c_data *picodlp_i2c_data = i2c_get_clientdata(client);
if (len < 1 || len > 32) {
dev_err(&client->dev,
"too long syn_write_block len %d\n", len);
return -EIO;
}
mutex_lock(&picodlp_i2c_data->xfer_lock);
msg.addr = client->addr;
msg.flags = 0;
msg.len = len;
msg.buf = data;
r = i2c_transfer(client->adapter, &msg, msg_count);
mutex_unlock(&picodlp_i2c_data->xfer_lock);
/*
* i2c_transfer returns:
* number of messages sent in case of success
* a negative error number in case of failure
*/
if (r != msg_count)
goto err;
/* In case of success */
for (i = 0; i < len; i++)
dev_dbg(&client->dev,
"addr %x bw 0x%02x[%d]: 0x%02x\n",
client->addr, data[0] + i, i, data[i]);
return 0;
err:
dev_err(&client->dev, "picodlp_i2c_write error\n");
return r;
}
static int picodlp_i2c_write(struct i2c_client *client, u8 reg, u32 value)
{
u8 data[5];
int i;
data[0] = reg;
for (i = 1; i < 5; i++)
data[i] = (value >> (32 - (i) * 8)) & 0xFF;
return picodlp_i2c_write_block(client, data, 5);
}
static int picodlp_i2c_write_array(struct i2c_client *client,
const struct picodlp_i2c_command commands[],
int count)
{
int i, r = 0;
for (i = 0; i < count; i++) {
r = picodlp_i2c_write(client, commands[i].reg,
commands[i].value);
if (r)
return r;
}
return r;
}
static int picodlp_wait_for_dma_done(struct i2c_client *client)
{
u8 trial = 100;
do {
msleep(1);
if (!trial--)
return -ETIMEDOUT;
} while (picodlp_i2c_read(client, MAIN_STATUS) & DMA_STATUS);
return 0;
}
/**
* picodlp_i2c_init: i2c_initialization routine
* client: i2c_client for communication
*
* return
* 0 : Success, no error
* error code : Failure
*/
static int picodlp_i2c_init(struct i2c_client *client)
{
int r;
static const struct picodlp_i2c_command init_cmd_set1[] = {
{SOFT_RESET, 1},
{DMD_PARK_TRIGGER, 1},
{MISC_REG, 5},
{SEQ_CONTROL, 0},
{SEQ_VECTOR, 0x100},
{DMD_BLOCK_COUNT, 7},
{DMD_VCC_CONTROL, 0x109},
{DMD_PARK_PULSE_COUNT, 0xA},
{DMD_PARK_PULSE_WIDTH, 0xB},
{DMD_PARK_DELAY, 0x2ED},
{DMD_SHADOW_ENABLE, 0},
{FLASH_OPCODE, 0xB},
{FLASH_DUMMY_BYTES, 1},
{FLASH_ADDR_BYTES, 3},
{PBC_CONTROL, 0},
{FLASH_START_ADDR, CMT_LUT_0_START_ADDR},
{FLASH_READ_BYTES, CMT_LUT_0_SIZE},
{CMT_SPLASH_LUT_START_ADDR, 0},
{CMT_SPLASH_LUT_DEST_SELECT, CMT_LUT_ALL},
{PBC_CONTROL, 1},
};
static const struct picodlp_i2c_command init_cmd_set2[] = {
{PBC_CONTROL, 0},
{CMT_SPLASH_LUT_DEST_SELECT, 0},
{PBC_CONTROL, 0},
{FLASH_START_ADDR, SEQUENCE_0_START_ADDR},
{FLASH_READ_BYTES, SEQUENCE_0_SIZE},
{SEQ_RESET_LUT_START_ADDR, 0},
{SEQ_RESET_LUT_DEST_SELECT, SEQ_SEQ_LUT},
{PBC_CONTROL, 1},
};
static const struct picodlp_i2c_command init_cmd_set3[] = {
{PBC_CONTROL, 0},
{SEQ_RESET_LUT_DEST_SELECT, 0},
{PBC_CONTROL, 0},
{FLASH_START_ADDR, DRC_TABLE_0_START_ADDR},
{FLASH_READ_BYTES, DRC_TABLE_0_SIZE},
{SEQ_RESET_LUT_START_ADDR, 0},
{SEQ_RESET_LUT_DEST_SELECT, SEQ_DRC_LUT_ALL},
{PBC_CONTROL, 1},
};
static const struct picodlp_i2c_command init_cmd_set4[] = {
{PBC_CONTROL, 0},
{SEQ_RESET_LUT_DEST_SELECT, 0},
{SDC_ENABLE, 1},
{AGC_CTRL, 7},
{CCA_C1A, 0x100},
{CCA_C1B, 0x0},
{CCA_C1C, 0x0},
{CCA_C2A, 0x0},
{CCA_C2B, 0x100},
{CCA_C2C, 0x0},
{CCA_C3A, 0x0},
{CCA_C3B, 0x0},
{CCA_C3C, 0x100},
{CCA_C7A, 0x100},
{CCA_C7B, 0x100},
{CCA_C7C, 0x100},
{CCA_ENABLE, 1},
{CPU_IF_MODE, 1},
{SHORT_FLIP, 1},
{CURTAIN_CONTROL, 0},
{DMD_PARK_TRIGGER, 0},
{R_DRIVE_CURRENT, 0x298},
{G_DRIVE_CURRENT, 0x298},
{B_DRIVE_CURRENT, 0x298},
{RGB_DRIVER_ENABLE, 7},
{SEQ_CONTROL, 0},
{ACTGEN_CONTROL, 0x10},
{SEQUENCE_MODE, SEQ_LOCK},
{DATA_FORMAT, RGB888},
{INPUT_RESOLUTION, WVGA_864_LANDSCAPE},
{INPUT_SOURCE, PARALLEL_RGB},
{CPU_IF_SYNC_METHOD, 1},
{SEQ_CONTROL, 1}
};
r = picodlp_i2c_write_array(client, init_cmd_set1,
ARRAY_SIZE(init_cmd_set1));
if (r)
return r;
r = picodlp_wait_for_dma_done(client);
if (r)
return r;
r = picodlp_i2c_write_array(client, init_cmd_set2,
ARRAY_SIZE(init_cmd_set2));
if (r)
return r;
r = picodlp_wait_for_dma_done(client);
if (r)
return r;
r = picodlp_i2c_write_array(client, init_cmd_set3,
ARRAY_SIZE(init_cmd_set3));
if (r)
return r;
r = picodlp_wait_for_dma_done(client);
if (r)
return r;
r = picodlp_i2c_write_array(client, init_cmd_set4,
ARRAY_SIZE(init_cmd_set4));
if (r)
return r;
return 0;
}
static int picodlp_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct picodlp_i2c_data *picodlp_i2c_data;
picodlp_i2c_data = kzalloc(sizeof(struct picodlp_i2c_data), GFP_KERNEL);
if (!picodlp_i2c_data)
return -ENOMEM;
mutex_init(&picodlp_i2c_data->xfer_lock);
i2c_set_clientdata(client, picodlp_i2c_data);
return 0;
}
static int picodlp_i2c_remove(struct i2c_client *client)
{
struct picodlp_i2c_data *picodlp_i2c_data =
i2c_get_clientdata(client);
kfree(picodlp_i2c_data);
return 0;
}
static struct i2c_driver picodlp_i2c_driver = {
.driver = {
.name = "picodlp_i2c_driver",
},
.probe = picodlp_i2c_probe,
.remove = picodlp_i2c_remove,
.id_table = picodlp_i2c_id,
};
static int picodlp_panel_power_on(struct omap_dss_device *dssdev)
{
int r, trial = 100;
struct picodlp_data *picod = dev_get_drvdata(&dssdev->dev);
struct picodlp_panel_data *picodlp_pdata = get_panel_data(dssdev);
if (dssdev->platform_enable) {
r = dssdev->platform_enable(dssdev);
if (r)
return r;
}
gpio_set_value(picodlp_pdata->pwrgood_gpio, 0);
msleep(1);
gpio_set_value(picodlp_pdata->pwrgood_gpio, 1);
while (!gpio_get_value(picodlp_pdata->emu_done_gpio)) {
if (!trial--) {
dev_err(&dssdev->dev, "emu_done signal not"
" going high\n");
return -ETIMEDOUT;
}
msleep(5);
}
/*
* As per dpp2600 programming guide,
* it is required to sleep for 1000ms after emu_done signal goes high
* then only i2c commands can be successfully sent to dpp2600
*/
msleep(1000);
r = omapdss_dpi_display_enable(dssdev);
if (r) {
dev_err(&dssdev->dev, "failed to enable DPI\n");
goto err1;
}
r = picodlp_i2c_init(picod->picodlp_i2c_client);
if (r)
goto err;
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return r;
err:
omapdss_dpi_display_disable(dssdev);
err1:
if (dssdev->platform_disable)
dssdev->platform_disable(dssdev);
return r;
}
static void picodlp_panel_power_off(struct omap_dss_device *dssdev)
{
struct picodlp_panel_data *picodlp_pdata = get_panel_data(dssdev);
omapdss_dpi_display_disable(dssdev);
gpio_set_value(picodlp_pdata->emu_done_gpio, 0);
gpio_set_value(picodlp_pdata->pwrgood_gpio, 0);
if (dssdev->platform_disable)
dssdev->platform_disable(dssdev);
}
static int picodlp_panel_probe(struct omap_dss_device *dssdev)
{
struct picodlp_data *picod;
struct picodlp_panel_data *picodlp_pdata = get_panel_data(dssdev);
struct i2c_adapter *adapter;
struct i2c_client *picodlp_i2c_client;
int r = 0, picodlp_adapter_id;
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;
picod = kzalloc(sizeof(struct picodlp_data), GFP_KERNEL);
if (!picod)
return -ENOMEM;
mutex_init(&picod->lock);
picodlp_adapter_id = picodlp_pdata->picodlp_adapter_id;
adapter = i2c_get_adapter(picodlp_adapter_id);
if (!adapter) {
dev_err(&dssdev->dev, "can't get i2c adapter\n");
r = -ENODEV;
goto err;
}
picodlp_i2c_client = i2c_new_device(adapter, &picodlp_i2c_board_info);
if (!picodlp_i2c_client) {
dev_err(&dssdev->dev, "can't add i2c device::"
" picodlp_i2c_client is NULL\n");
r = -ENODEV;
goto err;
}
picod->picodlp_i2c_client = picodlp_i2c_client;
dev_set_drvdata(&dssdev->dev, picod);
return r;
err:
kfree(picod);
return r;
}
static void picodlp_panel_remove(struct omap_dss_device *dssdev)
{
struct picodlp_data *picod = dev_get_drvdata(&dssdev->dev);
i2c_unregister_device(picod->picodlp_i2c_client);
dev_set_drvdata(&dssdev->dev, NULL);
dev_dbg(&dssdev->dev, "removing picodlp panel\n");
kfree(picod);
}
static int picodlp_panel_enable(struct omap_dss_device *dssdev)
{
struct picodlp_data *picod = dev_get_drvdata(&dssdev->dev);
int r;
dev_dbg(&dssdev->dev, "enabling picodlp panel\n");
mutex_lock(&picod->lock);
if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {
mutex_unlock(&picod->lock);
return -EINVAL;
}
r = picodlp_panel_power_on(dssdev);
mutex_unlock(&picod->lock);
return r;
}
static void picodlp_panel_disable(struct omap_dss_device *dssdev)
{
struct picodlp_data *picod = dev_get_drvdata(&dssdev->dev);
mutex_lock(&picod->lock);
/* Turn off DLP Power */
if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
picodlp_panel_power_off(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
mutex_unlock(&picod->lock);
dev_dbg(&dssdev->dev, "disabling picodlp panel\n");
}
static int picodlp_panel_suspend(struct omap_dss_device *dssdev)
{
struct picodlp_data *picod = dev_get_drvdata(&dssdev->dev);
mutex_lock(&picod->lock);
/* Turn off DLP Power */
if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
mutex_unlock(&picod->lock);
dev_err(&dssdev->dev, "unable to suspend picodlp panel,"
" panel is not ACTIVE\n");
return -EINVAL;
}
picodlp_panel_power_off(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
mutex_unlock(&picod->lock);
dev_dbg(&dssdev->dev, "suspending picodlp panel\n");
return 0;
}
static int picodlp_panel_resume(struct omap_dss_device *dssdev)
{
struct picodlp_data *picod = dev_get_drvdata(&dssdev->dev);
int r;
mutex_lock(&picod->lock);
if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) {
mutex_unlock(&picod->lock);
dev_err(&dssdev->dev, "unable to resume picodlp panel,"
" panel is not ACTIVE\n");
return -EINVAL;
}
r = picodlp_panel_power_on(dssdev);
mutex_unlock(&picod->lock);
dev_dbg(&dssdev->dev, "resuming picodlp panel\n");
return r;
}
static void picodlp_get_resolution(struct omap_dss_device *dssdev,
u16 *xres, u16 *yres)
{
*xres = dssdev->panel.timings.x_res;
*yres = dssdev->panel.timings.y_res;
}
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 = picodlp_get_resolution,
.suspend = picodlp_panel_suspend,
.resume = picodlp_panel_resume,
.driver = {
.name = "picodlp_panel",
.owner = THIS_MODULE,
},
};
static int __init picodlp_init(void)
{
int r = 0;
r = i2c_add_driver(&picodlp_i2c_driver);
if (r) {
printk(KERN_WARNING "picodlp_i2c_driver" \
" registration failed\n");
return r;
}
r = omap_dss_register_driver(&picodlp_driver);
if (r)
i2c_del_driver(&picodlp_i2c_driver);
return r;
}
static void __exit picodlp_exit(void)
{
i2c_del_driver(&picodlp_i2c_driver);
omap_dss_unregister_driver(&picodlp_driver);
}
module_init(picodlp_init);
module_exit(picodlp_exit);
MODULE_AUTHOR("Mythri P K <mythripk@ti.com>");
MODULE_DESCRIPTION("picodlp driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,288 @@
/*
* Header file required by picodlp panel driver
*
* Copyright (C) 2009-2011 Texas Instruments
* Author: Mythri P K <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/>.
*/
#ifndef __OMAP2_DISPLAY_PANEL_PICODLP_H
#define __OMAP2_DISPLAY_PANEL_PICODLP_H
/* Commands used for configuring picodlp panel */
#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
/* Color Coordinate Adjustment 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
/**
* DLP Pico Processor 2600 comes with flash
* We can do DMA operations from flash for accessing Look Up Tables
*/
#define DMA_STATUS 0x100
#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
/* DMD is Digital Micromirror Device */
#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
/* Look Up Table 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 definitions */
#define PARALLEL_RGB 0
#define INT_TEST_PATTERN 1
#define SPLASH_SCREEN 2
#define CPU_INTF 3
#define BT656 4
/* Standard input resolution definitions */
#define QWVGA_LANDSCAPE 3 /* (427h*240v) */
#define WVGA_864_LANDSCAPE 21 /* (864h*480v) */
#define WVGA_DMD_OPTICAL_TEST 35 /* (608h*684v) */
/* Standard data format definitions */
#define RGB565 0
#define RGB666 1
#define RGB888 2
/* Test Pattern definitions */
#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 definitions */
#define SEQ_FREE_RUN 0
#define SEQ_LOCK 1
/* curtain color definitions */
#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 definitions */
#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 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
#endif
@@ -0,0 +1,233 @@
/*
* LCD panel driver for Sharp LS037V7DW01
*
* Copyright (C) 2008 Nokia Corporation
* Author: Tomi Valkeinen <tomi.valkeinen@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/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 <video/omapdss.h>
struct sharp_data {
struct backlight_device *bl;
};
static struct omap_video_timings sharp_ls_timings = {
.x_res = 480,
.y_res = 640,
.pixel_clock = 19200,
.hsw = 2,
.hfp = 1,
.hbp = 28,
.vsw = 1,
.vfp = 1,
.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;
props.type = BACKLIGHT_RAW;
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 __exit 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)
{
int r = 0;
if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
return 0;
r = omapdss_dpi_display_enable(dssdev);
if (r)
goto err0;
/* wait couple of vsyncs until enabling the LCD */
msleep(50);
if (dssdev->platform_enable) {
r = dssdev->platform_enable(dssdev);
if (r)
goto err1;
}
return 0;
err1:
omapdss_dpi_display_disable(dssdev);
err0:
return r;
}
static void sharp_ls_power_off(struct omap_dss_device *dssdev)
{
if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
return;
if (dssdev->platform_disable)
dssdev->platform_disable(dssdev);
/* wait at least 5 vsyncs after disabling the LCD */
msleep(100);
omapdss_dpi_display_disable(dssdev);
}
static int sharp_ls_panel_enable(struct omap_dss_device *dssdev)
{
int r;
r = sharp_ls_power_on(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return r;
}
static void sharp_ls_panel_disable(struct omap_dss_device *dssdev)
{
sharp_ls_power_off(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
}
static int sharp_ls_panel_suspend(struct omap_dss_device *dssdev)
{
sharp_ls_power_off(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
return 0;
}
static int sharp_ls_panel_resume(struct omap_dss_device *dssdev)
{
int r;
r = sharp_ls_power_on(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return r;
}
static struct omap_dss_driver sharp_ls_driver = {
.probe = sharp_ls_panel_probe,
.remove = __exit_p(sharp_ls_panel_remove),
.enable = sharp_ls_panel_enable,
.disable = sharp_ls_panel_disable,
.suspend = sharp_ls_panel_suspend,
.resume = sharp_ls_panel_resume,
.driver = {
.name = "sharp_ls_panel",
.owner = THIS_MODULE,
},
};
static int __init sharp_ls_panel_drv_init(void)
{
return omap_dss_register_driver(&sharp_ls_driver);
}
static void __exit sharp_ls_panel_drv_exit(void)
{
omap_dss_unregister_driver(&sharp_ls_driver);
}
module_init(sharp_ls_panel_drv_init);
module_exit(sharp_ls_panel_drv_exit);
MODULE_LICENSE("GPL");
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,583 @@
/*
* LCD panel driver for TPO TD043MTEA1
*
* Author: Gražvydas Ignotas <notasas@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/spi/spi.h>
#include <linux/regulator/consumer.h>
#include <linux/gpio.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <video/omapdss.h>
#define TPO_R02_MODE(x) ((x) & 7)
#define TPO_R02_MODE_800x480 7
#define TPO_R02_NCLK_RISING BIT(3)
#define TPO_R02_HSYNC_HIGH BIT(4)
#define TPO_R02_VSYNC_HIGH BIT(5)
#define TPO_R03_NSTANDBY BIT(0)
#define TPO_R03_EN_CP_CLK BIT(1)
#define TPO_R03_EN_VGL_PUMP BIT(2)
#define TPO_R03_EN_PWM BIT(3)
#define TPO_R03_DRIVING_CAP_100 BIT(4)
#define TPO_R03_EN_PRE_CHARGE BIT(6)
#define TPO_R03_SOFTWARE_CTL BIT(7)
#define TPO_R04_NFLIP_H BIT(0)
#define TPO_R04_NFLIP_V BIT(1)
#define TPO_R04_CP_CLK_FREQ_1H BIT(2)
#define TPO_R04_VGL_FREQ_1H BIT(4)
#define TPO_R03_VAL_NORMAL (TPO_R03_NSTANDBY | TPO_R03_EN_CP_CLK | \
TPO_R03_EN_VGL_PUMP | TPO_R03_EN_PWM | \
TPO_R03_DRIVING_CAP_100 | TPO_R03_EN_PRE_CHARGE | \
TPO_R03_SOFTWARE_CTL)
#define TPO_R03_VAL_STANDBY (TPO_R03_DRIVING_CAP_100 | \
TPO_R03_EN_PRE_CHARGE | TPO_R03_SOFTWARE_CTL)
static const u16 tpo_td043_def_gamma[12] = {
105, 315, 381, 431, 490, 537, 579, 686, 780, 837, 880, 1023
};
struct tpo_td043_device {
struct spi_device *spi;
struct regulator *vcc_reg;
int nreset_gpio;
u16 gamma[12];
u32 mode;
u32 hmirror:1;
u32 vmirror:1;
u32 powered_on:1;
u32 spi_suspended:1;
u32 power_on_resume:1;
};
static int tpo_td043_write(struct spi_device *spi, u8 addr, u8 data)
{
struct spi_message m;
struct spi_transfer xfer;
u16 w;
int r;
spi_message_init(&m);
memset(&xfer, 0, sizeof(xfer));
w = ((u16)addr << 10) | (1 << 8) | data;
xfer.tx_buf = &w;
xfer.bits_per_word = 16;
xfer.len = 2;
spi_message_add_tail(&xfer, &m);
r = spi_sync(spi, &m);
if (r < 0)
dev_warn(&spi->dev, "failed to write to LCD reg (%d)\n", r);
return r;
}
static void tpo_td043_write_gamma(struct spi_device *spi, u16 gamma[12])
{
u8 i, val;
/* gamma bits [9:8] */
for (val = i = 0; i < 4; i++)
val |= (gamma[i] & 0x300) >> ((i + 1) * 2);
tpo_td043_write(spi, 0x11, val);
for (val = i = 0; i < 4; i++)
val |= (gamma[i+4] & 0x300) >> ((i + 1) * 2);
tpo_td043_write(spi, 0x12, val);
for (val = i = 0; i < 4; i++)
val |= (gamma[i+8] & 0x300) >> ((i + 1) * 2);
tpo_td043_write(spi, 0x13, val);
/* gamma bits [7:0] */
for (val = i = 0; i < 12; i++)
tpo_td043_write(spi, 0x14 + i, gamma[i] & 0xff);
}
static int tpo_td043_write_mirror(struct spi_device *spi, bool h, bool v)
{
u8 reg4 = TPO_R04_NFLIP_H | TPO_R04_NFLIP_V | \
TPO_R04_CP_CLK_FREQ_1H | TPO_R04_VGL_FREQ_1H;
if (h)
reg4 &= ~TPO_R04_NFLIP_H;
if (v)
reg4 &= ~TPO_R04_NFLIP_V;
return tpo_td043_write(spi, 4, reg4);
}
static int tpo_td043_set_hmirror(struct omap_dss_device *dssdev, bool enable)
{
struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev);
tpo_td043->hmirror = enable;
return tpo_td043_write_mirror(tpo_td043->spi, tpo_td043->hmirror,
tpo_td043->vmirror);
}
static bool tpo_td043_get_hmirror(struct omap_dss_device *dssdev)
{
struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev);
return tpo_td043->hmirror;
}
static ssize_t tpo_td043_vmirror_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct tpo_td043_device *tpo_td043 = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%d\n", tpo_td043->vmirror);
}
static ssize_t tpo_td043_vmirror_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct tpo_td043_device *tpo_td043 = dev_get_drvdata(dev);
int val;
int ret;
ret = kstrtoint(buf, 0, &val);
if (ret < 0)
return ret;
val = !!val;
ret = tpo_td043_write_mirror(tpo_td043->spi, tpo_td043->hmirror, val);
if (ret < 0)
return ret;
tpo_td043->vmirror = val;
return count;
}
static ssize_t tpo_td043_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct tpo_td043_device *tpo_td043 = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%d\n", tpo_td043->mode);
}
static ssize_t tpo_td043_mode_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct tpo_td043_device *tpo_td043 = dev_get_drvdata(dev);
long val;
int ret;
ret = kstrtol(buf, 0, &val);
if (ret != 0 || val & ~7)
return -EINVAL;
tpo_td043->mode = val;
val |= TPO_R02_NCLK_RISING;
tpo_td043_write(tpo_td043->spi, 2, val);
return count;
}
static ssize_t tpo_td043_gamma_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct tpo_td043_device *tpo_td043 = dev_get_drvdata(dev);
ssize_t len = 0;
int ret;
int i;
for (i = 0; i < ARRAY_SIZE(tpo_td043->gamma); i++) {
ret = snprintf(buf + len, PAGE_SIZE - len, "%u ",
tpo_td043->gamma[i]);
if (ret < 0)
return ret;
len += ret;
}
buf[len - 1] = '\n';
return len;
}
static ssize_t tpo_td043_gamma_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct tpo_td043_device *tpo_td043 = dev_get_drvdata(dev);
unsigned int g[12];
int ret;
int i;
ret = sscanf(buf, "%u %u %u %u %u %u %u %u %u %u %u %u",
&g[0], &g[1], &g[2], &g[3], &g[4], &g[5],
&g[6], &g[7], &g[8], &g[9], &g[10], &g[11]);
if (ret != 12)
return -EINVAL;
for (i = 0; i < 12; i++)
tpo_td043->gamma[i] = g[i];
tpo_td043_write_gamma(tpo_td043->spi, tpo_td043->gamma);
return count;
}
static DEVICE_ATTR(vmirror, S_IRUGO | S_IWUSR,
tpo_td043_vmirror_show, tpo_td043_vmirror_store);
static DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
tpo_td043_mode_show, tpo_td043_mode_store);
static DEVICE_ATTR(gamma, S_IRUGO | S_IWUSR,
tpo_td043_gamma_show, tpo_td043_gamma_store);
static struct attribute *tpo_td043_attrs[] = {
&dev_attr_vmirror.attr,
&dev_attr_mode.attr,
&dev_attr_gamma.attr,
NULL,
};
static struct attribute_group tpo_td043_attr_group = {
.attrs = tpo_td043_attrs,
};
static const struct omap_video_timings tpo_td043_timings = {
.x_res = 800,
.y_res = 480,
.pixel_clock = 36000,
.hsw = 1,
.hfp = 68,
.hbp = 214,
.vsw = 1,
.vfp = 39,
.vbp = 34,
};
static int tpo_td043_power_on(struct tpo_td043_device *tpo_td043)
{
int nreset_gpio = tpo_td043->nreset_gpio;
if (tpo_td043->powered_on)
return 0;
regulator_enable(tpo_td043->vcc_reg);
/* wait for regulator to stabilize */
msleep(160);
if (gpio_is_valid(nreset_gpio))
gpio_set_value(nreset_gpio, 1);
tpo_td043_write(tpo_td043->spi, 2,
TPO_R02_MODE(tpo_td043->mode) | TPO_R02_NCLK_RISING);
tpo_td043_write(tpo_td043->spi, 3, TPO_R03_VAL_NORMAL);
tpo_td043_write(tpo_td043->spi, 0x20, 0xf0);
tpo_td043_write(tpo_td043->spi, 0x21, 0xf0);
tpo_td043_write_mirror(tpo_td043->spi, tpo_td043->hmirror,
tpo_td043->vmirror);
tpo_td043_write_gamma(tpo_td043->spi, tpo_td043->gamma);
tpo_td043->powered_on = 1;
return 0;
}
static void tpo_td043_power_off(struct tpo_td043_device *tpo_td043)
{
int nreset_gpio = tpo_td043->nreset_gpio;
if (!tpo_td043->powered_on)
return;
tpo_td043_write(tpo_td043->spi, 3,
TPO_R03_VAL_STANDBY | TPO_R03_EN_PWM);
if (gpio_is_valid(nreset_gpio))
gpio_set_value(nreset_gpio, 0);
/* wait for at least 2 vsyncs before cutting off power */
msleep(50);
tpo_td043_write(tpo_td043->spi, 3, TPO_R03_VAL_STANDBY);
regulator_disable(tpo_td043->vcc_reg);
tpo_td043->powered_on = 0;
}
static int tpo_td043_enable_dss(struct omap_dss_device *dssdev)
{
struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev);
int r;
if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
return 0;
r = omapdss_dpi_display_enable(dssdev);
if (r)
goto err0;
if (dssdev->platform_enable) {
r = dssdev->platform_enable(dssdev);
if (r)
goto err1;
}
/*
* If we are resuming from system suspend, SPI clocks might not be
* enabled yet, so we'll program the LCD from SPI PM resume callback.
*/
if (!tpo_td043->spi_suspended) {
r = tpo_td043_power_on(tpo_td043);
if (r)
goto err1;
}
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return 0;
err1:
omapdss_dpi_display_disable(dssdev);
err0:
return r;
}
static void tpo_td043_disable_dss(struct omap_dss_device *dssdev)
{
struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev);
if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
return;
if (dssdev->platform_disable)
dssdev->platform_disable(dssdev);
omapdss_dpi_display_disable(dssdev);
if (!tpo_td043->spi_suspended)
tpo_td043_power_off(tpo_td043);
}
static int tpo_td043_enable(struct omap_dss_device *dssdev)
{
dev_dbg(&dssdev->dev, "enable\n");
return tpo_td043_enable_dss(dssdev);
}
static void tpo_td043_disable(struct omap_dss_device *dssdev)
{
dev_dbg(&dssdev->dev, "disable\n");
tpo_td043_disable_dss(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
}
static int tpo_td043_suspend(struct omap_dss_device *dssdev)
{
dev_dbg(&dssdev->dev, "suspend\n");
tpo_td043_disable_dss(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
return 0;
}
static int tpo_td043_resume(struct omap_dss_device *dssdev)
{
dev_dbg(&dssdev->dev, "resume\n");
return tpo_td043_enable_dss(dssdev);
}
static int tpo_td043_probe(struct omap_dss_device *dssdev)
{
struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev);
int nreset_gpio = dssdev->reset_gpio;
int ret = 0;
dev_dbg(&dssdev->dev, "probe\n");
if (tpo_td043 == NULL) {
dev_err(&dssdev->dev, "missing tpo_td043_device\n");
return -ENODEV;
}
dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IHS |
OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IPC;
dssdev->panel.timings = tpo_td043_timings;
dssdev->ctrl.pixel_size = 24;
tpo_td043->mode = TPO_R02_MODE_800x480;
memcpy(tpo_td043->gamma, tpo_td043_def_gamma, sizeof(tpo_td043->gamma));
tpo_td043->vcc_reg = regulator_get(&dssdev->dev, "vcc");
if (IS_ERR(tpo_td043->vcc_reg)) {
dev_err(&dssdev->dev, "failed to get LCD VCC regulator\n");
ret = PTR_ERR(tpo_td043->vcc_reg);
goto fail_regulator;
}
if (gpio_is_valid(nreset_gpio)) {
ret = gpio_request_one(nreset_gpio, GPIOF_OUT_INIT_LOW,
"lcd reset");
if (ret < 0) {
dev_err(&dssdev->dev, "couldn't request reset GPIO\n");
goto fail_gpio_req;
}
}
ret = sysfs_create_group(&dssdev->dev.kobj, &tpo_td043_attr_group);
if (ret)
dev_warn(&dssdev->dev, "failed to create sysfs files\n");
return 0;
fail_gpio_req:
regulator_put(tpo_td043->vcc_reg);
fail_regulator:
kfree(tpo_td043);
return ret;
}
static void tpo_td043_remove(struct omap_dss_device *dssdev)
{
struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev);
int nreset_gpio = dssdev->reset_gpio;
dev_dbg(&dssdev->dev, "remove\n");
sysfs_remove_group(&dssdev->dev.kobj, &tpo_td043_attr_group);
regulator_put(tpo_td043->vcc_reg);
if (gpio_is_valid(nreset_gpio))
gpio_free(nreset_gpio);
}
static struct omap_dss_driver tpo_td043_driver = {
.probe = tpo_td043_probe,
.remove = tpo_td043_remove,
.enable = tpo_td043_enable,
.disable = tpo_td043_disable,
.suspend = tpo_td043_suspend,
.resume = tpo_td043_resume,
.set_mirror = tpo_td043_set_hmirror,
.get_mirror = tpo_td043_get_hmirror,
.driver = {
.name = "tpo_td043mtea1_panel",
.owner = THIS_MODULE,
},
};
static int tpo_td043_spi_probe(struct spi_device *spi)
{
struct omap_dss_device *dssdev = spi->dev.platform_data;
struct tpo_td043_device *tpo_td043;
int ret;
if (dssdev == NULL) {
dev_err(&spi->dev, "missing dssdev\n");
return -ENODEV;
}
spi->bits_per_word = 16;
spi->mode = SPI_MODE_0;
ret = spi_setup(spi);
if (ret < 0) {
dev_err(&spi->dev, "spi_setup failed: %d\n", ret);
return ret;
}
tpo_td043 = kzalloc(sizeof(*tpo_td043), GFP_KERNEL);
if (tpo_td043 == NULL)
return -ENOMEM;
tpo_td043->spi = spi;
tpo_td043->nreset_gpio = dssdev->reset_gpio;
dev_set_drvdata(&spi->dev, tpo_td043);
dev_set_drvdata(&dssdev->dev, tpo_td043);
omap_dss_register_driver(&tpo_td043_driver);
return 0;
}
static int __devexit tpo_td043_spi_remove(struct spi_device *spi)
{
struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&spi->dev);
omap_dss_unregister_driver(&tpo_td043_driver);
kfree(tpo_td043);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int tpo_td043_spi_suspend(struct device *dev)
{
struct tpo_td043_device *tpo_td043 = dev_get_drvdata(dev);
dev_dbg(dev, "tpo_td043_spi_suspend, tpo %p\n", tpo_td043);
tpo_td043->power_on_resume = tpo_td043->powered_on;
tpo_td043_power_off(tpo_td043);
tpo_td043->spi_suspended = 1;
return 0;
}
static int tpo_td043_spi_resume(struct device *dev)
{
struct tpo_td043_device *tpo_td043 = dev_get_drvdata(dev);
int ret;
dev_dbg(dev, "tpo_td043_spi_resume\n");
if (tpo_td043->power_on_resume) {
ret = tpo_td043_power_on(tpo_td043);
if (ret)
return ret;
}
tpo_td043->spi_suspended = 0;
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(tpo_td043_spi_pm,
tpo_td043_spi_suspend, tpo_td043_spi_resume);
static struct spi_driver tpo_td043_spi_driver = {
.driver = {
.name = "tpo_td043mtea1_panel_spi",
.owner = THIS_MODULE,
.pm = &tpo_td043_spi_pm,
},
.probe = tpo_td043_spi_probe,
.remove = __devexit_p(tpo_td043_spi_remove),
};
module_spi_driver(tpo_td043_spi_driver);
MODULE_AUTHOR("Gražvydas Ignotas <notasas@gmail.com>");
MODULE_DESCRIPTION("TPO TD043MTEA1 LCD Driver");
MODULE_LICENSE("GPL");