diff options
Diffstat (limited to 'drivers/video/mvf_dcu.c')
-rw-r--r-- | drivers/video/mvf_dcu.c | 277 |
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)); |