mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-09 04:10:18 +09:00
add vga support on rockchip platform
This commit is contained in:
@@ -45,13 +45,20 @@ config RK32_DP
|
||||
bool "RK32 RGB to DisplayPort transmitter support "
|
||||
depends on RK_TRSM
|
||||
|
||||
config RK_VGA
|
||||
bool "VGA support on RockChip platform"
|
||||
depends on RK_TRSM
|
||||
default y
|
||||
help
|
||||
say y here will enable VGA on RockChip platform
|
||||
|
||||
config MIPI_DSI
|
||||
depends on RK_TRSM
|
||||
bool "Rockchip MIPI DSI support"
|
||||
|
||||
config TC358768_RGB2MIPI
|
||||
tristate "toshiba TC358768 RGB to MIPI DSI"
|
||||
depends on MIPI_DSI
|
||||
depends on MIPI_DSI
|
||||
help
|
||||
"a chip that change RGB interface parallel signal into DSI serial signal"
|
||||
|
||||
@@ -73,4 +80,5 @@ config RK616_MIPI_DSI_RST
|
||||
depends on MFD_RK616 && RK616_MIPI_DSI && RK616_USE_MCLK_12M
|
||||
default y
|
||||
help
|
||||
if you say y here: inset the hdmi, mipi lcd will be reset.
|
||||
if you say y here: inset the hdmi, mipi lcd will be reset.
|
||||
|
||||
|
||||
@@ -12,7 +12,8 @@ obj-$(CONFIG_DP501) += dp501.o
|
||||
obj-$(CONFIG_RK32_DP) += rk32_dp.o rk32_dp_reg.o
|
||||
obj-$(CONFIG_MIPI_DSI) += mipi_dsi.o
|
||||
obj-$(CONFIG_RK616_MIPI_DSI) += rk616_mipi_dsi.o
|
||||
obj-$(CONFIG_RK32_MIPI_DSI) += rk32_mipi_dsi.o
|
||||
obj-$(CONFIG_RK32_MIPI_DSI) += rk32_mipi_dsi.o
|
||||
obj-$(CONFIG_TC358768_RGB2MIPI) += tc358768.o
|
||||
obj-$(CONFIG_SSD2828_RGB2MIPI) += ssd2828.o
|
||||
obj-$(CONFIG_RK_VGA) += vga.o
|
||||
|
||||
|
||||
342
drivers/video/rockchip/transmitter/vga.c
Normal file
342
drivers/video/rockchip/transmitter/vga.c
Normal file
@@ -0,0 +1,342 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/display-sys.h>
|
||||
#include <linux/rk_screen.h>
|
||||
#include <linux/rk_fb.h>
|
||||
#ifdef CONFIG_OF
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#endif
|
||||
|
||||
#include "../../edid.h"
|
||||
|
||||
#ifdef CONFIG_SWITCH
|
||||
#include <linux/switch.h>
|
||||
#endif
|
||||
|
||||
|
||||
#define DDC_ADDR 0x50
|
||||
#define DDC_I2C_RATE 100*1000
|
||||
#define INVALID_GPIO -1
|
||||
#define GPIO_HIGH 1
|
||||
#define GPIO_LOW 0
|
||||
#define DISPLAY_SOURCE_LCDC0 0
|
||||
#define DISPLAY_SOURCE_LCDC1 1
|
||||
|
||||
//static char *vgaenvent[] = {"INTERFACE=VGA", NULL};
|
||||
|
||||
static const struct fb_videomode rk29_mode[] = {
|
||||
//name refresh xres yres pixclock h_bp h_fp v_bp v_fp h_pw v_pw polariry PorI flag(used for vic)
|
||||
{ "1024x768p@60Hz", 60, 1024, 768, KHZ2PICOS(65000), 160, 24, 29, 3, 136, 6, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0 },
|
||||
{ "1280x720p@60Hz", 60, 1280, 720, KHZ2PICOS(74250), 220, 110, 20, 5, 40, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0 },
|
||||
{ "1280x1024p@60Hz", 60, 1280, 1024, KHZ2PICOS(108000), 248, 48, 38, 1, 112, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0 },
|
||||
{ "1366x768p@60Hz", 60, 1366, 768, KHZ2PICOS(85500), 213, 70, 24, 3, 143, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0 },
|
||||
{ "1440x900p@60Hz", 60, 1440, 900, KHZ2PICOS(116500), 232, 80, 25, 3, 152, 6, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0 },
|
||||
{ "1680x1050p@60Hz", 60, 1680, 1050, KHZ2PICOS(146250), 280, 104, 30, 3, 176, 6, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0 },
|
||||
{ "1920x1080p@60Hz", 60, 1920, 1080, KHZ2PICOS(148500), 148, 88, 36, 4, 44, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0 },
|
||||
};
|
||||
|
||||
struct rockchip_vga {
|
||||
struct device *dev; /*i2c device*/
|
||||
struct rk_display_device *ddev; /*display device*/
|
||||
struct i2c_client *client;
|
||||
struct list_head modelist;
|
||||
struct fb_monspecs specs;
|
||||
struct rk_screen screen;
|
||||
int indx;
|
||||
int en_pin;
|
||||
int en_val;
|
||||
int lcdc_id;
|
||||
#ifdef CONFIG_SWITCH
|
||||
struct switch_dev switch_vga;
|
||||
#endif
|
||||
};
|
||||
|
||||
static int i2c_master_reg8_recv(const struct i2c_client *client,
|
||||
const char reg, char *buf, int count, int scl_rate)
|
||||
{
|
||||
struct i2c_adapter *adap=client->adapter;
|
||||
struct i2c_msg msgs[2];
|
||||
int ret;
|
||||
char reg_buf = reg;
|
||||
|
||||
msgs[0].addr = client->addr;
|
||||
msgs[0].flags = client->flags;
|
||||
msgs[0].len = 1;
|
||||
msgs[0].buf = ®_buf;
|
||||
msgs[0].scl_rate = scl_rate;
|
||||
|
||||
msgs[1].addr = client->addr;
|
||||
msgs[1].flags = client->flags | I2C_M_RD;
|
||||
msgs[1].len = count;
|
||||
msgs[1].buf = (char *)buf;
|
||||
msgs[1].scl_rate = scl_rate;
|
||||
|
||||
ret = i2c_transfer(adap, msgs, 2);
|
||||
|
||||
return (ret == 2)? count : ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static unsigned char *rk29fb_ddc_read(struct i2c_client *client)
|
||||
{
|
||||
int rc;
|
||||
unsigned char *buf = kzalloc(EDID_LENGTH, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
dev_err(&client->dev, "unable to allocate memory for EDID\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*Check ddc i2c communication is available or not*/
|
||||
rc = i2c_master_reg8_recv(client, 0, buf, 6, DDC_I2C_RATE);
|
||||
if (rc == 6) {
|
||||
memset(buf, 0, EDID_LENGTH);
|
||||
rc = i2c_master_reg8_recv(client, 0, buf, EDID_LENGTH, DDC_I2C_RATE);
|
||||
if(rc == EDID_LENGTH)
|
||||
return buf;
|
||||
}
|
||||
|
||||
dev_err(&client->dev, "unable to read EDID block.\n");
|
||||
kfree(buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int vga_mode2screen(struct fb_videomode *modedb, struct rk_screen *screen)
|
||||
{
|
||||
if(modedb == NULL || screen == NULL)
|
||||
return -1;
|
||||
|
||||
memset(screen, 0, sizeof(struct rk_screen));
|
||||
memcpy(&screen->mode, modedb, sizeof(*modedb));
|
||||
screen->mode.pixclock = PICOS2KHZ(screen->mode.pixclock);
|
||||
screen->mode.pixclock /= 250;
|
||||
screen->mode.pixclock *= 250;
|
||||
screen->mode.pixclock *= 1000;
|
||||
screen->xsize = screen->mode.xres;
|
||||
screen->ysize = screen->mode.yres;
|
||||
screen->overscan.left = 100;
|
||||
screen->overscan.top = 100;
|
||||
screen->overscan.right = 100;
|
||||
screen->overscan.bottom = 100;
|
||||
/* screen type & face */
|
||||
screen->type = SCREEN_RGB;
|
||||
screen->face = OUT_P888;
|
||||
|
||||
screen->pin_vsync = (screen->mode.sync & FB_SYNC_VERT_HIGH_ACT) ? 1:0;
|
||||
screen->pin_hsync = (screen->mode.sync & FB_SYNC_HOR_HIGH_ACT) ? 1:0;
|
||||
screen->pin_den = 0;
|
||||
screen->pin_dclk = 0;
|
||||
|
||||
/* Swap rule */
|
||||
screen->swap_rb = 0;
|
||||
screen->swap_rg = 0;
|
||||
screen->swap_gb = 0;
|
||||
screen->swap_delta = 0;
|
||||
screen->swap_dumy = 0;
|
||||
/* Operation function*/
|
||||
screen->init = NULL;
|
||||
screen->standby = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int vga_switch_screen(struct rockchip_vga *vga, int indx)
|
||||
{
|
||||
struct fb_videomode *mode = &vga->specs.modedb[indx];
|
||||
struct rk_screen *screen = &vga->screen;
|
||||
vga_mode2screen(mode, screen);
|
||||
rk_fb_switch_screen(screen, 1 ,vga->lcdc_id);
|
||||
vga->indx = indx;
|
||||
return 0;
|
||||
|
||||
|
||||
}
|
||||
static int vga_get_screen_info(struct rockchip_vga *vga)
|
||||
{
|
||||
u8 *edid;
|
||||
int i;
|
||||
struct fb_monspecs *specs = &vga->specs;
|
||||
struct list_head *modelist = &vga->modelist;
|
||||
edid = rk29fb_ddc_read(vga->client);
|
||||
if (!edid) {
|
||||
dev_info(vga->dev, "get edid failed!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
fb_edid_to_monspecs(edid,specs);
|
||||
INIT_LIST_HEAD(modelist);
|
||||
for (i = 0; i < specs->modedb_len; i++) {
|
||||
fb_add_videomode(&specs->modedb[i], modelist);
|
||||
dev_dbg(vga->dev, "%4dx%4d@%d---dclk:%ld\n",
|
||||
specs->modedb[i].xres, specs->modedb[i].yres,
|
||||
specs->modedb[i].refresh,
|
||||
(PICOS2KHZ(specs->modedb[i].pixclock)/250)*250*1000);
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int vga_get_modelist(struct rk_display_device *device,
|
||||
struct list_head **modelist)
|
||||
{
|
||||
struct rockchip_vga *vga = device->priv_data;
|
||||
*modelist = &vga->modelist;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vga_set_mode(struct rk_display_device *device,
|
||||
struct fb_videomode *mode)
|
||||
{
|
||||
struct rockchip_vga *vga = device->priv_data;
|
||||
struct rk_screen *screen = &vga->screen;
|
||||
vga_mode2screen(mode, screen);
|
||||
rk_fb_switch_screen(screen, 1 ,vga->lcdc_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vga_get_mode(struct rk_display_device *device,
|
||||
struct fb_videomode *mode)
|
||||
{
|
||||
//struct vga *vga = device->priv_data;
|
||||
//struct fb_videomode *vmode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct rk_display_ops vga_display_ops = {
|
||||
.getmodelist = vga_get_modelist,
|
||||
.setmode = vga_set_mode,
|
||||
.getmode = vga_get_mode,
|
||||
};
|
||||
|
||||
static int vga_display_probe(struct rk_display_device *device, void *devdata)
|
||||
{
|
||||
device->owner = THIS_MODULE;
|
||||
strcpy(device->type, "VGA");
|
||||
device->priority = DISPLAY_PRIORITY_VGA;
|
||||
device->priv_data = devdata;
|
||||
device->ops = &vga_display_ops;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct rk_display_driver display_vga = {
|
||||
.probe = vga_display_probe,
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct rk_display_device * vga_register_display_sysfs(struct rockchip_vga *vga)
|
||||
{
|
||||
return rk_display_device_register(&display_vga, vga->dev, vga);
|
||||
}
|
||||
|
||||
void vga_unregister_display_sysfs(struct rockchip_vga *vga)
|
||||
{
|
||||
if (vga->ddev)
|
||||
rk_display_device_unregister(vga->ddev);
|
||||
}
|
||||
|
||||
|
||||
static int vga_i2c_probe(struct i2c_client *client,const struct i2c_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct rockchip_vga *vga;
|
||||
struct device_node *np = client->dev.of_node;
|
||||
enum of_gpio_flags pwr_flags;
|
||||
|
||||
if (!np) {
|
||||
dev_err(&client->dev, "no device node found!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
vga = devm_kzalloc(&client->dev, sizeof(*vga), GFP_KERNEL);
|
||||
if (!vga) {
|
||||
dev_err(&client->dev, "allocate for vga failed!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
vga->client = client;
|
||||
vga->dev = &client->dev;
|
||||
i2c_set_clientdata(client, vga);
|
||||
vga->ddev = vga_register_display_sysfs(vga);
|
||||
if (IS_ERR(vga->ddev))
|
||||
dev_warn(vga->dev, "Unable to create device for vga :%ld",
|
||||
PTR_ERR(vga->ddev));
|
||||
|
||||
vga->en_pin = of_get_named_gpio_flags(np, "pwr_gpio", 0, &pwr_flags);
|
||||
if (!gpio_is_valid(vga->en_pin)) {
|
||||
dev_err(vga->dev, "failed to get pwr_gpio!\n");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
vga->en_val = (pwr_flags & OF_GPIO_ACTIVE_LOW) ? 0 : 1;
|
||||
vga->lcdc_id = DISPLAY_SOURCE_LCDC1;
|
||||
|
||||
ret = devm_gpio_request(vga->dev, vga->en_pin, "pwr_pin");
|
||||
if(ret < 0) {
|
||||
dev_err(vga->dev, "request for pwr_pin failed!\n ");
|
||||
goto err;
|
||||
}
|
||||
gpio_direction_output(vga->en_pin, vga->en_val);
|
||||
ret = vga_get_screen_info(vga);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
vga_switch_screen(vga, 7);
|
||||
|
||||
printk("VGA probe successful\n");
|
||||
return 0;
|
||||
err:
|
||||
vga_unregister_display_sysfs(vga);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static int vga_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
static struct of_device_id vga_dt_ids[] = {
|
||||
{.compatible = "rockchip,vga" },
|
||||
{ }
|
||||
};
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id vga_id[] = {
|
||||
{ "vga_i2c", 0 },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct i2c_driver vga_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "vga_i2c",
|
||||
.owner = THIS_MODULE,
|
||||
#if defined(CONFIG_OF)
|
||||
.of_match_table = of_match_ptr(vga_dt_ids),
|
||||
#endif
|
||||
},
|
||||
.probe = &vga_i2c_probe,
|
||||
.remove = &vga_i2c_remove,
|
||||
.id_table = vga_id,
|
||||
};
|
||||
|
||||
static int __init rockchip_vga_init(void)
|
||||
{
|
||||
return i2c_add_driver(&vga_i2c_driver);
|
||||
}
|
||||
|
||||
static void __exit rockchip_vga_exit(void)
|
||||
{
|
||||
i2c_del_driver(&vga_i2c_driver);
|
||||
}
|
||||
|
||||
module_init(rockchip_vga_init);
|
||||
module_exit(rockchip_vga_exit);
|
||||
Reference in New Issue
Block a user