summaryrefslogtreecommitdiff
path: root/drivers/video/mvf_dcu.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/mvf_dcu.c')
-rw-r--r--drivers/video/mvf_dcu.c277
1 files changed, 251 insertions, 26 deletions
diff --git a/drivers/video/mvf_dcu.c b/drivers/video/mvf_dcu.c
index 036097b77fcb..9eed2135dd31 100644
--- a/drivers/video/mvf_dcu.c
+++ b/drivers/video/mvf_dcu.c
@@ -38,6 +38,7 @@
#define DRIVER_NAME "mvf-dcu"
static struct fb_videomode __devinitdata mvf_dcu_default_mode = {
+#if !defined(CONFIG_COLIBRI_VF)
.xres = 480,
.yres = 272,
.left_margin = 2,
@@ -46,8 +47,23 @@ static struct fb_videomode __devinitdata mvf_dcu_default_mode = {
.lower_margin = 1,
.hsync_len = 41,
.vsync_len = 2,
- .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
.vmode = FB_VMODE_NONINTERLACED,
+#else /* !CONFIG_COLIBRI_VF */
+ .refresh = 60,
+ .xres = 640,
+ .yres = 480,
+ /* pixel clock period in picoseconds (25.18 MHz) */
+ .pixclock = 38000,
+ .left_margin = 48,
+ .right_margin = 16,
+ .upper_margin = 31,
+ .lower_margin = 11,
+ .hsync_len = 96,
+ .vsync_len = 2,
+ .sync = 0,
+ .vmode = FB_VMODE_NONINTERLACED,
+#endif /* !CONFIG_COLIBRI_VF */
};
static struct fb_videomode __devinitdata mvf_dcu_mode_db[] = {
@@ -61,7 +77,93 @@ static struct fb_videomode __devinitdata mvf_dcu_mode_db[] = {
.lower_margin = 1,
.hsync_len = 41,
.vsync_len = 2,
- .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .vmode = FB_VMODE_NONINTERLACED,
+ },
+ {
+ /* 640x480p 60hz: EIA/CEA-861-B Format 1 */
+ .name = "640x480",
+ .refresh = 60,
+ .xres = 640,
+ .yres = 480,
+ /* pixel clock period in picoseconds (25.18 MHz) */
+ .pixclock = 38000,
+ .left_margin = 48,
+ .right_margin = 16,
+ .upper_margin = 31,
+ .lower_margin = 11,
+ .hsync_len = 96,
+ .vsync_len = 2,
+ .sync = 0,
+ .vmode = FB_VMODE_NONINTERLACED,
+ },
+ {
+ /* 800x480@60 (e.g. EDT ET070080DH6) */
+ .name = "800x480",
+ .refresh = 60,
+ .xres = 800,
+ .yres = 480,
+ /* pixel clock period in picoseconds (33.26 MHz) */
+ .pixclock = 30066,
+ .left_margin = 216,
+ .right_margin = 40,
+ .upper_margin = 35,
+ .lower_margin = 10,
+ .hsync_len = 128,
+ .vsync_len = 2,
+ .sync = 0,
+ .vmode = FB_VMODE_NONINTERLACED,
+ },
+ {
+ /* 800x600@60 */
+ .name = "800x600",
+ .refresh = 60,
+ .xres = 800,
+ .yres = 600,
+ /* pixel clock period in picoseconds (40 MHz) */
+ .pixclock = 25000,
+ .left_margin = 88,
+ .right_margin = 40,
+ .upper_margin = 23,
+ .lower_margin = 1,
+ .hsync_len = 128,
+ .vsync_len = 4,
+ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .vmode = FB_VMODE_NONINTERLACED,
+ },
+ {
+ /* TouchRevolution Fusion 10 aka Chunghwa Picture Tubes
+ CLAA101NC05 10.1 inch 1024x600 single channel LVDS panel */
+ .name = "1024x600",
+ .refresh = 60,
+ .xres = 1024,
+ .yres = 600,
+ /* pixel clock period in picoseconds (48 MHz) */
+ .pixclock = 20833,
+ .left_margin = 104,
+ .right_margin = 43,
+ .upper_margin = 24,
+ .lower_margin = 20,
+ .hsync_len = 5,
+ .vsync_len = 5,
+ .sync = 0,
+ .vmode = FB_VMODE_NONINTERLACED,
+ },
+ {
+ /* 1024x768@60 */
+ .name = "1024x768",
+ .refresh = 60,
+ .xres = 1024,
+ .yres = 768,
+ /* pixel clock period in picoseconds (65 MHz) */
+ .pixclock = 15385,
+ .left_margin = 160,
+ .right_margin = 24,
+ .upper_margin = 29,
+ .lower_margin = 3,
+ .hsync_len = 136,
+ .vsync_len = 6,
+ .sync = 0,
.vmode = FB_VMODE_NONINTERLACED,
},
};
@@ -76,6 +178,8 @@ struct mvf_dcu_fb_data {
unsigned int irq;
struct clk *clk;
int fb_enabled;
+ int clock_pol;
+ int default_bpp;
};
struct mfb_info {
@@ -84,12 +188,11 @@ struct mfb_info {
char *id;
int registered;
int blank;
- char *mode_str;
- int default_bpp;
unsigned long pseudo_palette[16];
struct dcu_layer_desc *layer_desc;
int cursor_reset;
unsigned char g_alpha;
+ unsigned char blend;
unsigned int count;
int x_layer_d; /* layer display x offset to physical screen */
int y_layer_d; /* layer display y offset to physical screen */
@@ -103,6 +206,7 @@ static struct mfb_info mfb_template[] = {
.id = "Layer0",
.registered = 0,
.g_alpha = 0xff,
+ .blend = 0,
.count = 0,
.x_layer_d = 0,
.y_layer_d = 0,
@@ -113,6 +217,7 @@ static struct mfb_info mfb_template[] = {
.id = "Layer1",
.registered = 0,
.g_alpha = 0xff,
+ .blend = 0,
.count = 0,
.x_layer_d = 50,
.y_layer_d = 50,
@@ -123,6 +228,7 @@ static struct mfb_info mfb_template[] = {
.id = "Layer2",
.registered = 0,
.g_alpha = 0xff,
+ .blend = 0,
.count = 0,
.x_layer_d = 100,
.y_layer_d = 100,
@@ -133,6 +239,7 @@ static struct mfb_info mfb_template[] = {
.id = "Layer3",
.registered = 0,
.g_alpha = 0xff,
+ .blend = 0,
.count = 0,
.x_layer_d = 150,
.y_layer_d = 150,
@@ -270,8 +377,6 @@ static void adjust_layer_size_position(struct fb_var_screeninfo *var,
static int mvf_dcu_check_var(struct fb_var_screeninfo *var,
struct fb_info *info)
{
- struct mfb_info *mfbi = info->par;
-
if (var->xres_virtual < var->xres)
var->xres_virtual = var->xres;
if (var->yres_virtual < var->yres)
@@ -291,7 +396,7 @@ static int mvf_dcu_check_var(struct fb_var_screeninfo *var,
if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) &&
(var->bits_per_pixel != 16))
- var->bits_per_pixel = mfbi->default_bpp;
+ var->bits_per_pixel = 16;
switch (var->bits_per_pixel) {
case 16:
@@ -375,11 +480,36 @@ static void set_fix(struct fb_info *info)
fix->ypanstep = 1;
}
+static int calc_div_ratio(struct fb_info *info)
+{
+ struct mfb_info *mfbi = info->par;
+ struct mvf_dcu_fb_data *dcufb = mfbi->parent;
+ unsigned long dcu_clk;
+ unsigned long long tmp;
+
+ /*
+ * Calculation could be done more precisly when we take parent clock
+ * into account too. We can change between 452MHz and 480MHz (see
+ * arch/arm/mach-mvf/clock.c
+ */
+ dcu_clk = clk_get_rate(dcufb->clk);
+ tmp = info->var.pixclock * (unsigned long long)dcu_clk;
+
+ do_div(tmp, 1000000);
+
+ if (do_div(tmp, 1000000) > 500000)
+ tmp++;
+
+ tmp = tmp - 1;
+ return tmp;
+}
+
static void update_lcdc(struct fb_info *info)
{
struct fb_var_screeninfo *var = &info->var;
struct mfb_info *mfbi = info->par;
struct mvf_dcu_fb_data *dcu = mfbi->parent;
+ unsigned int ratio;
if (mfbi->type == DCU_TYPE_OFF) {
mvf_dcu_disable_panel(info);
@@ -417,11 +547,12 @@ static void update_lcdc(struct fb_info *info)
writel(DCU_MODE_BLEND_ITER(3) | DCU_MODE_RASTER_EN(1),
dcu->base + DCU_DCU_MODE);
- writel(9, dcu->base + DCU_DIV_RATIO);
+ ratio = calc_div_ratio(info);
+ writel(ratio, dcu->base + DCU_DIV_RATIO);
+
+ /* Set various clock polarity (DCUx_SYNPOL) */
+ writel(dcu->clock_pol, dcu->base + DCU_SYN_POL);
- writel(DCU_SYN_POL_INV_PXCK(0) | DCU_SYN_POL_NEG(0) |
- DCU_SYN_POL_INV_VS(1) | DCU_SYN_POL_INV_HS(1),
- dcu->base + DCU_SYN_POL);
writel(DCU_THRESHOLD_LS_BF_VS(0x3) | DCU_THRESHOLD_OUT_BUF_HIGH(0x78) |
DCU_THRESHOLD_OUT_BUF_LOW(0), dcu->base + DCU_THRESHOLD);
@@ -519,11 +650,24 @@ static int mvf_dcu_set_par(struct fb_info *info)
layer_desc->posx = mfbi->x_layer_d;
layer_desc->posy = mfbi->y_layer_d;
- layer_desc->blend = 0x01;
+ switch (var->bits_per_pixel) {
+ case 16:
+ layer_desc->bpp = BPP_16_RGB565;
+ break;
+ case 24:
+ layer_desc->bpp = BPP_24;
+ break;
+ case 32:
+ layer_desc->bpp = BPP_32_ARGB8888;
+ break;
+ default:
+ printk(KERN_ERR "Unable to support other bpp now\n");
+ }
+
+ layer_desc->blend = mfbi->blend;
layer_desc->chroma_key_en = 0;
layer_desc->lut_offset = 0;
layer_desc->rle_en = 0;
- layer_desc->bpp = BPP_24;
layer_desc->trans = mfbi->g_alpha;
layer_desc->safety_en = 0;
layer_desc->data_sel_clut = 0;
@@ -624,9 +768,8 @@ static int mvf_dcu_pan_display(struct fb_var_screeninfo *var,
static int mvf_dcu_blank(int blank_mode, struct fb_info *info)
{
- struct mfb_info *mfbi = info->par;
-
#ifdef CONFIG_MVF_DCU_BLANKING_TEST
+ struct mfb_info *mfbi = info->par;
mfbi->blank = blank_mode;
switch (blank_mode) {
@@ -686,6 +829,7 @@ static int mvf_dcu_ioctl(struct fb_info *info, unsigned int cmd,
case MFB_SET_ALPHA:
if (copy_from_user(&global_alpha, buf, sizeof(global_alpha)))
return -EFAULT;
+ mfbi->blend = 1;
mfbi->g_alpha = global_alpha;
mvf_dcu_check_var(&info->var, info);
mvf_dcu_set_par(info);
@@ -815,15 +959,6 @@ static int init_fbinfo(struct fb_info *info)
static int __devinit install_fb(struct fb_info *info)
{
struct mfb_info *mfbi = info->par;
- struct fb_videomode *db = mvf_dcu_mode_db;
- unsigned int dbsize = ARRAY_SIZE(mvf_dcu_mode_db);
- int rc;
-
- if (init_fbinfo(info))
- return -EINVAL;
-
- rc = fb_find_mode(&info->var, info, mfbi->mode_str, db, dbsize,
- &mvf_dcu_default_mode, mfbi->default_bpp);
if (mvf_dcu_check_var(&info->var, info)) {
printk(KERN_ERR "fb_check_var failed");
@@ -942,6 +1077,68 @@ static int mvf_dcu_resume(struct platform_device *pdev)
#define mvf_dcu_resume NULL
#endif
+static int parse_opt(struct mvf_dcu_fb_data *dcu, char *this_opt)
+{
+ if (!strncmp(this_opt, "hsync:", 6)) {
+ /* Inverted logic
+ * hsync:0 => active low => INV_HS(1)
+ * hsync:1 => active high => INV_HS(0)
+ */
+ if (simple_strtoul(this_opt+6, NULL, 0) == 0)
+ dcu->clock_pol |= DCU_SYN_POL_INV_HS(1);
+ else
+ dcu->clock_pol &= ~DCU_SYN_POL_INV_HS(1);
+ return 0;
+ } else if (!strncmp(this_opt, "vsync:", 6)) {
+ /* Inverted logic
+ * vsync:0 => active low => INV_VS(1)
+ * vsync:1 => active high => INV_VS(0)
+ */
+ if (simple_strtoul(this_opt+6, NULL, 0) == 0)
+ dcu->clock_pol |= DCU_SYN_POL_INV_VS(1);
+ else
+ dcu->clock_pol &= ~DCU_SYN_POL_INV_VS(1);
+ return 0;
+ } else if (!strncmp(this_opt, "pixclockpol:", 12)) {
+ /* Inverted logic too, altough, datasheet seems to
+ * be wrong here! (1 => Display samples data on
+ * _falling_ edge)
+ * pixclockpol:0 => falling edge => INV_PXCK(1)
+ * pixclockpol:1 => rising edge => INV_PXCK(0)
+ */
+ if (simple_strtoul(this_opt+12, NULL, 0) == 0)
+ dcu->clock_pol |= DCU_SYN_POL_INV_PXCK(1);
+ else
+ dcu->clock_pol &= ~DCU_SYN_POL_INV_PXCK(1);
+ return 0;
+ }
+
+ return -1;
+}
+
+static int mvf_dcu_parse_options(struct mvf_dcu_fb_data *dcu,
+ struct fb_info *info, char *option)
+{
+ char *this_opt;
+ struct fb_videomode *db = mvf_dcu_mode_db;
+ unsigned int dbsize = ARRAY_SIZE(mvf_dcu_mode_db);
+ int ret = 0;
+
+ while ((this_opt = strsep(&option, ",")) != NULL) {
+ /* Parse driver specific arguments */
+ if (parse_opt(dcu, this_opt) == 0)
+ continue;
+
+ /* No valid driver specific argument, has to be mode */
+ ret = fb_find_mode(&info->var, info, this_opt, db, dbsize,
+ &mvf_dcu_default_mode, dcu->default_bpp);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+
static int __devinit mvf_dcu_probe(struct platform_device *pdev)
{
struct mvf_dcu_platform_data *plat_data = pdev->dev.platform_data;
@@ -950,11 +1147,24 @@ static int __devinit mvf_dcu_probe(struct platform_device *pdev)
struct resource *res;
int ret = 0;
int i;
+ char *option = NULL;
dcu = kmalloc(sizeof(struct mvf_dcu_fb_data), GFP_KERNEL);
if (!dcu)
return -ENOMEM;
+ fb_get_options("dcufb", &option);
+
+ if (option != NULL) {
+ printk(KERN_INFO "dcufb: parse cmd options: %s\n", option);
+ } else {
+ option = plat_data->mode_str;
+ printk(KERN_INFO "dcufb: use default mode: %s\n", option);
+ }
+
+ if (!strcmp(option, "off"))
+ return -ENODEV;
+
for (i = 0; i < ARRAY_SIZE(dcu->mvf_dcu_info); i++) {
dcu->mvf_dcu_info[i] =
framebuffer_alloc(sizeof(struct mfb_info),
@@ -967,8 +1177,21 @@ static int __devinit mvf_dcu_probe(struct platform_device *pdev)
mfbi = dcu->mvf_dcu_info[i]->par;
memcpy(mfbi, &mfb_template[i], sizeof(struct mfb_info));
mfbi->parent = dcu;
- mfbi->mode_str = plat_data->mode_str;
- mfbi->default_bpp = plat_data->default_bpp;
+ if (init_fbinfo(dcu->mvf_dcu_info[i])) {
+ ret = -EINVAL;
+ goto failed_alloc_framebuffer;
+ }
+ }
+
+ dcu->default_bpp = plat_data->default_bpp;
+ dcu->clock_pol = DCU_SYN_POL_INV_HS(1) | DCU_SYN_POL_INV_VS(1) |
+ DCU_SYN_POL_INV_PXCK(1);
+
+ /* Use framebuffer of first layer to store display mode */
+ ret = mvf_dcu_parse_options(dcu, dcu->mvf_dcu_info[0], option);
+ if (ret < 0) {
+ ret = -EINVAL;
+ goto failed_alloc_framebuffer;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1003,9 +1226,11 @@ static int __devinit mvf_dcu_probe(struct platform_device *pdev)
goto failed_get_resource;
}
+#if !defined(CONFIG_COLIBRI_VF)
gpio_request_one(DCU_LCD_ENABLE_PIN, GPIOF_OUT_INIT_LOW, "DCU");
msleep(2);
gpio_set_value(DCU_LCD_ENABLE_PIN, 1);
+#endif
writel(0x20000000, MVF_IO_ADDRESS(MVF_TCON0_BASE_ADDR));