Files
linux-legacy/drivers/video/mxc/mxcfb_ch7026.c
Rob Herring be0524d38f ENGR00117389 Port 5.0.0 release to 2.6.31
This is i.MX BSP 5.0.0 release ported to 2.6.31

Signed-off-by: Rob Herring <r.herring@freescale.com>
Signed-off-by: Alan Tull <r80115@freescale.com>
Signed-off-by: Xinyu Chen <xinyu.chen@freescale.com>
2010-08-10 11:44:41 -05:00

370 lines
7.3 KiB
C

/*
* Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
/*!
* @defgroup Framebuffer Framebuffer Driver for SDC and ADC.
*/
/*!
* @file mxcfb_epson_vga.c
*
* @brief MXC Frame buffer driver for SDC
*
* @ingroup Framebuffer
*/
/*!
* Include files
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/console.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/i2c.h>
#include <linux/mxcfb.h>
#include <linux/ipu.h>
#include <mach/hardware.h>
static struct i2c_client *ch7026_client;
static int lcd_init(void);
static void lcd_poweron(struct fb_info *info);
static void lcd_poweroff(void);
static void (*lcd_reset) (void);
static struct regulator *io_reg;
static struct regulator *core_reg;
static struct regulator *analog_reg;
/* 8 800x600-60 VESA */
static struct fb_videomode mode = {
NULL, 60, 800, 600, 25000, 88, 40, 23, 1, 128, 4,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA
};
static void lcd_init_fb(struct fb_info *info)
{
struct fb_var_screeninfo var;
memset(&var, 0, sizeof(var));
fb_videomode_to_var(&var, &mode);
var.activate = FB_ACTIVATE_ALL;
acquire_console_sem();
info->flags |= FBINFO_MISC_USEREVENT;
fb_set_var(info, &var);
fb_blank(info, FB_BLANK_UNBLANK);
info->flags &= ~FBINFO_MISC_USEREVENT;
release_console_sem();
}
static int lcd_fb_event(struct notifier_block *nb, unsigned long val, void *v)
{
struct fb_event *event = v;
if (strcmp(event->info->fix.id, "DISP3 BG - DI1"))
return 0;
switch (val) {
case FB_EVENT_FB_REGISTERED:
lcd_init_fb(event->info);
lcd_poweron(event->info);
break;
case FB_EVENT_BLANK:
if (*((int *)event->data) == FB_BLANK_UNBLANK)
lcd_poweron(event->info);
else
lcd_poweroff();
break;
}
return 0;
}
static struct notifier_block nb = {
.notifier_call = lcd_fb_event,
};
/*!
* This function is called whenever the SPI slave device is detected.
*
* @param spi the SPI slave device
*
* @return Returns 0 on SUCCESS and error on FAILURE.
*/
static int __devinit lcd_probe(struct device *dev)
{
int ret = 0;
int i;
struct mxc_lcd_platform_data *plat = dev->platform_data;
if (plat) {
io_reg = regulator_get(dev, plat->io_reg);
if (!IS_ERR(io_reg)) {
regulator_set_voltage(io_reg, 1800000, 1800000);
regulator_enable(io_reg);
} else {
io_reg = NULL;
}
core_reg = regulator_get(dev, plat->core_reg);
if (!IS_ERR(core_reg)) {
regulator_set_voltage(core_reg, 2500000, 2500000);
regulator_enable(core_reg);
} else {
core_reg = NULL;
}
analog_reg = regulator_get(dev, plat->analog_reg);
if (!IS_ERR(analog_reg)) {
regulator_set_voltage(analog_reg, 2775000, 2775000);
regulator_enable(analog_reg);
} else {
analog_reg = NULL;
}
msleep(100);
lcd_reset = plat->reset;
if (lcd_reset)
lcd_reset();
}
for (i = 0; i < num_registered_fb; i++) {
if (strcmp(registered_fb[i]->fix.id, "DISP3 BG - DI1") == 0) {
ret = lcd_init();
if (ret < 0)
goto err;
lcd_init_fb(registered_fb[i]);
fb_show_logo(registered_fb[i], 0);
lcd_poweron(registered_fb[i]);
}
}
fb_register_client(&nb);
return 0;
err:
if (io_reg)
regulator_disable(io_reg);
if (core_reg)
regulator_disable(core_reg);
if (analog_reg)
regulator_disable(analog_reg);
return ret;
}
static int __devinit ch7026_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
ch7026_client = client;
return lcd_probe(&client->dev);
}
static int __devexit ch7026_remove(struct i2c_client *client)
{
fb_unregister_client(&nb);
lcd_poweroff();
regulator_put(io_reg);
regulator_put(core_reg);
regulator_put(analog_reg);
return 0;
}
static int ch7026_suspend(struct i2c_client *client, pm_message_t message)
{
return 0;
}
static int ch7026_resume(struct i2c_client *client)
{
return 0;
}
u8 reg_init[][2] = {
{ 0x02, 0x01 },
{ 0x02, 0x03 },
{ 0x03, 0x00 },
{ 0x06, 0x6B },
{ 0x08, 0x08 },
{ 0x09, 0x80 },
{ 0x0C, 0x0A },
{ 0x0D, 0x89 },
{ 0x0F, 0x23 },
{ 0x10, 0x20 },
{ 0x11, 0x20 },
{ 0x12, 0x40 },
{ 0x13, 0x28 },
{ 0x14, 0x80 },
{ 0x15, 0x52 },
{ 0x16, 0x58 },
{ 0x17, 0x74 },
{ 0x19, 0x01 },
{ 0x1A, 0x04 },
{ 0x1B, 0x23 },
{ 0x1C, 0x20 },
{ 0x1D, 0x20 },
{ 0x1F, 0x28 },
{ 0x20, 0x80 },
{ 0x21, 0x12 },
{ 0x22, 0x58 },
{ 0x23, 0x74 },
{ 0x25, 0x01 },
{ 0x26, 0x04 },
{ 0x37, 0x20 },
{ 0x39, 0x20 },
{ 0x3B, 0x20 },
{ 0x41, 0xA2 },
{ 0x4D, 0x03 },
{ 0x4E, 0x13 },
{ 0x4F, 0xB1 },
{ 0x50, 0x3B },
{ 0x51, 0x54 },
{ 0x52, 0x12 },
{ 0x53, 0x13 },
{ 0x55, 0xE5 },
{ 0x5E, 0x80 },
{ 0x69, 0x64 },
{ 0x7D, 0x62 },
{ 0x04, 0x00 },
{ 0x06, 0x69 },
/*
NOTE: The following five repeated sentences are used here to wait memory initial complete, please don't remove...(you could refer to Appendix A of programming guide document (CH7025(26)B Programming Guide Rev2.03.pdf) for detailed information about memory initialization!
*/
{ 0x03, 0x00 },
{ 0x03, 0x00 },
{ 0x03, 0x00 },
{ 0x03, 0x00 },
{ 0x03, 0x00 },
{ 0x06, 0x68 },
{ 0x02, 0x02 },
{ 0x02, 0x03 },
};
#define REGMAP_LENGTH (sizeof(reg_init) / (2*sizeof(u8)))
/*
* Send init commands to L4F00242T03
*
*/
static int lcd_init(void)
{
int i;
int dat;
dev_dbg(&ch7026_client->dev, "initializing CH7026\n");
/* read device ID */
msleep(100);
dat = i2c_smbus_read_byte_data(ch7026_client, 0x00);
dev_dbg(&ch7026_client->dev, "read id = 0x%02X\n", dat);
if (dat != 0x54)
return -ENODEV;
for (i = 0; i < REGMAP_LENGTH; ++i) {
if (i2c_smbus_write_byte_data
(ch7026_client, reg_init[i][0], reg_init[i][1]) < 0)
return -EIO;
}
return 0;
}
static int lcd_on;
/*
* Send Power On commands to L4F00242T03
*
*/
static void lcd_poweron(struct fb_info *info)
{
u16 data[4];
u32 refresh;
if (lcd_on)
return;
dev_dbg(&ch7026_client->dev, "turning on LCD\n");
data[0] = PICOS2KHZ(info->var.pixclock) / 10;
data[2] = info->var.hsync_len + info->var.left_margin +
info->var.xres + info->var.right_margin;
data[3] = info->var.vsync_len + info->var.upper_margin +
info->var.yres + info->var.lower_margin;
refresh = data[2] * data[3];
refresh = (PICOS2KHZ(info->var.pixclock) * 1000) / refresh;
data[1] = refresh * 100;
lcd_on = 1;
}
/*
* Send Power Off commands to L4F00242T03
*
*/
static void lcd_poweroff(void)
{
if (!lcd_on)
return;
dev_dbg(&ch7026_client->dev, "turning off LCD\n");
lcd_on = 0;
}
static const struct i2c_device_id ch7026_id[] = {
{"ch7026", 0},
{},
};
MODULE_DEVICE_TABLE(i2c, ch7026_id);
static struct i2c_driver ch7026_driver = {
.driver = {
.name = "ch7026",
},
.probe = ch7026_probe,
.remove = ch7026_remove,
.suspend = ch7026_suspend,
.resume = ch7026_resume,
.id_table = ch7026_id,
};
static int __init ch7026_init(void)
{
return i2c_add_driver(&ch7026_driver);
}
static void __exit ch7026_exit(void)
{
i2c_del_driver(&ch7026_driver);
}
module_init(ch7026_init);
module_exit(ch7026_exit);
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("CH7026 VGA driver");
MODULE_LICENSE("GPL");