diff options
| author | Chris Metcalf <cmetcalf@tilera.com> | 2010-08-13 19:59:15 -0400 |
|---|---|---|
| committer | Chris Metcalf <cmetcalf@tilera.com> | 2010-08-13 19:59:15 -0400 |
| commit | 7d72e6fa56c4100b9669efe0044f77ed9eb785a1 (patch) | |
| tree | 5e90bf4969809a1ab20b97432b85be20ccfaa1f4 /drivers/video | |
| parent | ba00376b0b13f234d839541a7b36a5bf5c2a4036 (diff) | |
| parent | 2be1f3a73dd02e38e181cf5abacb3d45a6a2d6b8 (diff) | |
Merge branch 'master' into for-linus
Diffstat (limited to 'drivers/video')
33 files changed, 2510 insertions, 177 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index a9ca72f301bf..8b31fdfefc98 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig | |||
| @@ -1896,6 +1896,13 @@ config FB_W100 | |||
| 1896 | 1896 | ||
| 1897 | If unsure, say N. | 1897 | If unsure, say N. |
| 1898 | 1898 | ||
| 1899 | config SH_MIPI_DSI | ||
| 1900 | tristate | ||
| 1901 | depends on (SUPERH || ARCH_SHMOBILE) && HAVE_CLK | ||
| 1902 | |||
| 1903 | config SH_LCD_MIPI_DSI | ||
| 1904 | bool | ||
| 1905 | |||
| 1899 | config FB_SH_MOBILE_LCDC | 1906 | config FB_SH_MOBILE_LCDC |
| 1900 | tristate "SuperH Mobile LCDC framebuffer support" | 1907 | tristate "SuperH Mobile LCDC framebuffer support" |
| 1901 | depends on FB && (SUPERH || ARCH_SHMOBILE) && HAVE_CLK | 1908 | depends on FB && (SUPERH || ARCH_SHMOBILE) && HAVE_CLK |
| @@ -1904,9 +1911,17 @@ config FB_SH_MOBILE_LCDC | |||
| 1904 | select FB_SYS_IMAGEBLIT | 1911 | select FB_SYS_IMAGEBLIT |
| 1905 | select FB_SYS_FOPS | 1912 | select FB_SYS_FOPS |
| 1906 | select FB_DEFERRED_IO | 1913 | select FB_DEFERRED_IO |
| 1914 | select SH_MIPI_DSI if SH_LCD_MIPI_DSI | ||
| 1907 | ---help--- | 1915 | ---help--- |
| 1908 | Frame buffer driver for the on-chip SH-Mobile LCD controller. | 1916 | Frame buffer driver for the on-chip SH-Mobile LCD controller. |
| 1909 | 1917 | ||
| 1918 | config FB_SH_MOBILE_HDMI | ||
| 1919 | tristate "SuperH Mobile HDMI controller support" | ||
| 1920 | depends on FB_SH_MOBILE_LCDC | ||
| 1921 | select FB_MODE_HELPERS | ||
| 1922 | ---help--- | ||
| 1923 | Driver for the on-chip SH-Mobile HDMI controller. | ||
| 1924 | |||
| 1910 | config FB_TMIO | 1925 | config FB_TMIO |
| 1911 | tristate "Toshiba Mobile IO FrameBuffer support" | 1926 | tristate "Toshiba Mobile IO FrameBuffer support" |
| 1912 | depends on FB && MFD_CORE | 1927 | depends on FB && MFD_CORE |
| @@ -1931,7 +1946,7 @@ config FB_TMIO_ACCELL | |||
| 1931 | 1946 | ||
| 1932 | config FB_S3C | 1947 | config FB_S3C |
| 1933 | tristate "Samsung S3C framebuffer support" | 1948 | tristate "Samsung S3C framebuffer support" |
| 1934 | depends on FB && ARCH_S3C64XX | 1949 | depends on FB && S3C_DEV_FB |
| 1935 | select FB_CFB_FILLRECT | 1950 | select FB_CFB_FILLRECT |
| 1936 | select FB_CFB_COPYAREA | 1951 | select FB_CFB_COPYAREA |
| 1937 | select FB_CFB_IMAGEBLIT | 1952 | select FB_CFB_IMAGEBLIT |
diff --git a/drivers/video/Makefile b/drivers/video/Makefile index f56a9cae2157..485e8ed1318c 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile | |||
| @@ -123,6 +123,8 @@ obj-$(CONFIG_FB_IBM_GXT4500) += gxt4500.o | |||
| 123 | obj-$(CONFIG_FB_PS3) += ps3fb.o | 123 | obj-$(CONFIG_FB_PS3) += ps3fb.o |
| 124 | obj-$(CONFIG_FB_SM501) += sm501fb.o | 124 | obj-$(CONFIG_FB_SM501) += sm501fb.o |
| 125 | obj-$(CONFIG_FB_XILINX) += xilinxfb.o | 125 | obj-$(CONFIG_FB_XILINX) += xilinxfb.o |
| 126 | obj-$(CONFIG_SH_MIPI_DSI) += sh_mipi_dsi.o | ||
| 127 | obj-$(CONFIG_FB_SH_MOBILE_HDMI) += sh_mobile_hdmi.o | ||
| 126 | obj-$(CONFIG_FB_SH_MOBILE_LCDC) += sh_mobile_lcdcfb.o | 128 | obj-$(CONFIG_FB_SH_MOBILE_LCDC) += sh_mobile_lcdcfb.o |
| 127 | obj-$(CONFIG_FB_OMAP) += omap/ | 129 | obj-$(CONFIG_FB_OMAP) += omap/ |
| 128 | obj-y += omap2/ | 130 | obj-y += omap2/ |
diff --git a/drivers/video/bw2.c b/drivers/video/bw2.c index c7796637bafd..4dc13467281d 100644 --- a/drivers/video/bw2.c +++ b/drivers/video/bw2.c | |||
| @@ -273,7 +273,7 @@ static int __devinit bw2_do_default_mode(struct bw2_par *par, | |||
| 273 | return 0; | 273 | return 0; |
| 274 | } | 274 | } |
| 275 | 275 | ||
| 276 | static int __devinit bw2_probe(struct of_device *op, const struct of_device_id *match) | 276 | static int __devinit bw2_probe(struct platform_device *op, const struct of_device_id *match) |
| 277 | { | 277 | { |
| 278 | struct device_node *dp = op->dev.of_node; | 278 | struct device_node *dp = op->dev.of_node; |
| 279 | struct fb_info *info; | 279 | struct fb_info *info; |
| @@ -350,7 +350,7 @@ out_err: | |||
| 350 | return err; | 350 | return err; |
| 351 | } | 351 | } |
| 352 | 352 | ||
| 353 | static int __devexit bw2_remove(struct of_device *op) | 353 | static int __devexit bw2_remove(struct platform_device *op) |
| 354 | { | 354 | { |
| 355 | struct fb_info *info = dev_get_drvdata(&op->dev); | 355 | struct fb_info *info = dev_get_drvdata(&op->dev); |
| 356 | struct bw2_par *par = info->par; | 356 | struct bw2_par *par = info->par; |
diff --git a/drivers/video/cg14.c b/drivers/video/cg14.c index d09fde8beb69..24249535ac86 100644 --- a/drivers/video/cg14.c +++ b/drivers/video/cg14.c | |||
| @@ -446,7 +446,7 @@ static struct sbus_mmap_map __cg14_mmap_map[CG14_MMAP_ENTRIES] __devinitdata = { | |||
| 446 | { .size = 0 } | 446 | { .size = 0 } |
| 447 | }; | 447 | }; |
| 448 | 448 | ||
| 449 | static void cg14_unmap_regs(struct of_device *op, struct fb_info *info, | 449 | static void cg14_unmap_regs(struct platform_device *op, struct fb_info *info, |
| 450 | struct cg14_par *par) | 450 | struct cg14_par *par) |
| 451 | { | 451 | { |
| 452 | if (par->regs) | 452 | if (par->regs) |
| @@ -463,7 +463,7 @@ static void cg14_unmap_regs(struct of_device *op, struct fb_info *info, | |||
| 463 | info->screen_base, info->fix.smem_len); | 463 | info->screen_base, info->fix.smem_len); |
| 464 | } | 464 | } |
| 465 | 465 | ||
| 466 | static int __devinit cg14_probe(struct of_device *op, const struct of_device_id *match) | 466 | static int __devinit cg14_probe(struct platform_device *op, const struct of_device_id *match) |
| 467 | { | 467 | { |
| 468 | struct device_node *dp = op->dev.of_node; | 468 | struct device_node *dp = op->dev.of_node; |
| 469 | struct fb_info *info; | 469 | struct fb_info *info; |
| @@ -570,7 +570,7 @@ out_err: | |||
| 570 | return err; | 570 | return err; |
| 571 | } | 571 | } |
| 572 | 572 | ||
| 573 | static int __devexit cg14_remove(struct of_device *op) | 573 | static int __devexit cg14_remove(struct platform_device *op) |
| 574 | { | 574 | { |
| 575 | struct fb_info *info = dev_get_drvdata(&op->dev); | 575 | struct fb_info *info = dev_get_drvdata(&op->dev); |
| 576 | struct cg14_par *par = info->par; | 576 | struct cg14_par *par = info->par; |
diff --git a/drivers/video/cg3.c b/drivers/video/cg3.c index 64aa29809fb9..09c0c3c42482 100644 --- a/drivers/video/cg3.c +++ b/drivers/video/cg3.c | |||
| @@ -346,7 +346,7 @@ static int __devinit cg3_do_default_mode(struct cg3_par *par) | |||
| 346 | return 0; | 346 | return 0; |
| 347 | } | 347 | } |
| 348 | 348 | ||
| 349 | static int __devinit cg3_probe(struct of_device *op, | 349 | static int __devinit cg3_probe(struct platform_device *op, |
| 350 | const struct of_device_id *match) | 350 | const struct of_device_id *match) |
| 351 | { | 351 | { |
| 352 | struct device_node *dp = op->dev.of_node; | 352 | struct device_node *dp = op->dev.of_node; |
| @@ -433,7 +433,7 @@ out_err: | |||
| 433 | return err; | 433 | return err; |
| 434 | } | 434 | } |
| 435 | 435 | ||
| 436 | static int __devexit cg3_remove(struct of_device *op) | 436 | static int __devexit cg3_remove(struct platform_device *op) |
| 437 | { | 437 | { |
| 438 | struct fb_info *info = dev_get_drvdata(&op->dev); | 438 | struct fb_info *info = dev_get_drvdata(&op->dev); |
| 439 | struct cg3_par *par = info->par; | 439 | struct cg3_par *par = info->par; |
diff --git a/drivers/video/cg6.c b/drivers/video/cg6.c index 2389a719dcc7..2b5a97058b08 100644 --- a/drivers/video/cg6.c +++ b/drivers/video/cg6.c | |||
| @@ -718,7 +718,7 @@ static void __devinit cg6_chip_init(struct fb_info *info) | |||
| 718 | sbus_writel(info->var.yres - 1, &fbc->clipmaxy); | 718 | sbus_writel(info->var.yres - 1, &fbc->clipmaxy); |
| 719 | } | 719 | } |
| 720 | 720 | ||
| 721 | static void cg6_unmap_regs(struct of_device *op, struct fb_info *info, | 721 | static void cg6_unmap_regs(struct platform_device *op, struct fb_info *info, |
| 722 | struct cg6_par *par) | 722 | struct cg6_par *par) |
| 723 | { | 723 | { |
| 724 | if (par->fbc) | 724 | if (par->fbc) |
| @@ -737,7 +737,7 @@ static void cg6_unmap_regs(struct of_device *op, struct fb_info *info, | |||
| 737 | info->fix.smem_len); | 737 | info->fix.smem_len); |
| 738 | } | 738 | } |
| 739 | 739 | ||
| 740 | static int __devinit cg6_probe(struct of_device *op, | 740 | static int __devinit cg6_probe(struct platform_device *op, |
| 741 | const struct of_device_id *match) | 741 | const struct of_device_id *match) |
| 742 | { | 742 | { |
| 743 | struct device_node *dp = op->dev.of_node; | 743 | struct device_node *dp = op->dev.of_node; |
| @@ -827,7 +827,7 @@ out_err: | |||
| 827 | return err; | 827 | return err; |
| 828 | } | 828 | } |
| 829 | 829 | ||
| 830 | static int __devexit cg6_remove(struct of_device *op) | 830 | static int __devexit cg6_remove(struct platform_device *op) |
| 831 | { | 831 | { |
| 832 | struct fb_info *info = dev_get_drvdata(&op->dev); | 832 | struct fb_info *info = dev_get_drvdata(&op->dev); |
| 833 | struct cg6_par *par = info->par; | 833 | struct cg6_par *par = info->par; |
diff --git a/drivers/video/console/bitblit.c b/drivers/video/console/bitblit.c index af88651b0735..28b1a834906b 100644 --- a/drivers/video/console/bitblit.c +++ b/drivers/video/console/bitblit.c | |||
| @@ -22,7 +22,7 @@ | |||
| 22 | /* | 22 | /* |
| 23 | * Accelerated handlers. | 23 | * Accelerated handlers. |
| 24 | */ | 24 | */ |
| 25 | static inline void update_attr(u8 *dst, u8 *src, int attribute, | 25 | static void update_attr(u8 *dst, u8 *src, int attribute, |
| 26 | struct vc_data *vc) | 26 | struct vc_data *vc) |
| 27 | { | 27 | { |
| 28 | int i, offset = (vc->vc_font.height < 10) ? 1 : 2; | 28 | int i, offset = (vc->vc_font.height < 10) ? 1 : 2; |
diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 26bf7cbfecc2..84f842331dfa 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c | |||
| @@ -287,7 +287,7 @@ static inline int fbcon_is_inactive(struct vc_data *vc, struct fb_info *info) | |||
| 287 | !vt_force_oops_output(vc); | 287 | !vt_force_oops_output(vc); |
| 288 | } | 288 | } |
| 289 | 289 | ||
| 290 | static inline int get_color(struct vc_data *vc, struct fb_info *info, | 290 | static int get_color(struct vc_data *vc, struct fb_info *info, |
| 291 | u16 c, int is_fg) | 291 | u16 c, int is_fg) |
| 292 | { | 292 | { |
| 293 | int depth = fb_get_color_depth(&info->var, &info->fix); | 293 | int depth = fb_get_color_depth(&info->var, &info->fix); |
diff --git a/drivers/video/console/fbcon_ccw.c b/drivers/video/console/fbcon_ccw.c index d135671d9961..41b32ae23dac 100644 --- a/drivers/video/console/fbcon_ccw.c +++ b/drivers/video/console/fbcon_ccw.c | |||
| @@ -22,7 +22,7 @@ | |||
| 22 | * Rotation 270 degrees | 22 | * Rotation 270 degrees |
| 23 | */ | 23 | */ |
| 24 | 24 | ||
| 25 | static inline void ccw_update_attr(u8 *dst, u8 *src, int attribute, | 25 | static void ccw_update_attr(u8 *dst, u8 *src, int attribute, |
| 26 | struct vc_data *vc) | 26 | struct vc_data *vc) |
| 27 | { | 27 | { |
| 28 | int i, j, offset = (vc->vc_font.height < 10) ? 1 : 2; | 28 | int i, j, offset = (vc->vc_font.height < 10) ? 1 : 2; |
diff --git a/drivers/video/console/fbcon_cw.c b/drivers/video/console/fbcon_cw.c index 126110f8454f..6a737827beb1 100644 --- a/drivers/video/console/fbcon_cw.c +++ b/drivers/video/console/fbcon_cw.c | |||
| @@ -22,7 +22,7 @@ | |||
| 22 | * Rotation 90 degrees | 22 | * Rotation 90 degrees |
| 23 | */ | 23 | */ |
| 24 | 24 | ||
| 25 | static inline void cw_update_attr(u8 *dst, u8 *src, int attribute, | 25 | static void cw_update_attr(u8 *dst, u8 *src, int attribute, |
| 26 | struct vc_data *vc) | 26 | struct vc_data *vc) |
| 27 | { | 27 | { |
| 28 | int i, j, offset = (vc->vc_font.height < 10) ? 1 : 2; | 28 | int i, j, offset = (vc->vc_font.height < 10) ? 1 : 2; |
diff --git a/drivers/video/console/fbcon_ud.c b/drivers/video/console/fbcon_ud.c index 93a3e7381b50..ff0872c0498b 100644 --- a/drivers/video/console/fbcon_ud.c +++ b/drivers/video/console/fbcon_ud.c | |||
| @@ -22,7 +22,7 @@ | |||
| 22 | * Rotation 180 degrees | 22 | * Rotation 180 degrees |
| 23 | */ | 23 | */ |
| 24 | 24 | ||
| 25 | static inline void ud_update_attr(u8 *dst, u8 *src, int attribute, | 25 | static void ud_update_attr(u8 *dst, u8 *src, int attribute, |
| 26 | struct vc_data *vc) | 26 | struct vc_data *vc) |
| 27 | { | 27 | { |
| 28 | int i, offset = (vc->vc_font.height < 10) ? 1 : 2; | 28 | int i, offset = (vc->vc_font.height < 10) ? 1 : 2; |
diff --git a/drivers/video/efifb.c b/drivers/video/efifb.c index 4a56f46af40a..815f84b07933 100644 --- a/drivers/video/efifb.c +++ b/drivers/video/efifb.c | |||
| @@ -16,7 +16,7 @@ | |||
| 16 | 16 | ||
| 17 | #include <video/vga.h> | 17 | #include <video/vga.h> |
| 18 | 18 | ||
| 19 | static struct fb_var_screeninfo efifb_defined __initdata = { | 19 | static struct fb_var_screeninfo efifb_defined __devinitdata = { |
| 20 | .activate = FB_ACTIVATE_NOW, | 20 | .activate = FB_ACTIVATE_NOW, |
| 21 | .height = -1, | 21 | .height = -1, |
| 22 | .width = -1, | 22 | .width = -1, |
| @@ -27,7 +27,7 @@ static struct fb_var_screeninfo efifb_defined __initdata = { | |||
| 27 | .vmode = FB_VMODE_NONINTERLACED, | 27 | .vmode = FB_VMODE_NONINTERLACED, |
| 28 | }; | 28 | }; |
| 29 | 29 | ||
| 30 | static struct fb_fix_screeninfo efifb_fix __initdata = { | 30 | static struct fb_fix_screeninfo efifb_fix __devinitdata = { |
| 31 | .id = "EFI VGA", | 31 | .id = "EFI VGA", |
| 32 | .type = FB_TYPE_PACKED_PIXELS, | 32 | .type = FB_TYPE_PACKED_PIXELS, |
| 33 | .accel = FB_ACCEL_NONE, | 33 | .accel = FB_ACCEL_NONE, |
| @@ -59,7 +59,7 @@ static struct efifb_dmi_info { | |||
| 59 | int stride; | 59 | int stride; |
| 60 | int width; | 60 | int width; |
| 61 | int height; | 61 | int height; |
| 62 | } dmi_list[] = { | 62 | } dmi_list[] __initdata = { |
| 63 | [M_I17] = { "i17", 0x80010000, 1472 * 4, 1440, 900 }, | 63 | [M_I17] = { "i17", 0x80010000, 1472 * 4, 1440, 900 }, |
| 64 | [M_I20] = { "i20", 0x80010000, 1728 * 4, 1680, 1050 }, /* guess */ | 64 | [M_I20] = { "i20", 0x80010000, 1728 * 4, 1680, 1050 }, /* guess */ |
| 65 | [M_I20_SR] = { "imac7", 0x40010000, 1728 * 4, 1680, 1050 }, | 65 | [M_I20_SR] = { "imac7", 0x40010000, 1728 * 4, 1680, 1050 }, |
| @@ -83,7 +83,7 @@ static int set_system(const struct dmi_system_id *id); | |||
| 83 | DMI_MATCH(DMI_PRODUCT_NAME, name) }, \ | 83 | DMI_MATCH(DMI_PRODUCT_NAME, name) }, \ |
| 84 | &dmi_list[enumid] } | 84 | &dmi_list[enumid] } |
| 85 | 85 | ||
| 86 | static struct dmi_system_id __initdata dmi_system_table[] = { | 86 | static const struct dmi_system_id dmi_system_table[] __initconst = { |
| 87 | EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "iMac4,1", M_I17), | 87 | EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "iMac4,1", M_I17), |
| 88 | /* At least one of these two will be right; maybe both? */ | 88 | /* At least one of these two will be right; maybe both? */ |
| 89 | EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "iMac5,1", M_I20), | 89 | EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "iMac5,1", M_I20), |
diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 731fce64df9d..b06647517c0e 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c | |||
| @@ -1362,6 +1362,7 @@ fb_mmap(struct file *file, struct vm_area_struct * vma) | |||
| 1362 | vma->vm_pgoff = off >> PAGE_SHIFT; | 1362 | vma->vm_pgoff = off >> PAGE_SHIFT; |
| 1363 | /* This is an IO map - tell maydump to skip this VMA */ | 1363 | /* This is an IO map - tell maydump to skip this VMA */ |
| 1364 | vma->vm_flags |= VM_IO | VM_RESERVED; | 1364 | vma->vm_flags |= VM_IO | VM_RESERVED; |
| 1365 | vma->vm_page_prot = vm_get_page_prot(vma->vm_flags); | ||
| 1365 | fb_pgprotect(file, vma, off); | 1366 | fb_pgprotect(file, vma, off); |
| 1366 | if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, | 1367 | if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, |
| 1367 | vma->vm_end - vma->vm_start, vma->vm_page_prot)) | 1368 | vma->vm_end - vma->vm_start, vma->vm_page_prot)) |
| @@ -1786,7 +1787,7 @@ static int ofonly __read_mostly; | |||
| 1786 | int fb_get_options(char *name, char **option) | 1787 | int fb_get_options(char *name, char **option) |
| 1787 | { | 1788 | { |
| 1788 | char *opt, *options = NULL; | 1789 | char *opt, *options = NULL; |
| 1789 | int opt_len, retval = 0; | 1790 | int retval = 0; |
| 1790 | int name_len = strlen(name), i; | 1791 | int name_len = strlen(name), i; |
| 1791 | 1792 | ||
| 1792 | if (name_len && ofonly && strncmp(name, "offb", 4)) | 1793 | if (name_len && ofonly && strncmp(name, "offb", 4)) |
| @@ -1796,8 +1797,7 @@ int fb_get_options(char *name, char **option) | |||
| 1796 | for (i = 0; i < FB_MAX; i++) { | 1797 | for (i = 0; i < FB_MAX; i++) { |
| 1797 | if (video_options[i] == NULL) | 1798 | if (video_options[i] == NULL) |
| 1798 | continue; | 1799 | continue; |
| 1799 | opt_len = strlen(video_options[i]); | 1800 | if (!video_options[i][0]) |
| 1800 | if (!opt_len) | ||
| 1801 | continue; | 1801 | continue; |
| 1802 | opt = video_options[i]; | 1802 | opt = video_options[i]; |
| 1803 | if (!strncmp(name, opt, name_len) && | 1803 | if (!strncmp(name, opt, name_len) && |
diff --git a/drivers/video/ffb.c b/drivers/video/ffb.c index f6ecfab296d3..6739b2af3bc0 100644 --- a/drivers/video/ffb.c +++ b/drivers/video/ffb.c | |||
| @@ -893,7 +893,7 @@ static void ffb_init_fix(struct fb_info *info) | |||
| 893 | info->fix.accel = FB_ACCEL_SUN_CREATOR; | 893 | info->fix.accel = FB_ACCEL_SUN_CREATOR; |
| 894 | } | 894 | } |
| 895 | 895 | ||
| 896 | static int __devinit ffb_probe(struct of_device *op, | 896 | static int __devinit ffb_probe(struct platform_device *op, |
| 897 | const struct of_device_id *match) | 897 | const struct of_device_id *match) |
| 898 | { | 898 | { |
| 899 | struct device_node *dp = op->dev.of_node; | 899 | struct device_node *dp = op->dev.of_node; |
| @@ -1023,7 +1023,7 @@ out_err: | |||
| 1023 | return err; | 1023 | return err; |
| 1024 | } | 1024 | } |
| 1025 | 1025 | ||
| 1026 | static int __devexit ffb_remove(struct of_device *op) | 1026 | static int __devexit ffb_remove(struct platform_device *op) |
| 1027 | { | 1027 | { |
| 1028 | struct fb_info *info = dev_get_drvdata(&op->dev); | 1028 | struct fb_info *info = dev_get_drvdata(&op->dev); |
| 1029 | struct ffb_par *par = info->par; | 1029 | struct ffb_par *par = info->par; |
diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c index e38ad2224540..8bbbf08fa3ce 100644 --- a/drivers/video/fsl-diu-fb.c +++ b/drivers/video/fsl-diu-fb.c | |||
| @@ -1393,7 +1393,7 @@ static void free_irq_local(int irq) | |||
| 1393 | * Power management hooks. Note that we won't be called from IRQ context, | 1393 | * Power management hooks. Note that we won't be called from IRQ context, |
| 1394 | * unlike the blank functions above, so we may sleep. | 1394 | * unlike the blank functions above, so we may sleep. |
| 1395 | */ | 1395 | */ |
| 1396 | static int fsl_diu_suspend(struct of_device *ofdev, pm_message_t state) | 1396 | static int fsl_diu_suspend(struct platform_device *ofdev, pm_message_t state) |
| 1397 | { | 1397 | { |
| 1398 | struct fsl_diu_data *machine_data; | 1398 | struct fsl_diu_data *machine_data; |
| 1399 | 1399 | ||
| @@ -1403,7 +1403,7 @@ static int fsl_diu_suspend(struct of_device *ofdev, pm_message_t state) | |||
| 1403 | return 0; | 1403 | return 0; |
| 1404 | } | 1404 | } |
| 1405 | 1405 | ||
| 1406 | static int fsl_diu_resume(struct of_device *ofdev) | 1406 | static int fsl_diu_resume(struct platform_device *ofdev) |
| 1407 | { | 1407 | { |
| 1408 | struct fsl_diu_data *machine_data; | 1408 | struct fsl_diu_data *machine_data; |
| 1409 | 1409 | ||
| @@ -1487,7 +1487,7 @@ static ssize_t show_monitor(struct device *device, | |||
| 1487 | return diu_ops.show_monitor_port(machine_data->monitor_port, buf); | 1487 | return diu_ops.show_monitor_port(machine_data->monitor_port, buf); |
| 1488 | } | 1488 | } |
| 1489 | 1489 | ||
| 1490 | static int __devinit fsl_diu_probe(struct of_device *ofdev, | 1490 | static int __devinit fsl_diu_probe(struct platform_device *ofdev, |
| 1491 | const struct of_device_id *match) | 1491 | const struct of_device_id *match) |
| 1492 | { | 1492 | { |
| 1493 | struct device_node *np = ofdev->dev.of_node; | 1493 | struct device_node *np = ofdev->dev.of_node; |
| @@ -1667,7 +1667,7 @@ error2: | |||
| 1667 | } | 1667 | } |
| 1668 | 1668 | ||
| 1669 | 1669 | ||
| 1670 | static int fsl_diu_remove(struct of_device *ofdev) | 1670 | static int fsl_diu_remove(struct platform_device *ofdev) |
| 1671 | { | 1671 | { |
| 1672 | struct fsl_diu_data *machine_data; | 1672 | struct fsl_diu_data *machine_data; |
| 1673 | int i; | 1673 | int i; |
diff --git a/drivers/video/igafb.c b/drivers/video/igafb.c index 15d200109446..d885c770eb84 100644 --- a/drivers/video/igafb.c +++ b/drivers/video/igafb.c | |||
| @@ -368,7 +368,7 @@ static int __init iga_init(struct fb_info *info, struct iga_par *par) | |||
| 368 | return 1; | 368 | return 1; |
| 369 | } | 369 | } |
| 370 | 370 | ||
| 371 | int __init igafb_init(void) | 371 | static int __init igafb_init(void) |
| 372 | { | 372 | { |
| 373 | struct fb_info *info; | 373 | struct fb_info *info; |
| 374 | struct pci_dev *pdev; | 374 | struct pci_dev *pdev; |
| @@ -531,6 +531,7 @@ int __init igafb_init(void) | |||
| 531 | iounmap(info->screen_base); | 531 | iounmap(info->screen_base); |
| 532 | kfree(par->mmap_map); | 532 | kfree(par->mmap_map); |
| 533 | kfree(info); | 533 | kfree(info); |
| 534 | return -ENODEV; | ||
| 534 | } | 535 | } |
| 535 | 536 | ||
| 536 | #ifdef CONFIG_SPARC | 537 | #ifdef CONFIG_SPARC |
| @@ -556,7 +557,7 @@ int __init igafb_init(void) | |||
| 556 | return 0; | 557 | return 0; |
| 557 | } | 558 | } |
| 558 | 559 | ||
| 559 | int __init igafb_setup(char *options) | 560 | static int __init igafb_setup(char *options) |
| 560 | { | 561 | { |
| 561 | char *this_opt; | 562 | char *this_opt; |
| 562 | 563 | ||
diff --git a/drivers/video/imxfb.c b/drivers/video/imxfb.c index 43f0639b1c10..5c363d026f64 100644 --- a/drivers/video/imxfb.c +++ b/drivers/video/imxfb.c | |||
| @@ -40,6 +40,12 @@ | |||
| 40 | */ | 40 | */ |
| 41 | #define DEBUG_VAR 1 | 41 | #define DEBUG_VAR 1 |
| 42 | 42 | ||
| 43 | #if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) || \ | ||
| 44 | (defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE) && \ | ||
| 45 | defined(CONFIG_FB_IMX_MODULE)) | ||
| 46 | #define PWMR_BACKLIGHT_AVAILABLE | ||
| 47 | #endif | ||
| 48 | |||
| 43 | #define DRIVER_NAME "imx-fb" | 49 | #define DRIVER_NAME "imx-fb" |
| 44 | 50 | ||
| 45 | #define LCDC_SSA 0x00 | 51 | #define LCDC_SSA 0x00 |
| @@ -175,7 +181,9 @@ struct imxfb_info { | |||
| 175 | 181 | ||
| 176 | struct imx_fb_videomode *mode; | 182 | struct imx_fb_videomode *mode; |
| 177 | int num_modes; | 183 | int num_modes; |
| 184 | #ifdef PWMR_BACKLIGHT_AVAILABLE | ||
| 178 | struct backlight_device *bl; | 185 | struct backlight_device *bl; |
| 186 | #endif | ||
| 179 | 187 | ||
| 180 | void (*lcd_power)(int); | 188 | void (*lcd_power)(int); |
| 181 | void (*backlight_power)(int); | 189 | void (*backlight_power)(int); |
| @@ -450,8 +458,7 @@ static int imxfb_set_par(struct fb_info *info) | |||
| 450 | return 0; | 458 | return 0; |
| 451 | } | 459 | } |
| 452 | 460 | ||
| 453 | 461 | #ifdef PWMR_BACKLIGHT_AVAILABLE | |
| 454 | |||
| 455 | static int imxfb_bl_get_brightness(struct backlight_device *bl) | 462 | static int imxfb_bl_get_brightness(struct backlight_device *bl) |
| 456 | { | 463 | { |
| 457 | struct imxfb_info *fbi = bl_get_data(bl); | 464 | struct imxfb_info *fbi = bl_get_data(bl); |
| @@ -516,6 +523,7 @@ static void imxfb_exit_backlight(struct imxfb_info *fbi) | |||
| 516 | if (fbi->bl) | 523 | if (fbi->bl) |
| 517 | backlight_device_unregister(fbi->bl); | 524 | backlight_device_unregister(fbi->bl); |
| 518 | } | 525 | } |
| 526 | #endif | ||
| 519 | 527 | ||
| 520 | static void imxfb_enable_controller(struct imxfb_info *fbi) | 528 | static void imxfb_enable_controller(struct imxfb_info *fbi) |
| 521 | { | 529 | { |
| @@ -647,6 +655,9 @@ static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *inf | |||
| 647 | fbi->regs + LCDC_SIZE); | 655 | fbi->regs + LCDC_SIZE); |
| 648 | 656 | ||
| 649 | writel(fbi->pcr, fbi->regs + LCDC_PCR); | 657 | writel(fbi->pcr, fbi->regs + LCDC_PCR); |
| 658 | #ifndef PWMR_BACKLIGHT_AVAILABLE | ||
| 659 | writel(fbi->pwmr, fbi->regs + LCDC_PWMR); | ||
| 660 | #endif | ||
| 650 | writel(fbi->lscr1, fbi->regs + LCDC_LSCR1); | 661 | writel(fbi->lscr1, fbi->regs + LCDC_LSCR1); |
| 651 | writel(fbi->dmacr, fbi->regs + LCDC_DMACR); | 662 | writel(fbi->dmacr, fbi->regs + LCDC_DMACR); |
| 652 | 663 | ||
| @@ -847,7 +858,9 @@ static int __init imxfb_probe(struct platform_device *pdev) | |||
| 847 | 858 | ||
| 848 | imxfb_enable_controller(fbi); | 859 | imxfb_enable_controller(fbi); |
| 849 | fbi->pdev = pdev; | 860 | fbi->pdev = pdev; |
| 861 | #ifdef PWMR_BACKLIGHT_AVAILABLE | ||
| 850 | imxfb_init_backlight(fbi); | 862 | imxfb_init_backlight(fbi); |
| 863 | #endif | ||
| 851 | 864 | ||
| 852 | return 0; | 865 | return 0; |
| 853 | 866 | ||
| @@ -885,7 +898,9 @@ static int __devexit imxfb_remove(struct platform_device *pdev) | |||
| 885 | 898 | ||
| 886 | imxfb_disable_controller(fbi); | 899 | imxfb_disable_controller(fbi); |
| 887 | 900 | ||
| 901 | #ifdef PWMR_BACKLIGHT_AVAILABLE | ||
| 888 | imxfb_exit_backlight(fbi); | 902 | imxfb_exit_backlight(fbi); |
| 903 | #endif | ||
| 889 | unregister_framebuffer(info); | 904 | unregister_framebuffer(info); |
| 890 | 905 | ||
| 891 | pdata = pdev->dev.platform_data; | 906 | pdata = pdev->dev.platform_data; |
diff --git a/drivers/video/leo.c b/drivers/video/leo.c index ad677637ffbb..b599e5e36ced 100644 --- a/drivers/video/leo.c +++ b/drivers/video/leo.c | |||
| @@ -529,7 +529,7 @@ static void leo_fixup_var_rgb(struct fb_var_screeninfo *var) | |||
| 529 | var->transp.length = 0; | 529 | var->transp.length = 0; |
| 530 | } | 530 | } |
| 531 | 531 | ||
| 532 | static void leo_unmap_regs(struct of_device *op, struct fb_info *info, | 532 | static void leo_unmap_regs(struct platform_device *op, struct fb_info *info, |
| 533 | struct leo_par *par) | 533 | struct leo_par *par) |
| 534 | { | 534 | { |
| 535 | if (par->lc_ss0_usr) | 535 | if (par->lc_ss0_usr) |
| @@ -547,7 +547,7 @@ static void leo_unmap_regs(struct of_device *op, struct fb_info *info, | |||
| 547 | of_iounmap(&op->resource[0], info->screen_base, 0x800000); | 547 | of_iounmap(&op->resource[0], info->screen_base, 0x800000); |
| 548 | } | 548 | } |
| 549 | 549 | ||
| 550 | static int __devinit leo_probe(struct of_device *op, | 550 | static int __devinit leo_probe(struct platform_device *op, |
| 551 | const struct of_device_id *match) | 551 | const struct of_device_id *match) |
| 552 | { | 552 | { |
| 553 | struct device_node *dp = op->dev.of_node; | 553 | struct device_node *dp = op->dev.of_node; |
| @@ -637,7 +637,7 @@ out_err: | |||
| 637 | return err; | 637 | return err; |
| 638 | } | 638 | } |
| 639 | 639 | ||
| 640 | static int __devexit leo_remove(struct of_device *op) | 640 | static int __devexit leo_remove(struct platform_device *op) |
| 641 | { | 641 | { |
| 642 | struct fb_info *info = dev_get_drvdata(&op->dev); | 642 | struct fb_info *info = dev_get_drvdata(&op->dev); |
| 643 | struct leo_par *par = info->par; | 643 | struct leo_par *par = info->par; |
diff --git a/drivers/video/matrox/i2c-matroxfb.c b/drivers/video/matrox/i2c-matroxfb.c index 403b14445a78..0fb280ead3dc 100644 --- a/drivers/video/matrox/i2c-matroxfb.c +++ b/drivers/video/matrox/i2c-matroxfb.c | |||
| @@ -191,7 +191,7 @@ static void* i2c_matroxfb_probe(struct matrox_fb_info* minfo) { | |||
| 191 | }; | 191 | }; |
| 192 | 192 | ||
| 193 | i2c_new_probed_device(&m2info->maven.adapter, | 193 | i2c_new_probed_device(&m2info->maven.adapter, |
| 194 | &maven_info, addr_list); | 194 | &maven_info, addr_list, NULL); |
| 195 | } | 195 | } |
| 196 | } | 196 | } |
| 197 | return m2info; | 197 | return m2info; |
diff --git a/drivers/video/mb862xx/mb862xxfb.c b/drivers/video/mb862xx/mb862xxfb.c index 4e2b8cc3d460..b1c4374cf940 100644 --- a/drivers/video/mb862xx/mb862xxfb.c +++ b/drivers/video/mb862xx/mb862xxfb.c | |||
| @@ -550,7 +550,7 @@ static int mb862xx_gdc_init(struct mb862xxfb_par *par) | |||
| 550 | return 0; | 550 | return 0; |
| 551 | } | 551 | } |
| 552 | 552 | ||
| 553 | static int __devinit of_platform_mb862xx_probe(struct of_device *ofdev, | 553 | static int __devinit of_platform_mb862xx_probe(struct platform_device *ofdev, |
| 554 | const struct of_device_id *id) | 554 | const struct of_device_id *id) |
| 555 | { | 555 | { |
| 556 | struct device_node *np = ofdev->dev.of_node; | 556 | struct device_node *np = ofdev->dev.of_node; |
| @@ -669,7 +669,7 @@ fbrel: | |||
| 669 | return ret; | 669 | return ret; |
| 670 | } | 670 | } |
| 671 | 671 | ||
| 672 | static int __devexit of_platform_mb862xx_remove(struct of_device *ofdev) | 672 | static int __devexit of_platform_mb862xx_remove(struct platform_device *ofdev) |
| 673 | { | 673 | { |
| 674 | struct fb_info *fbi = dev_get_drvdata(&ofdev->dev); | 674 | struct fb_info *fbi = dev_get_drvdata(&ofdev->dev); |
| 675 | struct mb862xxfb_par *par = fbi->par; | 675 | struct mb862xxfb_par *par = fbi->par; |
diff --git a/drivers/video/msm/mddi.c b/drivers/video/msm/mddi.c index c1ff271017a9..7c316c34dfca 100644 --- a/drivers/video/msm/mddi.c +++ b/drivers/video/msm/mddi.c | |||
| @@ -187,10 +187,8 @@ static void mddi_wait_interrupt(struct mddi_info *mddi, uint32_t intmask); | |||
| 187 | 187 | ||
| 188 | static void mddi_handle_rev_data_avail(struct mddi_info *mddi) | 188 | static void mddi_handle_rev_data_avail(struct mddi_info *mddi) |
| 189 | { | 189 | { |
| 190 | union mddi_rev *rev = mddi->rev_data; | ||
| 191 | uint32_t rev_data_count; | 190 | uint32_t rev_data_count; |
| 192 | uint32_t rev_crc_err_count; | 191 | uint32_t rev_crc_err_count; |
| 193 | int i; | ||
| 194 | struct reg_read_info *ri; | 192 | struct reg_read_info *ri; |
| 195 | size_t prev_offset; | 193 | size_t prev_offset; |
| 196 | uint16_t length; | 194 | uint16_t length; |
| @@ -670,7 +668,7 @@ static int __init mddi_rev_data_setup(struct mddi_info *mddi) | |||
| 670 | return 0; | 668 | return 0; |
| 671 | } | 669 | } |
| 672 | 670 | ||
| 673 | static int __init mddi_probe(struct platform_device *pdev) | 671 | static int __devinit mddi_probe(struct platform_device *pdev) |
| 674 | { | 672 | { |
| 675 | struct msm_mddi_platform_data *pdata = pdev->dev.platform_data; | 673 | struct msm_mddi_platform_data *pdata = pdev->dev.platform_data; |
| 676 | struct mddi_info *mddi = &mddi_info[pdev->id]; | 674 | struct mddi_info *mddi = &mddi_info[pdev->id]; |
diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c index 19c01c6208e8..3c28db03ad39 100644 --- a/drivers/video/msm/mdp.c +++ b/drivers/video/msm/mdp.c | |||
| @@ -258,7 +258,6 @@ int get_img(struct mdp_img *img, struct fb_info *info, | |||
| 258 | { | 258 | { |
| 259 | int put_needed, ret = 0; | 259 | int put_needed, ret = 0; |
| 260 | struct file *file; | 260 | struct file *file; |
| 261 | unsigned long vstart; | ||
| 262 | 261 | ||
| 263 | file = fget_light(img->memory_id, &put_needed); | 262 | file = fget_light(img->memory_id, &put_needed); |
| 264 | if (file == NULL) | 263 | if (file == NULL) |
diff --git a/drivers/video/p9100.c b/drivers/video/p9100.c index 688b055abab2..b6c3fc2db632 100644 --- a/drivers/video/p9100.c +++ b/drivers/video/p9100.c | |||
| @@ -249,7 +249,7 @@ static void p9100_init_fix(struct fb_info *info, int linebytes, struct device_no | |||
| 249 | info->fix.accel = FB_ACCEL_SUN_CGTHREE; | 249 | info->fix.accel = FB_ACCEL_SUN_CGTHREE; |
| 250 | } | 250 | } |
| 251 | 251 | ||
| 252 | static int __devinit p9100_probe(struct of_device *op, const struct of_device_id *match) | 252 | static int __devinit p9100_probe(struct platform_device *op, const struct of_device_id *match) |
| 253 | { | 253 | { |
| 254 | struct device_node *dp = op->dev.of_node; | 254 | struct device_node *dp = op->dev.of_node; |
| 255 | struct fb_info *info; | 255 | struct fb_info *info; |
| @@ -326,7 +326,7 @@ out_err: | |||
| 326 | return err; | 326 | return err; |
| 327 | } | 327 | } |
| 328 | 328 | ||
| 329 | static int __devexit p9100_remove(struct of_device *op) | 329 | static int __devexit p9100_remove(struct platform_device *op) |
| 330 | { | 330 | { |
| 331 | struct fb_info *info = dev_get_drvdata(&op->dev); | 331 | struct fb_info *info = dev_get_drvdata(&op->dev); |
| 332 | struct p9100_par *par = info->par; | 332 | struct p9100_par *par = info->par; |
diff --git a/drivers/video/platinumfb.c b/drivers/video/platinumfb.c index 72a1f4c04732..a50e1977b316 100644 --- a/drivers/video/platinumfb.c +++ b/drivers/video/platinumfb.c | |||
| @@ -533,7 +533,7 @@ static int __init platinumfb_setup(char *options) | |||
| 533 | #define invalidate_cache(addr) | 533 | #define invalidate_cache(addr) |
| 534 | #endif | 534 | #endif |
| 535 | 535 | ||
| 536 | static int __devinit platinumfb_probe(struct of_device* odev, | 536 | static int __devinit platinumfb_probe(struct platform_device* odev, |
| 537 | const struct of_device_id *match) | 537 | const struct of_device_id *match) |
| 538 | { | 538 | { |
| 539 | struct device_node *dp = odev->dev.of_node; | 539 | struct device_node *dp = odev->dev.of_node; |
| @@ -646,7 +646,7 @@ static int __devinit platinumfb_probe(struct of_device* odev, | |||
| 646 | return rc; | 646 | return rc; |
| 647 | } | 647 | } |
| 648 | 648 | ||
| 649 | static int __devexit platinumfb_remove(struct of_device* odev) | 649 | static int __devexit platinumfb_remove(struct platform_device* odev) |
| 650 | { | 650 | { |
| 651 | struct fb_info *info = dev_get_drvdata(&odev->dev); | 651 | struct fb_info *info = dev_get_drvdata(&odev->dev); |
| 652 | struct fb_info_platinum *pinfo = info->par; | 652 | struct fb_info_platinum *pinfo = info->par; |
diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c index 9682ecc60e12..f9aca9d13d1b 100644 --- a/drivers/video/s3c-fb.c +++ b/drivers/video/s3c-fb.c | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | /* linux/drivers/video/s3c-fb.c | 1 | /* linux/drivers/video/s3c-fb.c |
| 2 | * | 2 | * |
| 3 | * Copyright 2008 Openmoko Inc. | 3 | * Copyright 2008 Openmoko Inc. |
| 4 | * Copyright 2008 Simtec Electronics | 4 | * Copyright 2008-2010 Simtec Electronics |
| 5 | * Ben Dooks <ben@simtec.co.uk> | 5 | * Ben Dooks <ben@simtec.co.uk> |
| 6 | * http://armlinux.simtec.co.uk/ | 6 | * http://armlinux.simtec.co.uk/ |
| 7 | * | 7 | * |
| @@ -9,7 +9,7 @@ | |||
| 9 | * | 9 | * |
| 10 | * This program is free software; you can redistribute it and/or modify | 10 | * This program is free software; you can redistribute it and/or modify |
| 11 | * it under the terms of the GNU General Public License version 2 as | 11 | * it under the terms of the GNU General Public License version 2 as |
| 12 | * published by the Free Software Foundation. | 12 | * published by the Free Software FoundatIon. |
| 13 | */ | 13 | */ |
| 14 | 14 | ||
| 15 | #include <linux/kernel.h> | 15 | #include <linux/kernel.h> |
| @@ -21,9 +21,11 @@ | |||
| 21 | #include <linux/clk.h> | 21 | #include <linux/clk.h> |
| 22 | #include <linux/fb.h> | 22 | #include <linux/fb.h> |
| 23 | #include <linux/io.h> | 23 | #include <linux/io.h> |
| 24 | #include <linux/uaccess.h> | ||
| 25 | #include <linux/interrupt.h> | ||
| 24 | 26 | ||
| 25 | #include <mach/map.h> | 27 | #include <mach/map.h> |
| 26 | #include <mach/regs-fb.h> | 28 | #include <plat/regs-fb-v4.h> |
| 27 | #include <plat/fb.h> | 29 | #include <plat/fb.h> |
| 28 | 30 | ||
| 29 | /* This driver will export a number of framebuffer interfaces depending | 31 | /* This driver will export a number of framebuffer interfaces depending |
| @@ -36,9 +38,9 @@ | |||
| 36 | * output timings and as the control for the output power-down state. | 38 | * output timings and as the control for the output power-down state. |
| 37 | */ | 39 | */ |
| 38 | 40 | ||
| 39 | /* note, some of the functions that get called are derived from including | 41 | /* note, the previous use of <mach/regs-fb.h> to get platform specific data |
| 40 | * <mach/regs-fb.h> as they are specific to the architecture that the code | 42 | * has been replaced by using the platform device name to pick the correct |
| 41 | * is being built for. | 43 | * configuration data for the system. |
| 42 | */ | 44 | */ |
| 43 | 45 | ||
| 44 | #ifdef CONFIG_FB_S3C_DEBUG_REGWRITE | 46 | #ifdef CONFIG_FB_S3C_DEBUG_REGWRITE |
| @@ -48,13 +50,108 @@ | |||
| 48 | __raw_writel(v, r); } while(0) | 50 | __raw_writel(v, r); } while(0) |
| 49 | #endif /* FB_S3C_DEBUG_REGWRITE */ | 51 | #endif /* FB_S3C_DEBUG_REGWRITE */ |
| 50 | 52 | ||
| 53 | /* irq_flags bits */ | ||
| 54 | #define S3C_FB_VSYNC_IRQ_EN 0 | ||
| 55 | |||
| 56 | #define VSYNC_TIMEOUT_MSEC 50 | ||
| 57 | |||
| 51 | struct s3c_fb; | 58 | struct s3c_fb; |
| 52 | 59 | ||
| 60 | #define VALID_BPP(x) (1 << ((x) - 1)) | ||
| 61 | |||
| 62 | #define OSD_BASE(win, variant) ((variant).osd + ((win) * (variant).osd_stride)) | ||
| 63 | #define VIDOSD_A(win, variant) (OSD_BASE(win, variant) + 0x00) | ||
| 64 | #define VIDOSD_B(win, variant) (OSD_BASE(win, variant) + 0x04) | ||
| 65 | #define VIDOSD_C(win, variant) (OSD_BASE(win, variant) + 0x08) | ||
| 66 | #define VIDOSD_D(win, variant) (OSD_BASE(win, variant) + 0x0C) | ||
| 67 | |||
| 68 | /** | ||
| 69 | * struct s3c_fb_variant - fb variant information | ||
| 70 | * @is_2443: Set if S3C2443/S3C2416 style hardware. | ||
| 71 | * @nr_windows: The number of windows. | ||
| 72 | * @vidtcon: The base for the VIDTCONx registers | ||
| 73 | * @wincon: The base for the WINxCON registers. | ||
| 74 | * @winmap: The base for the WINxMAP registers. | ||
| 75 | * @keycon: The abse for the WxKEYCON registers. | ||
| 76 | * @buf_start: Offset of buffer start registers. | ||
| 77 | * @buf_size: Offset of buffer size registers. | ||
| 78 | * @buf_end: Offset of buffer end registers. | ||
| 79 | * @osd: The base for the OSD registers. | ||
| 80 | * @palette: Address of palette memory, or 0 if none. | ||
| 81 | * @has_prtcon: Set if has PRTCON register. | ||
| 82 | * @has_shadowcon: Set if has SHADOWCON register. | ||
| 83 | */ | ||
| 84 | struct s3c_fb_variant { | ||
| 85 | unsigned int is_2443:1; | ||
| 86 | unsigned short nr_windows; | ||
| 87 | unsigned short vidtcon; | ||
| 88 | unsigned short wincon; | ||
| 89 | unsigned short winmap; | ||
| 90 | unsigned short keycon; | ||
| 91 | unsigned short buf_start; | ||
| 92 | unsigned short buf_end; | ||
| 93 | unsigned short buf_size; | ||
| 94 | unsigned short osd; | ||
| 95 | unsigned short osd_stride; | ||
| 96 | unsigned short palette[S3C_FB_MAX_WIN]; | ||
| 97 | |||
| 98 | unsigned int has_prtcon:1; | ||
| 99 | unsigned int has_shadowcon:1; | ||
| 100 | }; | ||
| 101 | |||
| 102 | /** | ||
| 103 | * struct s3c_fb_win_variant | ||
| 104 | * @has_osd_c: Set if has OSD C register. | ||
| 105 | * @has_osd_d: Set if has OSD D register. | ||
| 106 | * @has_osd_alpha: Set if can change alpha transparency for a window. | ||
| 107 | * @palette_sz: Size of palette in entries. | ||
| 108 | * @palette_16bpp: Set if palette is 16bits wide. | ||
| 109 | * @osd_size_off: If != 0, supports setting up OSD for a window; the appropriate | ||
| 110 | * register is located at the given offset from OSD_BASE. | ||
| 111 | * @valid_bpp: 1 bit per BPP setting to show valid bits-per-pixel. | ||
| 112 | * | ||
| 113 | * valid_bpp bit x is set if (x+1)BPP is supported. | ||
| 114 | */ | ||
| 115 | struct s3c_fb_win_variant { | ||
| 116 | unsigned int has_osd_c:1; | ||
| 117 | unsigned int has_osd_d:1; | ||
| 118 | unsigned int has_osd_alpha:1; | ||
| 119 | unsigned int palette_16bpp:1; | ||
| 120 | unsigned short osd_size_off; | ||
| 121 | unsigned short palette_sz; | ||
| 122 | u32 valid_bpp; | ||
| 123 | }; | ||
| 124 | |||
| 125 | /** | ||
| 126 | * struct s3c_fb_driverdata - per-device type driver data for init time. | ||
| 127 | * @variant: The variant information for this driver. | ||
| 128 | * @win: The window information for each window. | ||
| 129 | */ | ||
| 130 | struct s3c_fb_driverdata { | ||
| 131 | struct s3c_fb_variant variant; | ||
| 132 | struct s3c_fb_win_variant *win[S3C_FB_MAX_WIN]; | ||
| 133 | }; | ||
| 134 | |||
| 135 | /** | ||
| 136 | * struct s3c_fb_palette - palette information | ||
| 137 | * @r: Red bitfield. | ||
| 138 | * @g: Green bitfield. | ||
| 139 | * @b: Blue bitfield. | ||
| 140 | * @a: Alpha bitfield. | ||
| 141 | */ | ||
| 142 | struct s3c_fb_palette { | ||
| 143 | struct fb_bitfield r; | ||
| 144 | struct fb_bitfield g; | ||
| 145 | struct fb_bitfield b; | ||
| 146 | struct fb_bitfield a; | ||
| 147 | }; | ||
| 148 | |||
| 53 | /** | 149 | /** |
| 54 | * struct s3c_fb_win - per window private data for each framebuffer. | 150 | * struct s3c_fb_win - per window private data for each framebuffer. |
| 55 | * @windata: The platform data supplied for the window configuration. | 151 | * @windata: The platform data supplied for the window configuration. |
| 56 | * @parent: The hardware that this window is part of. | 152 | * @parent: The hardware that this window is part of. |
| 57 | * @fbinfo: Pointer pack to the framebuffer info for this window. | 153 | * @fbinfo: Pointer pack to the framebuffer info for this window. |
| 154 | * @varint: The variant information for this window. | ||
| 58 | * @palette_buffer: Buffer/cache to hold palette entries. | 155 | * @palette_buffer: Buffer/cache to hold palette entries. |
| 59 | * @pseudo_palette: For use in TRUECOLOUR modes for entries 0..15/ | 156 | * @pseudo_palette: For use in TRUECOLOUR modes for entries 0..15/ |
| 60 | * @index: The window number of this window. | 157 | * @index: The window number of this window. |
| @@ -65,6 +162,7 @@ struct s3c_fb_win { | |||
| 65 | struct s3c_fb *parent; | 162 | struct s3c_fb *parent; |
| 66 | struct fb_info *fbinfo; | 163 | struct fb_info *fbinfo; |
| 67 | struct s3c_fb_palette palette; | 164 | struct s3c_fb_palette palette; |
| 165 | struct s3c_fb_win_variant variant; | ||
| 68 | 166 | ||
| 69 | u32 *palette_buffer; | 167 | u32 *palette_buffer; |
| 70 | u32 pseudo_palette[16]; | 168 | u32 pseudo_palette[16]; |
| @@ -72,37 +170,54 @@ struct s3c_fb_win { | |||
| 72 | }; | 170 | }; |
| 73 | 171 | ||
| 74 | /** | 172 | /** |
| 173 | * struct s3c_fb_vsync - vsync information | ||
| 174 | * @wait: a queue for processes waiting for vsync | ||
| 175 | * @count: vsync interrupt count | ||
| 176 | */ | ||
| 177 | struct s3c_fb_vsync { | ||
| 178 | wait_queue_head_t wait; | ||
| 179 | unsigned int count; | ||
| 180 | }; | ||
| 181 | |||
| 182 | /** | ||
| 75 | * struct s3c_fb - overall hardware state of the hardware | 183 | * struct s3c_fb - overall hardware state of the hardware |
| 76 | * @dev: The device that we bound to, for printing, etc. | 184 | * @dev: The device that we bound to, for printing, etc. |
| 77 | * @regs_res: The resource we claimed for the IO registers. | 185 | * @regs_res: The resource we claimed for the IO registers. |
| 78 | * @bus_clk: The clk (hclk) feeding our interface and possibly pixclk. | 186 | * @bus_clk: The clk (hclk) feeding our interface and possibly pixclk. |
| 79 | * @regs: The mapped hardware registers. | 187 | * @regs: The mapped hardware registers. |
| 188 | * @variant: Variant information for this hardware. | ||
| 80 | * @enabled: A bitmask of enabled hardware windows. | 189 | * @enabled: A bitmask of enabled hardware windows. |
| 81 | * @pdata: The platform configuration data passed with the device. | 190 | * @pdata: The platform configuration data passed with the device. |
| 82 | * @windows: The hardware windows that have been claimed. | 191 | * @windows: The hardware windows that have been claimed. |
| 192 | * @irq_no: IRQ line number | ||
| 193 | * @irq_flags: irq flags | ||
| 194 | * @vsync_info: VSYNC-related information (count, queues...) | ||
| 83 | */ | 195 | */ |
| 84 | struct s3c_fb { | 196 | struct s3c_fb { |
| 85 | struct device *dev; | 197 | struct device *dev; |
| 86 | struct resource *regs_res; | 198 | struct resource *regs_res; |
| 87 | struct clk *bus_clk; | 199 | struct clk *bus_clk; |
| 88 | void __iomem *regs; | 200 | void __iomem *regs; |
| 201 | struct s3c_fb_variant variant; | ||
| 89 | 202 | ||
| 90 | unsigned char enabled; | 203 | unsigned char enabled; |
| 91 | 204 | ||
| 92 | struct s3c_fb_platdata *pdata; | 205 | struct s3c_fb_platdata *pdata; |
| 93 | struct s3c_fb_win *windows[S3C_FB_MAX_WIN]; | 206 | struct s3c_fb_win *windows[S3C_FB_MAX_WIN]; |
| 207 | |||
| 208 | int irq_no; | ||
| 209 | unsigned long irq_flags; | ||
| 210 | struct s3c_fb_vsync vsync_info; | ||
| 94 | }; | 211 | }; |
| 95 | 212 | ||
| 96 | /** | 213 | /** |
| 97 | * s3c_fb_win_has_palette() - determine if a mode has a palette | 214 | * s3c_fb_validate_win_bpp - validate the bits-per-pixel for this mode. |
| 98 | * @win: The window number being queried. | 215 | * @win: The device window. |
| 99 | * @bpp: The number of bits per pixel to test. | 216 | * @bpp: The bit depth. |
| 100 | * | ||
| 101 | * Work out if the given window supports palletised data at the specified bpp. | ||
| 102 | */ | 217 | */ |
| 103 | static int s3c_fb_win_has_palette(unsigned int win, unsigned int bpp) | 218 | static bool s3c_fb_validate_win_bpp(struct s3c_fb_win *win, unsigned int bpp) |
| 104 | { | 219 | { |
| 105 | return s3c_fb_win_pal_size(win) <= (1 << bpp); | 220 | return win->variant.valid_bpp & VALID_BPP(bpp); |
| 106 | } | 221 | } |
| 107 | 222 | ||
| 108 | /** | 223 | /** |
| @@ -125,7 +240,7 @@ static int s3c_fb_check_var(struct fb_var_screeninfo *var, | |||
| 125 | var->xres_virtual = max((unsigned int)windata->virtual_x, var->xres); | 240 | var->xres_virtual = max((unsigned int)windata->virtual_x, var->xres); |
| 126 | var->yres_virtual = max((unsigned int)windata->virtual_y, var->yres); | 241 | var->yres_virtual = max((unsigned int)windata->virtual_y, var->yres); |
| 127 | 242 | ||
| 128 | if (!s3c_fb_validate_win_bpp(win->index, var->bits_per_pixel)) { | 243 | if (!s3c_fb_validate_win_bpp(win, var->bits_per_pixel)) { |
| 129 | dev_dbg(sfb->dev, "win %d: unsupported bpp %d\n", | 244 | dev_dbg(sfb->dev, "win %d: unsupported bpp %d\n", |
| 130 | win->index, var->bits_per_pixel); | 245 | win->index, var->bits_per_pixel); |
| 131 | return -EINVAL; | 246 | return -EINVAL; |
| @@ -140,7 +255,7 @@ static int s3c_fb_check_var(struct fb_var_screeninfo *var, | |||
| 140 | case 2: | 255 | case 2: |
| 141 | case 4: | 256 | case 4: |
| 142 | case 8: | 257 | case 8: |
| 143 | if (!s3c_fb_win_has_palette(win->index, var->bits_per_pixel)) { | 258 | if (sfb->variant.palette[win->index] != 0) { |
| 144 | /* non palletised, A:1,R:2,G:3,B:2 mode */ | 259 | /* non palletised, A:1,R:2,G:3,B:2 mode */ |
| 145 | var->red.offset = 4; | 260 | var->red.offset = 4; |
| 146 | var->green.offset = 2; | 261 | var->green.offset = 2; |
| @@ -255,6 +370,66 @@ static int s3c_fb_align_word(unsigned int bpp, unsigned int pix) | |||
| 255 | } | 370 | } |
| 256 | 371 | ||
| 257 | /** | 372 | /** |
| 373 | * vidosd_set_size() - set OSD size for a window | ||
| 374 | * | ||
| 375 | * @win: the window to set OSD size for | ||
| 376 | * @size: OSD size register value | ||
| 377 | */ | ||
| 378 | static void vidosd_set_size(struct s3c_fb_win *win, u32 size) | ||
| 379 | { | ||
| 380 | struct s3c_fb *sfb = win->parent; | ||
| 381 | |||
| 382 | /* OSD can be set up if osd_size_off != 0 for this window */ | ||
| 383 | if (win->variant.osd_size_off) | ||
| 384 | writel(size, sfb->regs + OSD_BASE(win->index, sfb->variant) | ||
| 385 | + win->variant.osd_size_off); | ||
| 386 | } | ||
| 387 | |||
| 388 | /** | ||
| 389 | * vidosd_set_alpha() - set alpha transparency for a window | ||
| 390 | * | ||
| 391 | * @win: the window to set OSD size for | ||
| 392 | * @alpha: alpha register value | ||
| 393 | */ | ||
| 394 | static void vidosd_set_alpha(struct s3c_fb_win *win, u32 alpha) | ||
| 395 | { | ||
| 396 | struct s3c_fb *sfb = win->parent; | ||
| 397 | |||
| 398 | if (win->variant.has_osd_alpha) | ||
| 399 | writel(alpha, sfb->regs + VIDOSD_C(win->index, sfb->variant)); | ||
| 400 | } | ||
| 401 | |||
| 402 | /** | ||
| 403 | * shadow_protect_win() - disable updating values from shadow registers at vsync | ||
| 404 | * | ||
| 405 | * @win: window to protect registers for | ||
| 406 | * @protect: 1 to protect (disable updates) | ||
| 407 | */ | ||
| 408 | static void shadow_protect_win(struct s3c_fb_win *win, bool protect) | ||
| 409 | { | ||
| 410 | struct s3c_fb *sfb = win->parent; | ||
| 411 | u32 reg; | ||
| 412 | |||
| 413 | if (protect) { | ||
| 414 | if (sfb->variant.has_prtcon) { | ||
| 415 | writel(PRTCON_PROTECT, sfb->regs + PRTCON); | ||
| 416 | } else if (sfb->variant.has_shadowcon) { | ||
| 417 | reg = readl(sfb->regs + SHADOWCON); | ||
| 418 | writel(reg | SHADOWCON_WINx_PROTECT(win->index), | ||
| 419 | sfb->regs + SHADOWCON); | ||
| 420 | } | ||
| 421 | } else { | ||
| 422 | if (sfb->variant.has_prtcon) { | ||
| 423 | writel(0, sfb->regs + PRTCON); | ||
| 424 | } else if (sfb->variant.has_shadowcon) { | ||
| 425 | reg = readl(sfb->regs + SHADOWCON); | ||
| 426 | writel(reg & ~SHADOWCON_WINx_PROTECT(win->index), | ||
| 427 | sfb->regs + SHADOWCON); | ||
| 428 | } | ||
| 429 | } | ||
| 430 | } | ||
| 431 | |||
| 432 | /** | ||
| 258 | * s3c_fb_set_par() - framebuffer request to set new framebuffer state. | 433 | * s3c_fb_set_par() - framebuffer request to set new framebuffer state. |
| 259 | * @info: The framebuffer to change. | 434 | * @info: The framebuffer to change. |
| 260 | * | 435 | * |
| @@ -266,14 +441,17 @@ static int s3c_fb_set_par(struct fb_info *info) | |||
| 266 | struct s3c_fb_win *win = info->par; | 441 | struct s3c_fb_win *win = info->par; |
| 267 | struct s3c_fb *sfb = win->parent; | 442 | struct s3c_fb *sfb = win->parent; |
| 268 | void __iomem *regs = sfb->regs; | 443 | void __iomem *regs = sfb->regs; |
| 444 | void __iomem *buf = regs; | ||
| 269 | int win_no = win->index; | 445 | int win_no = win->index; |
| 270 | u32 osdc_data = 0; | 446 | u32 alpha = 0; |
| 271 | u32 data; | 447 | u32 data; |
| 272 | u32 pagewidth; | 448 | u32 pagewidth; |
| 273 | int clkdiv; | 449 | int clkdiv; |
| 274 | 450 | ||
| 275 | dev_dbg(sfb->dev, "setting framebuffer parameters\n"); | 451 | dev_dbg(sfb->dev, "setting framebuffer parameters\n"); |
| 276 | 452 | ||
| 453 | shadow_protect_win(win, 1); | ||
| 454 | |||
| 277 | switch (var->bits_per_pixel) { | 455 | switch (var->bits_per_pixel) { |
| 278 | case 32: | 456 | case 32: |
| 279 | case 24: | 457 | case 24: |
| @@ -282,7 +460,7 @@ static int s3c_fb_set_par(struct fb_info *info) | |||
| 282 | info->fix.visual = FB_VISUAL_TRUECOLOR; | 460 | info->fix.visual = FB_VISUAL_TRUECOLOR; |
| 283 | break; | 461 | break; |
| 284 | case 8: | 462 | case 8: |
| 285 | if (s3c_fb_win_has_palette(win_no, 8)) | 463 | if (win->variant.palette_sz >= 256) |
| 286 | info->fix.visual = FB_VISUAL_PSEUDOCOLOR; | 464 | info->fix.visual = FB_VISUAL_PSEUDOCOLOR; |
| 287 | else | 465 | else |
| 288 | info->fix.visual = FB_VISUAL_TRUECOLOR; | 466 | info->fix.visual = FB_VISUAL_TRUECOLOR; |
| @@ -297,12 +475,15 @@ static int s3c_fb_set_par(struct fb_info *info) | |||
| 297 | 475 | ||
| 298 | info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8; | 476 | info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8; |
| 299 | 477 | ||
| 478 | info->fix.xpanstep = info->var.xres_virtual > info->var.xres ? 1 : 0; | ||
| 479 | info->fix.ypanstep = info->var.yres_virtual > info->var.yres ? 1 : 0; | ||
| 480 | |||
| 300 | /* disable the window whilst we update it */ | 481 | /* disable the window whilst we update it */ |
| 301 | writel(0, regs + WINCON(win_no)); | 482 | writel(0, regs + WINCON(win_no)); |
| 302 | 483 | ||
| 303 | /* use window 0 as the basis for the lcd output timings */ | 484 | /* use platform specified window as the basis for the lcd timings */ |
| 304 | 485 | ||
| 305 | if (win_no == 0) { | 486 | if (win_no == sfb->pdata->default_win) { |
| 306 | clkdiv = s3c_fb_calc_pixclk(sfb, var->pixclock); | 487 | clkdiv = s3c_fb_calc_pixclk(sfb, var->pixclock); |
| 307 | 488 | ||
| 308 | data = sfb->pdata->vidcon0; | 489 | data = sfb->pdata->vidcon0; |
| @@ -315,6 +496,9 @@ static int s3c_fb_set_par(struct fb_info *info) | |||
| 315 | 496 | ||
| 316 | /* write the timing data to the panel */ | 497 | /* write the timing data to the panel */ |
| 317 | 498 | ||
| 499 | if (sfb->variant.is_2443) | ||
| 500 | data |= (1 << 5); | ||
| 501 | |||
| 318 | data |= VIDCON0_ENVID | VIDCON0_ENVID_F; | 502 | data |= VIDCON0_ENVID | VIDCON0_ENVID_F; |
| 319 | writel(data, regs + VIDCON0); | 503 | writel(data, regs + VIDCON0); |
| 320 | 504 | ||
| @@ -322,53 +506,54 @@ static int s3c_fb_set_par(struct fb_info *info) | |||
| 322 | VIDTCON0_VFPD(var->lower_margin - 1) | | 506 | VIDTCON0_VFPD(var->lower_margin - 1) | |
| 323 | VIDTCON0_VSPW(var->vsync_len - 1); | 507 | VIDTCON0_VSPW(var->vsync_len - 1); |
| 324 | 508 | ||
| 325 | writel(data, regs + VIDTCON0); | 509 | writel(data, regs + sfb->variant.vidtcon); |
| 326 | 510 | ||
| 327 | data = VIDTCON1_HBPD(var->left_margin - 1) | | 511 | data = VIDTCON1_HBPD(var->left_margin - 1) | |
| 328 | VIDTCON1_HFPD(var->right_margin - 1) | | 512 | VIDTCON1_HFPD(var->right_margin - 1) | |
| 329 | VIDTCON1_HSPW(var->hsync_len - 1); | 513 | VIDTCON1_HSPW(var->hsync_len - 1); |
| 330 | 514 | ||
| 331 | writel(data, regs + VIDTCON1); | 515 | /* VIDTCON1 */ |
| 516 | writel(data, regs + sfb->variant.vidtcon + 4); | ||
| 332 | 517 | ||
| 333 | data = VIDTCON2_LINEVAL(var->yres - 1) | | 518 | data = VIDTCON2_LINEVAL(var->yres - 1) | |
| 334 | VIDTCON2_HOZVAL(var->xres - 1); | 519 | VIDTCON2_HOZVAL(var->xres - 1); |
| 335 | writel(data, regs + VIDTCON2); | 520 | writel(data, regs +sfb->variant.vidtcon + 8 ); |
| 336 | } | 521 | } |
| 337 | 522 | ||
| 338 | /* write the buffer address */ | 523 | /* write the buffer address */ |
| 339 | 524 | ||
| 340 | writel(info->fix.smem_start, regs + VIDW_BUF_START(win_no)); | 525 | /* start and end registers stride is 8 */ |
| 526 | buf = regs + win_no * 8; | ||
| 527 | |||
| 528 | writel(info->fix.smem_start, buf + sfb->variant.buf_start); | ||
| 341 | 529 | ||
| 342 | data = info->fix.smem_start + info->fix.line_length * var->yres; | 530 | data = info->fix.smem_start + info->fix.line_length * var->yres; |
| 343 | writel(data, regs + VIDW_BUF_END(win_no)); | 531 | writel(data, buf + sfb->variant.buf_end); |
| 344 | 532 | ||
| 345 | pagewidth = (var->xres * var->bits_per_pixel) >> 3; | 533 | pagewidth = (var->xres * var->bits_per_pixel) >> 3; |
| 346 | data = VIDW_BUF_SIZE_OFFSET(info->fix.line_length - pagewidth) | | 534 | data = VIDW_BUF_SIZE_OFFSET(info->fix.line_length - pagewidth) | |
| 347 | VIDW_BUF_SIZE_PAGEWIDTH(pagewidth); | 535 | VIDW_BUF_SIZE_PAGEWIDTH(pagewidth); |
| 348 | writel(data, regs + VIDW_BUF_SIZE(win_no)); | 536 | writel(data, regs + sfb->variant.buf_size + (win_no * 4)); |
| 349 | 537 | ||
| 350 | /* write 'OSD' registers to control position of framebuffer */ | 538 | /* write 'OSD' registers to control position of framebuffer */ |
| 351 | 539 | ||
| 352 | data = VIDOSDxA_TOPLEFT_X(0) | VIDOSDxA_TOPLEFT_Y(0); | 540 | data = VIDOSDxA_TOPLEFT_X(0) | VIDOSDxA_TOPLEFT_Y(0); |
| 353 | writel(data, regs + VIDOSD_A(win_no)); | 541 | writel(data, regs + VIDOSD_A(win_no, sfb->variant)); |
| 354 | 542 | ||
| 355 | data = VIDOSDxB_BOTRIGHT_X(s3c_fb_align_word(var->bits_per_pixel, | 543 | data = VIDOSDxB_BOTRIGHT_X(s3c_fb_align_word(var->bits_per_pixel, |
| 356 | var->xres - 1)) | | 544 | var->xres - 1)) | |
| 357 | VIDOSDxB_BOTRIGHT_Y(var->yres - 1); | 545 | VIDOSDxB_BOTRIGHT_Y(var->yres - 1); |
| 358 | 546 | ||
| 359 | writel(data, regs + VIDOSD_B(win_no)); | 547 | writel(data, regs + VIDOSD_B(win_no, sfb->variant)); |
| 360 | 548 | ||
| 361 | data = var->xres * var->yres; | 549 | data = var->xres * var->yres; |
| 362 | 550 | ||
| 363 | osdc_data = VIDISD14C_ALPHA1_R(0xf) | | 551 | alpha = VIDISD14C_ALPHA1_R(0xf) | |
| 364 | VIDISD14C_ALPHA1_G(0xf) | | 552 | VIDISD14C_ALPHA1_G(0xf) | |
| 365 | VIDISD14C_ALPHA1_B(0xf); | 553 | VIDISD14C_ALPHA1_B(0xf); |
| 366 | 554 | ||
| 367 | if (s3c_fb_has_osd_d(win_no)) { | 555 | vidosd_set_alpha(win, alpha); |
| 368 | writel(data, regs + VIDOSD_D(win_no)); | 556 | vidosd_set_size(win, data); |
| 369 | writel(osdc_data, regs + VIDOSD_C(win_no)); | ||
| 370 | } else | ||
| 371 | writel(data, regs + VIDOSD_C(win_no)); | ||
| 372 | 557 | ||
| 373 | data = WINCONx_ENWIN; | 558 | data = WINCONx_ENWIN; |
| 374 | 559 | ||
| @@ -424,13 +609,15 @@ static int s3c_fb_set_par(struct fb_info *info) | |||
| 424 | else | 609 | else |
| 425 | data |= WINCON0_BPPMODE_24BPP_888; | 610 | data |= WINCON0_BPPMODE_24BPP_888; |
| 426 | 611 | ||
| 612 | data |= WINCONx_WSWP; | ||
| 427 | data |= WINCONx_BURSTLEN_16WORD; | 613 | data |= WINCONx_BURSTLEN_16WORD; |
| 428 | break; | 614 | break; |
| 429 | } | 615 | } |
| 430 | 616 | ||
| 431 | /* It has no color key control register for window0 */ | 617 | /* Enable the colour keying for the window below this one */ |
| 432 | if (win_no > 0) { | 618 | if (win_no > 0) { |
| 433 | u32 keycon0_data = 0, keycon1_data = 0; | 619 | u32 keycon0_data = 0, keycon1_data = 0; |
| 620 | void __iomem *keycon = regs + sfb->variant.keycon; | ||
| 434 | 621 | ||
| 435 | keycon0_data = ~(WxKEYCON0_KEYBL_EN | | 622 | keycon0_data = ~(WxKEYCON0_KEYBL_EN | |
| 436 | WxKEYCON0_KEYEN_F | | 623 | WxKEYCON0_KEYEN_F | |
| @@ -438,12 +625,23 @@ static int s3c_fb_set_par(struct fb_info *info) | |||
| 438 | 625 | ||
| 439 | keycon1_data = WxKEYCON1_COLVAL(0xffffff); | 626 | keycon1_data = WxKEYCON1_COLVAL(0xffffff); |
| 440 | 627 | ||
| 441 | writel(keycon0_data, regs + WxKEYCONy(win_no-1, 0)); | 628 | keycon += (win_no - 1) * 8; |
| 442 | writel(keycon1_data, regs + WxKEYCONy(win_no-1, 1)); | 629 | |
| 630 | writel(keycon0_data, keycon + WKEYCON0); | ||
| 631 | writel(keycon1_data, keycon + WKEYCON1); | ||
| 632 | } | ||
| 633 | |||
| 634 | writel(data, regs + sfb->variant.wincon + (win_no * 4)); | ||
| 635 | writel(0x0, regs + sfb->variant.winmap + (win_no * 4)); | ||
| 636 | |||
| 637 | /* Enable DMA channel for this window */ | ||
| 638 | if (sfb->variant.has_shadowcon) { | ||
| 639 | data = readl(sfb->regs + SHADOWCON); | ||
| 640 | data |= SHADOWCON_CHx_ENABLE(win_no); | ||
| 641 | writel(data, sfb->regs + SHADOWCON); | ||
| 443 | } | 642 | } |
| 444 | 643 | ||
| 445 | writel(data, regs + WINCON(win_no)); | 644 | shadow_protect_win(win, 0); |
| 446 | writel(0x0, regs + WINxMAP(win_no)); | ||
| 447 | 645 | ||
| 448 | return 0; | 646 | return 0; |
| 449 | } | 647 | } |
| @@ -470,7 +668,7 @@ static void s3c_fb_update_palette(struct s3c_fb *sfb, | |||
| 470 | void __iomem *palreg; | 668 | void __iomem *palreg; |
| 471 | u32 palcon; | 669 | u32 palcon; |
| 472 | 670 | ||
| 473 | palreg = sfb->regs + s3c_fb_pal_reg(win->index, reg); | 671 | palreg = sfb->regs + sfb->variant.palette[win->index]; |
| 474 | 672 | ||
| 475 | dev_dbg(sfb->dev, "%s: win %d, reg %d (%p): %08x\n", | 673 | dev_dbg(sfb->dev, "%s: win %d, reg %d (%p): %08x\n", |
| 476 | __func__, win->index, reg, palreg, value); | 674 | __func__, win->index, reg, palreg, value); |
| @@ -480,10 +678,10 @@ static void s3c_fb_update_palette(struct s3c_fb *sfb, | |||
| 480 | palcon = readl(sfb->regs + WPALCON); | 678 | palcon = readl(sfb->regs + WPALCON); |
| 481 | writel(palcon | WPALCON_PAL_UPDATE, sfb->regs + WPALCON); | 679 | writel(palcon | WPALCON_PAL_UPDATE, sfb->regs + WPALCON); |
| 482 | 680 | ||
| 483 | if (s3c_fb_pal_is16(win->index)) | 681 | if (win->variant.palette_16bpp) |
| 484 | writew(value, palreg); | 682 | writew(value, palreg + (reg * 2)); |
| 485 | else | 683 | else |
| 486 | writel(value, palreg); | 684 | writel(value, palreg + (reg * 4)); |
| 487 | 685 | ||
| 488 | writel(palcon, sfb->regs + WPALCON); | 686 | writel(palcon, sfb->regs + WPALCON); |
| 489 | } | 687 | } |
| @@ -532,7 +730,7 @@ static int s3c_fb_setcolreg(unsigned regno, | |||
| 532 | break; | 730 | break; |
| 533 | 731 | ||
| 534 | case FB_VISUAL_PSEUDOCOLOR: | 732 | case FB_VISUAL_PSEUDOCOLOR: |
| 535 | if (regno < s3c_fb_win_pal_size(win->index)) { | 733 | if (regno < win->variant.palette_sz) { |
| 536 | val = chan_to_field(red, &win->palette.r); | 734 | val = chan_to_field(red, &win->palette.r); |
| 537 | val |= chan_to_field(green, &win->palette.g); | 735 | val |= chan_to_field(green, &win->palette.g); |
| 538 | val |= chan_to_field(blue, &win->palette.b); | 736 | val |= chan_to_field(blue, &win->palette.b); |
| @@ -591,7 +789,7 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info) | |||
| 591 | 789 | ||
| 592 | dev_dbg(sfb->dev, "blank mode %d\n", blank_mode); | 790 | dev_dbg(sfb->dev, "blank mode %d\n", blank_mode); |
| 593 | 791 | ||
| 594 | wincon = readl(sfb->regs + WINCON(index)); | 792 | wincon = readl(sfb->regs + sfb->variant.wincon + (index * 4)); |
| 595 | 793 | ||
| 596 | switch (blank_mode) { | 794 | switch (blank_mode) { |
| 597 | case FB_BLANK_POWERDOWN: | 795 | case FB_BLANK_POWERDOWN: |
| @@ -602,11 +800,11 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info) | |||
| 602 | case FB_BLANK_NORMAL: | 800 | case FB_BLANK_NORMAL: |
| 603 | /* disable the DMA and display 0x0 (black) */ | 801 | /* disable the DMA and display 0x0 (black) */ |
| 604 | writel(WINxMAP_MAP | WINxMAP_MAP_COLOUR(0x0), | 802 | writel(WINxMAP_MAP | WINxMAP_MAP_COLOUR(0x0), |
| 605 | sfb->regs + WINxMAP(index)); | 803 | sfb->regs + sfb->variant.winmap + (index * 4)); |
| 606 | break; | 804 | break; |
| 607 | 805 | ||
| 608 | case FB_BLANK_UNBLANK: | 806 | case FB_BLANK_UNBLANK: |
| 609 | writel(0x0, sfb->regs + WINxMAP(index)); | 807 | writel(0x0, sfb->regs + sfb->variant.winmap + (index * 4)); |
| 610 | wincon |= WINCONx_ENWIN; | 808 | wincon |= WINCONx_ENWIN; |
| 611 | sfb->enabled |= (1 << index); | 809 | sfb->enabled |= (1 << index); |
| 612 | break; | 810 | break; |
| @@ -617,7 +815,7 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info) | |||
| 617 | return 1; | 815 | return 1; |
| 618 | } | 816 | } |
| 619 | 817 | ||
| 620 | writel(wincon, sfb->regs + WINCON(index)); | 818 | writel(wincon, sfb->regs + sfb->variant.wincon + (index * 4)); |
| 621 | 819 | ||
| 622 | /* Check the enabled state to see if we need to be running the | 820 | /* Check the enabled state to see if we need to be running the |
| 623 | * main LCD interface, as if there are no active windows then | 821 | * main LCD interface, as if there are no active windows then |
| @@ -636,12 +834,185 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info) | |||
| 636 | /* we're stuck with this until we can do something about overriding | 834 | /* we're stuck with this until we can do something about overriding |
| 637 | * the power control using the blanking event for a single fb. | 835 | * the power control using the blanking event for a single fb. |
| 638 | */ | 836 | */ |
| 639 | if (index == 0) | 837 | if (index == sfb->pdata->default_win) |
| 640 | s3c_fb_enable(sfb, blank_mode != FB_BLANK_POWERDOWN ? 1 : 0); | 838 | s3c_fb_enable(sfb, blank_mode != FB_BLANK_POWERDOWN ? 1 : 0); |
| 641 | 839 | ||
| 642 | return 0; | 840 | return 0; |
| 643 | } | 841 | } |
| 644 | 842 | ||
| 843 | /** | ||
| 844 | * s3c_fb_pan_display() - Pan the display. | ||
| 845 | * | ||
| 846 | * Note that the offsets can be written to the device at any time, as their | ||
| 847 | * values are latched at each vsync automatically. This also means that only | ||
| 848 | * the last call to this function will have any effect on next vsync, but | ||
| 849 | * there is no need to sleep waiting for it to prevent tearing. | ||
| 850 | * | ||
| 851 | * @var: The screen information to verify. | ||
| 852 | * @info: The framebuffer device. | ||
| 853 | */ | ||
| 854 | static int s3c_fb_pan_display(struct fb_var_screeninfo *var, | ||
| 855 | struct fb_info *info) | ||
| 856 | { | ||
| 857 | struct s3c_fb_win *win = info->par; | ||
| 858 | struct s3c_fb *sfb = win->parent; | ||
| 859 | void __iomem *buf = sfb->regs + win->index * 8; | ||
| 860 | unsigned int start_boff, end_boff; | ||
| 861 | |||
| 862 | /* Offset in bytes to the start of the displayed area */ | ||
| 863 | start_boff = var->yoffset * info->fix.line_length; | ||
| 864 | /* X offset depends on the current bpp */ | ||
| 865 | if (info->var.bits_per_pixel >= 8) { | ||
| 866 | start_boff += var->xoffset * (info->var.bits_per_pixel >> 3); | ||
| 867 | } else { | ||
| 868 | switch (info->var.bits_per_pixel) { | ||
| 869 | case 4: | ||
| 870 | start_boff += var->xoffset >> 1; | ||
| 871 | break; | ||
| 872 | case 2: | ||
| 873 | start_boff += var->xoffset >> 2; | ||
| 874 | break; | ||
| 875 | case 1: | ||
| 876 | start_boff += var->xoffset >> 3; | ||
| 877 | break; | ||
| 878 | default: | ||
| 879 | dev_err(sfb->dev, "invalid bpp\n"); | ||
| 880 | return -EINVAL; | ||
| 881 | } | ||
| 882 | } | ||
| 883 | /* Offset in bytes to the end of the displayed area */ | ||
| 884 | end_boff = start_boff + var->yres * info->fix.line_length; | ||
| 885 | |||
| 886 | /* Temporarily turn off per-vsync update from shadow registers until | ||
| 887 | * both start and end addresses are updated to prevent corruption */ | ||
| 888 | shadow_protect_win(win, 1); | ||
| 889 | |||
| 890 | writel(info->fix.smem_start + start_boff, buf + sfb->variant.buf_start); | ||
| 891 | writel(info->fix.smem_start + end_boff, buf + sfb->variant.buf_end); | ||
| 892 | |||
| 893 | shadow_protect_win(win, 0); | ||
| 894 | |||
| 895 | return 0; | ||
| 896 | } | ||
| 897 | |||
| 898 | /** | ||
| 899 | * s3c_fb_enable_irq() - enable framebuffer interrupts | ||
| 900 | * @sfb: main hardware state | ||
| 901 | */ | ||
| 902 | static void s3c_fb_enable_irq(struct s3c_fb *sfb) | ||
| 903 | { | ||
| 904 | void __iomem *regs = sfb->regs; | ||
| 905 | u32 irq_ctrl_reg; | ||
| 906 | |||
| 907 | if (!test_and_set_bit(S3C_FB_VSYNC_IRQ_EN, &sfb->irq_flags)) { | ||
| 908 | /* IRQ disabled, enable it */ | ||
| 909 | irq_ctrl_reg = readl(regs + VIDINTCON0); | ||
| 910 | |||
| 911 | irq_ctrl_reg |= VIDINTCON0_INT_ENABLE; | ||
| 912 | irq_ctrl_reg |= VIDINTCON0_INT_FRAME; | ||
| 913 | |||
| 914 | irq_ctrl_reg &= ~VIDINTCON0_FRAMESEL0_MASK; | ||
| 915 | irq_ctrl_reg |= VIDINTCON0_FRAMESEL0_VSYNC; | ||
| 916 | irq_ctrl_reg &= ~VIDINTCON0_FRAMESEL1_MASK; | ||
| 917 | irq_ctrl_reg |= VIDINTCON0_FRAMESEL1_NONE; | ||
| 918 | |||
| 919 | writel(irq_ctrl_reg, regs + VIDINTCON0); | ||
| 920 | } | ||
| 921 | } | ||
| 922 | |||
| 923 | /** | ||
| 924 | * s3c_fb_disable_irq() - disable framebuffer interrupts | ||
| 925 | * @sfb: main hardware state | ||
| 926 | */ | ||
| 927 | static void s3c_fb_disable_irq(struct s3c_fb *sfb) | ||
| 928 | { | ||
| 929 | void __iomem *regs = sfb->regs; | ||
| 930 | u32 irq_ctrl_reg; | ||
| 931 | |||
| 932 | if (test_and_clear_bit(S3C_FB_VSYNC_IRQ_EN, &sfb->irq_flags)) { | ||
| 933 | /* IRQ enabled, disable it */ | ||
| 934 | irq_ctrl_reg = readl(regs + VIDINTCON0); | ||
| 935 | |||
| 936 | irq_ctrl_reg &= ~VIDINTCON0_INT_FRAME; | ||
| 937 | irq_ctrl_reg &= ~VIDINTCON0_INT_ENABLE; | ||
| 938 | |||
| 939 | writel(irq_ctrl_reg, regs + VIDINTCON0); | ||
| 940 | } | ||
| 941 | } | ||
| 942 | |||
| 943 | static irqreturn_t s3c_fb_irq(int irq, void *dev_id) | ||
| 944 | { | ||
| 945 | struct s3c_fb *sfb = dev_id; | ||
| 946 | void __iomem *regs = sfb->regs; | ||
| 947 | u32 irq_sts_reg; | ||
| 948 | |||
| 949 | irq_sts_reg = readl(regs + VIDINTCON1); | ||
| 950 | |||
| 951 | if (irq_sts_reg & VIDINTCON1_INT_FRAME) { | ||
| 952 | |||
| 953 | /* VSYNC interrupt, accept it */ | ||
| 954 | writel(VIDINTCON1_INT_FRAME, regs + VIDINTCON1); | ||
| 955 | |||
| 956 | sfb->vsync_info.count++; | ||
| 957 | wake_up_interruptible(&sfb->vsync_info.wait); | ||
| 958 | } | ||
| 959 | |||
| 960 | /* We only support waiting for VSYNC for now, so it's safe | ||
| 961 | * to always disable irqs here. | ||
| 962 | */ | ||
| 963 | s3c_fb_disable_irq(sfb); | ||
| 964 | |||
| 965 | return IRQ_HANDLED; | ||
| 966 | } | ||
| 967 | |||
| 968 | /** | ||
| 969 | * s3c_fb_wait_for_vsync() - sleep until next VSYNC interrupt or timeout | ||
| 970 | * @sfb: main hardware state | ||
| 971 | * @crtc: head index. | ||
| 972 | */ | ||
| 973 | static int s3c_fb_wait_for_vsync(struct s3c_fb *sfb, u32 crtc) | ||
| 974 | { | ||
| 975 | unsigned long count; | ||
| 976 | int ret; | ||
| 977 | |||
| 978 | if (crtc != 0) | ||
| 979 | return -ENODEV; | ||
| 980 | |||
| 981 | count = sfb->vsync_info.count; | ||
| 982 | s3c_fb_enable_irq(sfb); | ||
| 983 | ret = wait_event_interruptible_timeout(sfb->vsync_info.wait, | ||
| 984 | count != sfb->vsync_info.count, | ||
| 985 | msecs_to_jiffies(VSYNC_TIMEOUT_MSEC)); | ||
| 986 | if (ret == 0) | ||
| 987 | return -ETIMEDOUT; | ||
| 988 | |||
| 989 | return 0; | ||
| 990 | } | ||
| 991 | |||
| 992 | static int s3c_fb_ioctl(struct fb_info *info, unsigned int cmd, | ||
| 993 | unsigned long arg) | ||
| 994 | { | ||
| 995 | struct s3c_fb_win *win = info->par; | ||
| 996 | struct s3c_fb *sfb = win->parent; | ||
| 997 | int ret; | ||
| 998 | u32 crtc; | ||
| 999 | |||
| 1000 | switch (cmd) { | ||
| 1001 | case FBIO_WAITFORVSYNC: | ||
| 1002 | if (get_user(crtc, (u32 __user *)arg)) { | ||
| 1003 | ret = -EFAULT; | ||
| 1004 | break; | ||
| 1005 | } | ||
| 1006 | |||
| 1007 | ret = s3c_fb_wait_for_vsync(sfb, crtc); | ||
| 1008 | break; | ||
| 1009 | default: | ||
| 1010 | ret = -ENOTTY; | ||
| 1011 | } | ||
| 1012 | |||
| 1013 | return ret; | ||
| 1014 | } | ||
| 1015 | |||
| 645 | static struct fb_ops s3c_fb_ops = { | 1016 | static struct fb_ops s3c_fb_ops = { |
| 646 | .owner = THIS_MODULE, | 1017 | .owner = THIS_MODULE, |
| 647 | .fb_check_var = s3c_fb_check_var, | 1018 | .fb_check_var = s3c_fb_check_var, |
| @@ -651,9 +1022,33 @@ static struct fb_ops s3c_fb_ops = { | |||
| 651 | .fb_fillrect = cfb_fillrect, | 1022 | .fb_fillrect = cfb_fillrect, |
| 652 | .fb_copyarea = cfb_copyarea, | 1023 | .fb_copyarea = cfb_copyarea, |
| 653 | .fb_imageblit = cfb_imageblit, | 1024 | .fb_imageblit = cfb_imageblit, |
| 1025 | .fb_pan_display = s3c_fb_pan_display, | ||
| 1026 | .fb_ioctl = s3c_fb_ioctl, | ||
| 654 | }; | 1027 | }; |
| 655 | 1028 | ||
| 656 | /** | 1029 | /** |
| 1030 | * s3c_fb_missing_pixclock() - calculates pixel clock | ||
| 1031 | * @mode: The video mode to change. | ||
| 1032 | * | ||
| 1033 | * Calculate the pixel clock when none has been given through platform data. | ||
| 1034 | */ | ||
| 1035 | static void __devinit s3c_fb_missing_pixclock(struct fb_videomode *mode) | ||
| 1036 | { | ||
| 1037 | u64 pixclk = 1000000000000ULL; | ||
| 1038 | u32 div; | ||
| 1039 | |||
| 1040 | div = mode->left_margin + mode->hsync_len + mode->right_margin + | ||
| 1041 | mode->xres; | ||
| 1042 | div *= mode->upper_margin + mode->vsync_len + mode->lower_margin + | ||
| 1043 | mode->yres; | ||
| 1044 | div *= mode->refresh ? : 60; | ||
| 1045 | |||
| 1046 | do_div(pixclk, div); | ||
| 1047 | |||
| 1048 | mode->pixclock = pixclk; | ||
| 1049 | } | ||
| 1050 | |||
| 1051 | /** | ||
| 657 | * s3c_fb_alloc_memory() - allocate display memory for framebuffer window | 1052 | * s3c_fb_alloc_memory() - allocate display memory for framebuffer window |
| 658 | * @sfb: The base resources for the hardware. | 1053 | * @sfb: The base resources for the hardware. |
| 659 | * @win: The window to initialise memory for. | 1054 | * @win: The window to initialise memory for. |
| @@ -711,7 +1106,8 @@ static void s3c_fb_free_memory(struct s3c_fb *sfb, struct s3c_fb_win *win) | |||
| 711 | { | 1106 | { |
| 712 | struct fb_info *fbi = win->fbinfo; | 1107 | struct fb_info *fbi = win->fbinfo; |
| 713 | 1108 | ||
| 714 | dma_free_writecombine(sfb->dev, PAGE_ALIGN(fbi->fix.smem_len), | 1109 | if (fbi->screen_base) |
| 1110 | dma_free_writecombine(sfb->dev, PAGE_ALIGN(fbi->fix.smem_len), | ||
| 715 | fbi->screen_base, fbi->fix.smem_start); | 1111 | fbi->screen_base, fbi->fix.smem_start); |
| 716 | } | 1112 | } |
| 717 | 1113 | ||
| @@ -724,9 +1120,18 @@ static void s3c_fb_free_memory(struct s3c_fb *sfb, struct s3c_fb_win *win) | |||
| 724 | */ | 1120 | */ |
| 725 | static void s3c_fb_release_win(struct s3c_fb *sfb, struct s3c_fb_win *win) | 1121 | static void s3c_fb_release_win(struct s3c_fb *sfb, struct s3c_fb_win *win) |
| 726 | { | 1122 | { |
| 1123 | u32 data; | ||
| 1124 | |||
| 727 | if (win->fbinfo) { | 1125 | if (win->fbinfo) { |
| 1126 | if (sfb->variant.has_shadowcon) { | ||
| 1127 | data = readl(sfb->regs + SHADOWCON); | ||
| 1128 | data &= ~SHADOWCON_CHx_ENABLE(win->index); | ||
| 1129 | data &= ~SHADOWCON_CHx_LOCAL_ENABLE(win->index); | ||
| 1130 | writel(data, sfb->regs + SHADOWCON); | ||
| 1131 | } | ||
| 728 | unregister_framebuffer(win->fbinfo); | 1132 | unregister_framebuffer(win->fbinfo); |
| 729 | fb_dealloc_cmap(&win->fbinfo->cmap); | 1133 | if (win->fbinfo->cmap.len) |
| 1134 | fb_dealloc_cmap(&win->fbinfo->cmap); | ||
| 730 | s3c_fb_free_memory(sfb, win); | 1135 | s3c_fb_free_memory(sfb, win); |
| 731 | framebuffer_release(win->fbinfo); | 1136 | framebuffer_release(win->fbinfo); |
| 732 | } | 1137 | } |
| @@ -735,12 +1140,14 @@ static void s3c_fb_release_win(struct s3c_fb *sfb, struct s3c_fb_win *win) | |||
| 735 | /** | 1140 | /** |
| 736 | * s3c_fb_probe_win() - register an hardware window | 1141 | * s3c_fb_probe_win() - register an hardware window |
| 737 | * @sfb: The base resources for the hardware | 1142 | * @sfb: The base resources for the hardware |
| 1143 | * @variant: The variant information for this window. | ||
| 738 | * @res: Pointer to where to place the resultant window. | 1144 | * @res: Pointer to where to place the resultant window. |
| 739 | * | 1145 | * |
| 740 | * Allocate and do the basic initialisation for one of the hardware's graphics | 1146 | * Allocate and do the basic initialisation for one of the hardware's graphics |
| 741 | * windows. | 1147 | * windows. |
| 742 | */ | 1148 | */ |
| 743 | static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, | 1149 | static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, |
| 1150 | struct s3c_fb_win_variant *variant, | ||
| 744 | struct s3c_fb_win **res) | 1151 | struct s3c_fb_win **res) |
| 745 | { | 1152 | { |
| 746 | struct fb_var_screeninfo *var; | 1153 | struct fb_var_screeninfo *var; |
| @@ -751,9 +1158,11 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, | |||
| 751 | int palette_size; | 1158 | int palette_size; |
| 752 | int ret; | 1159 | int ret; |
| 753 | 1160 | ||
| 754 | dev_dbg(sfb->dev, "probing window %d\n", win_no); | 1161 | dev_dbg(sfb->dev, "probing window %d, variant %p\n", win_no, variant); |
| 1162 | |||
| 1163 | init_waitqueue_head(&sfb->vsync_info.wait); | ||
| 755 | 1164 | ||
| 756 | palette_size = s3c_fb_win_pal_size(win_no); | 1165 | palette_size = variant->palette_sz * 4; |
| 757 | 1166 | ||
| 758 | fbinfo = framebuffer_alloc(sizeof(struct s3c_fb_win) + | 1167 | fbinfo = framebuffer_alloc(sizeof(struct s3c_fb_win) + |
| 759 | palette_size * sizeof(u32), sfb->dev); | 1168 | palette_size * sizeof(u32), sfb->dev); |
| @@ -770,7 +1179,9 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, | |||
| 770 | WARN_ON(windata->win_mode.yres == 0); | 1179 | WARN_ON(windata->win_mode.yres == 0); |
| 771 | 1180 | ||
| 772 | win = fbinfo->par; | 1181 | win = fbinfo->par; |
| 1182 | *res = win; | ||
| 773 | var = &fbinfo->var; | 1183 | var = &fbinfo->var; |
| 1184 | win->variant = *variant; | ||
| 774 | win->fbinfo = fbinfo; | 1185 | win->fbinfo = fbinfo; |
| 775 | win->parent = sfb; | 1186 | win->parent = sfb; |
| 776 | win->windata = windata; | 1187 | win->windata = windata; |
| @@ -784,7 +1195,24 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, | |||
| 784 | } | 1195 | } |
| 785 | 1196 | ||
| 786 | /* setup the r/b/g positions for the window's palette */ | 1197 | /* setup the r/b/g positions for the window's palette */ |
| 787 | s3c_fb_init_palette(win_no, &win->palette); | 1198 | if (win->variant.palette_16bpp) { |
| 1199 | /* Set RGB 5:6:5 as default */ | ||
| 1200 | win->palette.r.offset = 11; | ||
| 1201 | win->palette.r.length = 5; | ||
| 1202 | win->palette.g.offset = 5; | ||
| 1203 | win->palette.g.length = 6; | ||
| 1204 | win->palette.b.offset = 0; | ||
| 1205 | win->palette.b.length = 5; | ||
| 1206 | |||
| 1207 | } else { | ||
| 1208 | /* Set 8bpp or 8bpp and 1bit alpha */ | ||
| 1209 | win->palette.r.offset = 16; | ||
| 1210 | win->palette.r.length = 8; | ||
| 1211 | win->palette.g.offset = 8; | ||
| 1212 | win->palette.g.length = 8; | ||
| 1213 | win->palette.b.offset = 0; | ||
| 1214 | win->palette.b.length = 8; | ||
| 1215 | } | ||
| 788 | 1216 | ||
| 789 | /* setup the initial video mode from the window */ | 1217 | /* setup the initial video mode from the window */ |
| 790 | fb_videomode_to_var(&fbinfo->var, initmode); | 1218 | fb_videomode_to_var(&fbinfo->var, initmode); |
| @@ -808,7 +1236,7 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, | |||
| 808 | 1236 | ||
| 809 | /* create initial colour map */ | 1237 | /* create initial colour map */ |
| 810 | 1238 | ||
| 811 | ret = fb_alloc_cmap(&fbinfo->cmap, s3c_fb_win_pal_size(win_no), 1); | 1239 | ret = fb_alloc_cmap(&fbinfo->cmap, win->variant.palette_sz, 1); |
| 812 | if (ret == 0) | 1240 | if (ret == 0) |
| 813 | fb_set_cmap(&fbinfo->cmap, fbinfo); | 1241 | fb_set_cmap(&fbinfo->cmap, fbinfo); |
| 814 | else | 1242 | else |
| @@ -826,7 +1254,6 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, | |||
| 826 | return ret; | 1254 | return ret; |
| 827 | } | 1255 | } |
| 828 | 1256 | ||
| 829 | *res = win; | ||
| 830 | dev_info(sfb->dev, "window %d: fb %s\n", win_no, fbinfo->fix.id); | 1257 | dev_info(sfb->dev, "window %d: fb %s\n", win_no, fbinfo->fix.id); |
| 831 | 1258 | ||
| 832 | return 0; | 1259 | return 0; |
| @@ -842,18 +1269,19 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, | |||
| 842 | static void s3c_fb_clear_win(struct s3c_fb *sfb, int win) | 1269 | static void s3c_fb_clear_win(struct s3c_fb *sfb, int win) |
| 843 | { | 1270 | { |
| 844 | void __iomem *regs = sfb->regs; | 1271 | void __iomem *regs = sfb->regs; |
| 845 | 1272 | u32 reg; | |
| 846 | writel(0, regs + WINCON(win)); | 1273 | |
| 847 | writel(0xffffff, regs + WxKEYCONy(win, 0)); | 1274 | writel(0, regs + sfb->variant.wincon + (win * 4)); |
| 848 | writel(0xffffff, regs + WxKEYCONy(win, 1)); | 1275 | writel(0, regs + VIDOSD_A(win, sfb->variant)); |
| 849 | 1276 | writel(0, regs + VIDOSD_B(win, sfb->variant)); | |
| 850 | writel(0, regs + VIDOSD_A(win)); | 1277 | writel(0, regs + VIDOSD_C(win, sfb->variant)); |
| 851 | writel(0, regs + VIDOSD_B(win)); | 1278 | reg = readl(regs + SHADOWCON); |
| 852 | writel(0, regs + VIDOSD_C(win)); | 1279 | writel(reg & ~SHADOWCON_WINx_PROTECT(win), regs + SHADOWCON); |
| 853 | } | 1280 | } |
| 854 | 1281 | ||
| 855 | static int __devinit s3c_fb_probe(struct platform_device *pdev) | 1282 | static int __devinit s3c_fb_probe(struct platform_device *pdev) |
| 856 | { | 1283 | { |
| 1284 | struct s3c_fb_driverdata *fbdrv; | ||
| 857 | struct device *dev = &pdev->dev; | 1285 | struct device *dev = &pdev->dev; |
| 858 | struct s3c_fb_platdata *pd; | 1286 | struct s3c_fb_platdata *pd; |
| 859 | struct s3c_fb *sfb; | 1287 | struct s3c_fb *sfb; |
| @@ -861,6 +1289,13 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) | |||
| 861 | int win; | 1289 | int win; |
| 862 | int ret = 0; | 1290 | int ret = 0; |
| 863 | 1291 | ||
| 1292 | fbdrv = (struct s3c_fb_driverdata *)platform_get_device_id(pdev)->driver_data; | ||
| 1293 | |||
| 1294 | if (fbdrv->variant.nr_windows > S3C_FB_MAX_WIN) { | ||
| 1295 | dev_err(dev, "too many windows, cannot attach\n"); | ||
| 1296 | return -EINVAL; | ||
| 1297 | } | ||
| 1298 | |||
| 864 | pd = pdev->dev.platform_data; | 1299 | pd = pdev->dev.platform_data; |
| 865 | if (!pd) { | 1300 | if (!pd) { |
| 866 | dev_err(dev, "no platform data specified\n"); | 1301 | dev_err(dev, "no platform data specified\n"); |
| @@ -873,8 +1308,11 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) | |||
| 873 | return -ENOMEM; | 1308 | return -ENOMEM; |
| 874 | } | 1309 | } |
| 875 | 1310 | ||
| 1311 | dev_dbg(dev, "allocate new framebuffer %p\n", sfb); | ||
| 1312 | |||
| 876 | sfb->dev = dev; | 1313 | sfb->dev = dev; |
| 877 | sfb->pdata = pd; | 1314 | sfb->pdata = pd; |
| 1315 | sfb->variant = fbdrv->variant; | ||
| 878 | 1316 | ||
| 879 | sfb->bus_clk = clk_get(dev, "lcd"); | 1317 | sfb->bus_clk = clk_get(dev, "lcd"); |
| 880 | if (IS_ERR(sfb->bus_clk)) { | 1318 | if (IS_ERR(sfb->bus_clk)) { |
| @@ -906,6 +1344,20 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) | |||
| 906 | goto err_req_region; | 1344 | goto err_req_region; |
| 907 | } | 1345 | } |
| 908 | 1346 | ||
| 1347 | res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | ||
| 1348 | if (!res) { | ||
| 1349 | dev_err(dev, "failed to acquire irq resource\n"); | ||
| 1350 | ret = -ENOENT; | ||
| 1351 | goto err_ioremap; | ||
| 1352 | } | ||
| 1353 | sfb->irq_no = res->start; | ||
| 1354 | ret = request_irq(sfb->irq_no, s3c_fb_irq, | ||
| 1355 | 0, "s3c_fb", sfb); | ||
| 1356 | if (ret) { | ||
| 1357 | dev_err(dev, "irq request failed\n"); | ||
| 1358 | goto err_ioremap; | ||
| 1359 | } | ||
| 1360 | |||
| 909 | dev_dbg(dev, "got resources (regs %p), probing windows\n", sfb->regs); | 1361 | dev_dbg(dev, "got resources (regs %p), probing windows\n", sfb->regs); |
| 910 | 1362 | ||
| 911 | /* setup gpio and output polarity controls */ | 1363 | /* setup gpio and output polarity controls */ |
| @@ -916,21 +1368,34 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) | |||
| 916 | 1368 | ||
| 917 | /* zero all windows before we do anything */ | 1369 | /* zero all windows before we do anything */ |
| 918 | 1370 | ||
| 919 | for (win = 0; win < S3C_FB_MAX_WIN; win++) | 1371 | for (win = 0; win < fbdrv->variant.nr_windows; win++) |
| 920 | s3c_fb_clear_win(sfb, win); | 1372 | s3c_fb_clear_win(sfb, win); |
| 921 | 1373 | ||
| 1374 | /* initialise colour key controls */ | ||
| 1375 | for (win = 0; win < (fbdrv->variant.nr_windows - 1); win++) { | ||
| 1376 | void __iomem *regs = sfb->regs + sfb->variant.keycon; | ||
| 1377 | |||
| 1378 | regs += (win * 8); | ||
| 1379 | writel(0xffffff, regs + WKEYCON0); | ||
| 1380 | writel(0xffffff, regs + WKEYCON1); | ||
| 1381 | } | ||
| 1382 | |||
| 922 | /* we have the register setup, start allocating framebuffers */ | 1383 | /* we have the register setup, start allocating framebuffers */ |
| 923 | 1384 | ||
| 924 | for (win = 0; win < S3C_FB_MAX_WIN; win++) { | 1385 | for (win = 0; win < fbdrv->variant.nr_windows; win++) { |
| 925 | if (!pd->win[win]) | 1386 | if (!pd->win[win]) |
| 926 | continue; | 1387 | continue; |
| 927 | 1388 | ||
| 928 | ret = s3c_fb_probe_win(sfb, win, &sfb->windows[win]); | 1389 | if (!pd->win[win]->win_mode.pixclock) |
| 1390 | s3c_fb_missing_pixclock(&pd->win[win]->win_mode); | ||
| 1391 | |||
| 1392 | ret = s3c_fb_probe_win(sfb, win, fbdrv->win[win], | ||
| 1393 | &sfb->windows[win]); | ||
| 929 | if (ret < 0) { | 1394 | if (ret < 0) { |
| 930 | dev_err(dev, "failed to create window %d\n", win); | 1395 | dev_err(dev, "failed to create window %d\n", win); |
| 931 | for (; win >= 0; win--) | 1396 | for (; win >= 0; win--) |
| 932 | s3c_fb_release_win(sfb, sfb->windows[win]); | 1397 | s3c_fb_release_win(sfb, sfb->windows[win]); |
| 933 | goto err_ioremap; | 1398 | goto err_irq; |
| 934 | } | 1399 | } |
| 935 | } | 1400 | } |
| 936 | 1401 | ||
| @@ -938,6 +1403,9 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) | |||
| 938 | 1403 | ||
| 939 | return 0; | 1404 | return 0; |
| 940 | 1405 | ||
| 1406 | err_irq: | ||
| 1407 | free_irq(sfb->irq_no, sfb); | ||
| 1408 | |||
| 941 | err_ioremap: | 1409 | err_ioremap: |
| 942 | iounmap(sfb->regs); | 1410 | iounmap(sfb->regs); |
| 943 | 1411 | ||
| @@ -970,6 +1438,8 @@ static int __devexit s3c_fb_remove(struct platform_device *pdev) | |||
| 970 | if (sfb->windows[win]) | 1438 | if (sfb->windows[win]) |
| 971 | s3c_fb_release_win(sfb, sfb->windows[win]); | 1439 | s3c_fb_release_win(sfb, sfb->windows[win]); |
| 972 | 1440 | ||
| 1441 | free_irq(sfb->irq_no, sfb); | ||
| 1442 | |||
| 973 | iounmap(sfb->regs); | 1443 | iounmap(sfb->regs); |
| 974 | 1444 | ||
| 975 | clk_disable(sfb->bus_clk); | 1445 | clk_disable(sfb->bus_clk); |
| @@ -1016,9 +1486,17 @@ static int s3c_fb_resume(struct platform_device *pdev) | |||
| 1016 | writel(pd->vidcon1, sfb->regs + VIDCON1); | 1486 | writel(pd->vidcon1, sfb->regs + VIDCON1); |
| 1017 | 1487 | ||
| 1018 | /* zero all windows before we do anything */ | 1488 | /* zero all windows before we do anything */ |
| 1019 | for (win_no = 0; win_no < S3C_FB_MAX_WIN; win_no++) | 1489 | for (win_no = 0; win_no < sfb->variant.nr_windows; win_no++) |
| 1020 | s3c_fb_clear_win(sfb, win_no); | 1490 | s3c_fb_clear_win(sfb, win_no); |
| 1021 | 1491 | ||
| 1492 | for (win_no = 0; win_no < sfb->variant.nr_windows - 1; win_no++) { | ||
| 1493 | void __iomem *regs = sfb->regs + sfb->variant.keycon; | ||
| 1494 | |||
| 1495 | regs += (win_no * 8); | ||
| 1496 | writel(0xffffff, regs + WKEYCON0); | ||
| 1497 | writel(0xffffff, regs + WKEYCON1); | ||
| 1498 | } | ||
| 1499 | |||
| 1022 | /* restore framebuffers */ | 1500 | /* restore framebuffers */ |
| 1023 | for (win_no = 0; win_no < S3C_FB_MAX_WIN; win_no++) { | 1501 | for (win_no = 0; win_no < S3C_FB_MAX_WIN; win_no++) { |
| 1024 | win = sfb->windows[win_no]; | 1502 | win = sfb->windows[win_no]; |
| @@ -1036,11 +1514,208 @@ static int s3c_fb_resume(struct platform_device *pdev) | |||
| 1036 | #define s3c_fb_resume NULL | 1514 | #define s3c_fb_resume NULL |
| 1037 | #endif | 1515 | #endif |
| 1038 | 1516 | ||
| 1517 | |||
| 1518 | #define VALID_BPP124 (VALID_BPP(1) | VALID_BPP(2) | VALID_BPP(4)) | ||
| 1519 | #define VALID_BPP1248 (VALID_BPP124 | VALID_BPP(8)) | ||
| 1520 | |||
| 1521 | static struct s3c_fb_win_variant s3c_fb_data_64xx_wins[] = { | ||
| 1522 | [0] = { | ||
| 1523 | .has_osd_c = 1, | ||
| 1524 | .osd_size_off = 0x8, | ||
| 1525 | .palette_sz = 256, | ||
| 1526 | .valid_bpp = VALID_BPP1248 | VALID_BPP(16) | VALID_BPP(24), | ||
| 1527 | }, | ||
| 1528 | [1] = { | ||
| 1529 | .has_osd_c = 1, | ||
| 1530 | .has_osd_d = 1, | ||
| 1531 | .osd_size_off = 0x12, | ||
| 1532 | .has_osd_alpha = 1, | ||
| 1533 | .palette_sz = 256, | ||
| 1534 | .valid_bpp = (VALID_BPP1248 | VALID_BPP(16) | | ||
| 1535 | VALID_BPP(18) | VALID_BPP(19) | | ||
| 1536 | VALID_BPP(24) | VALID_BPP(25)), | ||
| 1537 | }, | ||
| 1538 | [2] = { | ||
| 1539 | .has_osd_c = 1, | ||
| 1540 | .has_osd_d = 1, | ||
| 1541 | .osd_size_off = 0x12, | ||
| 1542 | .has_osd_alpha = 1, | ||
| 1543 | .palette_sz = 16, | ||
| 1544 | .palette_16bpp = 1, | ||
| 1545 | .valid_bpp = (VALID_BPP1248 | VALID_BPP(16) | | ||
| 1546 | VALID_BPP(18) | VALID_BPP(19) | | ||
| 1547 | VALID_BPP(24) | VALID_BPP(25)), | ||
| 1548 | }, | ||
| 1549 | [3] = { | ||
| 1550 | .has_osd_c = 1, | ||
| 1551 | .has_osd_alpha = 1, | ||
| 1552 | .palette_sz = 16, | ||
| 1553 | .palette_16bpp = 1, | ||
| 1554 | .valid_bpp = (VALID_BPP124 | VALID_BPP(16) | | ||
| 1555 | VALID_BPP(18) | VALID_BPP(19) | | ||
| 1556 | VALID_BPP(24) | VALID_BPP(25)), | ||
| 1557 | }, | ||
| 1558 | [4] = { | ||
| 1559 | .has_osd_c = 1, | ||
| 1560 | .has_osd_alpha = 1, | ||
| 1561 | .palette_sz = 4, | ||
| 1562 | .palette_16bpp = 1, | ||
| 1563 | .valid_bpp = (VALID_BPP(1) | VALID_BPP(2) | | ||
| 1564 | VALID_BPP(16) | VALID_BPP(18) | | ||
| 1565 | VALID_BPP(24) | VALID_BPP(25)), | ||
| 1566 | }, | ||
| 1567 | }; | ||
| 1568 | |||
| 1569 | static struct s3c_fb_driverdata s3c_fb_data_64xx = { | ||
| 1570 | .variant = { | ||
| 1571 | .nr_windows = 5, | ||
| 1572 | .vidtcon = VIDTCON0, | ||
| 1573 | .wincon = WINCON(0), | ||
| 1574 | .winmap = WINxMAP(0), | ||
| 1575 | .keycon = WKEYCON, | ||
| 1576 | .osd = VIDOSD_BASE, | ||
| 1577 | .osd_stride = 16, | ||
| 1578 | .buf_start = VIDW_BUF_START(0), | ||
| 1579 | .buf_size = VIDW_BUF_SIZE(0), | ||
| 1580 | .buf_end = VIDW_BUF_END(0), | ||
| 1581 | |||
| 1582 | .palette = { | ||
| 1583 | [0] = 0x400, | ||
| 1584 | [1] = 0x800, | ||
| 1585 | [2] = 0x300, | ||
| 1586 | [3] = 0x320, | ||
| 1587 | [4] = 0x340, | ||
| 1588 | }, | ||
| 1589 | |||
| 1590 | .has_prtcon = 1, | ||
| 1591 | }, | ||
| 1592 | .win[0] = &s3c_fb_data_64xx_wins[0], | ||
| 1593 | .win[1] = &s3c_fb_data_64xx_wins[1], | ||
| 1594 | .win[2] = &s3c_fb_data_64xx_wins[2], | ||
| 1595 | .win[3] = &s3c_fb_data_64xx_wins[3], | ||
| 1596 | .win[4] = &s3c_fb_data_64xx_wins[4], | ||
| 1597 | }; | ||
| 1598 | |||
| 1599 | static struct s3c_fb_driverdata s3c_fb_data_s5pc100 = { | ||
| 1600 | .variant = { | ||
| 1601 | .nr_windows = 5, | ||
| 1602 | .vidtcon = VIDTCON0, | ||
| 1603 | .wincon = WINCON(0), | ||
| 1604 | .winmap = WINxMAP(0), | ||
| 1605 | .keycon = WKEYCON, | ||
| 1606 | .osd = VIDOSD_BASE, | ||
| 1607 | .osd_stride = 16, | ||
| 1608 | .buf_start = VIDW_BUF_START(0), | ||
| 1609 | .buf_size = VIDW_BUF_SIZE(0), | ||
| 1610 | .buf_end = VIDW_BUF_END(0), | ||
| 1611 | |||
| 1612 | .palette = { | ||
| 1613 | [0] = 0x2400, | ||
| 1614 | [1] = 0x2800, | ||
| 1615 | [2] = 0x2c00, | ||
| 1616 | [3] = 0x3000, | ||
| 1617 | [4] = 0x3400, | ||
| 1618 | }, | ||
| 1619 | |||
| 1620 | .has_prtcon = 1, | ||
| 1621 | }, | ||
| 1622 | .win[0] = &s3c_fb_data_64xx_wins[0], | ||
| 1623 | .win[1] = &s3c_fb_data_64xx_wins[1], | ||
| 1624 | .win[2] = &s3c_fb_data_64xx_wins[2], | ||
| 1625 | .win[3] = &s3c_fb_data_64xx_wins[3], | ||
| 1626 | .win[4] = &s3c_fb_data_64xx_wins[4], | ||
| 1627 | }; | ||
| 1628 | |||
| 1629 | static struct s3c_fb_driverdata s3c_fb_data_s5pv210 = { | ||
| 1630 | .variant = { | ||
| 1631 | .nr_windows = 5, | ||
| 1632 | .vidtcon = VIDTCON0, | ||
| 1633 | .wincon = WINCON(0), | ||
| 1634 | .winmap = WINxMAP(0), | ||
| 1635 | .keycon = WKEYCON, | ||
| 1636 | .osd = VIDOSD_BASE, | ||
| 1637 | .osd_stride = 16, | ||
| 1638 | .buf_start = VIDW_BUF_START(0), | ||
| 1639 | .buf_size = VIDW_BUF_SIZE(0), | ||
| 1640 | .buf_end = VIDW_BUF_END(0), | ||
| 1641 | |||
| 1642 | .palette = { | ||
| 1643 | [0] = 0x2400, | ||
| 1644 | [1] = 0x2800, | ||
| 1645 | [2] = 0x2c00, | ||
| 1646 | [3] = 0x3000, | ||
| 1647 | [4] = 0x3400, | ||
| 1648 | }, | ||
| 1649 | |||
| 1650 | .has_shadowcon = 1, | ||
| 1651 | }, | ||
| 1652 | .win[0] = &s3c_fb_data_64xx_wins[0], | ||
| 1653 | .win[1] = &s3c_fb_data_64xx_wins[1], | ||
| 1654 | .win[2] = &s3c_fb_data_64xx_wins[2], | ||
| 1655 | .win[3] = &s3c_fb_data_64xx_wins[3], | ||
| 1656 | .win[4] = &s3c_fb_data_64xx_wins[4], | ||
| 1657 | }; | ||
| 1658 | |||
| 1659 | /* S3C2443/S3C2416 style hardware */ | ||
| 1660 | static struct s3c_fb_driverdata s3c_fb_data_s3c2443 = { | ||
| 1661 | .variant = { | ||
| 1662 | .nr_windows = 2, | ||
| 1663 | .is_2443 = 1, | ||
| 1664 | |||
| 1665 | .vidtcon = 0x08, | ||
| 1666 | .wincon = 0x14, | ||
| 1667 | .winmap = 0xd0, | ||
| 1668 | .keycon = 0xb0, | ||
| 1669 | .osd = 0x28, | ||
| 1670 | .osd_stride = 12, | ||
| 1671 | .buf_start = 0x64, | ||
| 1672 | .buf_size = 0x94, | ||
| 1673 | .buf_end = 0x7c, | ||
| 1674 | |||
| 1675 | .palette = { | ||
| 1676 | [0] = 0x400, | ||
| 1677 | [1] = 0x800, | ||
| 1678 | }, | ||
| 1679 | }, | ||
| 1680 | .win[0] = &(struct s3c_fb_win_variant) { | ||
| 1681 | .palette_sz = 256, | ||
| 1682 | .valid_bpp = VALID_BPP1248 | VALID_BPP(16) | VALID_BPP(24), | ||
| 1683 | }, | ||
| 1684 | .win[1] = &(struct s3c_fb_win_variant) { | ||
| 1685 | .has_osd_c = 1, | ||
| 1686 | .has_osd_alpha = 1, | ||
| 1687 | .palette_sz = 256, | ||
| 1688 | .valid_bpp = (VALID_BPP1248 | VALID_BPP(16) | | ||
| 1689 | VALID_BPP(18) | VALID_BPP(19) | | ||
| 1690 | VALID_BPP(24) | VALID_BPP(25) | | ||
| 1691 | VALID_BPP(28)), | ||
| 1692 | }, | ||
| 1693 | }; | ||
| 1694 | |||
| 1695 | static struct platform_device_id s3c_fb_driver_ids[] = { | ||
| 1696 | { | ||
| 1697 | .name = "s3c-fb", | ||
| 1698 | .driver_data = (unsigned long)&s3c_fb_data_64xx, | ||
| 1699 | }, { | ||
| 1700 | .name = "s5pc100-fb", | ||
| 1701 | .driver_data = (unsigned long)&s3c_fb_data_s5pc100, | ||
| 1702 | }, { | ||
| 1703 | .name = "s5pv210-fb", | ||
| 1704 | .driver_data = (unsigned long)&s3c_fb_data_s5pv210, | ||
| 1705 | }, { | ||
| 1706 | .name = "s3c2443-fb", | ||
| 1707 | .driver_data = (unsigned long)&s3c_fb_data_s3c2443, | ||
| 1708 | }, | ||
| 1709 | {}, | ||
| 1710 | }; | ||
| 1711 | MODULE_DEVICE_TABLE(platform, s3c_fb_driver_ids); | ||
| 1712 | |||
| 1039 | static struct platform_driver s3c_fb_driver = { | 1713 | static struct platform_driver s3c_fb_driver = { |
| 1040 | .probe = s3c_fb_probe, | 1714 | .probe = s3c_fb_probe, |
| 1041 | .remove = __devexit_p(s3c_fb_remove), | 1715 | .remove = __devexit_p(s3c_fb_remove), |
| 1042 | .suspend = s3c_fb_suspend, | 1716 | .suspend = s3c_fb_suspend, |
| 1043 | .resume = s3c_fb_resume, | 1717 | .resume = s3c_fb_resume, |
| 1718 | .id_table = s3c_fb_driver_ids, | ||
| 1044 | .driver = { | 1719 | .driver = { |
| 1045 | .name = "s3c-fb", | 1720 | .name = "s3c-fb", |
| 1046 | .owner = THIS_MODULE, | 1721 | .owner = THIS_MODULE, |
diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c new file mode 100644 index 000000000000..5699ce0c1780 --- /dev/null +++ b/drivers/video/sh_mipi_dsi.c | |||
| @@ -0,0 +1,505 @@ | |||
| 1 | /* | ||
| 2 | * Renesas SH-mobile MIPI DSI support | ||
| 3 | * | ||
| 4 | * Copyright (C) 2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de> | ||
| 5 | * | ||
| 6 | * This is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of version 2 of the GNU General Public License as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include <linux/clk.h> | ||
| 12 | #include <linux/delay.h> | ||
| 13 | #include <linux/init.h> | ||
| 14 | #include <linux/io.h> | ||
| 15 | #include <linux/platform_device.h> | ||
| 16 | #include <linux/slab.h> | ||
| 17 | #include <linux/string.h> | ||
| 18 | #include <linux/types.h> | ||
| 19 | |||
| 20 | #include <video/mipi_display.h> | ||
| 21 | #include <video/sh_mipi_dsi.h> | ||
| 22 | #include <video/sh_mobile_lcdc.h> | ||
| 23 | |||
| 24 | #define CMTSRTCTR 0x80d0 | ||
| 25 | #define CMTSRTREQ 0x8070 | ||
| 26 | |||
| 27 | #define DSIINTE 0x0060 | ||
| 28 | |||
| 29 | /* E.g., sh7372 has 2 MIPI-DSIs - one for each LCDC */ | ||
| 30 | #define MAX_SH_MIPI_DSI 2 | ||
| 31 | |||
| 32 | struct sh_mipi { | ||
| 33 | void __iomem *base; | ||
| 34 | struct clk *dsit_clk; | ||
| 35 | struct clk *dsip_clk; | ||
| 36 | }; | ||
| 37 | |||
| 38 | static struct sh_mipi *mipi_dsi[MAX_SH_MIPI_DSI]; | ||
| 39 | |||
| 40 | /* Protect the above array */ | ||
| 41 | static DEFINE_MUTEX(array_lock); | ||
| 42 | |||
| 43 | static struct sh_mipi *sh_mipi_by_handle(int handle) | ||
| 44 | { | ||
| 45 | if (handle >= ARRAY_SIZE(mipi_dsi) || handle < 0) | ||
| 46 | return NULL; | ||
| 47 | |||
| 48 | return mipi_dsi[handle]; | ||
| 49 | } | ||
| 50 | |||
| 51 | static int sh_mipi_send_short(struct sh_mipi *mipi, u8 dsi_cmd, | ||
| 52 | u8 cmd, u8 param) | ||
| 53 | { | ||
| 54 | u32 data = (dsi_cmd << 24) | (cmd << 16) | (param << 8); | ||
| 55 | int cnt = 100; | ||
| 56 | |||
| 57 | /* transmit a short packet to LCD panel */ | ||
| 58 | iowrite32(1 | data, mipi->base + 0x80d0); /* CMTSRTCTR */ | ||
| 59 | iowrite32(1, mipi->base + 0x8070); /* CMTSRTREQ */ | ||
| 60 | |||
| 61 | while ((ioread32(mipi->base + 0x8070) & 1) && --cnt) | ||
| 62 | udelay(1); | ||
| 63 | |||
| 64 | return cnt ? 0 : -ETIMEDOUT; | ||
| 65 | } | ||
| 66 | |||
| 67 | #define LCD_CHAN2MIPI(c) ((c) < LCDC_CHAN_MAINLCD || (c) > LCDC_CHAN_SUBLCD ? \ | ||
| 68 | -EINVAL : (c) - 1) | ||
| 69 | |||
| 70 | static int sh_mipi_dcs(int handle, u8 cmd) | ||
| 71 | { | ||
| 72 | struct sh_mipi *mipi = sh_mipi_by_handle(LCD_CHAN2MIPI(handle)); | ||
| 73 | if (!mipi) | ||
| 74 | return -ENODEV; | ||
| 75 | return sh_mipi_send_short(mipi, MIPI_DSI_DCS_SHORT_WRITE, cmd, 0); | ||
| 76 | } | ||
| 77 | |||
| 78 | static int sh_mipi_dcs_param(int handle, u8 cmd, u8 param) | ||
| 79 | { | ||
| 80 | struct sh_mipi *mipi = sh_mipi_by_handle(LCD_CHAN2MIPI(handle)); | ||
| 81 | if (!mipi) | ||
| 82 | return -ENODEV; | ||
| 83 | return sh_mipi_send_short(mipi, MIPI_DSI_DCS_SHORT_WRITE_PARAM, cmd, | ||
| 84 | param); | ||
| 85 | } | ||
| 86 | |||
| 87 | static void sh_mipi_dsi_enable(struct sh_mipi *mipi, bool enable) | ||
| 88 | { | ||
| 89 | /* | ||
| 90 | * enable LCDC data tx, transition to LPS after completion of each HS | ||
| 91 | * packet | ||
| 92 | */ | ||
| 93 | iowrite32(0x00000002 | enable, mipi->base + 0x8000); /* DTCTR */ | ||
| 94 | } | ||
| 95 | |||
| 96 | static void sh_mipi_shutdown(struct platform_device *pdev) | ||
| 97 | { | ||
| 98 | struct sh_mipi *mipi = platform_get_drvdata(pdev); | ||
| 99 | |||
| 100 | sh_mipi_dsi_enable(mipi, false); | ||
| 101 | } | ||
| 102 | |||
| 103 | static void mipi_display_on(void *arg, struct fb_info *info) | ||
| 104 | { | ||
| 105 | struct sh_mipi *mipi = arg; | ||
| 106 | |||
| 107 | sh_mipi_dsi_enable(mipi, true); | ||
| 108 | } | ||
| 109 | |||
| 110 | static void mipi_display_off(void *arg) | ||
| 111 | { | ||
| 112 | struct sh_mipi *mipi = arg; | ||
| 113 | |||
| 114 | sh_mipi_dsi_enable(mipi, false); | ||
| 115 | } | ||
| 116 | |||
| 117 | static int __init sh_mipi_setup(struct sh_mipi *mipi, | ||
| 118 | struct sh_mipi_dsi_info *pdata) | ||
| 119 | { | ||
| 120 | void __iomem *base = mipi->base; | ||
| 121 | struct sh_mobile_lcdc_chan_cfg *ch = pdata->lcd_chan; | ||
| 122 | u32 pctype, datatype, pixfmt; | ||
| 123 | u32 linelength; | ||
| 124 | bool yuv; | ||
| 125 | |||
| 126 | /* Select data format */ | ||
| 127 | switch (pdata->data_format) { | ||
| 128 | case MIPI_RGB888: | ||
| 129 | pctype = 0; | ||
| 130 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24; | ||
| 131 | pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; | ||
| 132 | linelength = ch->lcd_cfg.xres * 3; | ||
| 133 | yuv = false; | ||
| 134 | break; | ||
| 135 | case MIPI_RGB565: | ||
| 136 | pctype = 1; | ||
| 137 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16; | ||
| 138 | pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; | ||
| 139 | linelength = ch->lcd_cfg.xres * 2; | ||
| 140 | yuv = false; | ||
| 141 | break; | ||
| 142 | case MIPI_RGB666_LP: | ||
| 143 | pctype = 2; | ||
| 144 | datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18; | ||
| 145 | pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; | ||
| 146 | linelength = ch->lcd_cfg.xres * 3; | ||
| 147 | yuv = false; | ||
| 148 | break; | ||
| 149 | case MIPI_RGB666: | ||
| 150 | pctype = 3; | ||
| 151 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18; | ||
| 152 | pixfmt = MIPI_DCS_PIXEL_FMT_18BIT; | ||
| 153 | linelength = (ch->lcd_cfg.xres * 18 + 7) / 8; | ||
| 154 | yuv = false; | ||
| 155 | break; | ||
| 156 | case MIPI_BGR888: | ||
| 157 | pctype = 8; | ||
| 158 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24; | ||
| 159 | pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; | ||
| 160 | linelength = ch->lcd_cfg.xres * 3; | ||
| 161 | yuv = false; | ||
| 162 | break; | ||
| 163 | case MIPI_BGR565: | ||
| 164 | pctype = 9; | ||
| 165 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16; | ||
| 166 | pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; | ||
| 167 | linelength = ch->lcd_cfg.xres * 2; | ||
| 168 | yuv = false; | ||
| 169 | break; | ||
| 170 | case MIPI_BGR666_LP: | ||
| 171 | pctype = 0xa; | ||
| 172 | datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18; | ||
| 173 | pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; | ||
| 174 | linelength = ch->lcd_cfg.xres * 3; | ||
| 175 | yuv = false; | ||
| 176 | break; | ||
| 177 | case MIPI_BGR666: | ||
| 178 | pctype = 0xb; | ||
| 179 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18; | ||
| 180 | pixfmt = MIPI_DCS_PIXEL_FMT_18BIT; | ||
| 181 | linelength = (ch->lcd_cfg.xres * 18 + 7) / 8; | ||
| 182 | yuv = false; | ||
| 183 | break; | ||
| 184 | case MIPI_YUYV: | ||
| 185 | pctype = 4; | ||
| 186 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16; | ||
| 187 | pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; | ||
| 188 | linelength = ch->lcd_cfg.xres * 2; | ||
| 189 | yuv = true; | ||
| 190 | break; | ||
| 191 | case MIPI_UYVY: | ||
| 192 | pctype = 5; | ||
| 193 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16; | ||
| 194 | pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; | ||
| 195 | linelength = ch->lcd_cfg.xres * 2; | ||
| 196 | yuv = true; | ||
| 197 | break; | ||
| 198 | case MIPI_YUV420_L: | ||
| 199 | pctype = 6; | ||
| 200 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12; | ||
| 201 | pixfmt = MIPI_DCS_PIXEL_FMT_12BIT; | ||
| 202 | linelength = (ch->lcd_cfg.xres * 12 + 7) / 8; | ||
| 203 | yuv = true; | ||
| 204 | break; | ||
| 205 | case MIPI_YUV420: | ||
| 206 | pctype = 7; | ||
| 207 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12; | ||
| 208 | pixfmt = MIPI_DCS_PIXEL_FMT_12BIT; | ||
| 209 | /* Length of U/V line */ | ||
| 210 | linelength = (ch->lcd_cfg.xres + 1) / 2; | ||
| 211 | yuv = true; | ||
| 212 | break; | ||
| 213 | default: | ||
| 214 | return -EINVAL; | ||
| 215 | } | ||
| 216 | |||
| 217 | if ((yuv && ch->interface_type != YUV422) || | ||
| 218 | (!yuv && ch->interface_type != RGB24)) | ||
| 219 | return -EINVAL; | ||
| 220 | |||
| 221 | /* reset DSI link */ | ||
| 222 | iowrite32(0x00000001, base); /* SYSCTRL */ | ||
| 223 | /* Hold reset for 100 cycles of the slowest of bus, HS byte and LP clock */ | ||
| 224 | udelay(50); | ||
| 225 | iowrite32(0x00000000, base); /* SYSCTRL */ | ||
| 226 | |||
| 227 | /* setup DSI link */ | ||
| 228 | |||
| 229 | /* | ||
| 230 | * Default = ULPS enable | | ||
| 231 | * Contention detection enabled | | ||
| 232 | * EoT packet transmission enable | | ||
| 233 | * CRC check enable | | ||
| 234 | * ECC check enable | ||
| 235 | * additionally enable first two lanes | ||
| 236 | */ | ||
| 237 | iowrite32(0x00003703, base + 0x04); /* SYSCONF */ | ||
| 238 | /* | ||
| 239 | * T_wakeup = 0x7000 | ||
| 240 | * T_hs-trail = 3 | ||
| 241 | * T_hs-prepare = 3 | ||
| 242 | * T_clk-trail = 3 | ||
| 243 | * T_clk-prepare = 2 | ||
| 244 | */ | ||
| 245 | iowrite32(0x70003332, base + 0x08); /* TIMSET */ | ||
| 246 | /* no responses requested */ | ||
| 247 | iowrite32(0x00000000, base + 0x18); /* RESREQSET0 */ | ||
| 248 | /* request response to packets of type 0x28 */ | ||
| 249 | iowrite32(0x00000100, base + 0x1c); /* RESREQSET1 */ | ||
| 250 | /* High-speed transmission timeout, default 0xffffffff */ | ||
| 251 | iowrite32(0x0fffffff, base + 0x20); /* HSTTOVSET */ | ||
| 252 | /* LP reception timeout, default 0xffffffff */ | ||
| 253 | iowrite32(0x0fffffff, base + 0x24); /* LPRTOVSET */ | ||
| 254 | /* Turn-around timeout, default 0xffffffff */ | ||
| 255 | iowrite32(0x0fffffff, base + 0x28); /* TATOVSET */ | ||
| 256 | /* Peripheral reset timeout, default 0xffffffff */ | ||
| 257 | iowrite32(0x0fffffff, base + 0x2c); /* PRTOVSET */ | ||
| 258 | /* Enable timeout counters */ | ||
| 259 | iowrite32(0x00000f00, base + 0x30); /* DSICTRL */ | ||
| 260 | /* Interrupts not used, disable all */ | ||
| 261 | iowrite32(0, base + DSIINTE); | ||
| 262 | /* DSI-Tx bias on */ | ||
| 263 | iowrite32(0x00000001, base + 0x70); /* PHYCTRL */ | ||
| 264 | udelay(200); | ||
| 265 | /* Deassert resets, power on, set multiplier */ | ||
| 266 | iowrite32(0x03070b01, base + 0x70); /* PHYCTRL */ | ||
| 267 | |||
| 268 | /* setup l-bridge */ | ||
| 269 | |||
| 270 | /* | ||
| 271 | * Enable transmission of all packets, | ||
| 272 | * transmit LPS after each HS packet completion | ||
| 273 | */ | ||
| 274 | iowrite32(0x00000006, base + 0x8000); /* DTCTR */ | ||
| 275 | /* VSYNC width = 2 (<< 17) */ | ||
| 276 | iowrite32(0x00040000 | (pctype << 12) | datatype, base + 0x8020); /* VMCTR1 */ | ||
| 277 | /* | ||
| 278 | * Non-burst mode with sync pulses: VSE and HSE are output, | ||
| 279 | * HSA period allowed, no commands in LP | ||
| 280 | */ | ||
| 281 | iowrite32(0x00e00000, base + 0x8024); /* VMCTR2 */ | ||
| 282 | /* | ||
| 283 | * 0x660 = 1632 bytes per line (RGB24, 544 pixels: see | ||
| 284 | * sh_mobile_lcdc_info.ch[0].lcd_cfg.xres), HSALEN = 1 - default | ||
| 285 | * (unused, since VMCTR2[HSABM] = 0) | ||
| 286 | */ | ||
| 287 | iowrite32(1 | (linelength << 16), base + 0x8028); /* VMLEN1 */ | ||
| 288 | |||
| 289 | msleep(5); | ||
| 290 | |||
| 291 | /* setup LCD panel */ | ||
| 292 | |||
| 293 | /* cf. drivers/video/omap/lcd_mipid.c */ | ||
| 294 | sh_mipi_dcs(ch->chan, MIPI_DCS_EXIT_SLEEP_MODE); | ||
| 295 | msleep(120); | ||
| 296 | /* | ||
| 297 | * [7] - Page Address Mode | ||
| 298 | * [6] - Column Address Mode | ||
| 299 | * [5] - Page / Column Address Mode | ||
| 300 | * [4] - Display Device Line Refresh Order | ||
| 301 | * [3] - RGB/BGR Order | ||
| 302 | * [2] - Display Data Latch Data Order | ||
| 303 | * [1] - Flip Horizontal | ||
| 304 | * [0] - Flip Vertical | ||
| 305 | */ | ||
| 306 | sh_mipi_dcs_param(ch->chan, MIPI_DCS_SET_ADDRESS_MODE, 0x00); | ||
| 307 | /* cf. set_data_lines() */ | ||
| 308 | sh_mipi_dcs_param(ch->chan, MIPI_DCS_SET_PIXEL_FORMAT, | ||
| 309 | pixfmt << 4); | ||
| 310 | sh_mipi_dcs(ch->chan, MIPI_DCS_SET_DISPLAY_ON); | ||
| 311 | |||
| 312 | return 0; | ||
| 313 | } | ||
| 314 | |||
| 315 | static int __init sh_mipi_probe(struct platform_device *pdev) | ||
| 316 | { | ||
| 317 | struct sh_mipi *mipi; | ||
| 318 | struct sh_mipi_dsi_info *pdata = pdev->dev.platform_data; | ||
| 319 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 320 | unsigned long rate, f_current; | ||
| 321 | int idx = pdev->id, ret; | ||
| 322 | char dsip_clk[] = "dsi.p_clk"; | ||
| 323 | |||
| 324 | if (!res || idx >= ARRAY_SIZE(mipi_dsi) || !pdata) | ||
| 325 | return -ENODEV; | ||
| 326 | |||
| 327 | mutex_lock(&array_lock); | ||
| 328 | if (idx < 0) | ||
| 329 | for (idx = 0; idx < ARRAY_SIZE(mipi_dsi) && mipi_dsi[idx]; idx++) | ||
| 330 | ; | ||
| 331 | |||
| 332 | if (idx == ARRAY_SIZE(mipi_dsi)) { | ||
| 333 | ret = -EBUSY; | ||
| 334 | goto efindslot; | ||
| 335 | } | ||
| 336 | |||
| 337 | mipi = kzalloc(sizeof(*mipi), GFP_KERNEL); | ||
| 338 | if (!mipi) { | ||
| 339 | ret = -ENOMEM; | ||
| 340 | goto ealloc; | ||
| 341 | } | ||
| 342 | |||
| 343 | if (!request_mem_region(res->start, resource_size(res), pdev->name)) { | ||
| 344 | dev_err(&pdev->dev, "MIPI register region already claimed\n"); | ||
| 345 | ret = -EBUSY; | ||
| 346 | goto ereqreg; | ||
| 347 | } | ||
| 348 | |||
| 349 | mipi->base = ioremap(res->start, resource_size(res)); | ||
| 350 | if (!mipi->base) { | ||
| 351 | ret = -ENOMEM; | ||
| 352 | goto emap; | ||
| 353 | } | ||
| 354 | |||
| 355 | mipi->dsit_clk = clk_get(&pdev->dev, "dsit_clk"); | ||
| 356 | if (IS_ERR(mipi->dsit_clk)) { | ||
| 357 | ret = PTR_ERR(mipi->dsit_clk); | ||
| 358 | goto eclktget; | ||
| 359 | } | ||
| 360 | |||
| 361 | f_current = clk_get_rate(mipi->dsit_clk); | ||
| 362 | /* 80MHz required by the datasheet */ | ||
| 363 | rate = clk_round_rate(mipi->dsit_clk, 80000000); | ||
| 364 | if (rate > 0 && rate != f_current) | ||
| 365 | ret = clk_set_rate(mipi->dsit_clk, rate); | ||
| 366 | else | ||
| 367 | ret = rate; | ||
| 368 | if (ret < 0) | ||
| 369 | goto esettrate; | ||
| 370 | |||
| 371 | dev_dbg(&pdev->dev, "DSI-T clk %lu -> %lu\n", f_current, rate); | ||
| 372 | |||
| 373 | sprintf(dsip_clk, "dsi%1.1dp_clk", idx); | ||
| 374 | mipi->dsip_clk = clk_get(&pdev->dev, dsip_clk); | ||
| 375 | if (IS_ERR(mipi->dsip_clk)) { | ||
| 376 | ret = PTR_ERR(mipi->dsip_clk); | ||
| 377 | goto eclkpget; | ||
| 378 | } | ||
| 379 | |||
| 380 | f_current = clk_get_rate(mipi->dsip_clk); | ||
| 381 | /* Between 10 and 50MHz */ | ||
| 382 | rate = clk_round_rate(mipi->dsip_clk, 24000000); | ||
| 383 | if (rate > 0 && rate != f_current) | ||
| 384 | ret = clk_set_rate(mipi->dsip_clk, rate); | ||
| 385 | else | ||
| 386 | ret = rate; | ||
| 387 | if (ret < 0) | ||
| 388 | goto esetprate; | ||
| 389 | |||
| 390 | dev_dbg(&pdev->dev, "DSI-P clk %lu -> %lu\n", f_current, rate); | ||
| 391 | |||
| 392 | msleep(10); | ||
| 393 | |||
| 394 | ret = clk_enable(mipi->dsit_clk); | ||
| 395 | if (ret < 0) | ||
| 396 | goto eclkton; | ||
| 397 | |||
| 398 | ret = clk_enable(mipi->dsip_clk); | ||
| 399 | if (ret < 0) | ||
| 400 | goto eclkpon; | ||
| 401 | |||
| 402 | mipi_dsi[idx] = mipi; | ||
| 403 | |||
| 404 | ret = sh_mipi_setup(mipi, pdata); | ||
| 405 | if (ret < 0) | ||
| 406 | goto emipisetup; | ||
| 407 | |||
| 408 | mutex_unlock(&array_lock); | ||
| 409 | platform_set_drvdata(pdev, mipi); | ||
| 410 | |||
| 411 | /* Set up LCDC callbacks */ | ||
| 412 | pdata->lcd_chan->board_cfg.board_data = mipi; | ||
| 413 | pdata->lcd_chan->board_cfg.display_on = mipi_display_on; | ||
| 414 | pdata->lcd_chan->board_cfg.display_off = mipi_display_off; | ||
| 415 | |||
| 416 | return 0; | ||
| 417 | |||
| 418 | emipisetup: | ||
| 419 | mipi_dsi[idx] = NULL; | ||
| 420 | clk_disable(mipi->dsip_clk); | ||
| 421 | eclkpon: | ||
| 422 | clk_disable(mipi->dsit_clk); | ||
| 423 | eclkton: | ||
| 424 | esetprate: | ||
| 425 | clk_put(mipi->dsip_clk); | ||
| 426 | eclkpget: | ||
| 427 | esettrate: | ||
| 428 | clk_put(mipi->dsit_clk); | ||
| 429 | eclktget: | ||
| 430 | iounmap(mipi->base); | ||
| 431 | emap: | ||
| 432 | release_mem_region(res->start, resource_size(res)); | ||
| 433 | ereqreg: | ||
| 434 | kfree(mipi); | ||
| 435 | ealloc: | ||
| 436 | efindslot: | ||
| 437 | mutex_unlock(&array_lock); | ||
| 438 | |||
| 439 | return ret; | ||
| 440 | } | ||
| 441 | |||
| 442 | static int __exit sh_mipi_remove(struct platform_device *pdev) | ||
| 443 | { | ||
| 444 | struct sh_mipi_dsi_info *pdata = pdev->dev.platform_data; | ||
| 445 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 446 | struct sh_mipi *mipi = platform_get_drvdata(pdev); | ||
| 447 | int i, ret; | ||
| 448 | |||
| 449 | mutex_lock(&array_lock); | ||
| 450 | |||
| 451 | for (i = 0; i < ARRAY_SIZE(mipi_dsi) && mipi_dsi[i] != mipi; i++) | ||
| 452 | ; | ||
| 453 | |||
| 454 | if (i == ARRAY_SIZE(mipi_dsi)) { | ||
| 455 | ret = -EINVAL; | ||
| 456 | } else { | ||
| 457 | ret = 0; | ||
| 458 | mipi_dsi[i] = NULL; | ||
| 459 | } | ||
| 460 | |||
| 461 | mutex_unlock(&array_lock); | ||
| 462 | |||
| 463 | if (ret < 0) | ||
| 464 | return ret; | ||
| 465 | |||
| 466 | pdata->lcd_chan->board_cfg.display_on = NULL; | ||
| 467 | pdata->lcd_chan->board_cfg.display_off = NULL; | ||
| 468 | pdata->lcd_chan->board_cfg.board_data = NULL; | ||
| 469 | |||
| 470 | clk_disable(mipi->dsip_clk); | ||
| 471 | clk_disable(mipi->dsit_clk); | ||
| 472 | clk_put(mipi->dsit_clk); | ||
| 473 | clk_put(mipi->dsip_clk); | ||
| 474 | iounmap(mipi->base); | ||
| 475 | if (res) | ||
| 476 | release_mem_region(res->start, resource_size(res)); | ||
| 477 | platform_set_drvdata(pdev, NULL); | ||
| 478 | kfree(mipi); | ||
| 479 | |||
| 480 | return 0; | ||
| 481 | } | ||
| 482 | |||
| 483 | static struct platform_driver sh_mipi_driver = { | ||
| 484 | .remove = __exit_p(sh_mipi_remove), | ||
| 485 | .shutdown = sh_mipi_shutdown, | ||
| 486 | .driver = { | ||
| 487 | .name = "sh-mipi-dsi", | ||
| 488 | }, | ||
| 489 | }; | ||
| 490 | |||
| 491 | static int __init sh_mipi_init(void) | ||
| 492 | { | ||
| 493 | return platform_driver_probe(&sh_mipi_driver, sh_mipi_probe); | ||
| 494 | } | ||
| 495 | module_init(sh_mipi_init); | ||
| 496 | |||
| 497 | static void __exit sh_mipi_exit(void) | ||
| 498 | { | ||
| 499 | platform_driver_unregister(&sh_mipi_driver); | ||
| 500 | } | ||
| 501 | module_exit(sh_mipi_exit); | ||
| 502 | |||
| 503 | MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); | ||
| 504 | MODULE_DESCRIPTION("SuperH / ARM-shmobile MIPI DSI driver"); | ||
| 505 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/video/sh_mobile_hdmi.c b/drivers/video/sh_mobile_hdmi.c new file mode 100644 index 000000000000..2fde08cc66bf --- /dev/null +++ b/drivers/video/sh_mobile_hdmi.c | |||
| @@ -0,0 +1,1028 @@ | |||
| 1 | /* | ||
| 2 | * SH-Mobile High-Definition Multimedia Interface (HDMI) driver | ||
| 3 | * for SLISHDMI13T and SLIPHDMIT IP cores | ||
| 4 | * | ||
| 5 | * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License version 2 as | ||
| 9 | * published by the Free Software Foundation. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/clk.h> | ||
| 13 | #include <linux/console.h> | ||
| 14 | #include <linux/delay.h> | ||
| 15 | #include <linux/err.h> | ||
| 16 | #include <linux/init.h> | ||
| 17 | #include <linux/interrupt.h> | ||
| 18 | #include <linux/io.h> | ||
| 19 | #include <linux/module.h> | ||
| 20 | #include <linux/platform_device.h> | ||
| 21 | #include <linux/pm_runtime.h> | ||
| 22 | #include <linux/slab.h> | ||
| 23 | #include <linux/types.h> | ||
| 24 | #include <linux/workqueue.h> | ||
| 25 | |||
| 26 | #include <video/sh_mobile_hdmi.h> | ||
| 27 | #include <video/sh_mobile_lcdc.h> | ||
| 28 | |||
| 29 | #define HDMI_SYSTEM_CTRL 0x00 /* System control */ | ||
| 30 | #define HDMI_L_R_DATA_SWAP_CTRL_RPKT 0x01 /* L/R data swap control, | ||
| 31 | bits 19..16 of 20-bit N for Audio Clock Regeneration packet */ | ||
| 32 | #define HDMI_20_BIT_N_FOR_AUDIO_RPKT_15_8 0x02 /* bits 15..8 of 20-bit N for Audio Clock Regeneration packet */ | ||
| 33 | #define HDMI_20_BIT_N_FOR_AUDIO_RPKT_7_0 0x03 /* bits 7..0 of 20-bit N for Audio Clock Regeneration packet */ | ||
| 34 | #define HDMI_SPDIF_AUDIO_SAMP_FREQ_CTS 0x04 /* SPDIF audio sampling frequency, | ||
| 35 | bits 19..16 of Internal CTS */ | ||
| 36 | #define HDMI_INTERNAL_CTS_15_8 0x05 /* bits 15..8 of Internal CTS */ | ||
| 37 | #define HDMI_INTERNAL_CTS_7_0 0x06 /* bits 7..0 of Internal CTS */ | ||
| 38 | #define HDMI_EXTERNAL_CTS_19_16 0x07 /* External CTS */ | ||
| 39 | #define HDMI_EXTERNAL_CTS_15_8 0x08 /* External CTS */ | ||
| 40 | #define HDMI_EXTERNAL_CTS_7_0 0x09 /* External CTS */ | ||
| 41 | #define HDMI_AUDIO_SETTING_1 0x0A /* Audio setting.1 */ | ||
| 42 | #define HDMI_AUDIO_SETTING_2 0x0B /* Audio setting.2 */ | ||
| 43 | #define HDMI_I2S_AUDIO_SET 0x0C /* I2S audio setting */ | ||
| 44 | #define HDMI_DSD_AUDIO_SET 0x0D /* DSD audio setting */ | ||
| 45 | #define HDMI_DEBUG_MONITOR_1 0x0E /* Debug monitor.1 */ | ||
| 46 | #define HDMI_DEBUG_MONITOR_2 0x0F /* Debug monitor.2 */ | ||
| 47 | #define HDMI_I2S_INPUT_PIN_SWAP 0x10 /* I2S input pin swap */ | ||
| 48 | #define HDMI_AUDIO_STATUS_BITS_SETTING_1 0x11 /* Audio status bits setting.1 */ | ||
| 49 | #define HDMI_AUDIO_STATUS_BITS_SETTING_2 0x12 /* Audio status bits setting.2 */ | ||
| 50 | #define HDMI_CATEGORY_CODE 0x13 /* Category code */ | ||
| 51 | #define HDMI_SOURCE_NUM_AUDIO_WORD_LEN 0x14 /* Source number/Audio word length */ | ||
| 52 | #define HDMI_AUDIO_VIDEO_SETTING_1 0x15 /* Audio/Video setting.1 */ | ||
| 53 | #define HDMI_VIDEO_SETTING_1 0x16 /* Video setting.1 */ | ||
| 54 | #define HDMI_DEEP_COLOR_MODES 0x17 /* Deep Color Modes */ | ||
| 55 | |||
| 56 | /* 12 16- and 10-bit Color space conversion parameters: 0x18..0x2f */ | ||
| 57 | #define HDMI_COLOR_SPACE_CONVERSION_PARAMETERS 0x18 | ||
| 58 | |||
| 59 | #define HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS 0x30 /* External video parameter settings */ | ||
| 60 | #define HDMI_EXTERNAL_H_TOTAL_7_0 0x31 /* External horizontal total (LSB) */ | ||
| 61 | #define HDMI_EXTERNAL_H_TOTAL_11_8 0x32 /* External horizontal total (MSB) */ | ||
| 62 | #define HDMI_EXTERNAL_H_BLANK_7_0 0x33 /* External horizontal blank (LSB) */ | ||
| 63 | #define HDMI_EXTERNAL_H_BLANK_9_8 0x34 /* External horizontal blank (MSB) */ | ||
| 64 | #define HDMI_EXTERNAL_H_DELAY_7_0 0x35 /* External horizontal delay (LSB) */ | ||
| 65 | #define HDMI_EXTERNAL_H_DELAY_9_8 0x36 /* External horizontal delay (MSB) */ | ||
| 66 | #define HDMI_EXTERNAL_H_DURATION_7_0 0x37 /* External horizontal duration (LSB) */ | ||
| 67 | #define HDMI_EXTERNAL_H_DURATION_9_8 0x38 /* External horizontal duration (MSB) */ | ||
| 68 | #define HDMI_EXTERNAL_V_TOTAL_7_0 0x39 /* External vertical total (LSB) */ | ||
| 69 | #define HDMI_EXTERNAL_V_TOTAL_9_8 0x3A /* External vertical total (MSB) */ | ||
| 70 | #define HDMI_AUDIO_VIDEO_SETTING_2 0x3B /* Audio/Video setting.2 */ | ||
| 71 | #define HDMI_EXTERNAL_V_BLANK 0x3D /* External vertical blank */ | ||
| 72 | #define HDMI_EXTERNAL_V_DELAY 0x3E /* External vertical delay */ | ||
| 73 | #define HDMI_EXTERNAL_V_DURATION 0x3F /* External vertical duration */ | ||
| 74 | #define HDMI_CTRL_PKT_MANUAL_SEND_CONTROL 0x40 /* Control packet manual send control */ | ||
| 75 | #define HDMI_CTRL_PKT_AUTO_SEND 0x41 /* Control packet auto send with VSYNC control */ | ||
| 76 | #define HDMI_AUTO_CHECKSUM_OPTION 0x42 /* Auto checksum option */ | ||
| 77 | #define HDMI_VIDEO_SETTING_2 0x45 /* Video setting.2 */ | ||
| 78 | #define HDMI_OUTPUT_OPTION 0x46 /* Output option */ | ||
| 79 | #define HDMI_SLIPHDMIT_PARAM_OPTION 0x51 /* SLIPHDMIT parameter option */ | ||
| 80 | #define HDMI_HSYNC_PMENT_AT_EMB_7_0 0x52 /* HSYNC placement at embedded sync (LSB) */ | ||
| 81 | #define HDMI_HSYNC_PMENT_AT_EMB_15_8 0x53 /* HSYNC placement at embedded sync (MSB) */ | ||
| 82 | #define HDMI_VSYNC_PMENT_AT_EMB_7_0 0x54 /* VSYNC placement at embedded sync (LSB) */ | ||
| 83 | #define HDMI_VSYNC_PMENT_AT_EMB_14_8 0x55 /* VSYNC placement at embedded sync (MSB) */ | ||
| 84 | #define HDMI_SLIPHDMIT_PARAM_SETTINGS_1 0x56 /* SLIPHDMIT parameter settings.1 */ | ||
| 85 | #define HDMI_SLIPHDMIT_PARAM_SETTINGS_2 0x57 /* SLIPHDMIT parameter settings.2 */ | ||
| 86 | #define HDMI_SLIPHDMIT_PARAM_SETTINGS_3 0x58 /* SLIPHDMIT parameter settings.3 */ | ||
| 87 | #define HDMI_SLIPHDMIT_PARAM_SETTINGS_5 0x59 /* SLIPHDMIT parameter settings.5 */ | ||
| 88 | #define HDMI_SLIPHDMIT_PARAM_SETTINGS_6 0x5A /* SLIPHDMIT parameter settings.6 */ | ||
| 89 | #define HDMI_SLIPHDMIT_PARAM_SETTINGS_7 0x5B /* SLIPHDMIT parameter settings.7 */ | ||
| 90 | #define HDMI_SLIPHDMIT_PARAM_SETTINGS_8 0x5C /* SLIPHDMIT parameter settings.8 */ | ||
| 91 | #define HDMI_SLIPHDMIT_PARAM_SETTINGS_9 0x5D /* SLIPHDMIT parameter settings.9 */ | ||
| 92 | #define HDMI_SLIPHDMIT_PARAM_SETTINGS_10 0x5E /* SLIPHDMIT parameter settings.10 */ | ||
| 93 | #define HDMI_CTRL_PKT_BUF_INDEX 0x5F /* Control packet buffer index */ | ||
| 94 | #define HDMI_CTRL_PKT_BUF_ACCESS_HB0 0x60 /* Control packet data buffer access window - HB0 */ | ||
| 95 | #define HDMI_CTRL_PKT_BUF_ACCESS_HB1 0x61 /* Control packet data buffer access window - HB1 */ | ||
| 96 | #define HDMI_CTRL_PKT_BUF_ACCESS_HB2 0x62 /* Control packet data buffer access window - HB2 */ | ||
| 97 | #define HDMI_CTRL_PKT_BUF_ACCESS_PB0 0x63 /* Control packet data buffer access window - PB0 */ | ||
| 98 | #define HDMI_CTRL_PKT_BUF_ACCESS_PB1 0x64 /* Control packet data buffer access window - PB1 */ | ||
| 99 | #define HDMI_CTRL_PKT_BUF_ACCESS_PB2 0x65 /* Control packet data buffer access window - PB2 */ | ||
| 100 | #define HDMI_CTRL_PKT_BUF_ACCESS_PB3 0x66 /* Control packet data buffer access window - PB3 */ | ||
| 101 | #define HDMI_CTRL_PKT_BUF_ACCESS_PB4 0x67 /* Control packet data buffer access window - PB4 */ | ||
| 102 | #define HDMI_CTRL_PKT_BUF_ACCESS_PB5 0x68 /* Control packet data buffer access window - PB5 */ | ||
| 103 | #define HDMI_CTRL_PKT_BUF_ACCESS_PB6 0x69 /* Control packet data buffer access window - PB6 */ | ||
| 104 | #define HDMI_CTRL_PKT_BUF_ACCESS_PB7 0x6A /* Control packet data buffer access window - PB7 */ | ||
| 105 | #define HDMI_CTRL_PKT_BUF_ACCESS_PB8 0x6B /* Control packet data buffer access window - PB8 */ | ||
| 106 | #define HDMI_CTRL_PKT_BUF_ACCESS_PB9 0x6C /* Control packet data buffer access window - PB9 */ | ||
| 107 | #define HDMI_CTRL_PKT_BUF_ACCESS_PB10 0x6D /* Control packet data buffer access window - PB10 */ | ||
| 108 | #define HDMI_CTRL_PKT_BUF_ACCESS_PB11 0x6E /* Control packet data buffer access window - PB11 */ | ||
| 109 | #define HDMI_CTRL_PKT_BUF_ACCESS_PB12 0x6F /* Control packet data buffer access window - PB12 */ | ||
| 110 | #define HDMI_CTRL_PKT_BUF_ACCESS_PB13 0x70 /* Control packet data buffer access window - PB13 */ | ||
| 111 | #define HDMI_CTRL_PKT_BUF_ACCESS_PB14 0x71 /* Control packet data buffer access window - PB14 */ | ||
| 112 | #define HDMI_CTRL_PKT_BUF_ACCESS_PB15 0x72 /* Control packet data buffer access window - PB15 */ | ||
| 113 | #define HDMI_CTRL_PKT_BUF_ACCESS_PB16 0x73 /* Control packet data buffer access window - PB16 */ | ||
| 114 | #define HDMI_CTRL_PKT_BUF_ACCESS_PB17 0x74 /* Control packet data buffer access window - PB17 */ | ||
| 115 | #define HDMI_CTRL_PKT_BUF_ACCESS_PB18 0x75 /* Control packet data buffer access window - PB18 */ | ||
| 116 | #define HDMI_CTRL_PKT_BUF_ACCESS_PB19 0x76 /* Control packet data buffer access window - PB19 */ | ||
| 117 | #define HDMI_CTRL_PKT_BUF_ACCESS_PB20 0x77 /* Control packet data buffer access window - PB20 */ | ||
| 118 | #define HDMI_CTRL_PKT_BUF_ACCESS_PB21 0x78 /* Control packet data buffer access window - PB21 */ | ||
| 119 | #define HDMI_CTRL_PKT_BUF_ACCESS_PB22 0x79 /* Control packet data buffer access window - PB22 */ | ||
| 120 | #define HDMI_CTRL_PKT_BUF_ACCESS_PB23 0x7A /* Control packet data buffer access window - PB23 */ | ||
| 121 | #define HDMI_CTRL_PKT_BUF_ACCESS_PB24 0x7B /* Control packet data buffer access window - PB24 */ | ||
| 122 | #define HDMI_CTRL_PKT_BUF_ACCESS_PB25 0x7C /* Control packet data buffer access window - PB25 */ | ||
| 123 | #define HDMI_CTRL_PKT_BUF_ACCESS_PB26 0x7D /* Control packet data buffer access window - PB26 */ | ||
| 124 | #define HDMI_CTRL_PKT_BUF_ACCESS_PB27 0x7E /* Control packet data buffer access window - PB27 */ | ||
| 125 | #define HDMI_EDID_KSV_FIFO_ACCESS_WINDOW 0x80 /* EDID/KSV FIFO access window */ | ||
| 126 | #define HDMI_DDC_BUS_ACCESS_FREQ_CTRL_7_0 0x81 /* DDC bus access frequency control (LSB) */ | ||
| 127 | #define HDMI_DDC_BUS_ACCESS_FREQ_CTRL_15_8 0x82 /* DDC bus access frequency control (MSB) */ | ||
| 128 | #define HDMI_INTERRUPT_MASK_1 0x92 /* Interrupt mask.1 */ | ||
| 129 | #define HDMI_INTERRUPT_MASK_2 0x93 /* Interrupt mask.2 */ | ||
| 130 | #define HDMI_INTERRUPT_STATUS_1 0x94 /* Interrupt status.1 */ | ||
| 131 | #define HDMI_INTERRUPT_STATUS_2 0x95 /* Interrupt status.2 */ | ||
| 132 | #define HDMI_INTERRUPT_MASK_3 0x96 /* Interrupt mask.3 */ | ||
| 133 | #define HDMI_INTERRUPT_MASK_4 0x97 /* Interrupt mask.4 */ | ||
| 134 | #define HDMI_INTERRUPT_STATUS_3 0x98 /* Interrupt status.3 */ | ||
| 135 | #define HDMI_INTERRUPT_STATUS_4 0x99 /* Interrupt status.4 */ | ||
| 136 | #define HDMI_SOFTWARE_HDCP_CONTROL_1 0x9A /* Software HDCP control.1 */ | ||
| 137 | #define HDMI_FRAME_COUNTER 0x9C /* Frame counter */ | ||
| 138 | #define HDMI_FRAME_COUNTER_FOR_RI_CHECK 0x9D /* Frame counter for Ri check */ | ||
| 139 | #define HDMI_HDCP_CONTROL 0xAF /* HDCP control */ | ||
| 140 | #define HDMI_RI_FRAME_COUNT_REGISTER 0xB2 /* Ri frame count register */ | ||
| 141 | #define HDMI_DDC_BUS_CONTROL 0xB7 /* DDC bus control */ | ||
| 142 | #define HDMI_HDCP_STATUS 0xB8 /* HDCP status */ | ||
| 143 | #define HDMI_SHA0 0xB9 /* sha0 */ | ||
| 144 | #define HDMI_SHA1 0xBA /* sha1 */ | ||
| 145 | #define HDMI_SHA2 0xBB /* sha2 */ | ||
| 146 | #define HDMI_SHA3 0xBC /* sha3 */ | ||
| 147 | #define HDMI_SHA4 0xBD /* sha4 */ | ||
| 148 | #define HDMI_BCAPS_READ 0xBE /* BCAPS read / debug */ | ||
| 149 | #define HDMI_AKSV_BKSV_7_0_MONITOR 0xBF /* AKSV/BKSV[7:0] monitor */ | ||
| 150 | #define HDMI_AKSV_BKSV_15_8_MONITOR 0xC0 /* AKSV/BKSV[15:8] monitor */ | ||
| 151 | #define HDMI_AKSV_BKSV_23_16_MONITOR 0xC1 /* AKSV/BKSV[23:16] monitor */ | ||
| 152 | #define HDMI_AKSV_BKSV_31_24_MONITOR 0xC2 /* AKSV/BKSV[31:24] monitor */ | ||
| 153 | #define HDMI_AKSV_BKSV_39_32_MONITOR 0xC3 /* AKSV/BKSV[39:32] monitor */ | ||
| 154 | #define HDMI_EDID_SEGMENT_POINTER 0xC4 /* EDID segment pointer */ | ||
| 155 | #define HDMI_EDID_WORD_ADDRESS 0xC5 /* EDID word address */ | ||
| 156 | #define HDMI_EDID_DATA_FIFO_ADDRESS 0xC6 /* EDID data FIFO address */ | ||
| 157 | #define HDMI_NUM_OF_HDMI_DEVICES 0xC7 /* Number of HDMI devices */ | ||
| 158 | #define HDMI_HDCP_ERROR_CODE 0xC8 /* HDCP error code */ | ||
| 159 | #define HDMI_100MS_TIMER_SET 0xC9 /* 100ms timer setting */ | ||
| 160 | #define HDMI_5SEC_TIMER_SET 0xCA /* 5sec timer setting */ | ||
| 161 | #define HDMI_RI_READ_COUNT 0xCB /* Ri read count */ | ||
| 162 | #define HDMI_AN_SEED 0xCC /* An seed */ | ||
| 163 | #define HDMI_MAX_NUM_OF_RCIVRS_ALLOWED 0xCD /* Maximum number of receivers allowed */ | ||
| 164 | #define HDMI_HDCP_MEMORY_ACCESS_CONTROL_1 0xCE /* HDCP memory access control.1 */ | ||
| 165 | #define HDMI_HDCP_MEMORY_ACCESS_CONTROL_2 0xCF /* HDCP memory access control.2 */ | ||
| 166 | #define HDMI_HDCP_CONTROL_2 0xD0 /* HDCP Control 2 */ | ||
| 167 | #define HDMI_HDCP_KEY_MEMORY_CONTROL 0xD2 /* HDCP Key Memory Control */ | ||
| 168 | #define HDMI_COLOR_SPACE_CONV_CONFIG_1 0xD3 /* Color space conversion configuration.1 */ | ||
| 169 | #define HDMI_VIDEO_SETTING_3 0xD4 /* Video setting.3 */ | ||
| 170 | #define HDMI_RI_7_0 0xD5 /* Ri[7:0] */ | ||
| 171 | #define HDMI_RI_15_8 0xD6 /* Ri[15:8] */ | ||
| 172 | #define HDMI_PJ 0xD7 /* Pj */ | ||
| 173 | #define HDMI_SHA_RD 0xD8 /* sha_rd */ | ||
| 174 | #define HDMI_RI_7_0_SAVED 0xD9 /* Ri[7:0] saved */ | ||
| 175 | #define HDMI_RI_15_8_SAVED 0xDA /* Ri[15:8] saved */ | ||
| 176 | #define HDMI_PJ_SAVED 0xDB /* Pj saved */ | ||
| 177 | #define HDMI_NUM_OF_DEVICES 0xDC /* Number of devices */ | ||
| 178 | #define HDMI_HOT_PLUG_MSENS_STATUS 0xDF /* Hot plug/MSENS status */ | ||
| 179 | #define HDMI_BCAPS_WRITE 0xE0 /* bcaps */ | ||
| 180 | #define HDMI_BSTAT_7_0 0xE1 /* bstat[7:0] */ | ||
| 181 | #define HDMI_BSTAT_15_8 0xE2 /* bstat[15:8] */ | ||
| 182 | #define HDMI_BKSV_7_0 0xE3 /* bksv[7:0] */ | ||
| 183 | #define HDMI_BKSV_15_8 0xE4 /* bksv[15:8] */ | ||
| 184 | #define HDMI_BKSV_23_16 0xE5 /* bksv[23:16] */ | ||
| 185 | #define HDMI_BKSV_31_24 0xE6 /* bksv[31:24] */ | ||
| 186 | #define HDMI_BKSV_39_32 0xE7 /* bksv[39:32] */ | ||
| 187 | #define HDMI_AN_7_0 0xE8 /* An[7:0] */ | ||
| 188 | #define HDMI_AN_15_8 0xE9 /* An [15:8] */ | ||
| 189 | #define HDMI_AN_23_16 0xEA /* An [23:16] */ | ||
| 190 | #define HDMI_AN_31_24 0xEB /* An [31:24] */ | ||
| 191 | #define HDMI_AN_39_32 0xEC /* An [39:32] */ | ||
| 192 | #define HDMI_AN_47_40 0xED /* An [47:40] */ | ||
| 193 | #define HDMI_AN_55_48 0xEE /* An [55:48] */ | ||
| 194 | #define HDMI_AN_63_56 0xEF /* An [63:56] */ | ||
| 195 | #define HDMI_PRODUCT_ID 0xF0 /* Product ID */ | ||
| 196 | #define HDMI_REVISION_ID 0xF1 /* Revision ID */ | ||
| 197 | #define HDMI_TEST_MODE 0xFE /* Test mode */ | ||
| 198 | |||
| 199 | enum hotplug_state { | ||
| 200 | HDMI_HOTPLUG_DISCONNECTED, | ||
| 201 | HDMI_HOTPLUG_CONNECTED, | ||
| 202 | HDMI_HOTPLUG_EDID_DONE, | ||
| 203 | }; | ||
| 204 | |||
| 205 | struct sh_hdmi { | ||
| 206 | void __iomem *base; | ||
| 207 | enum hotplug_state hp_state; | ||
| 208 | struct clk *hdmi_clk; | ||
| 209 | struct device *dev; | ||
| 210 | struct fb_info *info; | ||
| 211 | struct delayed_work edid_work; | ||
| 212 | struct fb_var_screeninfo var; | ||
| 213 | }; | ||
| 214 | |||
| 215 | static void hdmi_write(struct sh_hdmi *hdmi, u8 data, u8 reg) | ||
| 216 | { | ||
| 217 | iowrite8(data, hdmi->base + reg); | ||
| 218 | } | ||
| 219 | |||
| 220 | static u8 hdmi_read(struct sh_hdmi *hdmi, u8 reg) | ||
| 221 | { | ||
| 222 | return ioread8(hdmi->base + reg); | ||
| 223 | } | ||
| 224 | |||
| 225 | /* External video parameter settings */ | ||
| 226 | static void hdmi_external_video_param(struct sh_hdmi *hdmi) | ||
| 227 | { | ||
| 228 | struct fb_var_screeninfo *var = &hdmi->var; | ||
| 229 | u16 htotal, hblank, hdelay, vtotal, vblank, vdelay, voffset; | ||
| 230 | u8 sync = 0; | ||
| 231 | |||
| 232 | htotal = var->xres + var->right_margin + var->left_margin + var->hsync_len; | ||
| 233 | |||
| 234 | hdelay = var->hsync_len + var->left_margin; | ||
| 235 | hblank = var->right_margin + hdelay; | ||
| 236 | |||
| 237 | /* | ||
| 238 | * Vertical timing looks a bit different in Figure 18, | ||
| 239 | * but let's try the same first by setting offset = 0 | ||
| 240 | */ | ||
| 241 | vtotal = var->yres + var->upper_margin + var->lower_margin + var->vsync_len; | ||
| 242 | |||
| 243 | vdelay = var->vsync_len + var->upper_margin; | ||
| 244 | vblank = var->lower_margin + vdelay; | ||
| 245 | voffset = min(var->upper_margin / 2, 6U); | ||
| 246 | |||
| 247 | /* | ||
| 248 | * [3]: VSYNC polarity: Positive | ||
| 249 | * [2]: HSYNC polarity: Positive | ||
| 250 | * [1]: Interlace/Progressive: Progressive | ||
| 251 | * [0]: External video settings enable: used. | ||
| 252 | */ | ||
| 253 | if (var->sync & FB_SYNC_HOR_HIGH_ACT) | ||
| 254 | sync |= 4; | ||
| 255 | if (var->sync & FB_SYNC_VERT_HIGH_ACT) | ||
| 256 | sync |= 8; | ||
| 257 | |||
| 258 | pr_debug("H: %u, %u, %u, %u; V: %u, %u, %u, %u; sync 0x%x\n", | ||
| 259 | htotal, hblank, hdelay, var->hsync_len, | ||
| 260 | vtotal, vblank, vdelay, var->vsync_len, sync); | ||
| 261 | |||
| 262 | hdmi_write(hdmi, sync | (voffset << 4), HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS); | ||
| 263 | |||
| 264 | hdmi_write(hdmi, htotal, HDMI_EXTERNAL_H_TOTAL_7_0); | ||
| 265 | hdmi_write(hdmi, htotal >> 8, HDMI_EXTERNAL_H_TOTAL_11_8); | ||
| 266 | |||
| 267 | hdmi_write(hdmi, hblank, HDMI_EXTERNAL_H_BLANK_7_0); | ||
| 268 | hdmi_write(hdmi, hblank >> 8, HDMI_EXTERNAL_H_BLANK_9_8); | ||
| 269 | |||
| 270 | hdmi_write(hdmi, hdelay, HDMI_EXTERNAL_H_DELAY_7_0); | ||
| 271 | hdmi_write(hdmi, hdelay >> 8, HDMI_EXTERNAL_H_DELAY_9_8); | ||
| 272 | |||
| 273 | hdmi_write(hdmi, var->hsync_len, HDMI_EXTERNAL_H_DURATION_7_0); | ||
| 274 | hdmi_write(hdmi, var->hsync_len >> 8, HDMI_EXTERNAL_H_DURATION_9_8); | ||
| 275 | |||
| 276 | hdmi_write(hdmi, vtotal, HDMI_EXTERNAL_V_TOTAL_7_0); | ||
| 277 | hdmi_write(hdmi, vtotal >> 8, HDMI_EXTERNAL_V_TOTAL_9_8); | ||
| 278 | |||
| 279 | hdmi_write(hdmi, vblank, HDMI_EXTERNAL_V_BLANK); | ||
| 280 | |||
| 281 | hdmi_write(hdmi, vdelay, HDMI_EXTERNAL_V_DELAY); | ||
| 282 | |||
| 283 | hdmi_write(hdmi, var->vsync_len, HDMI_EXTERNAL_V_DURATION); | ||
| 284 | |||
| 285 | /* Set bit 0 of HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS here for manual mode */ | ||
| 286 | } | ||
| 287 | |||
| 288 | /** | ||
| 289 | * sh_hdmi_video_config() | ||
| 290 | */ | ||
| 291 | static void sh_hdmi_video_config(struct sh_hdmi *hdmi) | ||
| 292 | { | ||
| 293 | /* | ||
| 294 | * [7:4]: Audio sampling frequency: 48kHz | ||
| 295 | * [3:1]: Input video format: RGB and YCbCr 4:4:4 (Y on Green) | ||
| 296 | * [0]: Internal/External DE select: internal | ||
| 297 | */ | ||
| 298 | hdmi_write(hdmi, 0x20, HDMI_AUDIO_VIDEO_SETTING_1); | ||
| 299 | |||
| 300 | /* | ||
| 301 | * [7:6]: Video output format: RGB 4:4:4 | ||
| 302 | * [5:4]: Input video data width: 8 bit | ||
| 303 | * [3:1]: EAV/SAV location: channel 1 | ||
| 304 | * [0]: Video input color space: RGB | ||
| 305 | */ | ||
| 306 | hdmi_write(hdmi, 0x34, HDMI_VIDEO_SETTING_1); | ||
| 307 | |||
| 308 | /* | ||
| 309 | * [7:6]: Together with bit [6] of HDMI_AUDIO_VIDEO_SETTING_2, which is | ||
| 310 | * left at 0 by default, this configures 24bpp and sets the Color Depth | ||
| 311 | * (CD) field in the General Control Packet | ||
| 312 | */ | ||
| 313 | hdmi_write(hdmi, 0x20, HDMI_DEEP_COLOR_MODES); | ||
| 314 | } | ||
| 315 | |||
| 316 | /** | ||
| 317 | * sh_hdmi_audio_config() | ||
| 318 | */ | ||
| 319 | static void sh_hdmi_audio_config(struct sh_hdmi *hdmi) | ||
| 320 | { | ||
| 321 | /* | ||
| 322 | * [7:4] L/R data swap control | ||
| 323 | * [3:0] appropriate N[19:16] | ||
| 324 | */ | ||
| 325 | hdmi_write(hdmi, 0x00, HDMI_L_R_DATA_SWAP_CTRL_RPKT); | ||
| 326 | /* appropriate N[15:8] */ | ||
| 327 | hdmi_write(hdmi, 0x18, HDMI_20_BIT_N_FOR_AUDIO_RPKT_15_8); | ||
| 328 | /* appropriate N[7:0] */ | ||
| 329 | hdmi_write(hdmi, 0x00, HDMI_20_BIT_N_FOR_AUDIO_RPKT_7_0); | ||
| 330 | |||
| 331 | /* [7:4] 48 kHz SPDIF not used */ | ||
| 332 | hdmi_write(hdmi, 0x20, HDMI_SPDIF_AUDIO_SAMP_FREQ_CTS); | ||
| 333 | |||
| 334 | /* | ||
| 335 | * [6:5] set required down sampling rate if required | ||
| 336 | * [4:3] set required audio source | ||
| 337 | */ | ||
| 338 | hdmi_write(hdmi, 0x00, HDMI_AUDIO_SETTING_1); | ||
| 339 | |||
| 340 | /* [3:0] set sending channel number for channel status */ | ||
| 341 | hdmi_write(hdmi, 0x40, HDMI_AUDIO_SETTING_2); | ||
| 342 | |||
| 343 | /* | ||
| 344 | * [5:2] set valid I2S source input pin | ||
| 345 | * [1:0] set input I2S source mode | ||
| 346 | */ | ||
| 347 | hdmi_write(hdmi, 0x04, HDMI_I2S_AUDIO_SET); | ||
| 348 | |||
| 349 | /* [7:4] set valid DSD source input pin */ | ||
| 350 | hdmi_write(hdmi, 0x00, HDMI_DSD_AUDIO_SET); | ||
| 351 | |||
| 352 | /* [7:0] set appropriate I2S input pin swap settings if required */ | ||
| 353 | hdmi_write(hdmi, 0x00, HDMI_I2S_INPUT_PIN_SWAP); | ||
| 354 | |||
| 355 | /* | ||
| 356 | * [7] set validity bit for channel status | ||
| 357 | * [3:0] set original sample frequency for channel status | ||
| 358 | */ | ||
| 359 | hdmi_write(hdmi, 0x00, HDMI_AUDIO_STATUS_BITS_SETTING_1); | ||
| 360 | |||
| 361 | /* | ||
| 362 | * [7] set value for channel status | ||
| 363 | * [6] set value for channel status | ||
| 364 | * [5] set copyright bit for channel status | ||
| 365 | * [4:2] set additional information for channel status | ||
| 366 | * [1:0] set clock accuracy for channel status | ||
| 367 | */ | ||
| 368 | hdmi_write(hdmi, 0x00, HDMI_AUDIO_STATUS_BITS_SETTING_2); | ||
| 369 | |||
| 370 | /* [7:0] set category code for channel status */ | ||
| 371 | hdmi_write(hdmi, 0x00, HDMI_CATEGORY_CODE); | ||
| 372 | |||
| 373 | /* | ||
| 374 | * [7:4] set source number for channel status | ||
| 375 | * [3:0] set word length for channel status | ||
| 376 | */ | ||
| 377 | hdmi_write(hdmi, 0x00, HDMI_SOURCE_NUM_AUDIO_WORD_LEN); | ||
| 378 | |||
| 379 | /* [7:4] set sample frequency for channel status */ | ||
| 380 | hdmi_write(hdmi, 0x20, HDMI_AUDIO_VIDEO_SETTING_1); | ||
| 381 | } | ||
| 382 | |||
| 383 | /** | ||
| 384 | * sh_hdmi_phy_config() | ||
| 385 | */ | ||
| 386 | static void sh_hdmi_phy_config(struct sh_hdmi *hdmi) | ||
| 387 | { | ||
| 388 | /* 720p, 8bit, 74.25MHz. Might need to be adjusted for other formats */ | ||
| 389 | hdmi_write(hdmi, 0x19, HDMI_SLIPHDMIT_PARAM_SETTINGS_1); | ||
| 390 | hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_2); | ||
| 391 | hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_3); | ||
| 392 | /* PLLA_CONFIG[7:0]: VCO gain, VCO offset, LPF resistance[0] */ | ||
| 393 | hdmi_write(hdmi, 0x44, HDMI_SLIPHDMIT_PARAM_SETTINGS_5); | ||
| 394 | hdmi_write(hdmi, 0x32, HDMI_SLIPHDMIT_PARAM_SETTINGS_6); | ||
| 395 | hdmi_write(hdmi, 0x4A, HDMI_SLIPHDMIT_PARAM_SETTINGS_7); | ||
| 396 | hdmi_write(hdmi, 0x0E, HDMI_SLIPHDMIT_PARAM_SETTINGS_8); | ||
| 397 | hdmi_write(hdmi, 0x25, HDMI_SLIPHDMIT_PARAM_SETTINGS_9); | ||
| 398 | hdmi_write(hdmi, 0x04, HDMI_SLIPHDMIT_PARAM_SETTINGS_10); | ||
| 399 | } | ||
| 400 | |||
| 401 | /** | ||
| 402 | * sh_hdmi_avi_infoframe_setup() - Auxiliary Video Information InfoFrame CONTROL PACKET | ||
| 403 | */ | ||
| 404 | static void sh_hdmi_avi_infoframe_setup(struct sh_hdmi *hdmi) | ||
| 405 | { | ||
| 406 | /* AVI InfoFrame */ | ||
| 407 | hdmi_write(hdmi, 0x06, HDMI_CTRL_PKT_BUF_INDEX); | ||
| 408 | |||
| 409 | /* Packet Type = 0x82 */ | ||
| 410 | hdmi_write(hdmi, 0x82, HDMI_CTRL_PKT_BUF_ACCESS_HB0); | ||
| 411 | |||
| 412 | /* Version = 0x02 */ | ||
| 413 | hdmi_write(hdmi, 0x02, HDMI_CTRL_PKT_BUF_ACCESS_HB1); | ||
| 414 | |||
| 415 | /* Length = 13 (0x0D) */ | ||
| 416 | hdmi_write(hdmi, 0x0D, HDMI_CTRL_PKT_BUF_ACCESS_HB2); | ||
| 417 | |||
| 418 | /* N. A. Checksum */ | ||
| 419 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0); | ||
| 420 | |||
| 421 | /* | ||
| 422 | * Y = RGB | ||
| 423 | * A0 = No Data | ||
| 424 | * B = Bar Data not valid | ||
| 425 | * S = No Data | ||
| 426 | */ | ||
| 427 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB1); | ||
| 428 | |||
| 429 | /* | ||
| 430 | * C = No Data | ||
| 431 | * M = 16:9 Picture Aspect Ratio | ||
| 432 | * R = Same as picture aspect ratio | ||
| 433 | */ | ||
| 434 | hdmi_write(hdmi, 0x28, HDMI_CTRL_PKT_BUF_ACCESS_PB2); | ||
| 435 | |||
| 436 | /* | ||
| 437 | * ITC = No Data | ||
| 438 | * EC = xvYCC601 | ||
| 439 | * Q = Default (depends on video format) | ||
| 440 | * SC = No Known non_uniform Scaling | ||
| 441 | */ | ||
| 442 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB3); | ||
| 443 | |||
| 444 | /* | ||
| 445 | * VIC = 1280 x 720p: ignored if external config is used | ||
| 446 | * Send 2 for 720 x 480p, 16 for 1080p | ||
| 447 | */ | ||
| 448 | hdmi_write(hdmi, 4, HDMI_CTRL_PKT_BUF_ACCESS_PB4); | ||
| 449 | |||
| 450 | /* PR = No Repetition */ | ||
| 451 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB5); | ||
| 452 | |||
| 453 | /* Line Number of End of Top Bar (lower 8 bits) */ | ||
| 454 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB6); | ||
| 455 | |||
| 456 | /* Line Number of End of Top Bar (upper 8 bits) */ | ||
| 457 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB7); | ||
| 458 | |||
| 459 | /* Line Number of Start of Bottom Bar (lower 8 bits) */ | ||
| 460 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB8); | ||
| 461 | |||
| 462 | /* Line Number of Start of Bottom Bar (upper 8 bits) */ | ||
| 463 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB9); | ||
| 464 | |||
| 465 | /* Pixel Number of End of Left Bar (lower 8 bits) */ | ||
| 466 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB10); | ||
| 467 | |||
| 468 | /* Pixel Number of End of Left Bar (upper 8 bits) */ | ||
| 469 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB11); | ||
| 470 | |||
| 471 | /* Pixel Number of Start of Right Bar (lower 8 bits) */ | ||
| 472 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB12); | ||
| 473 | |||
| 474 | /* Pixel Number of Start of Right Bar (upper 8 bits) */ | ||
| 475 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB13); | ||
| 476 | } | ||
| 477 | |||
| 478 | /** | ||
| 479 | * sh_hdmi_audio_infoframe_setup() - Audio InfoFrame of CONTROL PACKET | ||
| 480 | */ | ||
| 481 | static void sh_hdmi_audio_infoframe_setup(struct sh_hdmi *hdmi) | ||
| 482 | { | ||
| 483 | /* Audio InfoFrame */ | ||
| 484 | hdmi_write(hdmi, 0x08, HDMI_CTRL_PKT_BUF_INDEX); | ||
| 485 | |||
| 486 | /* Packet Type = 0x84 */ | ||
| 487 | hdmi_write(hdmi, 0x84, HDMI_CTRL_PKT_BUF_ACCESS_HB0); | ||
| 488 | |||
| 489 | /* Version Number = 0x01 */ | ||
| 490 | hdmi_write(hdmi, 0x01, HDMI_CTRL_PKT_BUF_ACCESS_HB1); | ||
| 491 | |||
| 492 | /* 0 Length = 10 (0x0A) */ | ||
| 493 | hdmi_write(hdmi, 0x0A, HDMI_CTRL_PKT_BUF_ACCESS_HB2); | ||
| 494 | |||
| 495 | /* n. a. Checksum */ | ||
| 496 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0); | ||
| 497 | |||
| 498 | /* Audio Channel Count = Refer to Stream Header */ | ||
| 499 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB1); | ||
| 500 | |||
| 501 | /* Refer to Stream Header */ | ||
| 502 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB2); | ||
| 503 | |||
| 504 | /* Format depends on coding type (i.e. CT0...CT3) */ | ||
| 505 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB3); | ||
| 506 | |||
| 507 | /* Speaker Channel Allocation = Front Right + Front Left */ | ||
| 508 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB4); | ||
| 509 | |||
| 510 | /* Level Shift Value = 0 dB, Down - mix is permitted or no information */ | ||
| 511 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB5); | ||
| 512 | |||
| 513 | /* Reserved (0) */ | ||
| 514 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB6); | ||
| 515 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB7); | ||
| 516 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB8); | ||
| 517 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB9); | ||
| 518 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB10); | ||
| 519 | } | ||
| 520 | |||
| 521 | /** | ||
| 522 | * sh_hdmi_gamut_metadata_setup() - Gamut Metadata Packet of CONTROL PACKET | ||
| 523 | */ | ||
| 524 | static void sh_hdmi_gamut_metadata_setup(struct sh_hdmi *hdmi) | ||
| 525 | { | ||
| 526 | int i; | ||
| 527 | |||
| 528 | /* Gamut Metadata Packet */ | ||
| 529 | hdmi_write(hdmi, 0x04, HDMI_CTRL_PKT_BUF_INDEX); | ||
| 530 | |||
| 531 | /* Packet Type = 0x0A */ | ||
| 532 | hdmi_write(hdmi, 0x0A, HDMI_CTRL_PKT_BUF_ACCESS_HB0); | ||
| 533 | /* Gamut Packet is not used, so default value */ | ||
| 534 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB1); | ||
| 535 | /* Gamut Packet is not used, so default value */ | ||
| 536 | hdmi_write(hdmi, 0x10, HDMI_CTRL_PKT_BUF_ACCESS_HB2); | ||
| 537 | |||
| 538 | /* GBD bytes 0 through 27 */ | ||
| 539 | for (i = 0; i <= 27; i++) | ||
| 540 | /* HDMI_CTRL_PKT_BUF_ACCESS_PB0_63H - PB27_7EH */ | ||
| 541 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0 + i); | ||
| 542 | } | ||
| 543 | |||
| 544 | /** | ||
| 545 | * sh_hdmi_acp_setup() - Audio Content Protection Packet (ACP) | ||
| 546 | */ | ||
| 547 | static void sh_hdmi_acp_setup(struct sh_hdmi *hdmi) | ||
| 548 | { | ||
| 549 | int i; | ||
| 550 | |||
| 551 | /* Audio Content Protection Packet (ACP) */ | ||
| 552 | hdmi_write(hdmi, 0x01, HDMI_CTRL_PKT_BUF_INDEX); | ||
| 553 | |||
| 554 | /* Packet Type = 0x04 */ | ||
| 555 | hdmi_write(hdmi, 0x04, HDMI_CTRL_PKT_BUF_ACCESS_HB0); | ||
| 556 | /* ACP_Type */ | ||
| 557 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB1); | ||
| 558 | /* Reserved (0) */ | ||
| 559 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB2); | ||
| 560 | |||
| 561 | /* GBD bytes 0 through 27 */ | ||
| 562 | for (i = 0; i <= 27; i++) | ||
| 563 | /* HDMI_CTRL_PKT_BUF_ACCESS_PB0 - PB27 */ | ||
| 564 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0 + i); | ||
| 565 | } | ||
| 566 | |||
| 567 | /** | ||
| 568 | * sh_hdmi_isrc1_setup() - ISRC1 Packet | ||
| 569 | */ | ||
| 570 | static void sh_hdmi_isrc1_setup(struct sh_hdmi *hdmi) | ||
| 571 | { | ||
| 572 | int i; | ||
| 573 | |||
| 574 | /* ISRC1 Packet */ | ||
| 575 | hdmi_write(hdmi, 0x02, HDMI_CTRL_PKT_BUF_INDEX); | ||
| 576 | |||
| 577 | /* Packet Type = 0x05 */ | ||
| 578 | hdmi_write(hdmi, 0x05, HDMI_CTRL_PKT_BUF_ACCESS_HB0); | ||
| 579 | /* ISRC_Cont, ISRC_Valid, Reserved (0), ISRC_Status */ | ||
| 580 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB1); | ||
| 581 | /* Reserved (0) */ | ||
| 582 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB2); | ||
| 583 | |||
| 584 | /* PB0 UPC_EAN_ISRC_0-15 */ | ||
| 585 | /* Bytes PB16-PB27 shall be set to a value of 0. */ | ||
| 586 | for (i = 0; i <= 27; i++) | ||
| 587 | /* HDMI_CTRL_PKT_BUF_ACCESS_PB0 - PB27 */ | ||
| 588 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0 + i); | ||
| 589 | } | ||
| 590 | |||
| 591 | /** | ||
| 592 | * sh_hdmi_isrc2_setup() - ISRC2 Packet | ||
| 593 | */ | ||
| 594 | static void sh_hdmi_isrc2_setup(struct sh_hdmi *hdmi) | ||
| 595 | { | ||
| 596 | int i; | ||
| 597 | |||
| 598 | /* ISRC2 Packet */ | ||
| 599 | hdmi_write(hdmi, 0x03, HDMI_CTRL_PKT_BUF_INDEX); | ||
| 600 | |||
| 601 | /* HB0 Packet Type = 0x06 */ | ||
| 602 | hdmi_write(hdmi, 0x06, HDMI_CTRL_PKT_BUF_ACCESS_HB0); | ||
| 603 | /* Reserved (0) */ | ||
| 604 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB1); | ||
| 605 | /* Reserved (0) */ | ||
| 606 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB2); | ||
| 607 | |||
| 608 | /* PB0 UPC_EAN_ISRC_16-31 */ | ||
| 609 | /* Bytes PB16-PB27 shall be set to a value of 0. */ | ||
| 610 | for (i = 0; i <= 27; i++) | ||
| 611 | /* HDMI_CTRL_PKT_BUF_ACCESS_PB0 - PB27 */ | ||
| 612 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0 + i); | ||
| 613 | } | ||
| 614 | |||
| 615 | /** | ||
| 616 | * sh_hdmi_configure() - Initialise HDMI for output | ||
| 617 | */ | ||
| 618 | static void sh_hdmi_configure(struct sh_hdmi *hdmi) | ||
| 619 | { | ||
| 620 | /* Configure video format */ | ||
| 621 | sh_hdmi_video_config(hdmi); | ||
| 622 | |||
| 623 | /* Configure audio format */ | ||
| 624 | sh_hdmi_audio_config(hdmi); | ||
| 625 | |||
| 626 | /* Configure PHY */ | ||
| 627 | sh_hdmi_phy_config(hdmi); | ||
| 628 | |||
| 629 | /* Auxiliary Video Information (AVI) InfoFrame */ | ||
| 630 | sh_hdmi_avi_infoframe_setup(hdmi); | ||
| 631 | |||
| 632 | /* Audio InfoFrame */ | ||
| 633 | sh_hdmi_audio_infoframe_setup(hdmi); | ||
| 634 | |||
| 635 | /* Gamut Metadata packet */ | ||
| 636 | sh_hdmi_gamut_metadata_setup(hdmi); | ||
| 637 | |||
| 638 | /* Audio Content Protection (ACP) Packet */ | ||
| 639 | sh_hdmi_acp_setup(hdmi); | ||
| 640 | |||
| 641 | /* ISRC1 Packet */ | ||
| 642 | sh_hdmi_isrc1_setup(hdmi); | ||
| 643 | |||
| 644 | /* ISRC2 Packet */ | ||
| 645 | sh_hdmi_isrc2_setup(hdmi); | ||
| 646 | |||
| 647 | /* | ||
| 648 | * Control packet auto send with VSYNC control: auto send | ||
| 649 | * General control, Gamut metadata, ISRC, and ACP packets | ||
| 650 | */ | ||
| 651 | hdmi_write(hdmi, 0x8E, HDMI_CTRL_PKT_AUTO_SEND); | ||
| 652 | |||
| 653 | /* FIXME */ | ||
| 654 | msleep(10); | ||
| 655 | |||
| 656 | /* PS mode b->d, reset PLLA and PLLB */ | ||
| 657 | hdmi_write(hdmi, 0x4C, HDMI_SYSTEM_CTRL); | ||
| 658 | |||
| 659 | udelay(10); | ||
| 660 | |||
| 661 | hdmi_write(hdmi, 0x40, HDMI_SYSTEM_CTRL); | ||
| 662 | } | ||
| 663 | |||
| 664 | static void sh_hdmi_read_edid(struct sh_hdmi *hdmi) | ||
| 665 | { | ||
| 666 | struct fb_var_screeninfo *var = &hdmi->var; | ||
| 667 | struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; | ||
| 668 | struct fb_videomode *lcd_cfg = &pdata->lcd_chan->lcd_cfg; | ||
| 669 | unsigned long height = var->height, width = var->width; | ||
| 670 | int i; | ||
| 671 | u8 edid[128]; | ||
| 672 | |||
| 673 | /* Read EDID */ | ||
| 674 | pr_debug("Read back EDID code:"); | ||
| 675 | for (i = 0; i < 128; i++) { | ||
| 676 | edid[i] = hdmi_read(hdmi, HDMI_EDID_KSV_FIFO_ACCESS_WINDOW); | ||
| 677 | #ifdef DEBUG | ||
| 678 | if ((i % 16) == 0) { | ||
| 679 | printk(KERN_CONT "\n"); | ||
| 680 | printk(KERN_DEBUG "%02X | %02X", i, edid[i]); | ||
| 681 | } else { | ||
| 682 | printk(KERN_CONT " %02X", edid[i]); | ||
| 683 | } | ||
| 684 | #endif | ||
| 685 | } | ||
| 686 | #ifdef DEBUG | ||
| 687 | printk(KERN_CONT "\n"); | ||
| 688 | #endif | ||
| 689 | fb_parse_edid(edid, var); | ||
| 690 | pr_debug("%u-%u-%u-%u x %u-%u-%u-%u @ %lu kHz monitor detected\n", | ||
| 691 | var->left_margin, var->xres, var->right_margin, var->hsync_len, | ||
| 692 | var->upper_margin, var->yres, var->lower_margin, var->vsync_len, | ||
| 693 | PICOS2KHZ(var->pixclock)); | ||
| 694 | |||
| 695 | /* FIXME: Use user-provided configuration instead of EDID */ | ||
| 696 | var->width = width; | ||
| 697 | var->xres = lcd_cfg->xres; | ||
| 698 | var->xres_virtual = lcd_cfg->xres; | ||
| 699 | var->left_margin = lcd_cfg->left_margin; | ||
| 700 | var->right_margin = lcd_cfg->right_margin; | ||
| 701 | var->hsync_len = lcd_cfg->hsync_len; | ||
| 702 | var->height = height; | ||
| 703 | var->yres = lcd_cfg->yres; | ||
| 704 | var->yres_virtual = lcd_cfg->yres * 2; | ||
| 705 | var->upper_margin = lcd_cfg->upper_margin; | ||
| 706 | var->lower_margin = lcd_cfg->lower_margin; | ||
| 707 | var->vsync_len = lcd_cfg->vsync_len; | ||
| 708 | var->sync = lcd_cfg->sync; | ||
| 709 | var->pixclock = lcd_cfg->pixclock; | ||
| 710 | |||
| 711 | hdmi_external_video_param(hdmi); | ||
| 712 | } | ||
| 713 | |||
| 714 | static irqreturn_t sh_hdmi_hotplug(int irq, void *dev_id) | ||
| 715 | { | ||
| 716 | struct sh_hdmi *hdmi = dev_id; | ||
| 717 | u8 status1, status2, mask1, mask2; | ||
| 718 | |||
| 719 | /* mode_b and PLLA and PLLB reset */ | ||
| 720 | hdmi_write(hdmi, 0x2C, HDMI_SYSTEM_CTRL); | ||
| 721 | |||
| 722 | /* How long shall reset be held? */ | ||
| 723 | udelay(10); | ||
| 724 | |||
| 725 | /* mode_b and PLLA and PLLB reset release */ | ||
| 726 | hdmi_write(hdmi, 0x20, HDMI_SYSTEM_CTRL); | ||
| 727 | |||
| 728 | status1 = hdmi_read(hdmi, HDMI_INTERRUPT_STATUS_1); | ||
| 729 | status2 = hdmi_read(hdmi, HDMI_INTERRUPT_STATUS_2); | ||
| 730 | |||
| 731 | mask1 = hdmi_read(hdmi, HDMI_INTERRUPT_MASK_1); | ||
| 732 | mask2 = hdmi_read(hdmi, HDMI_INTERRUPT_MASK_2); | ||
| 733 | |||
| 734 | /* Correct would be to ack only set bits, but the datasheet requires 0xff */ | ||
| 735 | hdmi_write(hdmi, 0xFF, HDMI_INTERRUPT_STATUS_1); | ||
| 736 | hdmi_write(hdmi, 0xFF, HDMI_INTERRUPT_STATUS_2); | ||
| 737 | |||
| 738 | if (printk_ratelimit()) | ||
| 739 | pr_debug("IRQ #%d: Status #1: 0x%x & 0x%x, #2: 0x%x & 0x%x\n", | ||
| 740 | irq, status1, mask1, status2, mask2); | ||
| 741 | |||
| 742 | if (!((status1 & mask1) | (status2 & mask2))) { | ||
| 743 | return IRQ_NONE; | ||
| 744 | } else if (status1 & 0xc0) { | ||
| 745 | u8 msens; | ||
| 746 | |||
| 747 | /* Datasheet specifies 10ms... */ | ||
| 748 | udelay(500); | ||
| 749 | |||
| 750 | msens = hdmi_read(hdmi, HDMI_HOT_PLUG_MSENS_STATUS); | ||
| 751 | pr_debug("MSENS 0x%x\n", msens); | ||
| 752 | /* Check, if hot plug & MSENS pin status are both high */ | ||
| 753 | if ((msens & 0xC0) == 0xC0) { | ||
| 754 | /* Display plug in */ | ||
| 755 | hdmi->hp_state = HDMI_HOTPLUG_CONNECTED; | ||
| 756 | |||
| 757 | /* Set EDID word address */ | ||
| 758 | hdmi_write(hdmi, 0x00, HDMI_EDID_WORD_ADDRESS); | ||
| 759 | /* Set EDID segment pointer */ | ||
| 760 | hdmi_write(hdmi, 0x00, HDMI_EDID_SEGMENT_POINTER); | ||
| 761 | /* Enable EDID interrupt */ | ||
| 762 | hdmi_write(hdmi, 0xC6, HDMI_INTERRUPT_MASK_1); | ||
| 763 | } else if (!(status1 & 0x80)) { | ||
| 764 | /* Display unplug, beware multiple interrupts */ | ||
| 765 | if (hdmi->hp_state != HDMI_HOTPLUG_DISCONNECTED) | ||
| 766 | schedule_delayed_work(&hdmi->edid_work, 0); | ||
| 767 | |||
| 768 | hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED; | ||
| 769 | /* display_off will switch back to mode_a */ | ||
| 770 | } | ||
| 771 | } else if (status1 & 2) { | ||
| 772 | /* EDID error interrupt: retry */ | ||
| 773 | /* Set EDID word address */ | ||
| 774 | hdmi_write(hdmi, 0x00, HDMI_EDID_WORD_ADDRESS); | ||
| 775 | /* Set EDID segment pointer */ | ||
| 776 | hdmi_write(hdmi, 0x00, HDMI_EDID_SEGMENT_POINTER); | ||
| 777 | } else if (status1 & 4) { | ||
| 778 | /* Disable EDID interrupt */ | ||
| 779 | hdmi_write(hdmi, 0xC0, HDMI_INTERRUPT_MASK_1); | ||
| 780 | hdmi->hp_state = HDMI_HOTPLUG_EDID_DONE; | ||
| 781 | schedule_delayed_work(&hdmi->edid_work, msecs_to_jiffies(10)); | ||
| 782 | } | ||
| 783 | |||
| 784 | return IRQ_HANDLED; | ||
| 785 | } | ||
| 786 | |||
| 787 | static void hdmi_display_on(void *arg, struct fb_info *info) | ||
| 788 | { | ||
| 789 | struct sh_hdmi *hdmi = arg; | ||
| 790 | struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; | ||
| 791 | |||
| 792 | if (info->var.xres != 1280 || info->var.yres != 720) { | ||
| 793 | dev_warn(info->device, "Unsupported framebuffer geometry %ux%u\n", | ||
| 794 | info->var.xres, info->var.yres); | ||
| 795 | return; | ||
| 796 | } | ||
| 797 | |||
| 798 | pr_debug("%s(%p): state %x\n", __func__, pdata->lcd_dev, info->state); | ||
| 799 | /* | ||
| 800 | * FIXME: not a good place to store fb_info. And we cannot nullify it | ||
| 801 | * even on monitor disconnect. What should the lifecycle be? | ||
| 802 | */ | ||
| 803 | hdmi->info = info; | ||
| 804 | switch (hdmi->hp_state) { | ||
| 805 | case HDMI_HOTPLUG_EDID_DONE: | ||
| 806 | /* PS mode d->e. All functions are active */ | ||
| 807 | hdmi_write(hdmi, 0x80, HDMI_SYSTEM_CTRL); | ||
| 808 | pr_debug("HDMI running\n"); | ||
| 809 | break; | ||
| 810 | case HDMI_HOTPLUG_DISCONNECTED: | ||
| 811 | info->state = FBINFO_STATE_SUSPENDED; | ||
| 812 | default: | ||
| 813 | hdmi->var = info->var; | ||
| 814 | } | ||
| 815 | } | ||
| 816 | |||
| 817 | static void hdmi_display_off(void *arg) | ||
| 818 | { | ||
| 819 | struct sh_hdmi *hdmi = arg; | ||
| 820 | struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; | ||
| 821 | |||
| 822 | pr_debug("%s(%p)\n", __func__, pdata->lcd_dev); | ||
| 823 | /* PS mode e->a */ | ||
| 824 | hdmi_write(hdmi, 0x10, HDMI_SYSTEM_CTRL); | ||
| 825 | } | ||
| 826 | |||
| 827 | /* Hotplug interrupt occurred, read EDID */ | ||
| 828 | static void edid_work_fn(struct work_struct *work) | ||
| 829 | { | ||
| 830 | struct sh_hdmi *hdmi = container_of(work, struct sh_hdmi, edid_work.work); | ||
| 831 | struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; | ||
| 832 | |||
| 833 | pr_debug("%s(%p): begin, hotplug status %d\n", __func__, | ||
| 834 | pdata->lcd_dev, hdmi->hp_state); | ||
| 835 | |||
| 836 | if (!pdata->lcd_dev) | ||
| 837 | return; | ||
| 838 | |||
| 839 | if (hdmi->hp_state == HDMI_HOTPLUG_EDID_DONE) { | ||
| 840 | pm_runtime_get_sync(hdmi->dev); | ||
| 841 | /* A device has been plugged in */ | ||
| 842 | sh_hdmi_read_edid(hdmi); | ||
| 843 | msleep(10); | ||
| 844 | sh_hdmi_configure(hdmi); | ||
| 845 | /* Switched to another (d) power-save mode */ | ||
| 846 | msleep(10); | ||
| 847 | |||
| 848 | if (!hdmi->info) | ||
| 849 | return; | ||
| 850 | |||
| 851 | acquire_console_sem(); | ||
| 852 | |||
| 853 | /* HDMI plug in */ | ||
| 854 | hdmi->info->var = hdmi->var; | ||
| 855 | if (hdmi->info->state != FBINFO_STATE_RUNNING) | ||
| 856 | fb_set_suspend(hdmi->info, 0); | ||
| 857 | else | ||
| 858 | hdmi_display_on(hdmi, hdmi->info); | ||
| 859 | |||
| 860 | release_console_sem(); | ||
| 861 | } else { | ||
| 862 | if (!hdmi->info) | ||
| 863 | return; | ||
| 864 | |||
| 865 | acquire_console_sem(); | ||
| 866 | |||
| 867 | /* HDMI disconnect */ | ||
| 868 | fb_set_suspend(hdmi->info, 1); | ||
| 869 | |||
| 870 | release_console_sem(); | ||
| 871 | pm_runtime_put(hdmi->dev); | ||
| 872 | } | ||
| 873 | |||
| 874 | pr_debug("%s(%p): end\n", __func__, pdata->lcd_dev); | ||
| 875 | } | ||
| 876 | |||
| 877 | static int __init sh_hdmi_probe(struct platform_device *pdev) | ||
| 878 | { | ||
| 879 | struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; | ||
| 880 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 881 | int irq = platform_get_irq(pdev, 0), ret; | ||
| 882 | struct sh_hdmi *hdmi; | ||
| 883 | long rate; | ||
| 884 | |||
| 885 | if (!res || !pdata || irq < 0) | ||
| 886 | return -ENODEV; | ||
| 887 | |||
| 888 | hdmi = kzalloc(sizeof(*hdmi), GFP_KERNEL); | ||
| 889 | if (!hdmi) { | ||
| 890 | dev_err(&pdev->dev, "Cannot allocate device data\n"); | ||
| 891 | return -ENOMEM; | ||
| 892 | } | ||
| 893 | |||
| 894 | hdmi->dev = &pdev->dev; | ||
| 895 | |||
| 896 | hdmi->hdmi_clk = clk_get(&pdev->dev, "ick"); | ||
| 897 | if (IS_ERR(hdmi->hdmi_clk)) { | ||
| 898 | ret = PTR_ERR(hdmi->hdmi_clk); | ||
| 899 | dev_err(&pdev->dev, "Unable to get clock: %d\n", ret); | ||
| 900 | goto egetclk; | ||
| 901 | } | ||
| 902 | |||
| 903 | rate = PICOS2KHZ(pdata->lcd_chan->lcd_cfg.pixclock) * 1000; | ||
| 904 | |||
| 905 | rate = clk_round_rate(hdmi->hdmi_clk, rate); | ||
| 906 | if (rate < 0) { | ||
| 907 | ret = rate; | ||
| 908 | dev_err(&pdev->dev, "Cannot get suitable rate: %ld\n", rate); | ||
| 909 | goto erate; | ||
| 910 | } | ||
| 911 | |||
| 912 | ret = clk_set_rate(hdmi->hdmi_clk, rate); | ||
| 913 | if (ret < 0) { | ||
| 914 | dev_err(&pdev->dev, "Cannot set rate %ld: %d\n", rate, ret); | ||
| 915 | goto erate; | ||
| 916 | } | ||
| 917 | |||
| 918 | pr_debug("HDMI set frequency %lu\n", rate); | ||
| 919 | |||
| 920 | ret = clk_enable(hdmi->hdmi_clk); | ||
| 921 | if (ret < 0) { | ||
| 922 | dev_err(&pdev->dev, "Cannot enable clock: %d\n", ret); | ||
| 923 | goto eclkenable; | ||
| 924 | } | ||
| 925 | |||
| 926 | dev_info(&pdev->dev, "Enabled HDMI clock at %luHz\n", rate); | ||
| 927 | |||
| 928 | if (!request_mem_region(res->start, resource_size(res), dev_name(&pdev->dev))) { | ||
| 929 | dev_err(&pdev->dev, "HDMI register region already claimed\n"); | ||
| 930 | ret = -EBUSY; | ||
| 931 | goto ereqreg; | ||
| 932 | } | ||
| 933 | |||
| 934 | hdmi->base = ioremap(res->start, resource_size(res)); | ||
| 935 | if (!hdmi->base) { | ||
| 936 | dev_err(&pdev->dev, "HDMI register region already claimed\n"); | ||
| 937 | ret = -ENOMEM; | ||
| 938 | goto emap; | ||
| 939 | } | ||
| 940 | |||
| 941 | platform_set_drvdata(pdev, hdmi); | ||
| 942 | |||
| 943 | #if 1 | ||
| 944 | /* Product and revision IDs are 0 in sh-mobile version */ | ||
| 945 | dev_info(&pdev->dev, "Detected HDMI controller 0x%x:0x%x\n", | ||
| 946 | hdmi_read(hdmi, HDMI_PRODUCT_ID), hdmi_read(hdmi, HDMI_REVISION_ID)); | ||
| 947 | #endif | ||
| 948 | |||
| 949 | /* Set up LCDC callbacks */ | ||
| 950 | pdata->lcd_chan->board_cfg.board_data = hdmi; | ||
| 951 | pdata->lcd_chan->board_cfg.display_on = hdmi_display_on; | ||
| 952 | pdata->lcd_chan->board_cfg.display_off = hdmi_display_off; | ||
| 953 | |||
| 954 | INIT_DELAYED_WORK(&hdmi->edid_work, edid_work_fn); | ||
| 955 | |||
| 956 | pm_runtime_enable(&pdev->dev); | ||
| 957 | pm_runtime_resume(&pdev->dev); | ||
| 958 | |||
| 959 | ret = request_irq(irq, sh_hdmi_hotplug, 0, | ||
| 960 | dev_name(&pdev->dev), hdmi); | ||
| 961 | if (ret < 0) { | ||
| 962 | dev_err(&pdev->dev, "Unable to request irq: %d\n", ret); | ||
| 963 | goto ereqirq; | ||
| 964 | } | ||
| 965 | |||
| 966 | return 0; | ||
| 967 | |||
| 968 | ereqirq: | ||
| 969 | pm_runtime_disable(&pdev->dev); | ||
| 970 | iounmap(hdmi->base); | ||
| 971 | emap: | ||
| 972 | release_mem_region(res->start, resource_size(res)); | ||
| 973 | ereqreg: | ||
| 974 | clk_disable(hdmi->hdmi_clk); | ||
| 975 | eclkenable: | ||
| 976 | erate: | ||
| 977 | clk_put(hdmi->hdmi_clk); | ||
| 978 | egetclk: | ||
| 979 | kfree(hdmi); | ||
| 980 | |||
| 981 | return ret; | ||
| 982 | } | ||
| 983 | |||
| 984 | static int __exit sh_hdmi_remove(struct platform_device *pdev) | ||
| 985 | { | ||
| 986 | struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; | ||
| 987 | struct sh_hdmi *hdmi = platform_get_drvdata(pdev); | ||
| 988 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 989 | int irq = platform_get_irq(pdev, 0); | ||
| 990 | |||
| 991 | pdata->lcd_chan->board_cfg.display_on = NULL; | ||
| 992 | pdata->lcd_chan->board_cfg.display_off = NULL; | ||
| 993 | pdata->lcd_chan->board_cfg.board_data = NULL; | ||
| 994 | |||
| 995 | free_irq(irq, hdmi); | ||
| 996 | pm_runtime_disable(&pdev->dev); | ||
| 997 | cancel_delayed_work_sync(&hdmi->edid_work); | ||
| 998 | clk_disable(hdmi->hdmi_clk); | ||
| 999 | clk_put(hdmi->hdmi_clk); | ||
| 1000 | iounmap(hdmi->base); | ||
| 1001 | release_mem_region(res->start, resource_size(res)); | ||
| 1002 | kfree(hdmi); | ||
| 1003 | |||
| 1004 | return 0; | ||
| 1005 | } | ||
| 1006 | |||
| 1007 | static struct platform_driver sh_hdmi_driver = { | ||
| 1008 | .remove = __exit_p(sh_hdmi_remove), | ||
| 1009 | .driver = { | ||
| 1010 | .name = "sh-mobile-hdmi", | ||
| 1011 | }, | ||
| 1012 | }; | ||
| 1013 | |||
| 1014 | static int __init sh_hdmi_init(void) | ||
| 1015 | { | ||
| 1016 | return platform_driver_probe(&sh_hdmi_driver, sh_hdmi_probe); | ||
| 1017 | } | ||
| 1018 | module_init(sh_hdmi_init); | ||
| 1019 | |||
| 1020 | static void __exit sh_hdmi_exit(void) | ||
| 1021 | { | ||
| 1022 | platform_driver_unregister(&sh_hdmi_driver); | ||
| 1023 | } | ||
| 1024 | module_exit(sh_hdmi_exit); | ||
| 1025 | |||
| 1026 | MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); | ||
| 1027 | MODULE_DESCRIPTION("SuperH / ARM-shmobile HDMI driver"); | ||
| 1028 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index 12c451a711e9..d72075a9f01c 100644 --- a/drivers/video/sh_mobile_lcdcfb.c +++ b/drivers/video/sh_mobile_lcdcfb.c | |||
| @@ -56,6 +56,7 @@ static int lcdc_shared_regs[] = { | |||
| 56 | /* per-channel registers */ | 56 | /* per-channel registers */ |
| 57 | enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R, | 57 | enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R, |
| 58 | LDSM2R, LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR, | 58 | LDSM2R, LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR, |
| 59 | LDHAJR, | ||
| 59 | NR_CH_REGS }; | 60 | NR_CH_REGS }; |
| 60 | 61 | ||
| 61 | static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = { | 62 | static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = { |
| @@ -74,6 +75,7 @@ static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = { | |||
| 74 | [LDVLNR] = 0x450, | 75 | [LDVLNR] = 0x450, |
| 75 | [LDVSYNR] = 0x454, | 76 | [LDVSYNR] = 0x454, |
| 76 | [LDPMR] = 0x460, | 77 | [LDPMR] = 0x460, |
| 78 | [LDHAJR] = 0x4a0, | ||
| 77 | }; | 79 | }; |
| 78 | 80 | ||
| 79 | static unsigned long lcdc_offs_sublcd[NR_CH_REGS] = { | 81 | static unsigned long lcdc_offs_sublcd[NR_CH_REGS] = { |
| @@ -137,6 +139,7 @@ struct sh_mobile_lcdc_priv { | |||
| 137 | struct clk *dot_clk; | 139 | struct clk *dot_clk; |
| 138 | unsigned long lddckr; | 140 | unsigned long lddckr; |
| 139 | struct sh_mobile_lcdc_chan ch[2]; | 141 | struct sh_mobile_lcdc_chan ch[2]; |
| 142 | struct notifier_block notifier; | ||
| 140 | unsigned long saved_shared_regs[NR_SHARED_REGS]; | 143 | unsigned long saved_shared_regs[NR_SHARED_REGS]; |
| 141 | int started; | 144 | int started; |
| 142 | }; | 145 | }; |
| @@ -404,6 +407,56 @@ static void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv, | |||
| 404 | lcdc_write(priv, _LDDCKSTPR, 1); /* stop dotclock */ | 407 | lcdc_write(priv, _LDDCKSTPR, 1); /* stop dotclock */ |
| 405 | } | 408 | } |
| 406 | 409 | ||
| 410 | static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch) | ||
| 411 | { | ||
| 412 | struct fb_var_screeninfo *var = &ch->info->var; | ||
| 413 | unsigned long h_total, hsync_pos; | ||
| 414 | u32 tmp; | ||
| 415 | |||
| 416 | tmp = ch->ldmt1r_value; | ||
| 417 | tmp |= (var->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 1 << 28; | ||
| 418 | tmp |= (var->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : 1 << 27; | ||
| 419 | tmp |= (ch->cfg.flags & LCDC_FLAGS_DWPOL) ? 1 << 26 : 0; | ||
| 420 | tmp |= (ch->cfg.flags & LCDC_FLAGS_DIPOL) ? 1 << 25 : 0; | ||
| 421 | tmp |= (ch->cfg.flags & LCDC_FLAGS_DAPOL) ? 1 << 24 : 0; | ||
| 422 | tmp |= (ch->cfg.flags & LCDC_FLAGS_HSCNT) ? 1 << 17 : 0; | ||
| 423 | tmp |= (ch->cfg.flags & LCDC_FLAGS_DWCNT) ? 1 << 16 : 0; | ||
| 424 | lcdc_write_chan(ch, LDMT1R, tmp); | ||
| 425 | |||
| 426 | /* setup SYS bus */ | ||
| 427 | lcdc_write_chan(ch, LDMT2R, ch->cfg.sys_bus_cfg.ldmt2r); | ||
| 428 | lcdc_write_chan(ch, LDMT3R, ch->cfg.sys_bus_cfg.ldmt3r); | ||
| 429 | |||
| 430 | /* horizontal configuration */ | ||
| 431 | h_total = var->xres + var->hsync_len + | ||
| 432 | var->left_margin + var->right_margin; | ||
| 433 | tmp = h_total / 8; /* HTCN */ | ||
| 434 | tmp |= (var->xres / 8) << 16; /* HDCN */ | ||
| 435 | lcdc_write_chan(ch, LDHCNR, tmp); | ||
| 436 | |||
| 437 | hsync_pos = var->xres + var->right_margin; | ||
| 438 | tmp = hsync_pos / 8; /* HSYNP */ | ||
| 439 | tmp |= (var->hsync_len / 8) << 16; /* HSYNW */ | ||
| 440 | lcdc_write_chan(ch, LDHSYNR, tmp); | ||
| 441 | |||
| 442 | /* vertical configuration */ | ||
| 443 | tmp = var->yres + var->vsync_len + | ||
| 444 | var->upper_margin + var->lower_margin; /* VTLN */ | ||
| 445 | tmp |= var->yres << 16; /* VDLN */ | ||
| 446 | lcdc_write_chan(ch, LDVLNR, tmp); | ||
| 447 | |||
| 448 | tmp = var->yres + var->lower_margin; /* VSYNP */ | ||
| 449 | tmp |= var->vsync_len << 16; /* VSYNW */ | ||
| 450 | lcdc_write_chan(ch, LDVSYNR, tmp); | ||
| 451 | |||
| 452 | /* Adjust horizontal synchronisation for HDMI */ | ||
| 453 | tmp = ((var->xres & 7) << 24) | | ||
| 454 | ((h_total & 7) << 16) | | ||
| 455 | ((var->hsync_len & 7) << 8) | | ||
| 456 | hsync_pos; | ||
| 457 | lcdc_write_chan(ch, LDHAJR, tmp); | ||
| 458 | } | ||
| 459 | |||
| 407 | static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) | 460 | static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) |
| 408 | { | 461 | { |
| 409 | struct sh_mobile_lcdc_chan *ch; | 462 | struct sh_mobile_lcdc_chan *ch; |
| @@ -470,49 +523,11 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) | |||
| 470 | if (!ch->enabled) | 523 | if (!ch->enabled) |
| 471 | continue; | 524 | continue; |
| 472 | 525 | ||
| 473 | tmp = ch->ldmt1r_value; | 526 | sh_mobile_lcdc_geometry(ch); |
| 474 | tmp |= (lcd_cfg->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 1 << 28; | ||
| 475 | tmp |= (lcd_cfg->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : 1 << 27; | ||
| 476 | tmp |= (ch->cfg.flags & LCDC_FLAGS_DWPOL) ? 1 << 26 : 0; | ||
| 477 | tmp |= (ch->cfg.flags & LCDC_FLAGS_DIPOL) ? 1 << 25 : 0; | ||
| 478 | tmp |= (ch->cfg.flags & LCDC_FLAGS_DAPOL) ? 1 << 24 : 0; | ||
| 479 | tmp |= (ch->cfg.flags & LCDC_FLAGS_HSCNT) ? 1 << 17 : 0; | ||
| 480 | tmp |= (ch->cfg.flags & LCDC_FLAGS_DWCNT) ? 1 << 16 : 0; | ||
| 481 | lcdc_write_chan(ch, LDMT1R, tmp); | ||
| 482 | |||
| 483 | /* setup SYS bus */ | ||
| 484 | lcdc_write_chan(ch, LDMT2R, ch->cfg.sys_bus_cfg.ldmt2r); | ||
| 485 | lcdc_write_chan(ch, LDMT3R, ch->cfg.sys_bus_cfg.ldmt3r); | ||
| 486 | |||
| 487 | /* horizontal configuration */ | ||
| 488 | tmp = lcd_cfg->xres + lcd_cfg->hsync_len; | ||
| 489 | tmp += lcd_cfg->left_margin; | ||
| 490 | tmp += lcd_cfg->right_margin; | ||
| 491 | tmp /= 8; /* HTCN */ | ||
| 492 | tmp |= (lcd_cfg->xres / 8) << 16; /* HDCN */ | ||
| 493 | lcdc_write_chan(ch, LDHCNR, tmp); | ||
| 494 | |||
| 495 | tmp = lcd_cfg->xres; | ||
| 496 | tmp += lcd_cfg->right_margin; | ||
| 497 | tmp /= 8; /* HSYNP */ | ||
| 498 | tmp |= (lcd_cfg->hsync_len / 8) << 16; /* HSYNW */ | ||
| 499 | lcdc_write_chan(ch, LDHSYNR, tmp); | ||
| 500 | 527 | ||
| 501 | /* power supply */ | 528 | /* power supply */ |
| 502 | lcdc_write_chan(ch, LDPMR, 0); | 529 | lcdc_write_chan(ch, LDPMR, 0); |
| 503 | 530 | ||
| 504 | /* vertical configuration */ | ||
| 505 | tmp = lcd_cfg->yres + lcd_cfg->vsync_len; | ||
| 506 | tmp += lcd_cfg->upper_margin; | ||
| 507 | tmp += lcd_cfg->lower_margin; /* VTLN */ | ||
| 508 | tmp |= lcd_cfg->yres << 16; /* VDLN */ | ||
| 509 | lcdc_write_chan(ch, LDVLNR, tmp); | ||
| 510 | |||
| 511 | tmp = lcd_cfg->yres; | ||
| 512 | tmp += lcd_cfg->lower_margin; /* VSYNP */ | ||
| 513 | tmp |= lcd_cfg->vsync_len << 16; /* VSYNW */ | ||
| 514 | lcdc_write_chan(ch, LDVSYNR, tmp); | ||
| 515 | |||
| 516 | board_cfg = &ch->cfg.board_cfg; | 531 | board_cfg = &ch->cfg.board_cfg; |
| 517 | if (board_cfg->setup_sys) | 532 | if (board_cfg->setup_sys) |
| 518 | ret = board_cfg->setup_sys(board_cfg->board_data, ch, | 533 | ret = board_cfg->setup_sys(board_cfg->board_data, ch, |
| @@ -577,7 +592,7 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) | |||
| 577 | 592 | ||
| 578 | board_cfg = &ch->cfg.board_cfg; | 593 | board_cfg = &ch->cfg.board_cfg; |
| 579 | if (board_cfg->display_on) | 594 | if (board_cfg->display_on) |
| 580 | board_cfg->display_on(board_cfg->board_data); | 595 | board_cfg->display_on(board_cfg->board_data, ch->info); |
| 581 | } | 596 | } |
| 582 | 597 | ||
| 583 | return 0; | 598 | return 0; |
| @@ -943,6 +958,62 @@ static const struct dev_pm_ops sh_mobile_lcdc_dev_pm_ops = { | |||
| 943 | .runtime_resume = sh_mobile_lcdc_runtime_resume, | 958 | .runtime_resume = sh_mobile_lcdc_runtime_resume, |
| 944 | }; | 959 | }; |
| 945 | 960 | ||
| 961 | static int sh_mobile_lcdc_notify(struct notifier_block *nb, | ||
| 962 | unsigned long action, void *data) | ||
| 963 | { | ||
| 964 | struct fb_event *event = data; | ||
| 965 | struct fb_info *info = event->info; | ||
| 966 | struct sh_mobile_lcdc_chan *ch = info->par; | ||
| 967 | struct sh_mobile_lcdc_board_cfg *board_cfg = &ch->cfg.board_cfg; | ||
| 968 | struct fb_var_screeninfo *var; | ||
| 969 | |||
| 970 | if (&ch->lcdc->notifier != nb) | ||
| 971 | return 0; | ||
| 972 | |||
| 973 | dev_dbg(info->dev, "%s(): action = %lu, data = %p\n", | ||
| 974 | __func__, action, event->data); | ||
| 975 | |||
| 976 | switch(action) { | ||
| 977 | case FB_EVENT_SUSPEND: | ||
| 978 | if (board_cfg->display_off) | ||
| 979 | board_cfg->display_off(board_cfg->board_data); | ||
| 980 | pm_runtime_put(info->device); | ||
| 981 | break; | ||
| 982 | case FB_EVENT_RESUME: | ||
| 983 | var = &info->var; | ||
| 984 | |||
| 985 | /* HDMI must be enabled before LCDC configuration */ | ||
| 986 | if (board_cfg->display_on) | ||
| 987 | board_cfg->display_on(board_cfg->board_data, ch->info); | ||
| 988 | |||
| 989 | /* Check if the new display is not in our modelist */ | ||
| 990 | if (ch->info->modelist.next && | ||
| 991 | !fb_match_mode(var, &ch->info->modelist)) { | ||
| 992 | struct fb_videomode mode; | ||
| 993 | int ret; | ||
| 994 | |||
| 995 | /* Can we handle this display? */ | ||
| 996 | if (var->xres > ch->cfg.lcd_cfg.xres || | ||
| 997 | var->yres > ch->cfg.lcd_cfg.yres) | ||
| 998 | return -ENOMEM; | ||
| 999 | |||
| 1000 | /* Add to the modelist */ | ||
| 1001 | fb_var_to_videomode(&mode, var); | ||
| 1002 | ret = fb_add_videomode(&mode, &ch->info->modelist); | ||
| 1003 | if (ret < 0) | ||
| 1004 | return ret; | ||
| 1005 | } | ||
| 1006 | |||
| 1007 | pm_runtime_get_sync(info->device); | ||
| 1008 | |||
| 1009 | sh_mobile_lcdc_geometry(ch); | ||
| 1010 | |||
| 1011 | break; | ||
| 1012 | } | ||
| 1013 | |||
| 1014 | return 0; | ||
| 1015 | } | ||
| 1016 | |||
| 946 | static int sh_mobile_lcdc_remove(struct platform_device *pdev); | 1017 | static int sh_mobile_lcdc_remove(struct platform_device *pdev); |
| 947 | 1018 | ||
| 948 | static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) | 1019 | static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) |
| @@ -1020,15 +1091,19 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) | |||
| 1020 | goto err1; | 1091 | goto err1; |
| 1021 | } | 1092 | } |
| 1022 | 1093 | ||
| 1094 | priv->base = ioremap_nocache(res->start, resource_size(res)); | ||
| 1095 | if (!priv->base) | ||
| 1096 | goto err1; | ||
| 1097 | |||
| 1023 | error = sh_mobile_lcdc_setup_clocks(pdev, pdata->clock_source, priv); | 1098 | error = sh_mobile_lcdc_setup_clocks(pdev, pdata->clock_source, priv); |
| 1024 | if (error) { | 1099 | if (error) { |
| 1025 | dev_err(&pdev->dev, "unable to setup clocks\n"); | 1100 | dev_err(&pdev->dev, "unable to setup clocks\n"); |
| 1026 | goto err1; | 1101 | goto err1; |
| 1027 | } | 1102 | } |
| 1028 | 1103 | ||
| 1029 | priv->base = ioremap_nocache(res->start, (res->end - res->start) + 1); | ||
| 1030 | |||
| 1031 | for (i = 0; i < j; i++) { | 1104 | for (i = 0; i < j; i++) { |
| 1105 | struct fb_var_screeninfo *var; | ||
| 1106 | struct fb_videomode *lcd_cfg; | ||
| 1032 | cfg = &priv->ch[i].cfg; | 1107 | cfg = &priv->ch[i].cfg; |
| 1033 | 1108 | ||
| 1034 | priv->ch[i].info = framebuffer_alloc(0, &pdev->dev); | 1109 | priv->ch[i].info = framebuffer_alloc(0, &pdev->dev); |
| @@ -1039,22 +1114,33 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) | |||
| 1039 | } | 1114 | } |
| 1040 | 1115 | ||
| 1041 | info = priv->ch[i].info; | 1116 | info = priv->ch[i].info; |
| 1117 | var = &info->var; | ||
| 1118 | lcd_cfg = &cfg->lcd_cfg; | ||
| 1042 | info->fbops = &sh_mobile_lcdc_ops; | 1119 | info->fbops = &sh_mobile_lcdc_ops; |
| 1043 | info->var.xres = info->var.xres_virtual = cfg->lcd_cfg.xres; | 1120 | var->xres = var->xres_virtual = lcd_cfg->xres; |
| 1044 | info->var.yres = cfg->lcd_cfg.yres; | 1121 | var->yres = lcd_cfg->yres; |
| 1045 | /* Default Y virtual resolution is 2x panel size */ | 1122 | /* Default Y virtual resolution is 2x panel size */ |
| 1046 | info->var.yres_virtual = info->var.yres * 2; | 1123 | var->yres_virtual = var->yres * 2; |
| 1047 | info->var.width = cfg->lcd_size_cfg.width; | 1124 | var->width = cfg->lcd_size_cfg.width; |
| 1048 | info->var.height = cfg->lcd_size_cfg.height; | 1125 | var->height = cfg->lcd_size_cfg.height; |
| 1049 | info->var.activate = FB_ACTIVATE_NOW; | 1126 | var->activate = FB_ACTIVATE_NOW; |
| 1050 | error = sh_mobile_lcdc_set_bpp(&info->var, cfg->bpp); | 1127 | var->left_margin = lcd_cfg->left_margin; |
| 1128 | var->right_margin = lcd_cfg->right_margin; | ||
| 1129 | var->upper_margin = lcd_cfg->upper_margin; | ||
| 1130 | var->lower_margin = lcd_cfg->lower_margin; | ||
| 1131 | var->hsync_len = lcd_cfg->hsync_len; | ||
| 1132 | var->vsync_len = lcd_cfg->vsync_len; | ||
| 1133 | var->sync = lcd_cfg->sync; | ||
| 1134 | var->pixclock = lcd_cfg->pixclock; | ||
| 1135 | |||
| 1136 | error = sh_mobile_lcdc_set_bpp(var, cfg->bpp); | ||
| 1051 | if (error) | 1137 | if (error) |
| 1052 | break; | 1138 | break; |
| 1053 | 1139 | ||
| 1054 | info->fix = sh_mobile_lcdc_fix; | 1140 | info->fix = sh_mobile_lcdc_fix; |
| 1055 | info->fix.line_length = cfg->lcd_cfg.xres * (cfg->bpp / 8); | 1141 | info->fix.line_length = lcd_cfg->xres * (cfg->bpp / 8); |
| 1056 | info->fix.smem_len = info->fix.line_length * | 1142 | info->fix.smem_len = info->fix.line_length * |
| 1057 | info->var.yres_virtual; | 1143 | var->yres_virtual; |
| 1058 | 1144 | ||
| 1059 | buf = dma_alloc_coherent(&pdev->dev, info->fix.smem_len, | 1145 | buf = dma_alloc_coherent(&pdev->dev, info->fix.smem_len, |
| 1060 | &priv->ch[i].dma_handle, GFP_KERNEL); | 1146 | &priv->ch[i].dma_handle, GFP_KERNEL); |
| @@ -1119,10 +1205,14 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) | |||
| 1119 | ch->cfg.bpp); | 1205 | ch->cfg.bpp); |
| 1120 | 1206 | ||
| 1121 | /* deferred io mode: disable clock to save power */ | 1207 | /* deferred io mode: disable clock to save power */ |
| 1122 | if (info->fbdefio) | 1208 | if (info->fbdefio || info->state == FBINFO_STATE_SUSPENDED) |
| 1123 | sh_mobile_lcdc_clk_off(priv); | 1209 | sh_mobile_lcdc_clk_off(priv); |
| 1124 | } | 1210 | } |
| 1125 | 1211 | ||
| 1212 | /* Failure ignored */ | ||
| 1213 | priv->notifier.notifier_call = sh_mobile_lcdc_notify; | ||
| 1214 | fb_register_client(&priv->notifier); | ||
| 1215 | |||
| 1126 | return 0; | 1216 | return 0; |
| 1127 | err1: | 1217 | err1: |
| 1128 | sh_mobile_lcdc_remove(pdev); | 1218 | sh_mobile_lcdc_remove(pdev); |
| @@ -1136,6 +1226,8 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev) | |||
| 1136 | struct fb_info *info; | 1226 | struct fb_info *info; |
| 1137 | int i; | 1227 | int i; |
| 1138 | 1228 | ||
| 1229 | fb_unregister_client(&priv->notifier); | ||
| 1230 | |||
| 1139 | for (i = 0; i < ARRAY_SIZE(priv->ch); i++) | 1231 | for (i = 0; i < ARRAY_SIZE(priv->ch); i++) |
| 1140 | if (priv->ch[i].info && priv->ch[i].info->dev) | 1232 | if (priv->ch[i].info && priv->ch[i].info->dev) |
| 1141 | unregister_framebuffer(priv->ch[i].info); | 1233 | unregister_framebuffer(priv->ch[i].info); |
diff --git a/drivers/video/sunxvr1000.c b/drivers/video/sunxvr1000.c index 7288934c0d49..5dbe06af2226 100644 --- a/drivers/video/sunxvr1000.c +++ b/drivers/video/sunxvr1000.c | |||
| @@ -111,7 +111,7 @@ static int __devinit gfb_set_fbinfo(struct gfb_info *gp) | |||
| 111 | return 0; | 111 | return 0; |
| 112 | } | 112 | } |
| 113 | 113 | ||
| 114 | static int __devinit gfb_probe(struct of_device *op, | 114 | static int __devinit gfb_probe(struct platform_device *op, |
| 115 | const struct of_device_id *match) | 115 | const struct of_device_id *match) |
| 116 | { | 116 | { |
| 117 | struct device_node *dp = op->dev.of_node; | 117 | struct device_node *dp = op->dev.of_node; |
| @@ -172,7 +172,7 @@ err_out: | |||
| 172 | return err; | 172 | return err; |
| 173 | } | 173 | } |
| 174 | 174 | ||
| 175 | static int __devexit gfb_remove(struct of_device *op) | 175 | static int __devexit gfb_remove(struct platform_device *op) |
| 176 | { | 176 | { |
| 177 | struct fb_info *info = dev_get_drvdata(&op->dev); | 177 | struct fb_info *info = dev_get_drvdata(&op->dev); |
| 178 | struct gfb_info *gp = info->par; | 178 | struct gfb_info *gp = info->par; |
diff --git a/drivers/video/tcx.c b/drivers/video/tcx.c index f375e0db6776..77ad27955cf0 100644 --- a/drivers/video/tcx.c +++ b/drivers/video/tcx.c | |||
| @@ -342,7 +342,7 @@ tcx_init_fix(struct fb_info *info, int linebytes) | |||
| 342 | info->fix.accel = FB_ACCEL_SUN_TCX; | 342 | info->fix.accel = FB_ACCEL_SUN_TCX; |
| 343 | } | 343 | } |
| 344 | 344 | ||
| 345 | static void tcx_unmap_regs(struct of_device *op, struct fb_info *info, | 345 | static void tcx_unmap_regs(struct platform_device *op, struct fb_info *info, |
| 346 | struct tcx_par *par) | 346 | struct tcx_par *par) |
| 347 | { | 347 | { |
| 348 | if (par->tec) | 348 | if (par->tec) |
| @@ -362,7 +362,7 @@ static void tcx_unmap_regs(struct of_device *op, struct fb_info *info, | |||
| 362 | info->screen_base, info->fix.smem_len); | 362 | info->screen_base, info->fix.smem_len); |
| 363 | } | 363 | } |
| 364 | 364 | ||
| 365 | static int __devinit tcx_probe(struct of_device *op, | 365 | static int __devinit tcx_probe(struct platform_device *op, |
| 366 | const struct of_device_id *match) | 366 | const struct of_device_id *match) |
| 367 | { | 367 | { |
| 368 | struct device_node *dp = op->dev.of_node; | 368 | struct device_node *dp = op->dev.of_node; |
| @@ -486,7 +486,7 @@ out_err: | |||
| 486 | return err; | 486 | return err; |
| 487 | } | 487 | } |
| 488 | 488 | ||
| 489 | static int __devexit tcx_remove(struct of_device *op) | 489 | static int __devexit tcx_remove(struct platform_device *op) |
| 490 | { | 490 | { |
| 491 | struct fb_info *info = dev_get_drvdata(&op->dev); | 491 | struct fb_info *info = dev_get_drvdata(&op->dev); |
| 492 | struct tcx_par *par = info->par; | 492 | struct tcx_par *par = info->par; |
diff --git a/drivers/video/uvesafb.c b/drivers/video/uvesafb.c index 7b8839ebf3c4..52ec0959d462 100644 --- a/drivers/video/uvesafb.c +++ b/drivers/video/uvesafb.c | |||
| @@ -1977,8 +1977,7 @@ static void __devexit uvesafb_exit(void) | |||
| 1977 | 1977 | ||
| 1978 | module_exit(uvesafb_exit); | 1978 | module_exit(uvesafb_exit); |
| 1979 | 1979 | ||
| 1980 | #define param_get_scroll NULL | 1980 | static int param_set_scroll(const char *val, const struct kernel_param *kp) |
| 1981 | static int param_set_scroll(const char *val, struct kernel_param *kp) | ||
| 1982 | { | 1981 | { |
| 1983 | ypan = 0; | 1982 | ypan = 0; |
| 1984 | 1983 | ||
| @@ -1993,7 +1992,9 @@ static int param_set_scroll(const char *val, struct kernel_param *kp) | |||
| 1993 | 1992 | ||
| 1994 | return 0; | 1993 | return 0; |
| 1995 | } | 1994 | } |
| 1996 | 1995 | static struct kernel_param_ops param_ops_scroll = { | |
| 1996 | .set = param_set_scroll, | ||
| 1997 | }; | ||
| 1997 | #define param_check_scroll(name, p) __param_check(name, p, void) | 1998 | #define param_check_scroll(name, p) __param_check(name, p, void) |
| 1998 | 1999 | ||
| 1999 | module_param_named(scroll, ypan, scroll, 0); | 2000 | module_param_named(scroll, ypan, scroll, 0); |
diff --git a/drivers/video/vt8623fb.c b/drivers/video/vt8623fb.c index d31dc96f838a..85d76ec4c63e 100644 --- a/drivers/video/vt8623fb.c +++ b/drivers/video/vt8623fb.c | |||
| @@ -726,7 +726,9 @@ static int __devinit vt8623_pci_probe(struct pci_dev *dev, const struct pci_devi | |||
| 726 | 726 | ||
| 727 | /* Prepare startup mode */ | 727 | /* Prepare startup mode */ |
| 728 | 728 | ||
| 729 | kparam_block_sysfs_write(mode_option); | ||
| 729 | rc = fb_find_mode(&(info->var), info, mode_option, NULL, 0, NULL, 8); | 730 | rc = fb_find_mode(&(info->var), info, mode_option, NULL, 0, NULL, 8); |
| 731 | kparam_unblock_sysfs_write(mode_option); | ||
| 730 | if (! ((rc == 1) || (rc == 2))) { | 732 | if (! ((rc == 1) || (rc == 2))) { |
| 731 | rc = -EINVAL; | 733 | rc = -EINVAL; |
| 732 | dev_err(info->device, "mode %s not found\n", mode_option); | 734 | dev_err(info->device, "mode %s not found\n", mode_option); |
diff --git a/drivers/video/xilinxfb.c b/drivers/video/xilinxfb.c index 29b5daacc217..0c9ce88e95e8 100644 --- a/drivers/video/xilinxfb.c +++ b/drivers/video/xilinxfb.c | |||
| @@ -397,7 +397,7 @@ static int xilinxfb_release(struct device *dev) | |||
| 397 | */ | 397 | */ |
| 398 | 398 | ||
| 399 | static int __devinit | 399 | static int __devinit |
| 400 | xilinxfb_of_probe(struct of_device *op, const struct of_device_id *match) | 400 | xilinxfb_of_probe(struct platform_device *op, const struct of_device_id *match) |
| 401 | { | 401 | { |
| 402 | const u32 *prop; | 402 | const u32 *prop; |
| 403 | u32 *p; | 403 | u32 *p; |
| @@ -477,7 +477,7 @@ xilinxfb_of_probe(struct of_device *op, const struct of_device_id *match) | |||
| 477 | return -ENODEV; | 477 | return -ENODEV; |
| 478 | } | 478 | } |
| 479 | 479 | ||
| 480 | static int __devexit xilinxfb_of_remove(struct of_device *op) | 480 | static int __devexit xilinxfb_of_remove(struct platform_device *op) |
| 481 | { | 481 | { |
| 482 | return xilinxfb_release(&op->dev); | 482 | return xilinxfb_release(&op->dev); |
| 483 | } | 483 | } |
