mirror of
https://github.com/genesi/linux-legacy.git
synced 2026-09-23 07:05:27 +00:00
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>
370 lines
7.3 KiB
C
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");
|