diff options
Diffstat (limited to 'drivers/video')
152 files changed, 12459 insertions, 8052 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index d5438ecebc88..e791427c0b2b 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -32,7 +32,7 @@ config VGASTATE config VIDEO_OUTPUT_CONTROL tristate "Lowlevel video output switch controls" help - This framework adds support for low-level control of the video + This framework adds support for low-level control of the video output switch. menuconfig FB @@ -693,7 +693,7 @@ config FB_STI BIOS routines contained in a ROM chip in HP PA-RISC based machines. Enabling this option will implement the linux framebuffer device using calls to the STI BIOS routines for initialisation. - + If you enable this option, you will get a planar framebuffer device /dev/fb which will work on the most common HP graphic cards of the NGLE family, including the artist chips (in the 7xx and Bxxx series), @@ -1125,6 +1125,18 @@ config FB_RIVA_BACKLIGHT help Say Y here if you want to control the backlight of your display. +config FB_I740 + tristate "Intel740 support (EXPERIMENTAL)" + depends on EXPERIMENTAL && FB && PCI + select FB_MODE_HELPERS + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + select VGASTATE + select FB_DDC + help + This driver supports graphics cards based on Intel740 chip. + config FB_I810 tristate "Intel 810/815 support (EXPERIMENTAL)" depends on EXPERIMENTAL && FB && PCI && X86_32 && AGP_INTEL @@ -1134,36 +1146,36 @@ config FB_I810 select FB_CFB_IMAGEBLIT select VGASTATE help - This driver supports the on-board graphics built in to the Intel 810 + This driver supports the on-board graphics built in to the Intel 810 and 815 chipsets. Say Y if you have and plan to use such a board. To compile this driver as a module, choose M here: the module will be called i810fb. - For more information, please read + For more information, please read <file:Documentation/fb/intel810.txt> config FB_I810_GTF bool "use VESA Generalized Timing Formula" depends on FB_I810 help - If you say Y, then the VESA standard, Generalized Timing Formula + If you say Y, then the VESA standard, Generalized Timing Formula or GTF, will be used to calculate the required video timing values - per video mode. Since the GTF allows nondiscrete timings + per video mode. Since the GTF allows nondiscrete timings (nondiscrete being a range of values as opposed to discrete being a - set of values), you'll be able to use any combination of horizontal + set of values), you'll be able to use any combination of horizontal and vertical resolutions, and vertical refresh rates without having to specify your own timing parameters. This is especially useful - to maximize the performance of an aging display, or if you just - have a display with nonstandard dimensions. A VESA compliant + to maximize the performance of an aging display, or if you just + have a display with nonstandard dimensions. A VESA compliant monitor is recommended, but can still work with non-compliant ones. - If you need or want this, then select this option. The timings may - not be compliant with Intel's recommended values. Use at your own + If you need or want this, then select this option. The timings may + not be compliant with Intel's recommended values. Use at your own risk. - If you say N, the driver will revert to discrete video timings + If you say N, the driver will revert to discrete video timings using a set recommended by Intel in their documentation. - + If unsure, say N. config FB_I810_I2C @@ -1281,10 +1293,10 @@ config FB_MATROX_G G450/G550 secondary head and digital output are supported without additional modules. - The driver starts in monitor mode. You must use the matroxset tool - (available at <ftp://platan.vc.cvut.cz/pub/linux/matrox-latest/>) to - swap primary and secondary head outputs, or to change output mode. - Secondary head driver always start in 640x480 resolution and you + The driver starts in monitor mode. You must use the matroxset tool + (available at <ftp://platan.vc.cvut.cz/pub/linux/matrox-latest/>) to + swap primary and secondary head outputs, or to change output mode. + Secondary head driver always start in 640x480 resolution and you must use fbset to change it. Do not forget that second head supports only 16 and 32 bpp @@ -1367,7 +1379,7 @@ config FB_RADEON_I2C select FB_DDC default y help - Say Y here if you want DDC/I2C support for your Radeon board. + Say Y here if you want DDC/I2C support for your Radeon board. config FB_RADEON_BACKLIGHT bool "Support for backlight control" @@ -1601,7 +1613,7 @@ config FB_NEOMAGIC select VGASTATE help This driver supports notebooks with NeoMagic PCI chips. - Say Y if you have such a graphics card. + Say Y if you have such a graphics card. To compile this driver as a module, choose M here: the module will be called neofb. @@ -1656,7 +1668,7 @@ config FB_VOODOO1 select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT ---help--- - Say Y here if you have a 3Dfx Voodoo Graphics (Voodoo1/sst1) or + Say Y here if you have a 3Dfx Voodoo Graphics (Voodoo1/sst1) or Voodoo2 (cvg) based graphics card. To compile this driver as a module, choose M here: the @@ -2003,18 +2015,6 @@ config FB_SH_MOBILE_HDMI ---help--- Driver for the on-chip SH-Mobile HDMI controller. -config FB_SH_MOBILE_MERAM - tristate "SuperH Mobile MERAM read ahead support for LCDC" - depends on FB_SH_MOBILE_LCDC - default y - ---help--- - Enable MERAM support for the SH-Mobile LCD controller. - - This will allow for caching of the framebuffer to provide more - reliable access under heavy main memory bus traffic situations. - Up to 4 memory channels can be configured, allowing 4 RGB or - 2 YCbCr framebuffers to be configured. - config FB_TMIO tristate "Toshiba Mobile IO FrameBuffer support" depends on FB && MFD_CORE @@ -2063,7 +2063,7 @@ config FB_S3C_DEBUG_REGWRITE config FB_S3C2410 tristate "S3C2410 LCD framebuffer support" - depends on FB && ARCH_S3C2410 + depends on FB && ARCH_S3C24XX select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT @@ -2235,6 +2235,7 @@ config FB_DA8XX select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT + select FB_CFB_REV_PIXELS_IN_BYTE ---help--- This is the frame buffer device driver for the TI LCD controller found on DA8xx/OMAP-L1xx SoCs. @@ -2271,6 +2272,7 @@ config XEN_FBDEV_FRONTEND select FB_SYS_IMAGEBLIT select FB_SYS_FOPS select FB_DEFERRED_IO + select INPUT_XEN_KBDDEV_FRONTEND select XEN_XENBUS_FRONTEND default y help @@ -2414,7 +2416,7 @@ config FB_PUV3_UNIGFX source "drivers/video/omap/Kconfig" source "drivers/video/omap2/Kconfig" source "drivers/video/tegra/Kconfig" - +source "drivers/video/exynos/Kconfig" source "drivers/video/backlight/Kconfig" if VT @@ -2425,4 +2427,16 @@ if FB || SGI_NEWPORT_CONSOLE source "drivers/video/logo/Kconfig" endif +config FB_SH_MOBILE_MERAM + tristate "SuperH Mobile MERAM read ahead support" + depends on (SUPERH || ARCH_SHMOBILE) + select GENERIC_ALLOCATOR + ---help--- + Enable MERAM support for the SuperH controller. + + This will allow for caching of the framebuffer to provide more + reliable access under heavy main memory bus traffic situations. + Up to 4 memory channels can be configured, allowing 4 RGB or + 2 YCbCr framebuffers to be configured. + endmenu diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 6221af54b6ac..81d13d90870f 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -15,6 +15,8 @@ obj-$(CONFIG_VT) += console/ obj-$(CONFIG_LOGO) += logo/ obj-y += backlight/ +obj-$(CONFIG_EXYNOS_VIDEO) += exynos/ + obj-$(CONFIG_FB_CFB_FILLRECT) += cfbfillrect.o obj-$(CONFIG_FB_CFB_COPYAREA) += cfbcopyarea.o obj-$(CONFIG_FB_CFB_IMAGEBLIT) += cfbimgblt.o @@ -37,6 +39,7 @@ obj-$(CONFIG_FB_GRVGA) += grvga.o obj-$(CONFIG_FB_PM2) += pm2fb.o obj-$(CONFIG_FB_PM3) += pm3fb.o +obj-$(CONFIG_FB_I740) += i740fb.o obj-$(CONFIG_FB_MATROX) += matrox/ obj-$(CONFIG_FB_RIVA) += riva/ obj-$(CONFIG_FB_NVIDIA) += nvidia/ diff --git a/drivers/video/amifb.c b/drivers/video/amifb.c index f23cae094f1b..887df9d81422 100644 --- a/drivers/video/amifb.c +++ b/drivers/video/amifb.c @@ -53,7 +53,6 @@ #include <linux/platform_device.h> #include <linux/uaccess.h> -#include <asm/system.h> #include <asm/irq.h> #include <asm/amigahw.h> #include <asm/amigaints.h> diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c index e40c00f2c2ba..d99505b16374 100644 --- a/drivers/video/atmel_lcdfb.c +++ b/drivers/video/atmel_lcdfb.c @@ -421,24 +421,18 @@ static int atmel_lcdfb_check_var(struct fb_var_screeninfo *var, var->red.length = var->green.length = var->blue.length = var->bits_per_pixel; break; - case 15: case 16: if (sinfo->lcd_wiring_mode == ATMEL_LCDC_WIRING_RGB) { /* RGB:565 mode */ var->red.offset = 11; var->blue.offset = 0; - var->green.length = 6; - } else if (sinfo->lcd_wiring_mode == ATMEL_LCDC_WIRING_RGB555) { - var->red.offset = 10; - var->blue.offset = 0; - var->green.length = 5; } else { - /* BGR:555 mode */ + /* BGR:565 mode */ var->red.offset = 0; - var->blue.offset = 10; - var->green.length = 5; + var->blue.offset = 11; } var->green.offset = 5; + var->green.length = 6; var->red.length = var->blue.length = 5; break; case 32: diff --git a/drivers/video/au1100fb.c b/drivers/video/au1100fb.c index de9da6774fd9..befcbd8ef019 100644 --- a/drivers/video/au1100fb.c +++ b/drivers/video/au1100fb.c @@ -477,7 +477,8 @@ static int __devinit au1100fb_drv_probe(struct platform_device *dev) u32 sys_clksrc; /* Allocate new device private */ - fbdev = kzalloc(sizeof(struct au1100fb_device), GFP_KERNEL); + fbdev = devm_kzalloc(&dev->dev, sizeof(struct au1100fb_device), + GFP_KERNEL); if (!fbdev) { print_err("fail to allocate device private record"); return -ENOMEM; @@ -498,8 +499,9 @@ static int __devinit au1100fb_drv_probe(struct platform_device *dev) au1100fb_fix.mmio_start = regs_res->start; au1100fb_fix.mmio_len = resource_size(regs_res); - if (!request_mem_region(au1100fb_fix.mmio_start, au1100fb_fix.mmio_len, - DRIVER_NAME)) { + if (!devm_request_mem_region(au1100fb_fix.mmio_start, + au1100fb_fix.mmio_len, + DRIVER_NAME)) { print_err("fail to lock memory region at 0x%08lx", au1100fb_fix.mmio_start); return -EBUSY; @@ -514,8 +516,9 @@ static int __devinit au1100fb_drv_probe(struct platform_device *dev) fbdev->fb_len = fbdev->panel->xres * fbdev->panel->yres * (fbdev->panel->bpp >> 3) * AU1100FB_NBR_VIDEO_BUFFERS; - fbdev->fb_mem = dma_alloc_coherent(&dev->dev, PAGE_ALIGN(fbdev->fb_len), - &fbdev->fb_phys, GFP_KERNEL); + fbdev->fb_mem = dmam_alloc_coherent(&dev->dev, &dev->dev, + PAGE_ALIGN(fbdev->fb_len), + &fbdev->fb_phys, GFP_KERNEL); if (!fbdev->fb_mem) { print_err("fail to allocate frambuffer (size: %dK))", fbdev->fb_len / 1024); @@ -557,14 +560,14 @@ static int __devinit au1100fb_drv_probe(struct platform_device *dev) fbdev->info.fbops = &au1100fb_ops; fbdev->info.fix = au1100fb_fix; - if (!(fbdev->info.pseudo_palette = kzalloc(sizeof(u32) * 16, GFP_KERNEL))) { + fbdev->info.pseudo_palette = + devm_kzalloc(&dev->dev, sizeof(u32) * 16, GFP_KERNEL); + if (!fbdev->info.pseudo_palette) return -ENOMEM; - } if (fb_alloc_cmap(&fbdev->info.cmap, AU1100_LCD_NBR_PALETTE_ENTRIES, 0) < 0) { print_err("Fail to allocate colormap (%d entries)", AU1100_LCD_NBR_PALETTE_ENTRIES); - kfree(fbdev->info.pseudo_palette); return -EFAULT; } @@ -582,9 +585,6 @@ static int __devinit au1100fb_drv_probe(struct platform_device *dev) return 0; failed: - if (fbdev->regs) { - release_mem_region(fbdev->regs_phys, fbdev->regs_len); - } if (fbdev->fb_mem) { dma_free_noncoherent(&dev->dev, fbdev->fb_len, fbdev->fb_mem, fbdev->fb_phys); @@ -592,10 +592,9 @@ failed: if (fbdev->info.cmap.len != 0) { fb_dealloc_cmap(&fbdev->info.cmap); } - kfree(fbdev); platform_set_drvdata(dev, NULL); - return 0; + return -ENODEV; } int au1100fb_drv_remove(struct platform_device *dev) @@ -615,14 +614,7 @@ int au1100fb_drv_remove(struct platform_device *dev) /* Clean up all probe data */ unregister_framebuffer(&fbdev->info); - release_mem_region(fbdev->regs_phys, fbdev->regs_len); - - dma_free_coherent(&dev->dev, PAGE_ALIGN(fbdev->fb_len), fbdev->fb_mem, - fbdev->fb_phys); - fb_dealloc_cmap(&fbdev->info.cmap); - kfree(fbdev->info.pseudo_palette); - kfree((void*)fbdev); return 0; } diff --git a/drivers/video/au1200fb.c b/drivers/video/au1200fb.c index 04e4479d5afd..3e9a773db09f 100644 --- a/drivers/video/au1200fb.c +++ b/drivers/video/au1200fb.c @@ -1724,7 +1724,7 @@ static int __devinit au1200fb_drv_probe(struct platform_device *dev) /* Allocate the framebuffer to the maximum screen size */ fbdev->fb_len = (win->w[plane].xres * win->w[plane].yres * bpp) / 8; - fbdev->fb_mem = dma_alloc_noncoherent(&dev->dev, + fbdev->fb_mem = dmam_alloc_noncoherent(&dev->dev, &dev->dev, PAGE_ALIGN(fbdev->fb_len), &fbdev->fb_phys, GFP_KERNEL); if (!fbdev->fb_mem) { @@ -1788,9 +1788,6 @@ static int __devinit au1200fb_drv_probe(struct platform_device *dev) failed: /* NOTE: This only does the current plane/window that failed; others are still active */ - if (fbdev->fb_mem) - dma_free_noncoherent(&dev->dev, PAGE_ALIGN(fbdev->fb_len), - fbdev->fb_mem, fbdev->fb_phys); if (fbi) { if (fbi->cmap.len != 0) fb_dealloc_cmap(&fbi->cmap); @@ -1817,10 +1814,6 @@ static int __devexit au1200fb_drv_remove(struct platform_device *dev) /* Clean up all probe data */ unregister_framebuffer(fbi); - if (fbdev->fb_mem) - dma_free_noncoherent(&dev->dev, - PAGE_ALIGN(fbdev->fb_len), - fbdev->fb_mem, fbdev->fb_phys); if (fbi->cmap.len != 0) fb_dealloc_cmap(&fbi->cmap); kfree(fbi->pseudo_palette); diff --git a/drivers/video/backlight/88pm860x_bl.c b/drivers/video/backlight/88pm860x_bl.c index a1376dc73d71..f49181c73113 100644 --- a/drivers/video/backlight/88pm860x_bl.c +++ b/drivers/video/backlight/88pm860x_bl.c @@ -67,6 +67,28 @@ static inline int wled_idc(int port) return ret; } +static int backlight_power_set(struct pm860x_chip *chip, int port, + int on) +{ + int ret = -EINVAL; + + switch (port) { + case PM8606_BACKLIGHT1: + ret = on ? pm8606_osc_enable(chip, WLED1_DUTY) : + pm8606_osc_disable(chip, WLED1_DUTY); + break; + case PM8606_BACKLIGHT2: + ret = on ? pm8606_osc_enable(chip, WLED2_DUTY) : + pm8606_osc_disable(chip, WLED2_DUTY); + break; + case PM8606_BACKLIGHT3: + ret = on ? pm8606_osc_enable(chip, WLED3_DUTY) : + pm8606_osc_disable(chip, WLED3_DUTY); + break; + } + return ret; +} + static int pm860x_backlight_set(struct backlight_device *bl, int brightness) { struct pm860x_backlight_data *data = bl_get_data(bl); @@ -79,6 +101,9 @@ static int pm860x_backlight_set(struct backlight_device *bl, int brightness) else value = brightness; + if (brightness) + backlight_power_set(chip, data->port, 1); + ret = pm860x_reg_write(data->i2c, wled_a(data->port), value); if (ret < 0) goto out; @@ -115,6 +140,9 @@ static int pm860x_backlight_set(struct backlight_device *bl, int brightness) if (ret < 0) goto out; + if (brightness == 0) + backlight_power_set(chip, data->port, 0); + dev_dbg(chip->dev, "set brightness %d\n", value); data->current_brightness = value; return 0; @@ -170,7 +198,6 @@ static int pm860x_backlight_probe(struct platform_device *pdev) struct backlight_device *bl; struct resource *res; struct backlight_properties props; - unsigned char value; char name[MFD_NAME_SIZE]; int ret; @@ -187,7 +214,8 @@ static int pm860x_backlight_probe(struct platform_device *pdev) return -EINVAL; } - data = kzalloc(sizeof(struct pm860x_backlight_data), GFP_KERNEL); + data = devm_kzalloc(&pdev->dev, sizeof(struct pm860x_backlight_data), + GFP_KERNEL); if (data == NULL) return -ENOMEM; strncpy(name, res->name, MFD_NAME_SIZE); @@ -200,7 +228,6 @@ static int pm860x_backlight_probe(struct platform_device *pdev) data->port = pdata->flags; if (data->port < 0) { dev_err(&pdev->dev, "wrong platform data is assigned"); - kfree(data); return -EINVAL; } @@ -211,33 +238,12 @@ static int pm860x_backlight_probe(struct platform_device *pdev) &pm860x_backlight_ops, &props); if (IS_ERR(bl)) { dev_err(&pdev->dev, "failed to register backlight\n"); - kfree(data); return PTR_ERR(bl); } bl->props.brightness = MAX_BRIGHTNESS; platform_set_drvdata(pdev, bl); - /* Enable reference VSYS */ - ret = pm860x_reg_read(data->i2c, PM8606_VSYS); - if (ret < 0) - goto out; - if ((ret & PM8606_VSYS_EN) == 0) { - value = ret | PM8606_VSYS_EN; - ret = pm860x_reg_write(data->i2c, PM8606_VSYS, value); - if (ret < 0) - goto out; - } - /* Enable reference OSC */ - ret = pm860x_reg_read(data->i2c, PM8606_MISC); - if (ret < 0) - goto out; - if ((ret & PM8606_MISC_OSC_EN) == 0) { - value = ret | PM8606_MISC_OSC_EN; - ret = pm860x_reg_write(data->i2c, PM8606_MISC, value); - if (ret < 0) - goto out; - } /* read current backlight */ ret = pm860x_backlight_get_brightness(bl); if (ret < 0) @@ -247,17 +253,14 @@ static int pm860x_backlight_probe(struct platform_device *pdev) return 0; out: backlight_device_unregister(bl); - kfree(data); return ret; } static int pm860x_backlight_remove(struct platform_device *pdev) { struct backlight_device *bl = platform_get_drvdata(pdev); - struct pm860x_backlight_data *data = bl_get_data(bl); backlight_device_unregister(bl); - kfree(data); return 0; } diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 472fda9ed4e9..800b57183c0a 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -344,6 +344,27 @@ config BACKLIGHT_AAT2870 If you have a AnalogicTech AAT2870 say Y to enable the backlight driver. +config BACKLIGHT_LP855X + tristate "Backlight driver for TI LP855X" + depends on BACKLIGHT_CLASS_DEVICE && I2C + help + This supports TI LP8550, LP8551, LP8552, LP8553 and LP8556 + backlight driver. + +config BACKLIGHT_OT200 + tristate "Backlight driver for ot200 visualisation device" + depends on BACKLIGHT_CLASS_DEVICE && CS5535_MFGPT && GPIO_CS5535 + help + To compile this driver as a module, choose M here: the module will be + called ot200_bl. + +config BACKLIGHT_PANDORA + tristate "Backlight driver for Pandora console" + depends on TWL4030_CORE + help + If you have a Pandora console, say Y to enable the + backlight driver. + endif # BACKLIGHT_CLASS_DEVICE endif # BACKLIGHT_LCD_SUPPORT diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index 173ddd438a01..cc4e04b3482b 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -22,7 +22,9 @@ obj-$(CONFIG_BACKLIGHT_GENERIC) += generic_bl.o obj-$(CONFIG_BACKLIGHT_HP700) += jornada720_bl.o obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o +obj-$(CONFIG_BACKLIGHT_LP855X) += lp855x_bl.o obj-$(CONFIG_BACKLIGHT_OMAP1) += omap1_bl.o +obj-$(CONFIG_BACKLIGHT_PANDORA) += pandora_bl.o obj-$(CONFIG_BACKLIGHT_PROGEAR) += progear_bl.o obj-$(CONFIG_BACKLIGHT_CARILLO_RANCH) += cr_bllcd.o obj-$(CONFIG_BACKLIGHT_PWM) += pwm_bl.o @@ -39,4 +41,4 @@ obj-$(CONFIG_BACKLIGHT_ADP8870) += adp8870_bl.o obj-$(CONFIG_BACKLIGHT_88PM860X) += 88pm860x_bl.o obj-$(CONFIG_BACKLIGHT_PCF50633) += pcf50633-backlight.o obj-$(CONFIG_BACKLIGHT_AAT2870) += aat2870_bl.o - +obj-$(CONFIG_BACKLIGHT_OT200) += ot200_bl.o diff --git a/drivers/video/backlight/aat2870_bl.c b/drivers/video/backlight/aat2870_bl.c index 203eaa65717e..24c2ba179e77 100644 --- a/drivers/video/backlight/aat2870_bl.c +++ b/drivers/video/backlight/aat2870_bl.c @@ -146,7 +146,9 @@ static int aat2870_bl_probe(struct platform_device *pdev) goto out; } - aat2870_bl = kzalloc(sizeof(struct aat2870_bl_driver_data), GFP_KERNEL); + aat2870_bl = devm_kzalloc(&pdev->dev, + sizeof(struct aat2870_bl_driver_data), + GFP_KERNEL); if (!aat2870_bl) { dev_err(&pdev->dev, "Failed to allocate memory for aat2870 backlight\n"); @@ -163,7 +165,7 @@ static int aat2870_bl_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Failed allocate memory for backlight device\n"); ret = PTR_ERR(bd); - goto out_kfree; + goto out; } aat2870_bl->pdev = pdev; @@ -200,8 +202,6 @@ static int aat2870_bl_probe(struct platform_device *pdev) out_bl_dev_unregister: backlight_device_unregister(bd); -out_kfree: - kfree(aat2870_bl); out: return ret; } @@ -216,7 +216,6 @@ static int aat2870_bl_remove(struct platform_device *pdev) backlight_update_status(bd); backlight_device_unregister(bd); - kfree(aat2870_bl); return 0; } diff --git a/drivers/video/backlight/adp5520_bl.c b/drivers/video/backlight/adp5520_bl.c index 2e630bf1164c..4911ea7989c8 100644 --- a/drivers/video/backlight/adp5520_bl.c +++ b/drivers/video/backlight/adp5520_bl.c @@ -289,7 +289,7 @@ static int __devinit adp5520_bl_probe(struct platform_device *pdev) struct adp5520_bl *data; int ret = 0; - data = kzalloc(sizeof(*data), GFP_KERNEL); + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (data == NULL) return -ENOMEM; @@ -298,7 +298,6 @@ static int __devinit adp5520_bl_probe(struct platform_device *pdev) if (data->pdata == NULL) { dev_err(&pdev->dev, "missing platform data\n"); - kfree(data); return -ENODEV; } @@ -314,7 +313,6 @@ static int __devinit adp5520_bl_probe(struct platform_device *pdev) &adp5520_bl_ops, &props); if (IS_ERR(bl)) { dev_err(&pdev->dev, "failed to register backlight\n"); - kfree(data); return PTR_ERR(bl); } @@ -326,7 +324,6 @@ static int __devinit adp5520_bl_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "failed to register sysfs\n"); backlight_device_unregister(bl); - kfree(data); } platform_set_drvdata(pdev, bl); @@ -348,7 +345,6 @@ static int __devexit adp5520_bl_remove(struct platform_device *pdev) &adp5520_bl_attr_group); backlight_device_unregister(bl); - kfree(data); return 0; } diff --git a/drivers/video/backlight/adp8860_bl.c b/drivers/video/backlight/adp8860_bl.c index 378276c9d3cf..550dbf0bb896 100644 --- a/drivers/video/backlight/adp8860_bl.c +++ b/drivers/video/backlight/adp8860_bl.c @@ -819,17 +819,7 @@ static struct i2c_driver adp8860_driver = { .id_table = adp8860_id, }; -static int __init adp8860_init(void) -{ - return i2c_add_driver(&adp8860_driver); -} -module_init(adp8860_init); - -static void __exit adp8860_exit(void) -{ - i2c_del_driver(&adp8860_driver); -} -module_exit(adp8860_exit); +module_i2c_driver(adp8860_driver); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); diff --git a/drivers/video/backlight/adp8870_bl.c b/drivers/video/backlight/adp8870_bl.c index 6735059376d6..9be58c6f18f1 100644 --- a/drivers/video/backlight/adp8870_bl.c +++ b/drivers/video/backlight/adp8870_bl.c @@ -991,17 +991,7 @@ static struct i2c_driver adp8870_driver = { .id_table = adp8870_id, }; -static int __init adp8870_init(void) -{ - return i2c_add_driver(&adp8870_driver); -} -module_init(adp8870_init); - -static void __exit adp8870_exit(void) -{ - i2c_del_driver(&adp8870_driver); -} -module_exit(adp8870_exit); +module_i2c_driver(adp8870_driver); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); diff --git a/drivers/video/backlight/ams369fg06.c b/drivers/video/backlight/ams369fg06.c index 7838a23fbdd1..7bdadc790117 100644 --- a/drivers/video/backlight/ams369fg06.c +++ b/drivers/video/backlight/ams369fg06.c @@ -629,18 +629,7 @@ static struct spi_driver ams369fg06_driver = { .resume = ams369fg06_resume, }; -static int __init ams369fg06_init(void) -{ - return spi_register_driver(&ams369fg06_driver); -} - -static void __exit ams369fg06_exit(void) -{ - spi_unregister_driver(&ams369fg06_driver); -} - -module_init(ams369fg06_init); -module_exit(ams369fg06_exit); +module_spi_driver(ams369fg06_driver); MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>"); MODULE_DESCRIPTION("ams369fg06 LCD Driver"); diff --git a/drivers/video/backlight/apple_bl.c b/drivers/video/backlight/apple_bl.c index be98d152b7fd..a523b255e124 100644 --- a/drivers/video/backlight/apple_bl.c +++ b/drivers/video/backlight/apple_bl.c @@ -24,6 +24,7 @@ #include <linux/io.h> #include <linux/pci.h> #include <linux/acpi.h> +#include <linux/atomic.h> static struct backlight_device *apple_backlight_device; @@ -221,14 +222,32 @@ static struct acpi_driver apple_bl_driver = { }, }; +static atomic_t apple_bl_registered = ATOMIC_INIT(0); + +int apple_bl_register(void) +{ + if (atomic_xchg(&apple_bl_registered, 1) == 0) + return acpi_bus_register_driver(&apple_bl_driver); + + return 0; +} +EXPORT_SYMBOL_GPL(apple_bl_register); + +void apple_bl_unregister(void) +{ + if (atomic_xchg(&apple_bl_registered, 0) == 1) + acpi_bus_unregister_driver(&apple_bl_driver); +} +EXPORT_SYMBOL_GPL(apple_bl_unregister); + static int __init apple_bl_init(void) { - return acpi_bus_register_driver(&apple_bl_driver); + return apple_bl_register(); } static void __exit apple_bl_exit(void) { - acpi_bus_unregister_driver(&apple_bl_driver); + apple_bl_unregister(); } module_init(apple_bl_init); diff --git a/drivers/video/backlight/corgi_lcd.c b/drivers/video/backlight/corgi_lcd.c index c6533bad26f8..6dab13fe562e 100644 --- a/drivers/video/backlight/corgi_lcd.c +++ b/drivers/video/backlight/corgi_lcd.c @@ -629,17 +629,7 @@ static struct spi_driver corgi_lcd_driver = { .resume = corgi_lcd_resume, }; -static int __init corgi_lcd_init(void) -{ - return spi_register_driver(&corgi_lcd_driver); -} -module_init(corgi_lcd_init); - -static void __exit corgi_lcd_exit(void) -{ - spi_unregister_driver(&corgi_lcd_driver); -} -module_exit(corgi_lcd_exit); +module_spi_driver(corgi_lcd_driver); MODULE_DESCRIPTION("LCD and backlight driver for SHARP C7x0/Cxx00"); MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>"); diff --git a/drivers/video/backlight/cr_bllcd.c b/drivers/video/backlight/cr_bllcd.c index 6c8c54041fae..22489eb5f3e0 100644 --- a/drivers/video/backlight/cr_bllcd.c +++ b/drivers/video/backlight/cr_bllcd.c @@ -212,7 +212,7 @@ static int cr_backlight_probe(struct platform_device *pdev) &gpio_bar); gpio_bar &= ~0x3F; - crp = kzalloc(sizeof(*crp), GFP_KERNEL); + crp = devm_kzalloc(&pdev->dev, sizeof(*crp), GFP_KERNEL); if (!crp) { lcd_device_unregister(ldp); backlight_device_unregister(bdp); @@ -243,7 +243,6 @@ static int cr_backlight_remove(struct platform_device *pdev) backlight_device_unregister(crp->cr_backlight_device); lcd_device_unregister(crp->cr_lcd_device); pci_dev_put(lpc_dev); - kfree(crp); return 0; } diff --git a/drivers/video/backlight/da903x_bl.c b/drivers/video/backlight/da903x_bl.c index abb4a06268f1..30e19681a30b 100644 --- a/drivers/video/backlight/da903x_bl.c +++ b/drivers/video/backlight/da903x_bl.c @@ -110,7 +110,7 @@ static int da903x_backlight_probe(struct platform_device *pdev) struct backlight_properties props; int max_brightness; - data = kzalloc(sizeof(*data), GFP_KERNEL); + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (data == NULL) return -ENOMEM; @@ -124,7 +124,6 @@ static int da903x_backlight_probe(struct platform_device *pdev) default: dev_err(&pdev->dev, "invalid backlight device ID(%d)\n", pdev->id); - kfree(data); return -EINVAL; } @@ -143,7 +142,6 @@ static int da903x_backlight_probe(struct platform_device *pdev) &da903x_backlight_ops, &props); if (IS_ERR(bl)) { dev_err(&pdev->dev, "failed to register backlight\n"); - kfree(data); return PTR_ERR(bl); } @@ -157,10 +155,8 @@ static int da903x_backlight_probe(struct platform_device *pdev) static int da903x_backlight_remove(struct platform_device *pdev) { struct backlight_device *bl = platform_get_drvdata(pdev); - struct da903x_backlight_data *data = bl_get_data(bl); backlight_device_unregister(bl); - kfree(data); return 0; } diff --git a/drivers/video/backlight/ep93xx_bl.c b/drivers/video/backlight/ep93xx_bl.c index b62b8b9063b5..08214e1f0958 100644 --- a/drivers/video/backlight/ep93xx_bl.c +++ b/drivers/video/backlight/ep93xx_bl.c @@ -17,11 +17,6 @@ #include <linux/fb.h> #include <linux/backlight.h> -#include <mach/hardware.h> - -#define EP93XX_RASTER_REG(x) (EP93XX_RASTER_BASE + (x)) -#define EP93XX_RASTER_BRIGHTNESS EP93XX_RASTER_REG(0x20) - #define EP93XX_MAX_COUNT 255 #define EP93XX_MAX_BRIGHT 255 #define EP93XX_DEF_BRIGHT 128 @@ -35,7 +30,7 @@ static int ep93xxbl_set(struct backlight_device *bl, int brightness) { struct ep93xxbl *ep93xxbl = bl_get_data(bl); - __raw_writel((brightness << 8) | EP93XX_MAX_COUNT, ep93xxbl->mmio); + writel((brightness << 8) | EP93XX_MAX_COUNT, ep93xxbl->mmio); ep93xxbl->brightness = brightness; @@ -70,21 +65,29 @@ static int __init ep93xxbl_probe(struct platform_device *dev) struct ep93xxbl *ep93xxbl; struct backlight_device *bl; struct backlight_properties props; + struct resource *res; ep93xxbl = devm_kzalloc(&dev->dev, sizeof(*ep93xxbl), GFP_KERNEL); if (!ep93xxbl) return -ENOMEM; + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!res) + return -ENXIO; + /* - * This register is located in the range already ioremap'ed by - * the framebuffer driver. A MFD driver seems a bit of overkill - * to handle this so use the static I/O mapping; this address - * is already virtual. + * FIXME - We don't do a request_mem_region here because we are + * sharing the register space with the framebuffer driver (see + * drivers/video/ep93xx-fb.c) and doing so will cause the second + * loaded driver to return -EBUSY. * * NOTE: No locking is required; the framebuffer does not touch * this register. */ - ep93xxbl->mmio = EP93XX_RASTER_BRIGHTNESS; + ep93xxbl->mmio = devm_ioremap(&dev->dev, res->start, + resource_size(res)); + if (!ep93xxbl->mmio) + return -ENXIO; memset(&props, 0, sizeof(struct backlight_properties)); props.type = BACKLIGHT_RAW; diff --git a/drivers/video/backlight/l4f00242t03.c b/drivers/video/backlight/l4f00242t03.c index 27d1d7a29c77..6022b67285ec 100644 --- a/drivers/video/backlight/l4f00242t03.c +++ b/drivers/video/backlight/l4f00242t03.c @@ -274,18 +274,7 @@ static struct spi_driver l4f00242t03_driver = { .shutdown = l4f00242t03_shutdown, }; -static __init int l4f00242t03_init(void) -{ - return spi_register_driver(&l4f00242t03_driver); -} - -static __exit void l4f00242t03_exit(void) -{ - spi_unregister_driver(&l4f00242t03_driver); -} - -module_init(l4f00242t03_init); -module_exit(l4f00242t03_exit); +module_spi_driver(l4f00242t03_driver); MODULE_AUTHOR("Alberto Panizzo <maramaopercheseimorto@gmail.com>"); MODULE_DESCRIPTION("EPSON L4F00242T03 LCD"); diff --git a/drivers/video/backlight/ld9040.c b/drivers/video/backlight/ld9040.c index 78dafc0c8fc5..efd352be21ae 100644 --- a/drivers/video/backlight/ld9040.c +++ b/drivers/video/backlight/ld9040.c @@ -856,18 +856,7 @@ static struct spi_driver ld9040_driver = { .resume = ld9040_resume, }; -static int __init ld9040_init(void) -{ - return spi_register_driver(&ld9040_driver); -} - -static void __exit ld9040_exit(void) -{ - spi_unregister_driver(&ld9040_driver); -} - -module_init(ld9040_init); -module_exit(ld9040_exit); +module_spi_driver(ld9040_driver); MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>"); MODULE_DESCRIPTION("ld9040 LCD Driver"); diff --git a/drivers/video/backlight/lms283gf05.c b/drivers/video/backlight/lms283gf05.c index 4ec78cfe26ea..4161f9e3982a 100644 --- a/drivers/video/backlight/lms283gf05.c +++ b/drivers/video/backlight/lms283gf05.c @@ -226,18 +226,7 @@ static struct spi_driver lms283gf05_driver = { .remove = __devexit_p(lms283gf05_remove), }; -static __init int lms283gf05_init(void) -{ - return spi_register_driver(&lms283gf05_driver); -} - -static __exit void lms283gf05_exit(void) -{ - spi_unregister_driver(&lms283gf05_driver); -} - -module_init(lms283gf05_init); -module_exit(lms283gf05_exit); +module_spi_driver(lms283gf05_driver); MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); MODULE_DESCRIPTION("LCD283GF05 LCD"); diff --git a/drivers/video/backlight/lp855x_bl.c b/drivers/video/backlight/lp855x_bl.c new file mode 100644 index 000000000000..72a0e0c917cf --- /dev/null +++ b/drivers/video/backlight/lp855x_bl.c @@ -0,0 +1,331 @@ +/* + * TI LP855x Backlight Driver + * + * Copyright (C) 2011 Texas Instruments + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/backlight.h> +#include <linux/err.h> +#include <linux/lp855x.h> + +/* Registers */ +#define BRIGHTNESS_CTRL (0x00) +#define DEVICE_CTRL (0x01) + +#define BUF_SIZE 20 +#define DEFAULT_BL_NAME "lcd-backlight" +#define MAX_BRIGHTNESS 255 + +struct lp855x { + const char *chipname; + enum lp855x_chip_id chip_id; + struct i2c_client *client; + struct backlight_device *bl; + struct device *dev; + struct mutex xfer_lock; + struct lp855x_platform_data *pdata; +}; + +static int lp855x_read_byte(struct lp855x *lp, u8 reg, u8 *data) +{ + int ret; + + mutex_lock(&lp->xfer_lock); + ret = i2c_smbus_read_byte_data(lp->client, reg); + if (ret < 0) { + mutex_unlock(&lp->xfer_lock); + dev_err(lp->dev, "failed to read 0x%.2x\n", reg); + return ret; + } + mutex_unlock(&lp->xfer_lock); + + *data = (u8)ret; + return 0; +} + +static int lp855x_write_byte(struct lp855x *lp, u8 reg, u8 data) +{ + int ret; + + mutex_lock(&lp->xfer_lock); + ret = i2c_smbus_write_byte_data(lp->client, reg, data); + mutex_unlock(&lp->xfer_lock); + + return ret; +} + +static bool lp855x_is_valid_rom_area(struct lp855x *lp, u8 addr) +{ + u8 start, end; + + switch (lp->chip_id) { + case LP8550: + case LP8551: + case LP8552: + case LP8553: + start = EEPROM_START; + end = EEPROM_END; + break; + case LP8556: + start = EPROM_START; + end = EPROM_END; + break; + default: + return false; + } + + return (addr >= start && addr <= end); +} + +static int lp855x_init_registers(struct lp855x *lp) +{ + u8 val, addr; + int i, ret; + struct lp855x_platform_data *pd = lp->pdata; + + val = pd->initial_brightness; + ret = lp855x_write_byte(lp, BRIGHTNESS_CTRL, val); + if (ret) + return ret; + + val = pd->device_control; + ret = lp855x_write_byte(lp, DEVICE_CTRL, val); + if (ret) + return ret; + + if (pd->load_new_rom_data && pd->size_program) { + for (i = 0; i < pd->size_program; i++) { + addr = pd->rom_data[i].addr; + val = pd->rom_data[i].val; + if (!lp855x_is_valid_rom_area(lp, addr)) + continue; + + ret = lp855x_write_byte(lp, addr, val); + if (ret) + return ret; + } + } + + return ret; +} + +static int lp855x_bl_update_status(struct backlight_device *bl) +{ + struct lp855x *lp = bl_get_data(bl); + enum lp855x_brightness_ctrl_mode mode = lp->pdata->mode; + + if (bl->props.state & BL_CORE_SUSPENDED) + bl->props.brightness = 0; + + if (mode == PWM_BASED) { + struct lp855x_pwm_data *pd = &lp->pdata->pwm_data; + int br = bl->props.brightness; + int max_br = bl->props.max_brightness; + + if (pd->pwm_set_intensity) + pd->pwm_set_intensity(br, max_br); + + } else if (mode == REGISTER_BASED) { + u8 val = bl->props.brightness; + lp855x_write_byte(lp, BRIGHTNESS_CTRL, val); + } + + return 0; +} + +static int lp855x_bl_get_brightness(struct backlight_device *bl) +{ + struct lp855x *lp = bl_get_data(bl); + enum lp855x_brightness_ctrl_mode mode = lp->pdata->mode; + + if (mode == PWM_BASED) { + struct lp855x_pwm_data *pd = &lp->pdata->pwm_data; + int max_br = bl->props.max_brightness; + + if (pd->pwm_get_intensity) + bl->props.brightness = pd->pwm_get_intensity(max_br); + + } else if (mode == REGISTER_BASED) { + u8 val = 0; + + lp855x_read_byte(lp, BRIGHTNESS_CTRL, &val); + bl->props.brightness = val; + } + + return bl->props.brightness; +} + +static const struct backlight_ops lp855x_bl_ops = { + .options = BL_CORE_SUSPENDRESUME, + .update_status = lp855x_bl_update_status, + .get_brightness = lp855x_bl_get_brightness, +}; + +static int lp855x_backlight_register(struct lp855x *lp) +{ + struct backlight_device *bl; + struct backlight_properties props; + struct lp855x_platform_data *pdata = lp->pdata; + char *name = pdata->name ? : DEFAULT_BL_NAME; + + props.type = BACKLIGHT_PLATFORM; + props.max_brightness = MAX_BRIGHTNESS; + + if (pdata->initial_brightness > props.max_brightness) + pdata->initial_brightness = props.max_brightness; + + props.brightness = pdata->initial_brightness; + + bl = backlight_device_register(name, lp->dev, lp, + &lp855x_bl_ops, &props); + if (IS_ERR(bl)) + return PTR_ERR(bl); + + lp->bl = bl; + + return 0; +} + +static void lp855x_backlight_unregister(struct lp855x *lp) +{ + if (lp->bl) + backlight_device_unregister(lp->bl); +} + +static ssize_t lp855x_get_chip_id(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct lp855x *lp = dev_get_drvdata(dev); + return scnprintf(buf, BUF_SIZE, "%s\n", lp->chipname); +} + +static ssize_t lp855x_get_bl_ctl_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct lp855x *lp = dev_get_drvdata(dev); + enum lp855x_brightness_ctrl_mode mode = lp->pdata->mode; + char *strmode = NULL; + + if (mode == PWM_BASED) + strmode = "pwm based"; + else if (mode == REGISTER_BASED) + strmode = "register based"; + + return scnprintf(buf, BUF_SIZE, "%s\n", strmode); +} + +static DEVICE_ATTR(chip_id, S_IRUGO, lp855x_get_chip_id, NULL); +static DEVICE_ATTR(bl_ctl_mode, S_IRUGO, lp855x_get_bl_ctl_mode, NULL); + +static struct attribute *lp855x_attributes[] = { + &dev_attr_chip_id.attr, + &dev_attr_bl_ctl_mode.attr, + NULL, +}; + +static const struct attribute_group lp855x_attr_group = { + .attrs = lp855x_attributes, +}; + +static int lp855x_probe(struct i2c_client *cl, const struct i2c_device_id *id) +{ + struct lp855x *lp; + struct lp855x_platform_data *pdata = cl->dev.platform_data; + enum lp855x_brightness_ctrl_mode mode; + int ret; + + if (!pdata) { + dev_err(&cl->dev, "no platform data supplied\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(cl->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) + return -EIO; + + lp = devm_kzalloc(&cl->dev, sizeof(struct lp855x), GFP_KERNEL); + if (!lp) + return -ENOMEM; + + mode = pdata->mode; + lp->client = cl; + lp->dev = &cl->dev; + lp->pdata = pdata; + lp->chipname = id->name; + lp->chip_id = id->driver_data; + i2c_set_clientdata(cl, lp); + + mutex_init(&lp->xfer_lock); + + ret = lp855x_init_registers(lp); + if (ret) { + dev_err(lp->dev, "i2c communication err: %d", ret); + if (mode == REGISTER_BASED) + goto err_dev; + } + + ret = lp855x_backlight_register(lp); + if (ret) { + dev_err(lp->dev, + "failed to register backlight. err: %d\n", ret); + goto err_dev; + } + + ret = sysfs_create_group(&lp->dev->kobj, &lp855x_attr_group); + if (ret) { + dev_err(lp->dev, "failed to register sysfs. err: %d\n", ret); + goto err_sysfs; + } + + backlight_update_status(lp->bl); + return 0; + +err_sysfs: + lp855x_backlight_unregister(lp); +err_dev: + return ret; +} + +static int __devexit lp855x_remove(struct i2c_client *cl) +{ + struct lp855x *lp = i2c_get_clientdata(cl); + + lp->bl->props.brightness = 0; + backlight_update_status(lp->bl); + sysfs_remove_group(&lp->dev->kobj, &lp855x_attr_group); + lp855x_backlight_unregister(lp); + + return 0; +} + +static const struct i2c_device_id lp855x_ids[] = { + {"lp8550", LP8550}, + {"lp8551", LP8551}, + {"lp8552", LP8552}, + {"lp8553", LP8553}, + {"lp8556", LP8556}, + { } +}; +MODULE_DEVICE_TABLE(i2c, lp855x_ids); + +static struct i2c_driver lp855x_driver = { + .driver = { + .name = "lp855x", + }, + .probe = lp855x_probe, + .remove = __devexit_p(lp855x_remove), + .id_table = lp855x_ids, +}; + +module_i2c_driver(lp855x_driver); + +MODULE_DESCRIPTION("Texas Instruments LP855x Backlight driver"); +MODULE_AUTHOR("Milo Kim <milo.kim@ti.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/backlight/ltv350qv.c b/drivers/video/backlight/ltv350qv.c index cca43c06d3c8..333949ff3265 100644 --- a/drivers/video/backlight/ltv350qv.c +++ b/drivers/video/backlight/ltv350qv.c @@ -321,17 +321,7 @@ static struct spi_driver ltv350qv_driver = { .resume = ltv350qv_resume, }; -static int __init ltv350qv_init(void) -{ - return spi_register_driver(<v350qv_driver); -} - -static void __exit ltv350qv_exit(void) -{ - spi_unregister_driver(<v350qv_driver); -} -module_init(ltv350qv_init); -module_exit(ltv350qv_exit); +module_spi_driver(ltv350qv_driver); MODULE_AUTHOR("Haavard Skinnemoen (Atmel)"); MODULE_DESCRIPTION("Samsung LTV350QV LCD Driver"); diff --git a/drivers/video/backlight/max8925_bl.c b/drivers/video/backlight/max8925_bl.c index c915e3b53886..e833ac72e063 100644 --- a/drivers/video/backlight/max8925_bl.c +++ b/drivers/video/backlight/max8925_bl.c @@ -129,7 +129,8 @@ static int __devinit max8925_backlight_probe(struct platform_device *pdev) return -EINVAL; } - data = kzalloc(sizeof(struct max8925_backlight_data), GFP_KERNEL); + data = devm_kzalloc(&pdev->dev, sizeof(struct max8925_backlight_data), + GFP_KERNEL); if (data == NULL) return -ENOMEM; strncpy(name, res->name, MAX8925_NAME_SIZE); @@ -143,7 +144,6 @@ static int __devinit max8925_backlight_probe(struct platform_device *pdev) &max8925_backlight_ops, &props); if (IS_ERR(bl)) { dev_err(&pdev->dev, "failed to register backlight\n"); - kfree(data); return PTR_ERR(bl); } bl->props.brightness = MAX_BRIGHTNESS; @@ -165,17 +165,14 @@ static int __devinit max8925_backlight_probe(struct platform_device *pdev) return 0; out: backlight_device_unregister(bl); - kfree(data); return ret; } static int __devexit max8925_backlight_remove(struct platform_device *pdev) { struct backlight_device *bl = platform_get_drvdata(pdev); - struct max8925_backlight_data *data = bl_get_data(bl); backlight_device_unregister(bl); - kfree(data); return 0; } diff --git a/drivers/video/backlight/omap1_bl.c b/drivers/video/backlight/omap1_bl.c index d8cde277ec83..0175bfb08a1c 100644 --- a/drivers/video/backlight/omap1_bl.c +++ b/drivers/video/backlight/omap1_bl.c @@ -141,7 +141,8 @@ static int omapbl_probe(struct platform_device *pdev) if (!pdata) return -ENXIO; - bl = kzalloc(sizeof(struct omap_backlight), GFP_KERNEL); + bl = devm_kzalloc(&pdev->dev, sizeof(struct omap_backlight), + GFP_KERNEL); if (unlikely(!bl)) return -ENOMEM; @@ -150,10 +151,8 @@ static int omapbl_probe(struct platform_device *pdev) props.max_brightness = OMAPBL_MAX_INTENSITY; dev = backlight_device_register("omap-bl", &pdev->dev, bl, &omapbl_ops, &props); - if (IS_ERR(dev)) { - kfree(bl); + if (IS_ERR(dev)) return PTR_ERR(dev); - } bl->powermode = FB_BLANK_POWERDOWN; bl->current_intensity = 0; @@ -177,10 +176,8 @@ static int omapbl_probe(struct platform_device *pdev) static int omapbl_remove(struct platform_device *pdev) { struct backlight_device *dev = platform_get_drvdata(pdev); - struct omap_backlight *bl = dev_get_drvdata(&dev->dev); backlight_device_unregister(dev); - kfree(bl); return 0; } diff --git a/drivers/video/backlight/ot200_bl.c b/drivers/video/backlight/ot200_bl.c new file mode 100644 index 000000000000..f519d55a294c --- /dev/null +++ b/drivers/video/backlight/ot200_bl.c @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2012 Bachmann electronic GmbH + * Christian Gmeiner <christian.gmeiner@gmail.com> + * + * Backlight driver for ot200 visualisation device from + * Bachmann electronic GmbH. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/fb.h> +#include <linux/backlight.h> +#include <linux/gpio.h> +#include <linux/cs5535.h> + +static struct cs5535_mfgpt_timer *pwm_timer; + +/* this array defines the mapping of brightness in % to pwm frequency */ +static const u8 dim_table[101] = {0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, + 4, 5, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 9, 9, + 10, 10, 11, 11, 12, 12, 13, 14, 15, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 26, 27, 28, + 30, 31, 33, 35, 37, 39, 41, 43, 45, 47, 50, + 53, 55, 58, 61, 65, 68, 72, 75, 79, 84, 88, + 93, 97, 103, 108, 114, 120, 126, 133, 140, + 147, 155, 163}; + +struct ot200_backlight_data { + int current_brightness; +}; + +#define GPIO_DIMM 27 +#define SCALE 1 +#define CMP1MODE 0x2 /* compare on GE; output high on compare + * greater than or equal */ +#define PWM_SETUP (SCALE | CMP1MODE << 6 | MFGPT_SETUP_CNTEN) +#define MAX_COMP2 163 + +static int ot200_backlight_update_status(struct backlight_device *bl) +{ + struct ot200_backlight_data *data = bl_get_data(bl); + int brightness = bl->props.brightness; + + if (bl->props.state & BL_CORE_FBBLANK) + brightness = 0; + + /* enable or disable PWM timer */ + if (brightness == 0) + cs5535_mfgpt_write(pwm_timer, MFGPT_REG_SETUP, 0); + else if (data->current_brightness == 0) { + cs5535_mfgpt_write(pwm_timer, MFGPT_REG_COUNTER, 0); + cs5535_mfgpt_write(pwm_timer, MFGPT_REG_SETUP, + MFGPT_SETUP_CNTEN); + } + + /* apply new brightness value */ + cs5535_mfgpt_write(pwm_timer, MFGPT_REG_CMP1, + MAX_COMP2 - dim_table[brightness]); + data->current_brightness = brightness; + + return 0; +} + +static int ot200_backlight_get_brightness(struct backlight_device *bl) +{ + struct ot200_backlight_data *data = bl_get_data(bl); + return data->current_brightness; +} + +static const struct backlight_ops ot200_backlight_ops = { + .update_status = ot200_backlight_update_status, + .get_brightness = ot200_backlight_get_brightness, +}; + +static int ot200_backlight_probe(struct platform_device *pdev) +{ + struct backlight_device *bl; + struct ot200_backlight_data *data; + struct backlight_properties props; + int retval = 0; + + /* request gpio */ + if (gpio_request(GPIO_DIMM, "ot200 backlight dimmer") < 0) { + dev_err(&pdev->dev, "failed to request GPIO %d\n", GPIO_DIMM); + return -ENODEV; + } + + /* request timer */ + pwm_timer = cs5535_mfgpt_alloc_timer(7, MFGPT_DOMAIN_ANY); + if (!pwm_timer) { + dev_err(&pdev->dev, "MFGPT 7 not available\n"); + retval = -ENODEV; + goto error_mfgpt_alloc; + } + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + retval = -ENOMEM; + goto error_kzalloc; + } + + /* setup gpio */ + cs5535_gpio_set(GPIO_DIMM, GPIO_OUTPUT_ENABLE); + cs5535_gpio_set(GPIO_DIMM, GPIO_OUTPUT_AUX1); + + /* setup timer */ + cs5535_mfgpt_write(pwm_timer, MFGPT_REG_CMP1, 0); + cs5535_mfgpt_write(pwm_timer, MFGPT_REG_CMP2, MAX_COMP2); + cs5535_mfgpt_write(pwm_timer, MFGPT_REG_SETUP, PWM_SETUP); + + data->current_brightness = 100; + props.max_brightness = 100; + props.brightness = 100; + props.type = BACKLIGHT_RAW; + + bl = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, data, + &ot200_backlight_ops, &props); + if (IS_ERR(bl)) { + dev_err(&pdev->dev, "failed to register backlight\n"); + retval = PTR_ERR(bl); + goto error_backlight_device_register; + } + + platform_set_drvdata(pdev, bl); + + return 0; + +error_backlight_device_register: + kfree(data); +error_kzalloc: + cs5535_mfgpt_free_timer(pwm_timer); +error_mfgpt_alloc: + gpio_free(GPIO_DIMM); + return retval; +} + +static int ot200_backlight_remove(struct platform_device *pdev) +{ + struct backlight_device *bl = platform_get_drvdata(pdev); + struct ot200_backlight_data *data = bl_get_data(bl); + + backlight_device_unregister(bl); + + /* on module unload set brightness to 100% */ + cs5535_mfgpt_write(pwm_timer, MFGPT_REG_COUNTER, 0); + cs5535_mfgpt_write(pwm_timer, MFGPT_REG_SETUP, MFGPT_SETUP_CNTEN); + cs5535_mfgpt_write(pwm_timer, MFGPT_REG_CMP1, + MAX_COMP2 - dim_table[100]); + + cs5535_mfgpt_free_timer(pwm_timer); + gpio_free(GPIO_DIMM); + + kfree(data); + return 0; +} + +static struct platform_driver ot200_backlight_driver = { + .driver = { + .name = "ot200-backlight", + .owner = THIS_MODULE, + }, + .probe = ot200_backlight_probe, + .remove = ot200_backlight_remove, +}; + +module_platform_driver(ot200_backlight_driver); + +MODULE_DESCRIPTION("backlight driver for ot200 visualisation device"); +MODULE_AUTHOR("Christian Gmeiner <christian.gmeiner@gmail.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ot200-backlight"); diff --git a/drivers/video/backlight/pandora_bl.c b/drivers/video/backlight/pandora_bl.c new file mode 100644 index 000000000000..4ec30748b447 --- /dev/null +++ b/drivers/video/backlight/pandora_bl.c @@ -0,0 +1,171 @@ +/* + * Backlight driver for Pandora handheld. + * Pandora uses TWL4030 PWM0 -> TPS61161 combo for control backlight. + * Based on pwm_bl.c + * + * Copyright 2009,2012 Gražvydas Ignotas <notasas@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/backlight.h> +#include <linux/i2c/twl.h> +#include <linux/err.h> + +#define TWL_PWM0_ON 0x00 +#define TWL_PWM0_OFF 0x01 + +#define TWL_INTBR_GPBR1 0x0c +#define TWL_INTBR_PMBR1 0x0d + +#define TWL_PMBR1_PWM0_MUXMASK 0x0c +#define TWL_PMBR1_PWM0 0x04 +#define PWM0_CLK_ENABLE BIT(0) +#define PWM0_ENABLE BIT(2) + +/* range accepted by hardware */ +#define MIN_VALUE 9 +#define MAX_VALUE 63 +#define MAX_USER_VALUE (MAX_VALUE - MIN_VALUE) + +#define PANDORABL_WAS_OFF BL_CORE_DRIVER1 + +static int pandora_backlight_update_status(struct backlight_device *bl) +{ + int brightness = bl->props.brightness; + u8 r; + + if (bl->props.power != FB_BLANK_UNBLANK) + brightness = 0; + if (bl->props.state & BL_CORE_FBBLANK) + brightness = 0; + if (bl->props.state & BL_CORE_SUSPENDED) + brightness = 0; + + if ((unsigned int)brightness > MAX_USER_VALUE) + brightness = MAX_USER_VALUE; + + if (brightness == 0) { + if (bl->props.state & PANDORABL_WAS_OFF) + goto done; + + /* first disable PWM0 output, then clock */ + twl_i2c_read_u8(TWL4030_MODULE_INTBR, &r, TWL_INTBR_GPBR1); + r &= ~PWM0_ENABLE; + twl_i2c_write_u8(TWL4030_MODULE_INTBR, r, TWL_INTBR_GPBR1); + r &= ~PWM0_CLK_ENABLE; + twl_i2c_write_u8(TWL4030_MODULE_INTBR, r, TWL_INTBR_GPBR1); + + goto done; + } + + if (bl->props.state & PANDORABL_WAS_OFF) { + /* + * set PWM duty cycle to max. TPS61161 seems to use this + * to calibrate it's PWM sensitivity when it starts. + */ + twl_i2c_write_u8(TWL4030_MODULE_PWM0, MAX_VALUE, + TWL_PWM0_OFF); + + /* first enable clock, then PWM0 out */ + twl_i2c_read_u8(TWL4030_MODULE_INTBR, &r, TWL_INTBR_GPBR1); + r &= ~PWM0_ENABLE; + r |= PWM0_CLK_ENABLE; + twl_i2c_write_u8(TWL4030_MODULE_INTBR, r, TWL_INTBR_GPBR1); + r |= PWM0_ENABLE; + twl_i2c_write_u8(TWL4030_MODULE_INTBR, r, TWL_INTBR_GPBR1); + + /* + * TI made it very easy to enable digital control, so easy that + * it often triggers unintentionally and disabes PWM control, + * so wait until 1 wire mode detection window ends. + */ + usleep_range(2000, 10000); + } + + twl_i2c_write_u8(TWL4030_MODULE_PWM0, MIN_VALUE + brightness, + TWL_PWM0_OFF); + +done: + if (brightness != 0) + bl->props.state &= ~PANDORABL_WAS_OFF; + else + bl->props.state |= PANDORABL_WAS_OFF; + + return 0; +} + +static int pandora_backlight_get_brightness(struct backlight_device *bl) +{ + return bl->props.brightness; +} + +static const struct backlight_ops pandora_backlight_ops = { + .options = BL_CORE_SUSPENDRESUME, + .update_status = pandora_backlight_update_status, + .get_brightness = pandora_backlight_get_brightness, +}; + +static int pandora_backlight_probe(struct platform_device *pdev) +{ + struct backlight_properties props; + struct backlight_device *bl; + u8 r; + + memset(&props, 0, sizeof(props)); + props.max_brightness = MAX_USER_VALUE; + props.type = BACKLIGHT_RAW; + bl = backlight_device_register(pdev->name, &pdev->dev, + NULL, &pandora_backlight_ops, &props); + if (IS_ERR(bl)) { + dev_err(&pdev->dev, "failed to register backlight\n"); + return PTR_ERR(bl); + } + + platform_set_drvdata(pdev, bl); + + /* 64 cycle period, ON position 0 */ + twl_i2c_write_u8(TWL4030_MODULE_PWM0, 0x80, TWL_PWM0_ON); + + bl->props.state |= PANDORABL_WAS_OFF; + bl->props.brightness = MAX_USER_VALUE; + backlight_update_status(bl); + + /* enable PWM function in pin mux */ + twl_i2c_read_u8(TWL4030_MODULE_INTBR, &r, TWL_INTBR_PMBR1); + r &= ~TWL_PMBR1_PWM0_MUXMASK; + r |= TWL_PMBR1_PWM0; + twl_i2c_write_u8(TWL4030_MODULE_INTBR, r, TWL_INTBR_PMBR1); + + return 0; +} + +static int pandora_backlight_remove(struct platform_device *pdev) +{ + struct backlight_device *bl = platform_get_drvdata(pdev); + backlight_device_unregister(bl); + return 0; +} + +static struct platform_driver pandora_backlight_driver = { + .driver = { + .name = "pandora-backlight", + .owner = THIS_MODULE, + }, + .probe = pandora_backlight_probe, + .remove = pandora_backlight_remove, +}; + +module_platform_driver(pandora_backlight_driver); + +MODULE_AUTHOR("Gražvydas Ignotas <notasas@gmail.com>"); +MODULE_DESCRIPTION("Pandora Backlight Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:pandora-backlight"); diff --git a/drivers/video/backlight/pcf50633-backlight.c b/drivers/video/backlight/pcf50633-backlight.c index 13e88b71daec..c65853cb9740 100644 --- a/drivers/video/backlight/pcf50633-backlight.c +++ b/drivers/video/backlight/pcf50633-backlight.c @@ -101,14 +101,13 @@ static const struct backlight_ops pcf50633_bl_ops = { static int __devinit pcf50633_bl_probe(struct platform_device *pdev) { - int ret; struct pcf50633_bl *pcf_bl; struct device *parent = pdev->dev.parent; struct pcf50633_platform_data *pcf50633_data = parent->platform_data; struct pcf50633_bl_platform_data *pdata = pcf50633_data->backlight_data; struct backlight_properties bl_props; - pcf_bl = kzalloc(sizeof(*pcf_bl), GFP_KERNEL); + pcf_bl = devm_kzalloc(&pdev->dev, sizeof(*pcf_bl), GFP_KERNEL); if (!pcf_bl) return -ENOMEM; @@ -129,10 +128,8 @@ static int __devinit pcf50633_bl_probe(struct platform_device *pdev) pcf_bl->bl = backlight_device_register(pdev->name, &pdev->dev, pcf_bl, &pcf50633_bl_ops, &bl_props); - if (IS_ERR(pcf_bl->bl)) { - ret = PTR_ERR(pcf_bl->bl); - goto err_free; - } + if (IS_ERR(pcf_bl->bl)) + return PTR_ERR(pcf_bl->bl); platform_set_drvdata(pdev, pcf_bl); @@ -145,11 +142,6 @@ static int __devinit pcf50633_bl_probe(struct platform_device *pdev) backlight_update_status(pcf_bl->bl); return 0; - -err_free: - kfree(pcf_bl); - - return ret; } static int __devexit pcf50633_bl_remove(struct platform_device *pdev) @@ -160,8 +152,6 @@ static int __devexit pcf50633_bl_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); - kfree(pcf_bl); - return 0; } diff --git a/drivers/video/backlight/platform_lcd.c b/drivers/video/backlight/platform_lcd.c index f0bf491ed087..b6672340d6c7 100644 --- a/drivers/video/backlight/platform_lcd.c +++ b/drivers/video/backlight/platform_lcd.c @@ -121,9 +121,9 @@ static int __devexit platform_lcd_remove(struct platform_device *pdev) } #ifdef CONFIG_PM -static int platform_lcd_suspend(struct platform_device *pdev, pm_message_t st) +static int platform_lcd_suspend(struct device *dev) { - struct platform_lcd *plcd = platform_get_drvdata(pdev); + struct platform_lcd *plcd = dev_get_drvdata(dev); plcd->suspended = 1; platform_lcd_set_power(plcd->lcd, plcd->power); @@ -131,29 +131,30 @@ static int platform_lcd_suspend(struct platform_device *pdev, pm_message_t st) return 0; } -static int platform_lcd_resume(struct platform_device *pdev) +static int platform_lcd_resume(struct device *dev) { - struct platform_lcd *plcd = platform_get_drvdata(pdev); + struct platform_lcd *plcd = dev_get_drvdata(dev); plcd->suspended = 0; platform_lcd_set_power(plcd->lcd, plcd->power); return 0; } -#else -#define platform_lcd_suspend NULL -#define platform_lcd_resume NULL + +static SIMPLE_DEV_PM_OPS(platform_lcd_pm_ops, platform_lcd_suspend, + platform_lcd_resume); #endif static struct platform_driver platform_lcd_driver = { .driver = { .name = "platform-lcd", .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &platform_lcd_pm_ops, +#endif }, .probe = platform_lcd_probe, .remove = __devexit_p(platform_lcd_remove), - .suspend = platform_lcd_suspend, - .resume = platform_lcd_resume, }; module_platform_driver(platform_lcd_driver); diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index 7496d04e1d3c..342b7d7cbb63 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -102,7 +102,7 @@ static int pwm_backlight_probe(struct platform_device *pdev) return ret; } - pb = kzalloc(sizeof(*pb), GFP_KERNEL); + pb = devm_kzalloc(&pdev->dev, sizeof(*pb), GFP_KERNEL); if (!pb) { dev_err(&pdev->dev, "no memory for state\n"); ret = -ENOMEM; @@ -121,7 +121,7 @@ static int pwm_backlight_probe(struct platform_device *pdev) if (IS_ERR(pb->pwm)) { dev_err(&pdev->dev, "unable to request PWM for backlight\n"); ret = PTR_ERR(pb->pwm); - goto err_pwm; + goto err_alloc; } else dev_dbg(&pdev->dev, "got pwm for backlight\n"); @@ -144,8 +144,6 @@ static int pwm_backlight_probe(struct platform_device *pdev) err_bl: pwm_free(pb->pwm); -err_pwm: - kfree(pb); err_alloc: if (data->exit) data->exit(&pdev->dev); @@ -162,7 +160,6 @@ static int pwm_backlight_remove(struct platform_device *pdev) pwm_config(pb->pwm, 0, pb->period); pwm_disable(pb->pwm); pwm_free(pb->pwm); - kfree(pb); if (data->exit) data->exit(&pdev->dev); return 0; diff --git a/drivers/video/backlight/s6e63m0.c b/drivers/video/backlight/s6e63m0.c index 516db703dd24..e264f55b2574 100644 --- a/drivers/video/backlight/s6e63m0.c +++ b/drivers/video/backlight/s6e63m0.c @@ -909,18 +909,7 @@ static struct spi_driver s6e63m0_driver = { .resume = s6e63m0_resume, }; -static int __init s6e63m0_init(void) -{ - return spi_register_driver(&s6e63m0_driver); -} - -static void __exit s6e63m0_exit(void) -{ - spi_unregister_driver(&s6e63m0_driver); -} - -module_init(s6e63m0_init); -module_exit(s6e63m0_exit); +module_spi_driver(s6e63m0_driver); MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>"); MODULE_DESCRIPTION("S6E63M0 LCD Driver"); diff --git a/drivers/video/backlight/tdo24m.c b/drivers/video/backlight/tdo24m.c index 1997e12a1057..2368b8e5f89e 100644 --- a/drivers/video/backlight/tdo24m.c +++ b/drivers/video/backlight/tdo24m.c @@ -459,17 +459,7 @@ static struct spi_driver tdo24m_driver = { .resume = tdo24m_resume, }; -static int __init tdo24m_init(void) -{ - return spi_register_driver(&tdo24m_driver); -} -module_init(tdo24m_init); - -static void __exit tdo24m_exit(void) -{ - spi_unregister_driver(&tdo24m_driver); -} -module_exit(tdo24m_exit); +module_spi_driver(tdo24m_driver); MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>"); MODULE_DESCRIPTION("Driver for Toppoly TDO24M LCD Panel"); diff --git a/drivers/video/backlight/tosa_bl.c b/drivers/video/backlight/tosa_bl.c index 425a7365470b..2b241abced43 100644 --- a/drivers/video/backlight/tosa_bl.c +++ b/drivers/video/backlight/tosa_bl.c @@ -181,18 +181,7 @@ static struct i2c_driver tosa_bl_driver = { .id_table = tosa_bl_id, }; -static int __init tosa_bl_init(void) -{ - return i2c_add_driver(&tosa_bl_driver); -} - -static void __exit tosa_bl_exit(void) -{ - i2c_del_driver(&tosa_bl_driver); -} - -module_init(tosa_bl_init); -module_exit(tosa_bl_exit); +module_i2c_driver(tosa_bl_driver); MODULE_AUTHOR("Dmitry Baryshkov"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/backlight/tosa_lcd.c b/drivers/video/backlight/tosa_lcd.c index 772f6015219a..2231aec23918 100644 --- a/drivers/video/backlight/tosa_lcd.c +++ b/drivers/video/backlight/tosa_lcd.c @@ -271,7 +271,7 @@ static int tosa_lcd_resume(struct spi_device *spi) } #else #define tosa_lcd_suspend NULL -#define tosa_lcd_reume NULL +#define tosa_lcd_resume NULL #endif static struct spi_driver tosa_lcd_driver = { @@ -285,18 +285,7 @@ static struct spi_driver tosa_lcd_driver = { .resume = tosa_lcd_resume, }; -static int __init tosa_lcd_init(void) -{ - return spi_register_driver(&tosa_lcd_driver); -} - -static void __exit tosa_lcd_exit(void) -{ - spi_unregister_driver(&tosa_lcd_driver); -} - -module_init(tosa_lcd_init); -module_exit(tosa_lcd_exit); +module_spi_driver(tosa_lcd_driver); MODULE_AUTHOR("Dmitry Baryshkov"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/backlight/vgg2432a4.c b/drivers/video/backlight/vgg2432a4.c index b49063c831e7..b617fae9aa26 100644 --- a/drivers/video/backlight/vgg2432a4.c +++ b/drivers/video/backlight/vgg2432a4.c @@ -262,20 +262,7 @@ static struct spi_driver vgg2432a4_driver = { .resume = vgg2432a4_resume, }; -/* Device driver initialisation */ - -static int __init vgg2432a4_init(void) -{ - return spi_register_driver(&vgg2432a4_driver); -} - -static void __exit vgg2432a4_exit(void) -{ - spi_unregister_driver(&vgg2432a4_driver); -} - -module_init(vgg2432a4_init); -module_exit(vgg2432a4_exit); +module_spi_driver(vgg2432a4_driver); MODULE_AUTHOR("Ben Dooks <ben-linux@fluff.org>"); MODULE_DESCRIPTION("VGG2432A4 LCD Driver"); diff --git a/drivers/video/backlight/wm831x_bl.c b/drivers/video/backlight/wm831x_bl.c index 4e915f5eca99..5d365deb5f82 100644 --- a/drivers/video/backlight/wm831x_bl.c +++ b/drivers/video/backlight/wm831x_bl.c @@ -186,7 +186,7 @@ static int wm831x_backlight_probe(struct platform_device *pdev) if (ret < 0) return ret; - data = kzalloc(sizeof(*data), GFP_KERNEL); + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (data == NULL) return -ENOMEM; @@ -200,7 +200,6 @@ static int wm831x_backlight_probe(struct platform_device *pdev) &wm831x_backlight_ops, &props); if (IS_ERR(bl)) { dev_err(&pdev->dev, "failed to register backlight\n"); - kfree(data); return PTR_ERR(bl); } @@ -211,7 +210,6 @@ static int wm831x_backlight_probe(struct platform_device *pdev) /* Disable the DCDC if it was started so we can bootstrap */ wm831x_set_bits(wm831x, WM831X_DCDC_ENABLE, WM831X_DC4_ENA, 0); - backlight_update_status(bl); return 0; @@ -220,10 +218,8 @@ static int wm831x_backlight_probe(struct platform_device *pdev) static int wm831x_backlight_remove(struct platform_device *pdev) { struct backlight_device *bl = platform_get_drvdata(pdev); - struct wm831x_backlight_data *data = bl_get_data(bl); backlight_device_unregister(bl); - kfree(data); return 0; } diff --git a/drivers/video/bf537-lq035.c b/drivers/video/bf537-lq035.c index bea53c1a4950..befbc80d11fc 100644 --- a/drivers/video/bf537-lq035.c +++ b/drivers/video/bf537-lq035.c @@ -383,23 +383,19 @@ static int __devinit request_ports(void) } #if (defined(UD) && defined(LBR)) - if (gpio_request(UD, KBUILD_MODNAME)) { + if (gpio_request_one(UD, GPIOF_OUT_INIT_LOW, KBUILD_MODNAME)) { pr_err("requesting GPIO %d failed\n", UD); return -EBUSY; } - if (gpio_request(LBR, KBUILD_MODNAME)) { + if (gpio_request_one(LBR, GPIOF_OUT_INIT_HIGH, KBUILD_MODNAME)) { pr_err("requesting GPIO %d failed\n", LBR); gpio_free(UD); return -EBUSY; } - - gpio_direction_output(UD, 0); - gpio_direction_output(LBR, 1); - #endif - if (gpio_request(MOD, KBUILD_MODNAME)) { + if (gpio_request_one(MOD, GPIOF_OUT_INIT_HIGH, KBUILD_MODNAME)) { pr_err("requesting GPIO %d failed\n", MOD); #if (defined(UD) && defined(LBR)) gpio_free(LBR); @@ -408,8 +404,6 @@ static int __devinit request_ports(void) return -EBUSY; } - gpio_direction_output(MOD, 1); - SSYNC(); return 0; } diff --git a/drivers/video/bf54x-lq043fb.c b/drivers/video/bf54x-lq043fb.c index 46b03f53985f..dc2f0047769b 100644 --- a/drivers/video/bf54x-lq043fb.c +++ b/drivers/video/bf54x-lq043fb.c @@ -240,7 +240,7 @@ static int request_ports(struct bfin_bf54xfb_info *fbi) u16 eppi_req_18[] = EPPI0_18; u16 disp = fbi->mach_info->disp; - if (gpio_request(disp, DRIVER_NAME)) { + if (gpio_request_one(disp, GPIOF_OUT_INIT_HIGH, DRIVER_NAME)) { printk(KERN_ERR "Requesting GPIO %d failed\n", disp); return -EFAULT; } @@ -263,8 +263,6 @@ static int request_ports(struct bfin_bf54xfb_info *fbi) } } - gpio_direction_output(disp, 1); - return 0; } diff --git a/drivers/video/bfin-lq035q1-fb.c b/drivers/video/bfin-lq035q1-fb.c index c633068372c9..86922ac84412 100644 --- a/drivers/video/bfin-lq035q1-fb.c +++ b/drivers/video/bfin-lq035q1-fb.c @@ -365,10 +365,10 @@ static int __devinit bfin_lq035q1_request_ports(struct platform_device *pdev, * Drive PPI_FS3 Low */ if (ANOMALY_05000400) { - int ret = gpio_request(P_IDENT(P_PPI0_FS3), "PPI_FS3"); + int ret = gpio_request_one(P_IDENT(P_PPI0_FS3), + GPIOF_OUT_INIT_LOW, "PPI_FS3"); if (ret) return ret; - gpio_direction_output(P_IDENT(P_PPI0_FS3), 0); } if (ppi16) @@ -716,14 +716,14 @@ static int __devinit bfin_lq035q1_probe(struct platform_device *pdev) } if (info->disp_info->use_bl) { - ret = gpio_request(info->disp_info->gpio_bl, "LQ035 Backlight"); + ret = gpio_request_one(info->disp_info->gpio_bl, + GPIOF_OUT_INIT_LOW, "LQ035 Backlight"); if (ret) { dev_err(&pdev->dev, "failed to request GPIO %d\n", info->disp_info->gpio_bl); goto out9; } - gpio_direction_output(info->disp_info->gpio_bl, 0); } ret = register_framebuffer(fbinfo); diff --git a/drivers/video/bfin_adv7393fb.c b/drivers/video/bfin_adv7393fb.c index 811dd7f6aa41..1a268a294478 100644 --- a/drivers/video/bfin_adv7393fb.c +++ b/drivers/video/bfin_adv7393fb.c @@ -36,9 +36,7 @@ #include <linux/dma-mapping.h> #include <linux/proc_fs.h> #include <linux/platform_device.h> - #include <linux/i2c.h> -#include <linux/i2c-dev.h> #include "bfin_adv7393fb.h" @@ -411,12 +409,13 @@ static int __devinit bfin_adv7393_fb_probe(struct i2c_client *client, /* Workaround "PPI Does Not Start Properly In Specific Mode" */ if (ANOMALY_05000400) { - if (gpio_request(P_IDENT(P_PPI0_FS3), "PPI0_FS3")) { + ret = gpio_request_one(P_IDENT(P_PPI0_FS3), GPIOF_OUT_INIT_LOW, + "PPI0_FS3") + if (ret) { dev_err(&client->dev, "PPI0_FS3 GPIO request failed\n"); ret = -EBUSY; goto out_8; } - gpio_direction_output(P_IDENT(P_PPI0_FS3), 0); } if (peripheral_request_list(ppi_pins, DRIVER_NAME)) { diff --git a/drivers/video/bt431.h b/drivers/video/bt431.h index c826f2787bad..04e0cfbba538 100644 --- a/drivers/video/bt431.h +++ b/drivers/video/bt431.h @@ -8,7 +8,6 @@ * archive for more details. */ #include <linux/types.h> -#include <asm/system.h> /* * Bt431 cursor generator registers, 32-bit aligned. diff --git a/drivers/video/bt455.h b/drivers/video/bt455.h index b7591fea7add..80f61b03e9ae 100644 --- a/drivers/video/bt455.h +++ b/drivers/video/bt455.h @@ -8,7 +8,6 @@ * archive for more details. */ #include <linux/types.h> -#include <asm/system.h> /* * Bt455 byte-wide registers, 32-bit aligned. diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 8745637e4b7e..2e471c22abf5 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -77,7 +77,6 @@ #include <linux/crc32.h> /* For counting font checksums */ #include <asm/fb.h> #include <asm/irq.h> -#include <asm/system.h> #include "fbcon.h" diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c index a122d9287d16..6d1596629040 100644 --- a/drivers/video/console/newport_con.c +++ b/drivers/video/console/newport_con.c @@ -22,7 +22,6 @@ #include <asm/io.h> #include <asm/uaccess.h> -#include <asm/system.h> #include <asm/page.h> #include <asm/pgtable.h> #include <asm/gio_device.h> diff --git a/drivers/video/cyber2000fb.c b/drivers/video/cyber2000fb.c index 850380795b05..c1527f5b47ee 100644 --- a/drivers/video/cyber2000fb.c +++ b/drivers/video/cyber2000fb.c @@ -51,7 +51,6 @@ #include <linux/i2c-algo-bit.h> #include <asm/pgtable.h> -#include <asm/system.h> #ifdef __arm__ #include <asm/mach-types.h> diff --git a/drivers/video/da8xx-fb.c b/drivers/video/da8xx-fb.c index 29577bf1f559..47118c75a4c0 100644 --- a/drivers/video/da8xx-fb.c +++ b/drivers/video/da8xx-fb.c @@ -32,6 +32,7 @@ #include <linux/console.h> #include <linux/slab.h> #include <video/da8xx-fb.h> +#include <asm/div64.h> #define DRIVER_NAME "da8xx_lcdc" @@ -161,6 +162,7 @@ struct da8xx_fb_par { int vsync_timeout; #ifdef CONFIG_CPU_FREQ struct notifier_block freq_transition; + unsigned int lcd_fck_rate; #endif void (*panel_power_ctrl)(int); }; @@ -174,7 +176,6 @@ static struct fb_var_screeninfo da8xx_fb_var __devinitdata = { .activate = 0, .height = -1, .width = -1, - .pixclock = 46666, /* 46us - AUO display */ .accel_flags = 0, .left_margin = LEFT_MARGIN, .right_margin = RIGHT_MARGIN, @@ -238,6 +239,20 @@ static struct da8xx_panel known_lcd_panels[] = { .pxl_clk = 7833600, .invert_pxl_clk = 0, }, + [2] = { + /* Hitachi SP10Q010 */ + .name = "SP10Q010", + .width = 320, + .height = 240, + .hfp = 10, + .hbp = 10, + .hsw = 10, + .vfp = 10, + .vbp = 10, + .vsw = 10, + .pxl_clk = 7833600, + .invert_pxl_clk = 0, + }, }; /* Enable the Raster Engine of the LCD Controller */ @@ -546,7 +561,26 @@ static int fb_setcolreg(unsigned regno, unsigned red, unsigned green, if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) return 1; - if (info->var.bits_per_pixel == 8) { + if (info->var.bits_per_pixel == 4) { + if (regno > 15) + return 1; + + if (info->var.grayscale) { + pal = regno; + } else { + red >>= 4; + green >>= 8; + blue >>= 12; + + pal = (red & 0x0f00); + pal |= (green & 0x00f0); + pal |= (blue & 0x000f); + } + if (regno == 0) + pal |= 0x2000; + palette[regno] = pal; + + } else if (info->var.bits_per_pixel == 8) { red >>= 4; green >>= 8; blue >>= 12; @@ -801,6 +835,7 @@ static int fb_check_var(struct fb_var_screeninfo *var, var->blue.length = 8; var->transp.offset = 0; var->transp.length = 0; + var->nonstd = 0; break; case 4: var->red.offset = 0; @@ -811,6 +846,7 @@ static int fb_check_var(struct fb_var_screeninfo *var, var->blue.length = 4; var->transp.offset = 0; var->transp.length = 0; + var->nonstd = FB_NONSTD_REV_PIX_IN_B; break; case 16: /* RGB 565 */ var->red.offset = 11; @@ -821,6 +857,7 @@ static int fb_check_var(struct fb_var_screeninfo *var, var->blue.length = 5; var->transp.offset = 0; var->transp.length = 0; + var->nonstd = 0; break; default: err = -EINVAL; @@ -840,11 +877,13 @@ static int lcd_da8xx_cpufreq_transition(struct notifier_block *nb, struct da8xx_fb_par *par; par = container_of(nb, struct da8xx_fb_par, freq_transition); - if (val == CPUFREQ_PRECHANGE) { - lcd_disable_raster(); - } else if (val == CPUFREQ_POSTCHANGE) { - lcd_calc_clk_divider(par); - lcd_enable_raster(); + if (val == CPUFREQ_POSTCHANGE) { + if (par->lcd_fck_rate != clk_get_rate(par->lcdc_clk)) { + par->lcd_fck_rate = clk_get_rate(par->lcdc_clk); + lcd_disable_raster(); + lcd_calc_clk_divider(par); + lcd_enable_raster(); + } } return 0; @@ -1048,6 +1087,22 @@ static struct fb_ops da8xx_fb_ops = { .fb_blank = cfb_blank, }; +/* Calculate and return pixel clock period in pico seconds */ +static unsigned int da8xxfb_pixel_clk_period(struct da8xx_fb_par *par) +{ + unsigned int lcd_clk, div; + unsigned int configured_pix_clk; + unsigned long long pix_clk_period_picosec = 1000000000000ULL; + + lcd_clk = clk_get_rate(par->lcdc_clk); + div = lcd_clk / par->pxl_clk; + configured_pix_clk = (lcd_clk / div); + + do_div(pix_clk_period_picosec, configured_pix_clk); + + return pix_clk_period_picosec; +} + static int __devinit fb_probe(struct platform_device *device) { struct da8xx_lcdc_platform_data *fb_pdata = @@ -1137,6 +1192,9 @@ static int __devinit fb_probe(struct platform_device *device) par = da8xx_fb_info->par; par->lcdc_clk = fb_clk; +#ifdef CONFIG_CPU_FREQ + par->lcd_fck_rate = clk_get_rate(fb_clk); +#endif par->pxl_clk = lcdc_info->pxl_clk; if (fb_pdata->panel_power_ctrl) { par->panel_power_ctrl = fb_pdata->panel_power_ctrl; @@ -1209,6 +1267,11 @@ static int __devinit fb_probe(struct platform_device *device) da8xx_fb_var.hsync_len = lcdc_info->hsw; da8xx_fb_var.vsync_len = lcdc_info->vsw; + da8xx_fb_var.right_margin = lcdc_info->hfp; + da8xx_fb_var.left_margin = lcdc_info->hbp; + da8xx_fb_var.lower_margin = lcdc_info->vfp; + da8xx_fb_var.upper_margin = lcdc_info->vbp; + da8xx_fb_var.pixclock = da8xxfb_pixel_clk_period(par); /* Initialize fbinfo */ da8xx_fb_info->flags = FBINFO_FLAG_DEFAULT; @@ -1264,8 +1327,8 @@ static int __devinit fb_probe(struct platform_device *device) irq_freq: #ifdef CONFIG_CPU_FREQ lcd_da8xx_cpufreq_deregister(par); -#endif err_cpu_freq: +#endif unregister_framebuffer(da8xx_fb_info); err_dealloc_cmap: diff --git a/drivers/video/dnfb.c b/drivers/video/dnfb.c index ec56d2544c73..49e3dda1a361 100644 --- a/drivers/video/dnfb.c +++ b/drivers/video/dnfb.c @@ -7,7 +7,6 @@ #include <linux/platform_device.h> #include <asm/setup.h> -#include <asm/system.h> #include <asm/irq.h> #include <asm/amigahw.h> #include <asm/amigaints.h> diff --git a/drivers/video/ep93xx-fb.c b/drivers/video/ep93xx-fb.c index 2e830ec52a5a..f8babbeee275 100644 --- a/drivers/video/ep93xx-fb.c +++ b/drivers/video/ep93xx-fb.c @@ -519,12 +519,15 @@ static int __devinit ep93xxfb_probe(struct platform_device *pdev) goto failed; } - res = request_mem_region(res->start, resource_size(res), pdev->name); - if (!res) { - err = -EBUSY; - goto failed; - } - + /* + * FIXME - We don't do a request_mem_region here because we are + * sharing the register space with the backlight driver (see + * drivers/video/backlight/ep93xx_bl.c) and doing so will cause + * the second loaded driver to return -EBUSY. + * + * NOTE: No locking is required; the backlight does not touch + * any of the framebuffer registers. + */ fbi->res = res; fbi->mmio_base = ioremap(res->start, resource_size(res)); if (!fbi->mmio_base) { @@ -586,8 +589,6 @@ failed: clk_put(fbi->clk); if (fbi->mmio_base) iounmap(fbi->mmio_base); - if (fbi->res) - release_mem_region(fbi->res->start, resource_size(fbi->res)); ep93xxfb_dealloc_videomem(info); if (&info->cmap) fb_dealloc_cmap(&info->cmap); @@ -608,7 +609,6 @@ static int __devexit ep93xxfb_remove(struct platform_device *pdev) clk_disable(fbi->clk); clk_put(fbi->clk); iounmap(fbi->mmio_base); - release_mem_region(fbi->res->start, resource_size(fbi->res)); ep93xxfb_dealloc_videomem(info); fb_dealloc_cmap(&info->cmap); diff --git a/drivers/video/exynos/Kconfig b/drivers/video/exynos/Kconfig new file mode 100644 index 000000000000..1b035b2eb6b6 --- /dev/null +++ b/drivers/video/exynos/Kconfig @@ -0,0 +1,37 @@ +# +# Exynos Video configuration +# + +menuconfig EXYNOS_VIDEO + bool "Exynos Video driver support" + help + This enables support for EXYNOS Video device. + +if EXYNOS_VIDEO + +# +# MIPI DSI driver +# + +config EXYNOS_MIPI_DSI + bool "EXYNOS MIPI DSI driver support." + depends on ARCH_S5PV210 || ARCH_EXYNOS + help + This enables support for MIPI-DSI device. + +config EXYNOS_LCD_S6E8AX0 + bool "S6E8AX0 MIPI AMOLED LCD Driver" + depends on (EXYNOS_MIPI_DSI && BACKLIGHT_CLASS_DEVICE && LCD_CLASS_DEVICE) + default n + help + If you have an S6E8AX0 MIPI AMOLED LCD Panel, say Y to enable its + LCD control driver. + +config EXYNOS_DP + bool "EXYNOS DP driver support" + depends on ARCH_EXYNOS + default n + help + This enables support for DP device. + +endif # EXYNOS_VIDEO diff --git a/drivers/video/exynos/Makefile b/drivers/video/exynos/Makefile new file mode 100644 index 000000000000..ec7772e452a9 --- /dev/null +++ b/drivers/video/exynos/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for the exynos video drivers. +# + +obj-$(CONFIG_EXYNOS_MIPI_DSI) += exynos_mipi_dsi.o exynos_mipi_dsi_common.o \ + exynos_mipi_dsi_lowlevel.o +obj-$(CONFIG_EXYNOS_LCD_S6E8AX0) += s6e8ax0.o +obj-$(CONFIG_EXYNOS_DP) += exynos_dp_core.o exynos_dp_reg.o diff --git a/drivers/video/exynos/exynos_dp_core.c b/drivers/video/exynos/exynos_dp_core.c new file mode 100644 index 000000000000..2a4481cf260c --- /dev/null +++ b/drivers/video/exynos/exynos_dp_core.c @@ -0,0 +1,1058 @@ +/* + * Samsung SoC DP (Display Port) interface driver. + * + * Copyright (C) 2012 Samsung Electronics Co., Ltd. + * Author: Jingoo Han <jg1.han@samsung.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/delay.h> + +#include <video/exynos_dp.h> + +#include <plat/cpu.h> + +#include "exynos_dp_core.h" + +static int exynos_dp_init_dp(struct exynos_dp_device *dp) +{ + exynos_dp_reset(dp); + + /* SW defined function Normal operation */ + exynos_dp_enable_sw_function(dp); + + exynos_dp_config_interrupt(dp); + exynos_dp_init_analog_func(dp); + + exynos_dp_init_hpd(dp); + exynos_dp_init_aux(dp); + + return 0; +} + +static int exynos_dp_detect_hpd(struct exynos_dp_device *dp) +{ + int timeout_loop = 0; + + exynos_dp_init_hpd(dp); + + udelay(200); + + while (exynos_dp_get_plug_in_status(dp) != 0) { + timeout_loop++; + if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) { + dev_err(dp->dev, "failed to get hpd plug status\n"); + return -ETIMEDOUT; + } + udelay(10); + } + + return 0; +} + +static unsigned char exynos_dp_calc_edid_check_sum(unsigned char *edid_data) +{ + int i; + unsigned char sum = 0; + + for (i = 0; i < EDID_BLOCK_LENGTH; i++) + sum = sum + edid_data[i]; + + return sum; +} + +static int exynos_dp_read_edid(struct exynos_dp_device *dp) +{ + unsigned char edid[EDID_BLOCK_LENGTH * 2]; + unsigned int extend_block = 0; + unsigned char sum; + unsigned char test_vector; + int retval; + + /* + * EDID device address is 0x50. + * However, if necessary, you must have set upper address + * into E-EDID in I2C device, 0x30. + */ + + /* Read Extension Flag, Number of 128-byte EDID extension blocks */ + exynos_dp_read_byte_from_i2c(dp, I2C_EDID_DEVICE_ADDR, + EDID_EXTENSION_FLAG, + &extend_block); + + if (extend_block > 0) { + dev_dbg(dp->dev, "EDID data includes a single extension!\n"); + + /* Read EDID data */ + retval = exynos_dp_read_bytes_from_i2c(dp, I2C_EDID_DEVICE_ADDR, + EDID_HEADER_PATTERN, + EDID_BLOCK_LENGTH, + &edid[EDID_HEADER_PATTERN]); + if (retval != 0) { + dev_err(dp->dev, "EDID Read failed!\n"); + return -EIO; + } + sum = exynos_dp_calc_edid_check_sum(edid); + if (sum != 0) { + dev_err(dp->dev, "EDID bad checksum!\n"); + return -EIO; + } + + /* Read additional EDID data */ + retval = exynos_dp_read_bytes_from_i2c(dp, + I2C_EDID_DEVICE_ADDR, + EDID_BLOCK_LENGTH, + EDID_BLOCK_LENGTH, + &edid[EDID_BLOCK_LENGTH]); + if (retval != 0) { + dev_err(dp->dev, "EDID Read failed!\n"); + return -EIO; + } + sum = exynos_dp_calc_edid_check_sum(&edid[EDID_BLOCK_LENGTH]); + if (sum != 0) { + dev_err(dp->dev, "EDID bad checksum!\n"); + return -EIO; + } + + exynos_dp_read_byte_from_dpcd(dp, DPCD_ADDR_TEST_REQUEST, + &test_vector); + if (test_vector & DPCD_TEST_EDID_READ) { + exynos_dp_write_byte_to_dpcd(dp, + DPCD_ADDR_TEST_EDID_CHECKSUM, + edid[EDID_BLOCK_LENGTH + EDID_CHECKSUM]); + exynos_dp_write_byte_to_dpcd(dp, + DPCD_ADDR_TEST_RESPONSE, + DPCD_TEST_EDID_CHECKSUM_WRITE); + } + } else { + dev_info(dp->dev, "EDID data does not include any extensions.\n"); + + /* Read EDID data */ + retval = exynos_dp_read_bytes_from_i2c(dp, + I2C_EDID_DEVICE_ADDR, + EDID_HEADER_PATTERN, + EDID_BLOCK_LENGTH, + &edid[EDID_HEADER_PATTERN]); + if (retval != 0) { + dev_err(dp->dev, "EDID Read failed!\n"); + return -EIO; + } + sum = exynos_dp_calc_edid_check_sum(edid); + if (sum != 0) { + dev_err(dp->dev, "EDID bad checksum!\n"); + return -EIO; + } + + exynos_dp_read_byte_from_dpcd(dp, + DPCD_ADDR_TEST_REQUEST, + &test_vector); + if (test_vector & DPCD_TEST_EDID_READ) { + exynos_dp_write_byte_to_dpcd(dp, + DPCD_ADDR_TEST_EDID_CHECKSUM, + edid[EDID_CHECKSUM]); + exynos_dp_write_byte_to_dpcd(dp, + DPCD_ADDR_TEST_RESPONSE, + DPCD_TEST_EDID_CHECKSUM_WRITE); + } + } + + dev_err(dp->dev, "EDID Read success!\n"); + return 0; +} + +static int exynos_dp_handle_edid(struct exynos_dp_device *dp) +{ + u8 buf[12]; + int i; + int retval; + + /* Read DPCD DPCD_ADDR_DPCD_REV~RECEIVE_PORT1_CAP_1 */ + exynos_dp_read_bytes_from_dpcd(dp, + DPCD_ADDR_DPCD_REV, + 12, buf); + + /* Read EDID */ + for (i = 0; i < 3; i++) { + retval = exynos_dp_read_edid(dp); + if (retval == 0) + break; + } + + return retval; +} + +static void exynos_dp_enable_rx_to_enhanced_mode(struct exynos_dp_device *dp, + bool enable) +{ + u8 data; + + exynos_dp_read_byte_from_dpcd(dp, DPCD_ADDR_LANE_COUNT_SET, &data); + + if (enable) + exynos_dp_write_byte_to_dpcd(dp, DPCD_ADDR_LANE_COUNT_SET, + DPCD_ENHANCED_FRAME_EN | + DPCD_LANE_COUNT_SET(data)); + else + exynos_dp_write_byte_to_dpcd(dp, DPCD_ADDR_LANE_COUNT_SET, + DPCD_LANE_COUNT_SET(data)); +} + +static int exynos_dp_is_enhanced_mode_available(struct exynos_dp_device *dp) +{ + u8 data; + int retval; + + exynos_dp_read_byte_from_dpcd(dp, DPCD_ADDR_MAX_LANE_COUNT, &data); + retval = DPCD_ENHANCED_FRAME_CAP(data); + + return retval; +} + +static void exynos_dp_set_enhanced_mode(struct exynos_dp_device *dp) +{ + u8 data; + + data = exynos_dp_is_enhanced_mode_available(dp); + exynos_dp_enable_rx_to_enhanced_mode(dp, data); + exynos_dp_enable_enhanced_mode(dp, data); +} + +static void exynos_dp_training_pattern_dis(struct exynos_dp_device *dp) +{ + exynos_dp_set_training_pattern(dp, DP_NONE); + + exynos_dp_write_byte_to_dpcd(dp, + DPCD_ADDR_TRAINING_PATTERN_SET, + DPCD_TRAINING_PATTERN_DISABLED); +} + +static void exynos_dp_set_lane_lane_pre_emphasis(struct exynos_dp_device *dp, + int pre_emphasis, int lane) +{ + switch (lane) { + case 0: + exynos_dp_set_lane0_pre_emphasis(dp, pre_emphasis); + break; + case 1: + exynos_dp_set_lane1_pre_emphasis(dp, pre_emphasis); + break; + + case 2: + exynos_dp_set_lane2_pre_emphasis(dp, pre_emphasis); + break; + + case 3: + exynos_dp_set_lane3_pre_emphasis(dp, pre_emphasis); + break; + } +} + +static void exynos_dp_link_start(struct exynos_dp_device *dp) +{ + u8 buf[5]; + int lane; + int lane_count; + + lane_count = dp->link_train.lane_count; + + dp->link_train.lt_state = CLOCK_RECOVERY; + dp->link_train.eq_loop = 0; + + for (lane = 0; lane < lane_count; lane++) + dp->link_train.cr_loop[lane] = 0; + + /* Set sink to D0 (Sink Not Ready) mode. */ + exynos_dp_write_byte_to_dpcd(dp, DPCD_ADDR_SINK_POWER_STATE, + DPCD_SET_POWER_STATE_D0); + + /* Set link rate and count as you want to establish*/ + exynos_dp_set_link_bandwidth(dp, dp->link_train.link_rate); + exynos_dp_set_lane_count(dp, dp->link_train.lane_count); + + /* Setup RX configuration */ + buf[0] = dp->link_train.link_rate; + buf[1] = dp->link_train.lane_count; + exynos_dp_write_bytes_to_dpcd(dp, DPCD_ADDR_LINK_BW_SET, + 2, buf); + + /* Set TX pre-emphasis to minimum */ + for (lane = 0; lane < lane_count; lane++) + exynos_dp_set_lane_lane_pre_emphasis(dp, + PRE_EMPHASIS_LEVEL_0, lane); + + /* Set training pattern 1 */ + exynos_dp_set_training_pattern(dp, TRAINING_PTN1); + + /* Set RX training pattern */ + buf[0] = DPCD_SCRAMBLING_DISABLED | + DPCD_TRAINING_PATTERN_1; + exynos_dp_write_byte_to_dpcd(dp, + DPCD_ADDR_TRAINING_PATTERN_SET, buf[0]); + + for (lane = 0; lane < lane_count; lane++) + buf[lane] = DPCD_PRE_EMPHASIS_PATTERN2_LEVEL0 | + DPCD_VOLTAGE_SWING_PATTERN1_LEVEL0; + exynos_dp_write_bytes_to_dpcd(dp, + DPCD_ADDR_TRAINING_PATTERN_SET, + lane_count, buf); +} + +static unsigned char exynos_dp_get_lane_status(u8 link_status[6], int lane) +{ + int shift = (lane & 1) * 4; + u8 link_value = link_status[lane>>1]; + + return (link_value >> shift) & 0xf; +} + +static int exynos_dp_clock_recovery_ok(u8 link_status[6], int lane_count) +{ + int lane; + u8 lane_status; + + for (lane = 0; lane < lane_count; lane++) { + lane_status = exynos_dp_get_lane_status(link_status, lane); + if ((lane_status & DPCD_LANE_CR_DONE) == 0) + return -EINVAL; + } + return 0; +} + +static int exynos_dp_channel_eq_ok(u8 link_status[6], int lane_count) +{ + int lane; + u8 lane_align; + u8 lane_status; + + lane_align = link_status[2]; + if ((lane_align == DPCD_INTERLANE_ALIGN_DONE) == 0) + return -EINVAL; + + for (lane = 0; lane < lane_count; lane++) { + lane_status = exynos_dp_get_lane_status(link_status, lane); + lane_status &= DPCD_CHANNEL_EQ_BITS; + if (lane_status != DPCD_CHANNEL_EQ_BITS) + return -EINVAL; + } + return 0; +} + +static unsigned char exynos_dp_get_adjust_request_voltage(u8 adjust_request[2], + int lane) +{ + int shift = (lane & 1) * 4; + u8 link_value = adjust_request[lane>>1]; + + return (link_value >> shift) & 0x3; +} + +static unsigned char exynos_dp_get_adjust_request_pre_emphasis( + u8 adjust_request[2], + int lane) +{ + int shift = (lane & 1) * 4; + u8 link_value = adjust_request[lane>>1]; + + return ((link_value >> shift) & 0xc) >> 2; +} + +static void exynos_dp_set_lane_link_training(struct exynos_dp_device *dp, + u8 training_lane_set, int lane) +{ + switch (lane) { + case 0: + exynos_dp_set_lane0_link_training(dp, training_lane_set); + break; + case 1: + exynos_dp_set_lane1_link_training(dp, training_lane_set); + break; + + case 2: + exynos_dp_set_lane2_link_training(dp, training_lane_set); + break; + + case 3: + exynos_dp_set_lane3_link_training(dp, training_lane_set); + break; + } +} + +static unsigned int exynos_dp_get_lane_link_training( + struct exynos_dp_device *dp, + int lane) +{ + u32 reg; + + switch (lane) { + case 0: + reg = exynos_dp_get_lane0_link_training(dp); + break; + case 1: + reg = exynos_dp_get_lane1_link_training(dp); + break; + case 2: + reg = exynos_dp_get_lane2_link_training(dp); + break; + case 3: + reg = exynos_dp_get_lane3_link_training(dp); + break; + } + + return reg; +} + +static void exynos_dp_reduce_link_rate(struct exynos_dp_device *dp) +{ + if (dp->link_train.link_rate == LINK_RATE_2_70GBPS) { + /* set to reduced bit rate */ + dp->link_train.link_rate = LINK_RATE_1_62GBPS; + dev_err(dp->dev, "set to bandwidth %.2x\n", + dp->link_train.link_rate); + dp->link_train.lt_state = START; + } else { + exynos_dp_training_pattern_dis(dp); + /* set enhanced mode if available */ + exynos_dp_set_enhanced_mode(dp); + dp->link_train.lt_state = FAILED; + } +} + +static void exynos_dp_get_adjust_train(struct exynos_dp_device *dp, + u8 adjust_request[2]) +{ + int lane; + int lane_count; + u8 voltage_swing; + u8 pre_emphasis; + u8 training_lane; + + lane_count = dp->link_train.lane_count; + for (lane = 0; lane < lane_count; lane++) { + voltage_swing = exynos_dp_get_adjust_request_voltage( + adjust_request, lane); + pre_emphasis = exynos_dp_get_adjust_request_pre_emphasis( + adjust_request, lane); + training_lane = DPCD_VOLTAGE_SWING_SET(voltage_swing) | + DPCD_PRE_EMPHASIS_SET(pre_emphasis); + + if (voltage_swing == VOLTAGE_LEVEL_3 || + pre_emphasis == PRE_EMPHASIS_LEVEL_3) { + training_lane |= DPCD_MAX_SWING_REACHED; + training_lane |= DPCD_MAX_PRE_EMPHASIS_REACHED; + } + dp->link_train.training_lane[lane] = training_lane; + } +} + +static int exynos_dp_check_max_cr_loop(struct exynos_dp_device *dp, + u8 voltage_swing) +{ + int lane; + int lane_count; + + lane_count = dp->link_train.lane_count; + for (lane = 0; lane < lane_count; lane++) { + if (voltage_swing == VOLTAGE_LEVEL_3 || + dp->link_train.cr_loop[lane] == MAX_CR_LOOP) + return -EINVAL; + } + return 0; +} + +static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp) +{ + u8 data; + u8 link_status[6]; + int lane; + int lane_count; + u8 buf[5]; + + u8 *adjust_request; + u8 voltage_swing; + u8 pre_emphasis; + u8 training_lane; + + udelay(100); + + exynos_dp_read_bytes_from_dpcd(dp, DPCD_ADDR_LANE0_1_STATUS, + 6, link_status); + lane_count = dp->link_train.lane_count; + + if (exynos_dp_clock_recovery_ok(link_status, lane_count) == 0) { + /* set training pattern 2 for EQ */ + exynos_dp_set_training_pattern(dp, TRAINING_PTN2); + + adjust_request = link_status + (DPCD_ADDR_ADJUST_REQUEST_LANE0_1 + - DPCD_ADDR_LANE0_1_STATUS); + + exynos_dp_get_adjust_train(dp, adjust_request); + + buf[0] = DPCD_SCRAMBLING_DISABLED | + DPCD_TRAINING_PATTERN_2; + exynos_dp_write_byte_to_dpcd(dp, + DPCD_ADDR_TRAINING_LANE0_SET, + buf[0]); + + for (lane = 0; lane < lane_count; lane++) { + exynos_dp_set_lane_link_training(dp, + dp->link_train.training_lane[lane], + lane); + buf[lane] = dp->link_train.training_lane[lane]; + exynos_dp_write_byte_to_dpcd(dp, + DPCD_ADDR_TRAINING_LANE0_SET + lane, + buf[lane]); + } + dp->link_train.lt_state = EQUALIZER_TRAINING; + } else { + exynos_dp_read_byte_from_dpcd(dp, + DPCD_ADDR_ADJUST_REQUEST_LANE0_1, + &data); + adjust_request[0] = data; + + exynos_dp_read_byte_from_dpcd(dp, + DPCD_ADDR_ADJUST_REQUEST_LANE2_3, + &data); + adjust_request[1] = data; + + for (lane = 0; lane < lane_count; lane++) { + training_lane = exynos_dp_get_lane_link_training( + dp, lane); + voltage_swing = exynos_dp_get_adjust_request_voltage( + adjust_request, lane); + pre_emphasis = exynos_dp_get_adjust_request_pre_emphasis( + adjust_request, lane); + if ((DPCD_VOLTAGE_SWING_GET(training_lane) == voltage_swing) && + (DPCD_PRE_EMPHASIS_GET(training_lane) == pre_emphasis)) + dp->link_train.cr_loop[lane]++; + dp->link_train.training_lane[lane] = training_lane; + } + + if (exynos_dp_check_max_cr_loop(dp, voltage_swing) != 0) { + exynos_dp_reduce_link_rate(dp); + } else { + exynos_dp_get_adjust_train(dp, adjust_request); + + for (lane = 0; lane < lane_count; lane++) { + exynos_dp_set_lane_link_training(dp, + dp->link_train.training_lane[lane], + lane); + buf[lane] = dp->link_train.training_lane[lane]; + exynos_dp_write_byte_to_dpcd(dp, + DPCD_ADDR_TRAINING_LANE0_SET + lane, + buf[lane]); + } + } + } + + return 0; +} + +static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp) +{ + u8 link_status[6]; + int lane; + int lane_count; + u8 buf[5]; + u32 reg; + + u8 *adjust_request; + + udelay(400); + + exynos_dp_read_bytes_from_dpcd(dp, DPCD_ADDR_LANE0_1_STATUS, + 6, link_status); + lane_count = dp->link_train.lane_count; + + if (exynos_dp_clock_recovery_ok(link_status, lane_count) == 0) { + adjust_request = link_status + (DPCD_ADDR_ADJUST_REQUEST_LANE0_1 + - DPCD_ADDR_LANE0_1_STATUS); + + if (exynos_dp_channel_eq_ok(link_status, lane_count) == 0) { + /* traing pattern Set to Normal */ + exynos_dp_training_pattern_dis(dp); + + dev_info(dp->dev, "Link Training success!\n"); + + exynos_dp_get_link_bandwidth(dp, ®); + dp->link_train.link_rate = reg; + dev_dbg(dp->dev, "final bandwidth = %.2x\n", + dp->link_train.link_rate); + + exynos_dp_get_lane_count(dp, ®); + dp->link_train.lane_count = reg; + dev_dbg(dp->dev, "final lane count = %.2x\n", + dp->link_train.lane_count); + /* set enhanced mode if available */ + exynos_dp_set_enhanced_mode(dp); + + dp->link_train.lt_state = FINISHED; + } else { + /* not all locked */ + dp->link_train.eq_loop++; + + if (dp->link_train.eq_loop > MAX_EQ_LOOP) { + exynos_dp_reduce_link_rate(dp); + } else { + exynos_dp_get_adjust_train(dp, adjust_request); + + for (lane = 0; lane < lane_count; lane++) { + exynos_dp_set_lane_link_training(dp, + dp->link_train.training_lane[lane], + lane); + buf[lane] = dp->link_train.training_lane[lane]; + exynos_dp_write_byte_to_dpcd(dp, + DPCD_ADDR_TRAINING_LANE0_SET + lane, + buf[lane]); + } + } + } + } else { + exynos_dp_reduce_link_rate(dp); + } + + return 0; +} + +static void exynos_dp_get_max_rx_bandwidth(struct exynos_dp_device *dp, + u8 *bandwidth) +{ + u8 data; + + /* + * For DP rev.1.1, Maximum link rate of Main Link lanes + * 0x06 = 1.62 Gbps, 0x0a = 2.7 Gbps + */ + exynos_dp_read_byte_from_dpcd(dp, DPCD_ADDR_MAX_LINK_RATE, &data); + *bandwidth = data; +} + +static void exynos_dp_get_max_rx_lane_count(struct exynos_dp_device *dp, + u8 *lane_count) +{ + u8 data; + + /* + * For DP rev.1.1, Maximum number of Main Link lanes + * 0x01 = 1 lane, 0x02 = 2 lanes, 0x04 = 4 lanes + */ + exynos_dp_read_byte_from_dpcd(dp, DPCD_ADDR_MAX_LANE_COUNT, &data); + *lane_count = DPCD_MAX_LANE_COUNT(data); +} + +static void exynos_dp_init_training(struct exynos_dp_device *dp, + enum link_lane_count_type max_lane, + enum link_rate_type max_rate) +{ + /* + * MACRO_RST must be applied after the PLL_LOCK to avoid + * the DP inter pair skew issue for at least 10 us + */ + exynos_dp_reset_macro(dp); + + /* Initialize by reading RX's DPCD */ + exynos_dp_get_max_rx_bandwidth(dp, &dp->link_train.link_rate); + exynos_dp_get_max_rx_lane_count(dp, &dp->link_train.lane_count); + + if ((dp->link_train.link_rate != LINK_RATE_1_62GBPS) && + (dp->link_train.link_rate != LINK_RATE_2_70GBPS)) { + dev_err(dp->dev, "Rx Max Link Rate is abnormal :%x !\n", + dp->link_train.link_rate); + dp->link_train.link_rate = LINK_RATE_1_62GBPS; + } + + if (dp->link_train.lane_count == 0) { + dev_err(dp->dev, "Rx Max Lane count is abnormal :%x !\n", + dp->link_train.lane_count); + dp->link_train.lane_count = (u8)LANE_COUNT1; + } + + /* Setup TX lane count & rate */ + if (dp->link_train.lane_count > max_lane) + dp->link_train.lane_count = max_lane; + if (dp->link_train.link_rate > max_rate) + dp->link_train.link_rate = max_rate; + + /* All DP analog module power up */ + exynos_dp_set_analog_power_down(dp, POWER_ALL, 0); +} + +static int exynos_dp_sw_link_training(struct exynos_dp_device *dp) +{ + int retval = 0; + int training_finished; + + /* Turn off unnecessary lane */ + if (dp->link_train.lane_count == 1) + exynos_dp_set_analog_power_down(dp, CH1_BLOCK, 1); + + training_finished = 0; + + dp->link_train.lt_state = START; + + /* Process here */ + while (!training_finished) { + switch (dp->link_train.lt_state) { + case START: + exynos_dp_link_start(dp); + break; + case CLOCK_RECOVERY: + exynos_dp_process_clock_recovery(dp); + break; + case EQUALIZER_TRAINING: + exynos_dp_process_equalizer_training(dp); + break; + case FINISHED: + training_finished = 1; + break; + case FAILED: + return -EREMOTEIO; + } + } + + return retval; +} + +static int exynos_dp_set_link_train(struct exynos_dp_device *dp, + u32 count, + u32 bwtype) +{ + int i; + int retval; + + for (i = 0; i < DP_TIMEOUT_LOOP_COUNT; i++) { + exynos_dp_init_training(dp, count, bwtype); + retval = exynos_dp_sw_link_training(dp); + if (retval == 0) + break; + + udelay(100); + } + + return retval; +} + +static int exynos_dp_config_video(struct exynos_dp_device *dp, + struct video_info *video_info) +{ + int retval = 0; + int timeout_loop = 0; + int done_count = 0; + + exynos_dp_config_video_slave_mode(dp, video_info); + + exynos_dp_set_video_color_format(dp, video_info->color_depth, + video_info->color_space, + video_info->dynamic_range, + video_info->ycbcr_coeff); + + if (exynos_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) { + dev_err(dp->dev, "PLL is not locked yet.\n"); + return -EINVAL; + } + + for (;;) { + timeout_loop++; + if (exynos_dp_is_slave_video_stream_clock_on(dp) == 0) + break; + if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) { + dev_err(dp->dev, "Timeout of video streamclk ok\n"); + return -ETIMEDOUT; + } + + mdelay(100); + } + + /* Set to use the register calculated M/N video */ + exynos_dp_set_video_cr_mn(dp, CALCULATED_M, 0, 0); + + /* For video bist, Video timing must be generated by register */ + exynos_dp_set_video_timing_mode(dp, VIDEO_TIMING_FROM_CAPTURE); + + /* Disable video mute */ + exynos_dp_enable_video_mute(dp, 0); + + /* Configure video slave mode */ + exynos_dp_enable_video_master(dp, 0); + + /* Enable video */ + exynos_dp_start_video(dp); + + timeout_loop = 0; + + for (;;) { + timeout_loop++; + if (exynos_dp_is_video_stream_on(dp) == 0) { + done_count++; + if (done_count > 10) + break; + } else if (done_count) { + done_count = 0; + } + if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) { + dev_err(dp->dev, "Timeout of video streamclk ok\n"); + return -ETIMEDOUT; + } + + mdelay(100); + } + + if (retval != 0) + dev_err(dp->dev, "Video stream is not detected!\n"); + + return retval; +} + +static void exynos_dp_enable_scramble(struct exynos_dp_device *dp, bool enable) +{ + u8 data; + + if (enable) { + exynos_dp_enable_scrambling(dp); + + exynos_dp_read_byte_from_dpcd(dp, + DPCD_ADDR_TRAINING_PATTERN_SET, + &data); + exynos_dp_write_byte_to_dpcd(dp, + DPCD_ADDR_TRAINING_PATTERN_SET, + (u8)(data & ~DPCD_SCRAMBLING_DISABLED)); + } else { + exynos_dp_disable_scrambling(dp); + + exynos_dp_read_byte_from_dpcd(dp, + DPCD_ADDR_TRAINING_PATTERN_SET, + &data); + exynos_dp_write_byte_to_dpcd(dp, + DPCD_ADDR_TRAINING_PATTERN_SET, + (u8)(data | DPCD_SCRAMBLING_DISABLED)); + } +} + +static irqreturn_t exynos_dp_irq_handler(int irq, void *arg) +{ + struct exynos_dp_device *dp = arg; + + dev_err(dp->dev, "exynos_dp_irq_handler\n"); + return IRQ_HANDLED; +} + +static int __devinit exynos_dp_probe(struct platform_device *pdev) +{ + struct resource *res; + struct exynos_dp_device *dp; + struct exynos_dp_platdata *pdata; + + int ret = 0; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "no platform data\n"); + return -EINVAL; + } + + dp = kzalloc(sizeof(struct exynos_dp_device), GFP_KERNEL); + if (!dp) { + dev_err(&pdev->dev, "no memory for device data\n"); + return -ENOMEM; + } + + dp->dev = &pdev->dev; + + dp->clock = clk_get(&pdev->dev, "dp"); + if (IS_ERR(dp->clock)) { + dev_err(&pdev->dev, "failed to get clock\n"); + ret = PTR_ERR(dp->clock); + goto err_dp; + } + + clk_enable(dp->clock); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to get registers\n"); + ret = -EINVAL; + goto err_clock; + } + + res = request_mem_region(res->start, resource_size(res), + dev_name(&pdev->dev)); + if (!res) { + dev_err(&pdev->dev, "failed to request registers region\n"); + ret = -EINVAL; + goto err_clock; + } + + dp->res = res; + + dp->reg_base = ioremap(res->start, resource_size(res)); + if (!dp->reg_base) { + dev_err(&pdev->dev, "failed to ioremap\n"); + ret = -ENOMEM; + goto err_req_region; + } + + dp->irq = platform_get_irq(pdev, 0); + if (!dp->irq) { + dev_err(&pdev->dev, "failed to get irq\n"); + ret = -ENODEV; + goto err_ioremap; + } + + ret = request_irq(dp->irq, exynos_dp_irq_handler, 0, + "exynos-dp", dp); + if (ret) { + dev_err(&pdev->dev, "failed to request irq\n"); + goto err_ioremap; + } + + dp->video_info = pdata->video_info; + if (pdata->phy_init) + pdata->phy_init(); + + exynos_dp_init_dp(dp); + + ret = exynos_dp_detect_hpd(dp); + if (ret) { + dev_err(&pdev->dev, "unable to detect hpd\n"); + goto err_irq; + } + + exynos_dp_handle_edid(dp); + + ret = exynos_dp_set_link_train(dp, dp->video_info->lane_count, + dp->video_info->link_rate); + if (ret) { + dev_err(&pdev->dev, "unable to do link train\n"); + goto err_irq; + } + + exynos_dp_enable_scramble(dp, 1); + exynos_dp_enable_rx_to_enhanced_mode(dp, 1); + exynos_dp_enable_enhanced_mode(dp, 1); + + exynos_dp_set_lane_count(dp, dp->video_info->lane_count); + exynos_dp_set_link_bandwidth(dp, dp->video_info->link_rate); + + exynos_dp_init_video(dp); + ret = exynos_dp_config_video(dp, dp->video_info); + if (ret) { + dev_err(&pdev->dev, "unable to config video\n"); + goto err_irq; + } + + platform_set_drvdata(pdev, dp); + + return 0; + +err_irq: + free_irq(dp->irq, dp); +err_ioremap: + iounmap(dp->reg_base); +err_req_region: + release_mem_region(res->start, resource_size(res)); +err_clock: + clk_put(dp->clock); +err_dp: + kfree(dp); + + return ret; +} + +static int __devexit exynos_dp_remove(struct platform_device *pdev) +{ + struct exynos_dp_platdata *pdata = pdev->dev.platform_data; + struct exynos_dp_device *dp = platform_get_drvdata(pdev); + + if (pdata && pdata->phy_exit) + pdata->phy_exit(); + + free_irq(dp->irq, dp); + iounmap(dp->reg_base); + + clk_disable(dp->clock); + clk_put(dp->clock); + + release_mem_region(dp->res->start, resource_size(dp->res)); + + kfree(dp); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int exynos_dp_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct exynos_dp_platdata *pdata = pdev->dev.platform_data; + struct exynos_dp_device *dp = platform_get_drvdata(pdev); + + if (pdata && pdata->phy_exit) + pdata->phy_exit(); + + clk_disable(dp->clock); + + return 0; +} + +static int exynos_dp_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct exynos_dp_platdata *pdata = pdev->dev.platform_data; + struct exynos_dp_device *dp = platform_get_drvdata(pdev); + + if (pdata && pdata->phy_init) + pdata->phy_init(); + + clk_enable(dp->clock); + + exynos_dp_init_dp(dp); + + exynos_dp_detect_hpd(dp); + exynos_dp_handle_edid(dp); + + exynos_dp_set_link_train(dp, dp->video_info->lane_count, + dp->video_info->link_rate); + + exynos_dp_enable_scramble(dp, 1); + exynos_dp_enable_rx_to_enhanced_mode(dp, 1); + exynos_dp_enable_enhanced_mode(dp, 1); + + exynos_dp_set_lane_count(dp, dp->video_info->lane_count); + exynos_dp_set_link_bandwidth(dp, dp->video_info->link_rate); + + exynos_dp_init_video(dp); + exynos_dp_config_video(dp, dp->video_info); + + return 0; +} +#endif + +static const struct dev_pm_ops exynos_dp_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(exynos_dp_suspend, exynos_dp_resume) +}; + +static struct platform_driver exynos_dp_driver = { + .probe = exynos_dp_probe, + .remove = __devexit_p(exynos_dp_remove), + .driver = { + .name = "exynos-dp", + .owner = THIS_MODULE, + .pm = &exynos_dp_pm_ops, + }, +}; + +module_platform_driver(exynos_dp_driver); + +MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>"); +MODULE_DESCRIPTION("Samsung SoC DP Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/exynos/exynos_dp_core.h b/drivers/video/exynos/exynos_dp_core.h new file mode 100644 index 000000000000..90ceaca0fa24 --- /dev/null +++ b/drivers/video/exynos/exynos_dp_core.h @@ -0,0 +1,206 @@ +/* + * Header file for Samsung DP (Display Port) interface driver. + * + * Copyright (C) 2012 Samsung Electronics Co., Ltd. + * Author: Jingoo Han <jg1.han@samsung.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef _EXYNOS_DP_CORE_H +#define _EXYNOS_DP_CORE_H + +struct link_train { + int eq_loop; + int cr_loop[4]; + + u8 link_rate; + u8 lane_count; + u8 training_lane[4]; + + enum link_training_state lt_state; +}; + +struct exynos_dp_device { + struct device *dev; + struct resource *res; + struct clk *clock; + unsigned int irq; + void __iomem *reg_base; + + struct video_info *video_info; + struct link_train link_train; +}; + +/* exynos_dp_reg.c */ +void exynos_dp_enable_video_mute(struct exynos_dp_device *dp, bool enable); +void exynos_dp_stop_video(struct exynos_dp_device *dp); +void exynos_dp_lane_swap(struct exynos_dp_device *dp, bool enable); +void exynos_dp_init_interrupt(struct exynos_dp_device *dp); +void exynos_dp_reset(struct exynos_dp_device *dp); +void exynos_dp_config_interrupt(struct exynos_dp_device *dp); +u32 exynos_dp_get_pll_lock_status(struct exynos_dp_device *dp); +void exynos_dp_set_pll_power_down(struct exynos_dp_device *dp, bool enable); +void exynos_dp_set_analog_power_down(struct exynos_dp_device *dp, + enum analog_power_block block, + bool enable); +void exynos_dp_init_analog_func(struct exynos_dp_device *dp); +void exynos_dp_init_hpd(struct exynos_dp_device *dp); +void exynos_dp_reset_aux(struct exynos_dp_device *dp); +void exynos_dp_init_aux(struct exynos_dp_device *dp); +int exynos_dp_get_plug_in_status(struct exynos_dp_device *dp); +void exynos_dp_enable_sw_function(struct exynos_dp_device *dp); +int exynos_dp_start_aux_transaction(struct exynos_dp_device *dp); +int exynos_dp_write_byte_to_dpcd(struct exynos_dp_device *dp, + unsigned int reg_addr, + unsigned char data); +int exynos_dp_read_byte_from_dpcd(struct exynos_dp_device *dp, + unsigned int reg_addr, + unsigned char *data); +int exynos_dp_write_bytes_to_dpcd(struct exynos_dp_device *dp, + unsigned int reg_addr, + unsigned int count, + unsigned char data[]); +int exynos_dp_read_bytes_from_dpcd(struct exynos_dp_device *dp, + unsigned int reg_addr, + unsigned int count, + unsigned char data[]); +int exynos_dp_select_i2c_device(struct exynos_dp_device *dp, + unsigned int device_addr, + unsigned int reg_addr); +int exynos_dp_read_byte_from_i2c(struct exynos_dp_device *dp, + unsigned int device_addr, + unsigned int reg_addr, + unsigned int *data); +int exynos_dp_read_bytes_from_i2c(struct exynos_dp_device *dp, + unsigned int device_addr, + unsigned int reg_addr, + unsigned int count, + unsigned char edid[]); +void exynos_dp_set_link_bandwidth(struct exynos_dp_device *dp, u32 bwtype); +void exynos_dp_get_link_bandwidth(struct exynos_dp_device *dp, u32 *bwtype); +void exynos_dp_set_lane_count(struct exynos_dp_device *dp, u32 count); +void exynos_dp_get_lane_count(struct exynos_dp_device *dp, u32 *count); +void exynos_dp_set_link_bandwidth(struct exynos_dp_device *dp, u32 bwtype); +void exynos_dp_get_link_bandwidth(struct exynos_dp_device *dp, u32 *bwtype); +void exynos_dp_set_lane_count(struct exynos_dp_device *dp, u32 count); +void exynos_dp_get_lane_count(struct exynos_dp_device *dp, u32 *count); +void exynos_dp_enable_enhanced_mode(struct exynos_dp_device *dp, bool enable); +void exynos_dp_set_training_pattern(struct exynos_dp_device *dp, + enum pattern_set pattern); +void exynos_dp_set_lane0_pre_emphasis(struct exynos_dp_device *dp, u32 level); +void exynos_dp_set_lane1_pre_emphasis(struct exynos_dp_device *dp, u32 level); +void exynos_dp_set_lane2_pre_emphasis(struct exynos_dp_device *dp, u32 level); +void exynos_dp_set_lane3_pre_emphasis(struct exynos_dp_device *dp, u32 level); +void exynos_dp_set_lane0_link_training(struct exynos_dp_device *dp, + u32 training_lane); +void exynos_dp_set_lane1_link_training(struct exynos_dp_device *dp, + u32 training_lane); +void exynos_dp_set_lane2_link_training(struct exynos_dp_device *dp, + u32 training_lane); +void exynos_dp_set_lane3_link_training(struct exynos_dp_device *dp, + u32 training_lane); +u32 exynos_dp_get_lane0_link_training(struct exynos_dp_device *dp); +u32 exynos_dp_get_lane1_link_training(struct exynos_dp_device *dp); +u32 exynos_dp_get_lane2_link_training(struct exynos_dp_device *dp); +u32 exynos_dp_get_lane3_link_training(struct exynos_dp_device *dp); +void exynos_dp_reset_macro(struct exynos_dp_device *dp); +int exynos_dp_init_video(struct exynos_dp_device *dp); + +void exynos_dp_set_video_color_format(struct exynos_dp_device *dp, + u32 color_depth, + u32 color_space, + u32 dynamic_range, + u32 ycbcr_coeff); +int exynos_dp_is_slave_video_stream_clock_on(struct exynos_dp_device *dp); +void exynos_dp_set_video_cr_mn(struct exynos_dp_device *dp, + enum clock_recovery_m_value_type type, + u32 m_value, + u32 n_value); +void exynos_dp_set_video_timing_mode(struct exynos_dp_device *dp, u32 type); +void exynos_dp_enable_video_master(struct exynos_dp_device *dp, bool enable); +void exynos_dp_start_video(struct exynos_dp_device *dp); +int exynos_dp_is_video_stream_on(struct exynos_dp_device *dp); +void exynos_dp_config_video_slave_mode(struct exynos_dp_device *dp, + struct video_info *video_info); +void exynos_dp_enable_scrambling(struct exynos_dp_device *dp); +void exynos_dp_disable_scrambling(struct exynos_dp_device *dp); + +/* I2C EDID Chip ID, Slave Address */ +#define I2C_EDID_DEVICE_ADDR 0x50 +#define I2C_E_EDID_DEVICE_ADDR 0x30 + +#define EDID_BLOCK_LENGTH 0x80 +#define EDID_HEADER_PATTERN 0x00 +#define EDID_EXTENSION_FLAG 0x7e +#define EDID_CHECKSUM 0x7f + +/* Definition for DPCD Register */ +#define DPCD_ADDR_DPCD_REV 0x0000 +#define DPCD_ADDR_MAX_LINK_RATE 0x0001 +#define DPCD_ADDR_MAX_LANE_COUNT 0x0002 +#define DPCD_ADDR_LINK_BW_SET 0x0100 +#define DPCD_ADDR_LANE_COUNT_SET 0x0101 +#define DPCD_ADDR_TRAINING_PATTERN_SET 0x0102 +#define DPCD_ADDR_TRAINING_LANE0_SET 0x0103 +#define DPCD_ADDR_LANE0_1_STATUS 0x0202 +#define DPCD_ADDR_LANE_ALIGN__STATUS_UPDATED 0x0204 +#define DPCD_ADDR_ADJUST_REQUEST_LANE0_1 0x0206 +#define DPCD_ADDR_ADJUST_REQUEST_LANE2_3 0x0207 +#define DPCD_ADDR_TEST_REQUEST 0x0218 +#define DPCD_ADDR_TEST_RESPONSE 0x0260 +#define DPCD_ADDR_TEST_EDID_CHECKSUM 0x0261 +#define DPCD_ADDR_SINK_POWER_STATE 0x0600 + +/* DPCD_ADDR_MAX_LANE_COUNT */ +#define DPCD_ENHANCED_FRAME_CAP(x) (((x) >> 7) & 0x1) +#define DPCD_MAX_LANE_COUNT(x) ((x) & 0x1f) + +/* DPCD_ADDR_LANE_COUNT_SET */ +#define DPCD_ENHANCED_FRAME_EN (0x1 << 7) +#define DPCD_LANE_COUNT_SET(x) ((x) & 0x1f) + +/* DPCD_ADDR_TRAINING_PATTERN_SET */ +#define DPCD_SCRAMBLING_DISABLED (0x1 << 5) +#define DPCD_SCRAMBLING_ENABLED (0x0 << 5) +#define DPCD_TRAINING_PATTERN_2 (0x2 << 0) +#define DPCD_TRAINING_PATTERN_1 (0x1 << 0) +#define DPCD_TRAINING_PATTERN_DISABLED (0x0 << 0) + +/* DPCD_ADDR_TRAINING_LANE0_SET */ +#define DPCD_MAX_PRE_EMPHASIS_REACHED (0x1 << 5) +#define DPCD_PRE_EMPHASIS_SET(x) (((x) & 0x3) << 3) +#define DPCD_PRE_EMPHASIS_GET(x) (((x) >> 3) & 0x3) +#define DPCD_PRE_EMPHASIS_PATTERN2_LEVEL0 (0x0 << 3) +#define DPCD_MAX_SWING_REACHED (0x1 << 2) +#define DPCD_VOLTAGE_SWING_SET(x) (((x) & 0x3) << 0) +#define DPCD_VOLTAGE_SWING_GET(x) (((x) >> 0) & 0x3) +#define DPCD_VOLTAGE_SWING_PATTERN1_LEVEL0 (0x0 << 0) + +/* DPCD_ADDR_LANE0_1_STATUS */ +#define DPCD_LANE_SYMBOL_LOCKED (0x1 << 2) +#define DPCD_LANE_CHANNEL_EQ_DONE (0x1 << 1) +#define DPCD_LANE_CR_DONE (0x1 << 0) +#define DPCD_CHANNEL_EQ_BITS (DPCD_LANE_CR_DONE| \ + DPCD_LANE_CHANNEL_EQ_DONE|\ + DPCD_LANE_SYMBOL_LOCKED) + +/* DPCD_ADDR_LANE_ALIGN__STATUS_UPDATED */ +#define DPCD_LINK_STATUS_UPDATED (0x1 << 7) +#define DPCD_DOWNSTREAM_PORT_STATUS_CHANGED (0x1 << 6) +#define DPCD_INTERLANE_ALIGN_DONE (0x1 << 0) + +/* DPCD_ADDR_TEST_REQUEST */ +#define DPCD_TEST_EDID_READ (0x1 << 2) + +/* DPCD_ADDR_TEST_RESPONSE */ +#define DPCD_TEST_EDID_CHECKSUM_WRITE (0x1 << 2) + +/* DPCD_ADDR_SINK_POWER_STATE */ +#define DPCD_SET_POWER_STATE_D0 (0x1 << 0) +#define DPCD_SET_POWER_STATE_D4 (0x2 << 0) + +#endif /* _EXYNOS_DP_CORE_H */ diff --git a/drivers/video/exynos/exynos_dp_reg.c b/drivers/video/exynos/exynos_dp_reg.c new file mode 100644 index 000000000000..6548afa0e3d2 --- /dev/null +++ b/drivers/video/exynos/exynos_dp_reg.c @@ -0,0 +1,1173 @@ +/* + * Samsung DP (Display port) register interface driver. + * + * Copyright (C) 2012 Samsung Electronics Co., Ltd. + * Author: Jingoo Han <jg1.han@samsung.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/device.h> +#include <linux/io.h> +#include <linux/delay.h> + +#include <video/exynos_dp.h> + +#include <plat/cpu.h> + +#include "exynos_dp_core.h" +#include "exynos_dp_reg.h" + +#define COMMON_INT_MASK_1 (0) +#define COMMON_INT_MASK_2 (0) +#define COMMON_INT_MASK_3 (0) +#define COMMON_INT_MASK_4 (0) +#define INT_STA_MASK (0) + +void exynos_dp_enable_video_mute(struct exynos_dp_device *dp, bool enable) +{ + u32 reg; + + if (enable) { + reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_1); + reg |= HDCP_VIDEO_MUTE; + writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_1); + } else { + reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_1); + reg &= ~HDCP_VIDEO_MUTE; + writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_1); + } +} + +void exynos_dp_stop_video(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_1); + reg &= ~VIDEO_EN; + writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_1); +} + +void exynos_dp_lane_swap(struct exynos_dp_device *dp, bool enable) +{ + u32 reg; + + if (enable) + reg = LANE3_MAP_LOGIC_LANE_0 | LANE2_MAP_LOGIC_LANE_1 | + LANE1_MAP_LOGIC_LANE_2 | LANE0_MAP_LOGIC_LANE_3; + else + reg = LANE3_MAP_LOGIC_LANE_3 | LANE2_MAP_LOGIC_LANE_2 | + LANE1_MAP_LOGIC_LANE_1 | LANE0_MAP_LOGIC_LANE_0; + + writel(reg, dp->reg_base + EXYNOS_DP_LANE_MAP); +} + +void exynos_dp_init_interrupt(struct exynos_dp_device *dp) +{ + /* Set interrupt pin assertion polarity as high */ + writel(INT_POL, dp->reg_base + EXYNOS_DP_INT_CTL); + + /* Clear pending regisers */ + writel(0xff, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_1); + writel(0x4f, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_2); + writel(0xe0, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_3); + writel(0xe7, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_4); + writel(0x63, dp->reg_base + EXYNOS_DP_INT_STA); + + /* 0:mask,1: unmask */ + writel(0x00, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_1); + writel(0x00, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_2); + writel(0x00, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_3); + writel(0x00, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_4); + writel(0x00, dp->reg_base + EXYNOS_DP_INT_STA_MASK); +} + +void exynos_dp_reset(struct exynos_dp_device *dp) +{ + u32 reg; + + writel(RESET_DP_TX, dp->reg_base + EXYNOS_DP_TX_SW_RESET); + + exynos_dp_stop_video(dp); + exynos_dp_enable_video_mute(dp, 0); + + reg = MASTER_VID_FUNC_EN_N | SLAVE_VID_FUNC_EN_N | + AUD_FIFO_FUNC_EN_N | AUD_FUNC_EN_N | + HDCP_FUNC_EN_N | SW_FUNC_EN_N; + writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_1); + + reg = SSC_FUNC_EN_N | AUX_FUNC_EN_N | + SERDES_FIFO_FUNC_EN_N | + LS_CLK_DOMAIN_FUNC_EN_N; + writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_2); + + udelay(20); + + exynos_dp_lane_swap(dp, 0); + + writel(0x0, dp->reg_base + EXYNOS_DP_SYS_CTL_1); + writel(0x40, dp->reg_base + EXYNOS_DP_SYS_CTL_2); + writel(0x0, dp->reg_base + EXYNOS_DP_SYS_CTL_3); + writel(0x0, dp->reg_base + EXYNOS_DP_SYS_CTL_4); + + writel(0x0, dp->reg_base + EXYNOS_DP_PKT_SEND_CTL); + writel(0x0, dp->reg_base + EXYNOS_DP_HDCP_CTL); + + writel(0x5e, dp->reg_base + EXYNOS_DP_HPD_DEGLITCH_L); + writel(0x1a, dp->reg_base + EXYNOS_DP_HPD_DEGLITCH_H); + + writel(0x10, dp->reg_base + EXYNOS_DP_LINK_DEBUG_CTL); + + writel(0x0, dp->reg_base + EXYNOS_DP_PHY_TEST); + + writel(0x0, dp->reg_base + EXYNOS_DP_VIDEO_FIFO_THRD); + writel(0x20, dp->reg_base + EXYNOS_DP_AUDIO_MARGIN); + + writel(0x4, dp->reg_base + EXYNOS_DP_M_VID_GEN_FILTER_TH); + writel(0x2, dp->reg_base + EXYNOS_DP_M_AUD_GEN_FILTER_TH); + + writel(0x00000101, dp->reg_base + EXYNOS_DP_SOC_GENERAL_CTL); + + exynos_dp_init_interrupt(dp); +} + +void exynos_dp_config_interrupt(struct exynos_dp_device *dp) +{ + u32 reg; + + /* 0: mask, 1: unmask */ + reg = COMMON_INT_MASK_1; + writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_1); + + reg = COMMON_INT_MASK_2; + writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_2); + + reg = COMMON_INT_MASK_3; + writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_3); + + reg = COMMON_INT_MASK_4; + writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_4); + + reg = INT_STA_MASK; + writel(reg, dp->reg_base + EXYNOS_DP_INT_STA_MASK); +} + +u32 exynos_dp_get_pll_lock_status(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = readl(dp->reg_base + EXYNOS_DP_DEBUG_CTL); + if (reg & PLL_LOCK) + return PLL_LOCKED; + else + return PLL_UNLOCKED; +} + +void exynos_dp_set_pll_power_down(struct exynos_dp_device *dp, bool enable) +{ + u32 reg; + + if (enable) { + reg = readl(dp->reg_base + EXYNOS_DP_PLL_CTL); + reg |= DP_PLL_PD; + writel(reg, dp->reg_base + EXYNOS_DP_PLL_CTL); + } else { + reg = readl(dp->reg_base + EXYNOS_DP_PLL_CTL); + reg &= ~DP_PLL_PD; + writel(reg, dp->reg_base + EXYNOS_DP_PLL_CTL); + } +} + +void exynos_dp_set_analog_power_down(struct exynos_dp_device *dp, + enum analog_power_block block, + bool enable) +{ + u32 reg; + + switch (block) { + case AUX_BLOCK: + if (enable) { + reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD); + reg |= AUX_PD; + writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD); + } else { + reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD); + reg &= ~AUX_PD; + writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD); + } + break; + case CH0_BLOCK: + if (enable) { + reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD); + reg |= CH0_PD; + writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD); + } else { + reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD); + reg &= ~CH0_PD; + writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD); + } + break; + case CH1_BLOCK: + if (enable) { + reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD); + reg |= CH1_PD; + writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD); + } else { + reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD); + reg &= ~CH1_PD; + writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD); + } + break; + case CH2_BLOCK: + if (enable) { + reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD); + reg |= CH2_PD; + writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD); + } else { + reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD); + reg &= ~CH2_PD; + writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD); + } + break; + case CH3_BLOCK: + if (enable) { + reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD); + reg |= CH3_PD; + writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD); + } else { + reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD); + reg &= ~CH3_PD; + writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD); + } + break; + case ANALOG_TOTAL: + if (enable) { + reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD); + reg |= DP_PHY_PD; + writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD); + } else { + reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD); + reg &= ~DP_PHY_PD; + writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD); + } + break; + case POWER_ALL: + if (enable) { + reg = DP_PHY_PD | AUX_PD | CH3_PD | CH2_PD | + CH1_PD | CH0_PD; + writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD); + } else { + writel(0x00, dp->reg_base + EXYNOS_DP_PHY_PD); + } + break; + default: + break; + } +} + +void exynos_dp_init_analog_func(struct exynos_dp_device *dp) +{ + u32 reg; + + exynos_dp_set_analog_power_down(dp, POWER_ALL, 0); + + reg = PLL_LOCK_CHG; + writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_1); + + reg = readl(dp->reg_base + EXYNOS_DP_DEBUG_CTL); + reg &= ~(F_PLL_LOCK | PLL_LOCK_CTRL); + writel(reg, dp->reg_base + EXYNOS_DP_DEBUG_CTL); + + /* Power up PLL */ + if (exynos_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) + exynos_dp_set_pll_power_down(dp, 0); + + /* Enable Serdes FIFO function and Link symbol clock domain module */ + reg = readl(dp->reg_base + EXYNOS_DP_FUNC_EN_2); + reg &= ~(SERDES_FIFO_FUNC_EN_N | LS_CLK_DOMAIN_FUNC_EN_N + | AUX_FUNC_EN_N); + writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_2); +} + +void exynos_dp_init_hpd(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = HOTPLUG_CHG | HPD_LOST | PLUG; + writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_4); + + reg = INT_HPD; + writel(reg, dp->reg_base + EXYNOS_DP_INT_STA); + + reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3); + reg &= ~(F_HPD | HPD_CTRL); + writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_3); +} + +void exynos_dp_reset_aux(struct exynos_dp_device *dp) +{ + u32 reg; + + /* Disable AUX channel module */ + reg = readl(dp->reg_base + EXYNOS_DP_FUNC_EN_2); + reg |= AUX_FUNC_EN_N; + writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_2); +} + +void exynos_dp_init_aux(struct exynos_dp_device *dp) +{ + u32 reg; + + /* Clear inerrupts related to AUX channel */ + reg = RPLY_RECEIV | AUX_ERR; + writel(reg, dp->reg_base + EXYNOS_DP_INT_STA); + + exynos_dp_reset_aux(dp); + + /* Disable AUX transaction H/W retry */ + reg = AUX_BIT_PERIOD_EXPECTED_DELAY(3) | AUX_HW_RETRY_COUNT_SEL(0)| + AUX_HW_RETRY_INTERVAL_600_MICROSECONDS; + writel(reg, dp->reg_base + EXYNOS_DP_AUX_HW_RETRY_CTL) ; + + /* Receive AUX Channel DEFER commands equal to DEFFER_COUNT*64 */ + reg = DEFER_CTRL_EN | DEFER_COUNT(1); + writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_DEFER_CTL); + + /* Enable AUX channel module */ + reg = readl(dp->reg_base + EXYNOS_DP_FUNC_EN_2); + reg &= ~AUX_FUNC_EN_N; + writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_2); +} + +int exynos_dp_get_plug_in_status(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3); + if (reg & HPD_STATUS) + return 0; + + return -EINVAL; +} + +void exynos_dp_enable_sw_function(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = readl(dp->reg_base + EXYNOS_DP_FUNC_EN_1); + reg &= ~SW_FUNC_EN_N; + writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_1); +} + +int exynos_dp_start_aux_transaction(struct exynos_dp_device *dp) +{ + int reg; + int retval = 0; + + /* Enable AUX CH operation */ + reg = readl(dp->reg_base + EXYNOS_DP_AUX_CH_CTL_2); + reg |= AUX_EN; + writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_2); + + /* Is AUX CH command reply received? */ + reg = readl(dp->reg_base + EXYNOS_DP_INT_STA); + while (!(reg & RPLY_RECEIV)) + reg = readl(dp->reg_base + EXYNOS_DP_INT_STA); + + /* Clear interrupt source for AUX CH command reply */ + writel(RPLY_RECEIV, dp->reg_base + EXYNOS_DP_INT_STA); + + /* Clear interrupt source for AUX CH access error */ + reg = readl(dp->reg_base + EXYNOS_DP_INT_STA); + if (reg & AUX_ERR) { + writel(AUX_ERR, dp->reg_base + EXYNOS_DP_INT_STA); + return -EREMOTEIO; + } + + /* Check AUX CH error access status */ + reg = readl(dp->reg_base + EXYNOS_DP_AUX_CH_STA); + if ((reg & AUX_STATUS_MASK) != 0) { + dev_err(dp->dev, "AUX CH error happens: %d\n\n", + reg & AUX_STATUS_MASK); + return -EREMOTEIO; + } + + return retval; +} + +int exynos_dp_write_byte_to_dpcd(struct exynos_dp_device *dp, + unsigned int reg_addr, + unsigned char data) +{ + u32 reg; + int i; + int retval; + + for (i = 0; i < 3; i++) { + /* Clear AUX CH data buffer */ + reg = BUF_CLR; + writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL); + + /* Select DPCD device address */ + reg = AUX_ADDR_7_0(reg_addr); + writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_7_0); + reg = AUX_ADDR_15_8(reg_addr); + writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_15_8); + reg = AUX_ADDR_19_16(reg_addr); + writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_19_16); + + /* Write data buffer */ + reg = (unsigned int)data; + writel(reg, dp->reg_base + EXYNOS_DP_BUF_DATA_0); + + /* + * Set DisplayPort transaction and write 1 byte + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + reg = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE; + writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_1); + + /* Start AUX transaction */ + retval = exynos_dp_start_aux_transaction(dp); + if (retval == 0) + break; + else + dev_err(dp->dev, "Aux Transaction fail!\n"); + } + + return retval; +} + +int exynos_dp_read_byte_from_dpcd(struct exynos_dp_device *dp, + unsigned int reg_addr, + unsigned char *data) +{ + u32 reg; + int i; + int retval; + + for (i = 0; i < 10; i++) { + /* Clear AUX CH data buffer */ + reg = BUF_CLR; + writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL); + + /* Select DPCD device address */ + reg = AUX_ADDR_7_0(reg_addr); + writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_7_0); + reg = AUX_ADDR_15_8(reg_addr); + writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_15_8); + reg = AUX_ADDR_19_16(reg_addr); + writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_19_16); + + /* + * Set DisplayPort transaction and read 1 byte + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + reg = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ; + writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_1); + + /* Start AUX transaction */ + retval = exynos_dp_start_aux_transaction(dp); + if (retval == 0) + break; + else + dev_err(dp->dev, "Aux Transaction fail!\n"); + } + + /* Read data buffer */ + reg = readl(dp->reg_base + EXYNOS_DP_BUF_DATA_0); + *data = (unsigned char)(reg & 0xff); + + return retval; +} + +int exynos_dp_write_bytes_to_dpcd(struct exynos_dp_device *dp, + unsigned int reg_addr, + unsigned int count, + unsigned char data[]) +{ + u32 reg; + unsigned int start_offset; + unsigned int cur_data_count; + unsigned int cur_data_idx; + int i; + int retval = 0; + + /* Clear AUX CH data buffer */ + reg = BUF_CLR; + writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL); + + start_offset = 0; + while (start_offset < count) { + /* Buffer size of AUX CH is 16 * 4bytes */ + if ((count - start_offset) > 16) + cur_data_count = 16; + else + cur_data_count = count - start_offset; + + for (i = 0; i < 10; i++) { + /* Select DPCD device address */ + reg = AUX_ADDR_7_0(reg_addr + start_offset); + writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_7_0); + reg = AUX_ADDR_15_8(reg_addr + start_offset); + writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_15_8); + reg = AUX_ADDR_19_16(reg_addr + start_offset); + writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_19_16); + + for (cur_data_idx = 0; cur_data_idx < cur_data_count; + cur_data_idx++) { + reg = data[start_offset + cur_data_idx]; + writel(reg, dp->reg_base + EXYNOS_DP_BUF_DATA_0 + + 4 * cur_data_idx); + } + + /* + * Set DisplayPort transaction and write + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + reg = AUX_LENGTH(cur_data_count) | + AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE; + writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_1); + + /* Start AUX transaction */ + retval = exynos_dp_start_aux_transaction(dp); + if (retval == 0) + break; + else + dev_err(dp->dev, "Aux Transaction fail!\n"); + } + + start_offset += cur_data_count; + } + + return retval; +} + +int exynos_dp_read_bytes_from_dpcd(struct exynos_dp_device *dp, + unsigned int reg_addr, + unsigned int count, + unsigned char data[]) +{ + u32 reg; + unsigned int start_offset; + unsigned int cur_data_count; + unsigned int cur_data_idx; + int i; + int retval = 0; + + /* Clear AUX CH data buffer */ + reg = BUF_CLR; + writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL); + + start_offset = 0; + while (start_offset < count) { + /* Buffer size of AUX CH is 16 * 4bytes */ + if ((count - start_offset) > 16) + cur_data_count = 16; + else + cur_data_count = count - start_offset; + + /* AUX CH Request Transaction process */ + for (i = 0; i < 10; i++) { + /* Select DPCD device address */ + reg = AUX_ADDR_7_0(reg_addr + start_offset); + writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_7_0); + reg = AUX_ADDR_15_8(reg_addr + start_offset); + writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_15_8); + reg = AUX_ADDR_19_16(reg_addr + start_offset); + writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_19_16); + + /* + * Set DisplayPort transaction and read + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + reg = AUX_LENGTH(cur_data_count) | + AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ; + writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_1); + + /* Start AUX transaction */ + retval = exynos_dp_start_aux_transaction(dp); + if (retval == 0) + break; + else + dev_err(dp->dev, "Aux Transaction fail!\n"); + } + + for (cur_data_idx = 0; cur_data_idx < cur_data_count; + cur_data_idx++) { + reg = readl(dp->reg_base + EXYNOS_DP_BUF_DATA_0 + + 4 * cur_data_idx); + data[start_offset + cur_data_idx] = + (unsigned char)reg; + } + + start_offset += cur_data_count; + } + + return retval; +} + +int exynos_dp_select_i2c_device(struct exynos_dp_device *dp, + unsigned int device_addr, + unsigned int reg_addr) +{ + u32 reg; + int retval; + + /* Set EDID device address */ + reg = device_addr; + writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_7_0); + writel(0x0, dp->reg_base + EXYNOS_DP_AUX_ADDR_15_8); + writel(0x0, dp->reg_base + EXYNOS_DP_AUX_ADDR_19_16); + + /* Set offset from base address of EDID device */ + writel(reg_addr, dp->reg_base + EXYNOS_DP_BUF_DATA_0); + + /* + * Set I2C transaction and write address + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + reg = AUX_TX_COMM_I2C_TRANSACTION | AUX_TX_COMM_MOT | + AUX_TX_COMM_WRITE; + writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_1); + + /* Start AUX transaction */ + retval = exynos_dp_start_aux_transaction(dp); + if (retval != 0) + dev_err(dp->dev, "Aux Transaction fail!\n"); + + return retval; +} + +int exynos_dp_read_byte_from_i2c(struct exynos_dp_device *dp, + unsigned int device_addr, + unsigned int reg_addr, + unsigned int *data) +{ + u32 reg; + int i; + int retval; + + for (i = 0; i < 10; i++) { + /* Clear AUX CH data buffer */ + reg = BUF_CLR; + writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL); + + /* Select EDID device */ + retval = exynos_dp_select_i2c_device(dp, device_addr, reg_addr); + if (retval != 0) { + dev_err(dp->dev, "Select EDID device fail!\n"); + continue; + } + + /* + * Set I2C transaction and read data + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + reg = AUX_TX_COMM_I2C_TRANSACTION | + AUX_TX_COMM_READ; + writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_1); + + /* Start AUX transaction */ + retval = exynos_dp_start_aux_transaction(dp); + if (retval == 0) + break; + else + dev_err(dp->dev, "Aux Transaction fail!\n"); + } + + /* Read data */ + if (retval == 0) + *data = readl(dp->reg_base + EXYNOS_DP_BUF_DATA_0); + + return retval; +} + +int exynos_dp_read_bytes_from_i2c(struct exynos_dp_device *dp, + unsigned int device_addr, + unsigned int reg_addr, + unsigned int count, + unsigned char edid[]) +{ + u32 reg; + unsigned int i, j; + unsigned int cur_data_idx; + unsigned int defer = 0; + int retval = 0; + + for (i = 0; i < count; i += 16) { + for (j = 0; j < 100; j++) { + /* Clear AUX CH data buffer */ + reg = BUF_CLR; + writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL); + + /* Set normal AUX CH command */ + reg = readl(dp->reg_base + EXYNOS_DP_AUX_CH_CTL_2); + reg &= ~ADDR_ONLY; + writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_2); + + /* + * If Rx sends defer, Tx sends only reads + * request without sending addres + */ + if (!defer) + retval = exynos_dp_select_i2c_device(dp, + device_addr, reg_addr + i); + else + defer = 0; + + if (retval == 0) { + /* + * Set I2C transaction and write data + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + reg = AUX_LENGTH(16) | + AUX_TX_COMM_I2C_TRANSACTION | + AUX_TX_COMM_READ; + writel(reg, dp->reg_base + + EXYNOS_DP_AUX_CH_CTL_1); + + /* Start AUX transaction */ + retval = exynos_dp_start_aux_transaction(dp); + if (retval == 0) + break; + else + dev_err(dp->dev, "Aux Transaction fail!\n"); + } + /* Check if Rx sends defer */ + reg = readl(dp->reg_base + EXYNOS_DP_AUX_RX_COMM); + if (reg == AUX_RX_COMM_AUX_DEFER || + reg == AUX_RX_COMM_I2C_DEFER) { + dev_err(dp->dev, "Defer: %d\n\n", reg); + defer = 1; + } + } + + for (cur_data_idx = 0; cur_data_idx < 16; cur_data_idx++) { + reg = readl(dp->reg_base + EXYNOS_DP_BUF_DATA_0 + + 4 * cur_data_idx); + edid[i + cur_data_idx] = (unsigned char)reg; + } + } + + return retval; +} + +void exynos_dp_set_link_bandwidth(struct exynos_dp_device *dp, u32 bwtype) +{ + u32 reg; + + reg = bwtype; + if ((bwtype == LINK_RATE_2_70GBPS) || (bwtype == LINK_RATE_1_62GBPS)) + writel(reg, dp->reg_base + EXYNOS_DP_LINK_BW_SET); +} + +void exynos_dp_get_link_bandwidth(struct exynos_dp_device *dp, u32 *bwtype) +{ + u32 reg; + + reg = readl(dp->reg_base + EXYNOS_DP_LINK_BW_SET); + *bwtype = reg; +} + +void exynos_dp_set_lane_count(struct exynos_dp_device *dp, u32 count) +{ + u32 reg; + + reg = count; + writel(reg, dp->reg_base + EXYNOS_DP_LANE_COUNT_SET); +} + +void exynos_dp_get_lane_count(struct exynos_dp_device *dp, u32 *count) +{ + u32 reg; + + reg = readl(dp->reg_base + EXYNOS_DP_LANE_COUNT_SET); + *count = reg; +} + +void exynos_dp_enable_enhanced_mode(struct exynos_dp_device *dp, bool enable) +{ + u32 reg; + + if (enable) { + reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_4); + reg |= ENHANCED; + writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_4); + } else { + reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_4); + reg &= ~ENHANCED; + writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_4); + } +} + +void exynos_dp_set_training_pattern(struct exynos_dp_device *dp, + enum pattern_set pattern) +{ + u32 reg; + + switch (pattern) { + case PRBS7: + reg = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_PRBS7; + writel(reg, dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET); + break; + case D10_2: + reg = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_D10_2; + writel(reg, dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET); + break; + case TRAINING_PTN1: + reg = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN1; + writel(reg, dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET); + break; + case TRAINING_PTN2: + reg = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN2; + writel(reg, dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET); + break; + case DP_NONE: + reg = SCRAMBLING_ENABLE | + LINK_QUAL_PATTERN_SET_DISABLE | + SW_TRAINING_PATTERN_SET_NORMAL; + writel(reg, dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET); + break; + default: + break; + } +} + +void exynos_dp_set_lane0_pre_emphasis(struct exynos_dp_device *dp, u32 level) +{ + u32 reg; + + reg = level << PRE_EMPHASIS_SET_SHIFT; + writel(reg, dp->reg_base + EXYNOS_DP_LN0_LINK_TRAINING_CTL); +} + +void exynos_dp_set_lane1_pre_emphasis(struct exynos_dp_device *dp, u32 level) +{ + u32 reg; + + reg = level << PRE_EMPHASIS_SET_SHIFT; + writel(reg, dp->reg_base + EXYNOS_DP_LN1_LINK_TRAINING_CTL); +} + +void exynos_dp_set_lane2_pre_emphasis(struct exynos_dp_device *dp, u32 level) +{ + u32 reg; + + reg = level << PRE_EMPHASIS_SET_SHIFT; + writel(reg, dp->reg_base + EXYNOS_DP_LN2_LINK_TRAINING_CTL); +} + +void exynos_dp_set_lane3_pre_emphasis(struct exynos_dp_device *dp, u32 level) +{ + u32 reg; + + reg = level << PRE_EMPHASIS_SET_SHIFT; + writel(reg, dp->reg_base + EXYNOS_DP_LN3_LINK_TRAINING_CTL); +} + +void exynos_dp_set_lane0_link_training(struct exynos_dp_device *dp, + u32 training_lane) +{ + u32 reg; + + reg = training_lane; + writel(reg, dp->reg_base + EXYNOS_DP_LN0_LINK_TRAINING_CTL); +} + +void exynos_dp_set_lane1_link_training(struct exynos_dp_device *dp, + u32 training_lane) +{ + u32 reg; + + reg = training_lane; + writel(reg, dp->reg_base + EXYNOS_DP_LN1_LINK_TRAINING_CTL); +} + +void exynos_dp_set_lane2_link_training(struct exynos_dp_device *dp, + u32 training_lane) +{ + u32 reg; + + reg = training_lane; + writel(reg, dp->reg_base + EXYNOS_DP_LN2_LINK_TRAINING_CTL); +} + +void exynos_dp_set_lane3_link_training(struct exynos_dp_device *dp, + u32 training_lane) +{ + u32 reg; + + reg = training_lane; + writel(reg, dp->reg_base + EXYNOS_DP_LN3_LINK_TRAINING_CTL); +} + +u32 exynos_dp_get_lane0_link_training(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = readl(dp->reg_base + EXYNOS_DP_LN0_LINK_TRAINING_CTL); + return reg; +} + +u32 exynos_dp_get_lane1_link_training(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = readl(dp->reg_base + EXYNOS_DP_LN1_LINK_TRAINING_CTL); + return reg; +} + +u32 exynos_dp_get_lane2_link_training(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = readl(dp->reg_base + EXYNOS_DP_LN2_LINK_TRAINING_CTL); + return reg; +} + +u32 exynos_dp_get_lane3_link_training(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = readl(dp->reg_base + EXYNOS_DP_LN3_LINK_TRAINING_CTL); + return reg; +} + +void exynos_dp_reset_macro(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = readl(dp->reg_base + EXYNOS_DP_PHY_TEST); + reg |= MACRO_RST; + writel(reg, dp->reg_base + EXYNOS_DP_PHY_TEST); + + /* 10 us is the minimum reset time. */ + udelay(10); + + reg &= ~MACRO_RST; + writel(reg, dp->reg_base + EXYNOS_DP_PHY_TEST); +} + +int exynos_dp_init_video(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = VSYNC_DET | VID_FORMAT_CHG | VID_CLK_CHG; + writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_1); + + reg = 0x0; + writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_1); + + reg = CHA_CRI(4) | CHA_CTRL; + writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_2); + + reg = 0x0; + writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_3); + + reg = VID_HRES_TH(2) | VID_VRES_TH(0); + writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_8); + + return 0; +} + +void exynos_dp_set_video_color_format(struct exynos_dp_device *dp, + u32 color_depth, + u32 color_space, + u32 dynamic_range, + u32 ycbcr_coeff) +{ + u32 reg; + + /* Configure the input color depth, color space, dynamic range */ + reg = (dynamic_range << IN_D_RANGE_SHIFT) | + (color_depth << IN_BPC_SHIFT) | + (color_space << IN_COLOR_F_SHIFT); + writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_2); + + /* Set Input Color YCbCr Coefficients to ITU601 or ITU709 */ + reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_3); + reg &= ~IN_YC_COEFFI_MASK; + if (ycbcr_coeff) + reg |= IN_YC_COEFFI_ITU709; + else + reg |= IN_YC_COEFFI_ITU601; + writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_3); +} + +int exynos_dp_is_slave_video_stream_clock_on(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_1); + writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_1); + + reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_1); + + if (!(reg & DET_STA)) { + dev_dbg(dp->dev, "Input stream clock not detected.\n"); + return -EINVAL; + } + + reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_2); + writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_2); + + reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_2); + dev_dbg(dp->dev, "wait SYS_CTL_2.\n"); + + if (reg & CHA_STA) { + dev_dbg(dp->dev, "Input stream clk is changing\n"); + return -EINVAL; + } + + return 0; +} + +void exynos_dp_set_video_cr_mn(struct exynos_dp_device *dp, + enum clock_recovery_m_value_type type, + u32 m_value, + u32 n_value) +{ + u32 reg; + + if (type == REGISTER_M) { + reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_4); + reg |= FIX_M_VID; + writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_4); + reg = m_value & 0xff; + writel(reg, dp->reg_base + EXYNOS_DP_M_VID_0); + reg = (m_value >> 8) & 0xff; + writel(reg, dp->reg_base + EXYNOS_DP_M_VID_1); + reg = (m_value >> 16) & 0xff; + writel(reg, dp->reg_base + EXYNOS_DP_M_VID_2); + + reg = n_value & 0xff; + writel(reg, dp->reg_base + EXYNOS_DP_N_VID_0); + reg = (n_value >> 8) & 0xff; + writel(reg, dp->reg_base + EXYNOS_DP_N_VID_1); + reg = (n_value >> 16) & 0xff; + writel(reg, dp->reg_base + EXYNOS_DP_N_VID_2); + } else { + reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_4); + reg &= ~FIX_M_VID; + writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_4); + + writel(0x00, dp->reg_base + EXYNOS_DP_N_VID_0); + writel(0x80, dp->reg_base + EXYNOS_DP_N_VID_1); + writel(0x00, dp->reg_base + EXYNOS_DP_N_VID_2); + } +} + +void exynos_dp_set_video_timing_mode(struct exynos_dp_device *dp, u32 type) +{ + u32 reg; + + if (type == VIDEO_TIMING_FROM_CAPTURE) { + reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); + reg &= ~FORMAT_SEL; + writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); + } else { + reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); + reg |= FORMAT_SEL; + writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); + } +} + +void exynos_dp_enable_video_master(struct exynos_dp_device *dp, bool enable) +{ + u32 reg; + + if (enable) { + reg = readl(dp->reg_base + EXYNOS_DP_SOC_GENERAL_CTL); + reg &= ~VIDEO_MODE_MASK; + reg |= VIDEO_MASTER_MODE_EN | VIDEO_MODE_MASTER_MODE; + writel(reg, dp->reg_base + EXYNOS_DP_SOC_GENERAL_CTL); + } else { + reg = readl(dp->reg_base + EXYNOS_DP_SOC_GENERAL_CTL); + reg &= ~VIDEO_MODE_MASK; + reg |= VIDEO_MODE_SLAVE_MODE; + writel(reg, dp->reg_base + EXYNOS_DP_SOC_GENERAL_CTL); + } +} + +void exynos_dp_start_video(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_1); + reg |= VIDEO_EN; + writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_1); +} + +int exynos_dp_is_video_stream_on(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3); + writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_3); + + reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3); + if (!(reg & STRM_VALID)) { + dev_dbg(dp->dev, "Input video stream is not detected.\n"); + return -EINVAL; + } + + return 0; +} + +void exynos_dp_config_video_slave_mode(struct exynos_dp_device *dp, + struct video_info *video_info) +{ + u32 reg; + + reg = readl(dp->reg_base + EXYNOS_DP_FUNC_EN_1); + reg &= ~(MASTER_VID_FUNC_EN_N|SLAVE_VID_FUNC_EN_N); + reg |= MASTER_VID_FUNC_EN_N; + writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_1); + + reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); + reg &= ~INTERACE_SCAN_CFG; + reg |= (video_info->interlaced << 2); + writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); + + reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); + reg &= ~VSYNC_POLARITY_CFG; + reg |= (video_info->v_sync_polarity << 1); + writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); + + reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); + reg &= ~HSYNC_POLARITY_CFG; + reg |= (video_info->h_sync_polarity << 0); + writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); + + reg = AUDIO_MODE_SPDIF_MODE | VIDEO_MODE_SLAVE_MODE; + writel(reg, dp->reg_base + EXYNOS_DP_SOC_GENERAL_CTL); +} + +void exynos_dp_enable_scrambling(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = readl(dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET); + reg &= ~SCRAMBLING_DISABLE; + writel(reg, dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET); +} + +void exynos_dp_disable_scrambling(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = readl(dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET); + reg |= SCRAMBLING_DISABLE; + writel(reg, dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET); +} diff --git a/drivers/video/exynos/exynos_dp_reg.h b/drivers/video/exynos/exynos_dp_reg.h new file mode 100644 index 000000000000..42f608e2a43e --- /dev/null +++ b/drivers/video/exynos/exynos_dp_reg.h @@ -0,0 +1,335 @@ +/* + * Register definition file for Samsung DP driver + * + * Copyright (C) 2012 Samsung Electronics Co., Ltd. + * Author: Jingoo Han <jg1.han@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _EXYNOS_DP_REG_H +#define _EXYNOS_DP_REG_H + +#define EXYNOS_DP_TX_SW_RESET 0x14 +#define EXYNOS_DP_FUNC_EN_1 0x18 +#define EXYNOS_DP_FUNC_EN_2 0x1C +#define EXYNOS_DP_VIDEO_CTL_1 0x20 +#define EXYNOS_DP_VIDEO_CTL_2 0x24 +#define EXYNOS_DP_VIDEO_CTL_3 0x28 + +#define EXYNOS_DP_VIDEO_CTL_8 0x3C +#define EXYNOS_DP_VIDEO_CTL_10 0x44 + +#define EXYNOS_DP_LANE_MAP 0x35C + +#define EXYNOS_DP_AUX_HW_RETRY_CTL 0x390 + +#define EXYNOS_DP_COMMON_INT_STA_1 0x3C4 +#define EXYNOS_DP_COMMON_INT_STA_2 0x3C8 +#define EXYNOS_DP_COMMON_INT_STA_3 0x3CC +#define EXYNOS_DP_COMMON_INT_STA_4 0x3D0 +#define EXYNOS_DP_INT_STA 0x3DC +#define EXYNOS_DP_COMMON_INT_MASK_1 0x3E0 +#define EXYNOS_DP_COMMON_INT_MASK_2 0x3E4 +#define EXYNOS_DP_COMMON_INT_MASK_3 0x3E8 +#define EXYNOS_DP_COMMON_INT_MASK_4 0x3EC +#define EXYNOS_DP_INT_STA_MASK 0x3F8 +#define EXYNOS_DP_INT_CTL 0x3FC + +#define EXYNOS_DP_SYS_CTL_1 0x600 +#define EXYNOS_DP_SYS_CTL_2 0x604 +#define EXYNOS_DP_SYS_CTL_3 0x608 +#define EXYNOS_DP_SYS_CTL_4 0x60C + +#define EXYNOS_DP_PKT_SEND_CTL 0x640 +#define EXYNOS_DP_HDCP_CTL 0x648 + +#define EXYNOS_DP_LINK_BW_SET 0x680 +#define EXYNOS_DP_LANE_COUNT_SET 0x684 +#define EXYNOS_DP_TRAINING_PTN_SET 0x688 +#define EXYNOS_DP_LN0_LINK_TRAINING_CTL 0x68C +#define EXYNOS_DP_LN1_LINK_TRAINING_CTL 0x690 +#define EXYNOS_DP_LN2_LINK_TRAINING_CTL 0x694 +#define EXYNOS_DP_LN3_LINK_TRAINING_CTL 0x698 + +#define EXYNOS_DP_DEBUG_CTL 0x6C0 +#define EXYNOS_DP_HPD_DEGLITCH_L 0x6C4 +#define EXYNOS_DP_HPD_DEGLITCH_H 0x6C8 +#define EXYNOS_DP_LINK_DEBUG_CTL 0x6E0 + +#define EXYNOS_DP_M_VID_0 0x700 +#define EXYNOS_DP_M_VID_1 0x704 +#define EXYNOS_DP_M_VID_2 0x708 +#define EXYNOS_DP_N_VID_0 0x70C +#define EXYNOS_DP_N_VID_1 0x710 +#define EXYNOS_DP_N_VID_2 0x714 + +#define EXYNOS_DP_PLL_CTL 0x71C +#define EXYNOS_DP_PHY_PD 0x720 +#define EXYNOS_DP_PHY_TEST 0x724 + +#define EXYNOS_DP_VIDEO_FIFO_THRD 0x730 +#define EXYNOS_DP_AUDIO_MARGIN 0x73C + +#define EXYNOS_DP_M_VID_GEN_FILTER_TH 0x764 +#define EXYNOS_DP_M_AUD_GEN_FILTER_TH 0x778 +#define EXYNOS_DP_AUX_CH_STA 0x780 +#define EXYNOS_DP_AUX_CH_DEFER_CTL 0x788 +#define EXYNOS_DP_AUX_RX_COMM 0x78C +#define EXYNOS_DP_BUFFER_DATA_CTL 0x790 +#define EXYNOS_DP_AUX_CH_CTL_1 0x794 +#define EXYNOS_DP_AUX_ADDR_7_0 0x798 +#define EXYNOS_DP_AUX_ADDR_15_8 0x79C +#define EXYNOS_DP_AUX_ADDR_19_16 0x7A0 +#define EXYNOS_DP_AUX_CH_CTL_2 0x7A4 + +#define EXYNOS_DP_BUF_DATA_0 0x7C0 + +#define EXYNOS_DP_SOC_GENERAL_CTL 0x800 + +/* EXYNOS_DP_TX_SW_RESET */ +#define RESET_DP_TX (0x1 << 0) + +/* EXYNOS_DP_FUNC_EN_1 */ +#define MASTER_VID_FUNC_EN_N (0x1 << 7) +#define SLAVE_VID_FUNC_EN_N (0x1 << 5) +#define AUD_FIFO_FUNC_EN_N (0x1 << 4) +#define AUD_FUNC_EN_N (0x1 << 3) +#define HDCP_FUNC_EN_N (0x1 << 2) +#define CRC_FUNC_EN_N (0x1 << 1) +#define SW_FUNC_EN_N (0x1 << 0) + +/* EXYNOS_DP_FUNC_EN_2 */ +#define SSC_FUNC_EN_N (0x1 << 7) +#define AUX_FUNC_EN_N (0x1 << 2) +#define SERDES_FIFO_FUNC_EN_N (0x1 << 1) +#define LS_CLK_DOMAIN_FUNC_EN_N (0x1 << 0) + +/* EXYNOS_DP_VIDEO_CTL_1 */ +#define VIDEO_EN (0x1 << 7) +#define HDCP_VIDEO_MUTE (0x1 << 6) + +/* EXYNOS_DP_VIDEO_CTL_1 */ +#define IN_D_RANGE_MASK (0x1 << 7) +#define IN_D_RANGE_SHIFT (7) +#define IN_D_RANGE_CEA (0x1 << 7) +#define IN_D_RANGE_VESA (0x0 << 7) +#define IN_BPC_MASK (0x7 << 4) +#define IN_BPC_SHIFT (4) +#define IN_BPC_12_BITS (0x3 << 4) +#define IN_BPC_10_BITS (0x2 << 4) +#define IN_BPC_8_BITS (0x1 << 4) +#define IN_BPC_6_BITS (0x0 << 4) +#define IN_COLOR_F_MASK (0x3 << 0) +#define IN_COLOR_F_SHIFT (0) +#define IN_COLOR_F_YCBCR444 (0x2 << 0) +#define IN_COLOR_F_YCBCR422 (0x1 << 0) +#define IN_COLOR_F_RGB (0x0 << 0) + +/* EXYNOS_DP_VIDEO_CTL_3 */ +#define IN_YC_COEFFI_MASK (0x1 << 7) +#define IN_YC_COEFFI_SHIFT (7) +#define IN_YC_COEFFI_ITU709 (0x1 << 7) +#define IN_YC_COEFFI_ITU601 (0x0 << 7) +#define VID_CHK_UPDATE_TYPE_MASK (0x1 << 4) +#define VID_CHK_UPDATE_TYPE_SHIFT (4) +#define VID_CHK_UPDATE_TYPE_1 (0x1 << 4) +#define VID_CHK_UPDATE_TYPE_0 (0x0 << 4) + +/* EXYNOS_DP_VIDEO_CTL_8 */ +#define VID_HRES_TH(x) (((x) & 0xf) << 4) +#define VID_VRES_TH(x) (((x) & 0xf) << 0) + +/* EXYNOS_DP_VIDEO_CTL_10 */ +#define FORMAT_SEL (0x1 << 4) +#define INTERACE_SCAN_CFG (0x1 << 2) +#define VSYNC_POLARITY_CFG (0x1 << 1) +#define HSYNC_POLARITY_CFG (0x1 << 0) + +/* EXYNOS_DP_LANE_MAP */ +#define LANE3_MAP_LOGIC_LANE_0 (0x0 << 6) +#define LANE3_MAP_LOGIC_LANE_1 (0x1 << 6) +#define LANE3_MAP_LOGIC_LANE_2 (0x2 << 6) +#define LANE3_MAP_LOGIC_LANE_3 (0x3 << 6) +#define LANE2_MAP_LOGIC_LANE_0 (0x0 << 4) +#define LANE2_MAP_LOGIC_LANE_1 (0x1 << 4) +#define LANE2_MAP_LOGIC_LANE_2 (0x2 << 4) +#define LANE2_MAP_LOGIC_LANE_3 (0x3 << 4) +#define LANE1_MAP_LOGIC_LANE_0 (0x0 << 2) +#define LANE1_MAP_LOGIC_LANE_1 (0x1 << 2) +#define LANE1_MAP_LOGIC_LANE_2 (0x2 << 2) +#define LANE1_MAP_LOGIC_LANE_3 (0x3 << 2) +#define LANE0_MAP_LOGIC_LANE_0 (0x0 << 0) +#define LANE0_MAP_LOGIC_LANE_1 (0x1 << 0) +#define LANE0_MAP_LOGIC_LANE_2 (0x2 << 0) +#define LANE0_MAP_LOGIC_LANE_3 (0x3 << 0) + +/* EXYNOS_DP_AUX_HW_RETRY_CTL */ +#define AUX_BIT_PERIOD_EXPECTED_DELAY(x) (((x) & 0x7) << 8) +#define AUX_HW_RETRY_INTERVAL_MASK (0x3 << 3) +#define AUX_HW_RETRY_INTERVAL_600_MICROSECONDS (0x0 << 3) +#define AUX_HW_RETRY_INTERVAL_800_MICROSECONDS (0x1 << 3) +#define AUX_HW_RETRY_INTERVAL_1000_MICROSECONDS (0x2 << 3) +#define AUX_HW_RETRY_INTERVAL_1800_MICROSECONDS (0x3 << 3) +#define AUX_HW_RETRY_COUNT_SEL(x) (((x) & 0x7) << 0) + +/* EXYNOS_DP_COMMON_INT_STA_1 */ +#define VSYNC_DET (0x1 << 7) +#define PLL_LOCK_CHG (0x1 << 6) +#define SPDIF_ERR (0x1 << 5) +#define SPDIF_UNSTBL (0x1 << 4) +#define VID_FORMAT_CHG (0x1 << 3) +#define AUD_CLK_CHG (0x1 << 2) +#define VID_CLK_CHG (0x1 << 1) +#define SW_INT (0x1 << 0) + +/* EXYNOS_DP_COMMON_INT_STA_2 */ +#define ENC_EN_CHG (0x1 << 6) +#define HW_BKSV_RDY (0x1 << 3) +#define HW_SHA_DONE (0x1 << 2) +#define HW_AUTH_STATE_CHG (0x1 << 1) +#define HW_AUTH_DONE (0x1 << 0) + +/* EXYNOS_DP_COMMON_INT_STA_3 */ +#define AFIFO_UNDER (0x1 << 7) +#define AFIFO_OVER (0x1 << 6) +#define R0_CHK_FLAG (0x1 << 5) + +/* EXYNOS_DP_COMMON_INT_STA_4 */ +#define PSR_ACTIVE (0x1 << 7) +#define PSR_INACTIVE (0x1 << 6) +#define SPDIF_BI_PHASE_ERR (0x1 << 5) +#define HOTPLUG_CHG (0x1 << 2) +#define HPD_LOST (0x1 << 1) +#define PLUG (0x1 << 0) + +/* EXYNOS_DP_INT_STA */ +#define INT_HPD (0x1 << 6) +#define HW_TRAINING_FINISH (0x1 << 5) +#define RPLY_RECEIV (0x1 << 1) +#define AUX_ERR (0x1 << 0) + +/* EXYNOS_DP_INT_CTL */ +#define SOFT_INT_CTRL (0x1 << 2) +#define INT_POL (0x1 << 0) + +/* EXYNOS_DP_SYS_CTL_1 */ +#define DET_STA (0x1 << 2) +#define FORCE_DET (0x1 << 1) +#define DET_CTRL (0x1 << 0) + +/* EXYNOS_DP_SYS_CTL_2 */ +#define CHA_CRI(x) (((x) & 0xf) << 4) +#define CHA_STA (0x1 << 2) +#define FORCE_CHA (0x1 << 1) +#define CHA_CTRL (0x1 << 0) + +/* EXYNOS_DP_SYS_CTL_3 */ +#define HPD_STATUS (0x1 << 6) +#define F_HPD (0x1 << 5) +#define HPD_CTRL (0x1 << 4) +#define HDCP_RDY (0x1 << 3) +#define STRM_VALID (0x1 << 2) +#define F_VALID (0x1 << 1) +#define VALID_CTRL (0x1 << 0) + +/* EXYNOS_DP_SYS_CTL_4 */ +#define FIX_M_AUD (0x1 << 4) +#define ENHANCED (0x1 << 3) +#define FIX_M_VID (0x1 << 2) +#define M_VID_UPDATE_CTRL (0x3 << 0) + +/* EXYNOS_DP_TRAINING_PTN_SET */ +#define SCRAMBLER_TYPE (0x1 << 9) +#define HW_LINK_TRAINING_PATTERN (0x1 << 8) +#define SCRAMBLING_DISABLE (0x1 << 5) +#define SCRAMBLING_ENABLE (0x0 << 5) +#define LINK_QUAL_PATTERN_SET_MASK (0x3 << 2) +#define LINK_QUAL_PATTERN_SET_PRBS7 (0x3 << 2) +#define LINK_QUAL_PATTERN_SET_D10_2 (0x1 << 2) +#define LINK_QUAL_PATTERN_SET_DISABLE (0x0 << 2) +#define SW_TRAINING_PATTERN_SET_MASK (0x3 << 0) +#define SW_TRAINING_PATTERN_SET_PTN2 (0x2 << 0) +#define SW_TRAINING_PATTERN_SET_PTN1 (0x1 << 0) +#define SW_TRAINING_PATTERN_SET_NORMAL (0x0 << 0) + +/* EXYNOS_DP_LN0_LINK_TRAINING_CTL */ +#define PRE_EMPHASIS_SET_SHIFT (3) + +/* EXYNOS_DP_DEBUG_CTL */ +#define PLL_LOCK (0x1 << 4) +#define F_PLL_LOCK (0x1 << 3) +#define PLL_LOCK_CTRL (0x1 << 2) +#define PN_INV (0x1 << 0) + +/* EXYNOS_DP_PLL_CTL */ +#define DP_PLL_PD (0x1 << 7) +#define DP_PLL_RESET (0x1 << 6) +#define DP_PLL_LOOP_BIT_DEFAULT (0x1 << 4) +#define DP_PLL_REF_BIT_1_1250V (0x5 << 0) +#define DP_PLL_REF_BIT_1_2500V (0x7 << 0) + +/* EXYNOS_DP_PHY_PD */ +#define DP_PHY_PD (0x1 << 5) +#define AUX_PD (0x1 << 4) +#define CH3_PD (0x1 << 3) +#define CH2_PD (0x1 << 2) +#define CH1_PD (0x1 << 1) +#define CH0_PD (0x1 << 0) + +/* EXYNOS_DP_PHY_TEST */ +#define MACRO_RST (0x1 << 5) +#define CH1_TEST (0x1 << 1) +#define CH0_TEST (0x1 << 0) + +/* EXYNOS_DP_AUX_CH_STA */ +#define AUX_BUSY (0x1 << 4) +#define AUX_STATUS_MASK (0xf << 0) + +/* EXYNOS_DP_AUX_CH_DEFER_CTL */ +#define DEFER_CTRL_EN (0x1 << 7) +#define DEFER_COUNT(x) (((x) & 0x7f) << 0) + +/* EXYNOS_DP_AUX_RX_COMM */ +#define AUX_RX_COMM_I2C_DEFER (0x2 << 2) +#define AUX_RX_COMM_AUX_DEFER (0x2 << 0) + +/* EXYNOS_DP_BUFFER_DATA_CTL */ +#define BUF_CLR (0x1 << 7) +#define BUF_DATA_COUNT(x) (((x) & 0x1f) << 0) + +/* EXYNOS_DP_AUX_CH_CTL_1 */ +#define AUX_LENGTH(x) (((x - 1) & 0xf) << 4) +#define AUX_TX_COMM_MASK (0xf << 0) +#define AUX_TX_COMM_DP_TRANSACTION (0x1 << 3) +#define AUX_TX_COMM_I2C_TRANSACTION (0x0 << 3) +#define AUX_TX_COMM_MOT (0x1 << 2) +#define AUX_TX_COMM_WRITE (0x0 << 0) +#define AUX_TX_COMM_READ (0x1 << 0) + +/* EXYNOS_DP_AUX_ADDR_7_0 */ +#define AUX_ADDR_7_0(x) (((x) >> 0) & 0xff) + +/* EXYNOS_DP_AUX_ADDR_15_8 */ +#define AUX_ADDR_15_8(x) (((x) >> 8) & 0xff) + +/* EXYNOS_DP_AUX_ADDR_19_16 */ +#define AUX_ADDR_19_16(x) (((x) >> 16) & 0x0f) + +/* EXYNOS_DP_AUX_CH_CTL_2 */ +#define ADDR_ONLY (0x1 << 1) +#define AUX_EN (0x1 << 0) + +/* EXYNOS_DP_SOC_GENERAL_CTL */ +#define AUDIO_MODE_SPDIF_MODE (0x1 << 8) +#define AUDIO_MODE_MASTER_MODE (0x0 << 8) +#define MASTER_VIDEO_INTERLACE_EN (0x1 << 4) +#define VIDEO_MASTER_CLK_SEL (0x1 << 2) +#define VIDEO_MASTER_MODE_EN (0x1 << 1) +#define VIDEO_MODE_MASK (0x1 << 0) +#define VIDEO_MODE_SLAVE_MODE (0x1 << 0) +#define VIDEO_MODE_MASTER_MODE (0x0 << 0) + +#endif /* _EXYNOS_DP_REG_H */ diff --git a/drivers/video/exynos/exynos_mipi_dsi.c b/drivers/video/exynos/exynos_mipi_dsi.c new file mode 100644 index 000000000000..557091dc0e97 --- /dev/null +++ b/drivers/video/exynos/exynos_mipi_dsi.c @@ -0,0 +1,600 @@ +/* linux/drivers/video/exynos/exynos_mipi_dsi.c + * + * Samsung SoC MIPI-DSIM driver. + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd + * + * InKi Dae, <inki.dae@samsung.com> + * Donghwa Lee, <dh09.lee@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/clk.h> +#include <linux/mutex.h> +#include <linux/wait.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/fb.h> +#include <linux/ctype.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/memory.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/kthread.h> +#include <linux/notifier.h> +#include <linux/regulator/consumer.h> +#include <linux/pm_runtime.h> + +#include <video/exynos_mipi_dsim.h> + +#include <plat/fb.h> + +#include "exynos_mipi_dsi_common.h" +#include "exynos_mipi_dsi_lowlevel.h" + +struct mipi_dsim_ddi { + int bus_id; + struct list_head list; + struct mipi_dsim_lcd_device *dsim_lcd_dev; + struct mipi_dsim_lcd_driver *dsim_lcd_drv; +}; + +static LIST_HEAD(dsim_ddi_list); + +static DEFINE_MUTEX(mipi_dsim_lock); + +static struct mipi_dsim_platform_data *to_dsim_plat(struct platform_device + *pdev) +{ + return pdev->dev.platform_data; +} + +static struct regulator_bulk_data supplies[] = { + { .supply = "vdd10", }, + { .supply = "vdd18", }, +}; + +static int exynos_mipi_regulator_enable(struct mipi_dsim_device *dsim) +{ + int ret; + + mutex_lock(&dsim->lock); + ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies); + mutex_unlock(&dsim->lock); + + return ret; +} + +static int exynos_mipi_regulator_disable(struct mipi_dsim_device *dsim) +{ + int ret; + + mutex_lock(&dsim->lock); + ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies); + mutex_unlock(&dsim->lock); + + return ret; +} + +/* update all register settings to MIPI DSI controller. */ +static void exynos_mipi_update_cfg(struct mipi_dsim_device *dsim) +{ + /* + * data from Display controller(FIMD) is not transferred in video mode + * but in case of command mode, all settings is not updated to + * registers. + */ + exynos_mipi_dsi_stand_by(dsim, 0); + + exynos_mipi_dsi_init_dsim(dsim); + exynos_mipi_dsi_init_link(dsim); + + exynos_mipi_dsi_set_hs_enable(dsim); + + /* set display timing. */ + exynos_mipi_dsi_set_display_mode(dsim, dsim->dsim_config); + + /* + * data from Display controller(FIMD) is transferred in video mode + * but in case of command mode, all settigs is updated to registers. + */ + exynos_mipi_dsi_stand_by(dsim, 1); +} + +static int exynos_mipi_dsi_early_blank_mode(struct mipi_dsim_device *dsim, + int power) +{ + struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv; + struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev; + + switch (power) { + case FB_BLANK_POWERDOWN: + if (dsim->suspended) + return 0; + + if (client_drv && client_drv->suspend) + client_drv->suspend(client_dev); + + clk_disable(dsim->clock); + + exynos_mipi_regulator_disable(dsim); + + dsim->suspended = true; + + break; + default: + break; + } + + return 0; +} + +static int exynos_mipi_dsi_blank_mode(struct mipi_dsim_device *dsim, int power) +{ + struct platform_device *pdev = to_platform_device(dsim->dev); + struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv; + struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev; + + switch (power) { + case FB_BLANK_UNBLANK: + if (!dsim->suspended) + return 0; + + /* lcd panel power on. */ + if (client_drv && client_drv->power_on) + client_drv->power_on(client_dev, 1); + + exynos_mipi_regulator_disable(dsim); + + /* enable MIPI-DSI PHY. */ + if (dsim->pd->phy_enable) + dsim->pd->phy_enable(pdev, true); + + clk_enable(dsim->clock); + + exynos_mipi_update_cfg(dsim); + + /* set lcd panel sequence commands. */ + if (client_drv && client_drv->set_sequence) + client_drv->set_sequence(client_dev); + + dsim->suspended = false; + + break; + case FB_BLANK_NORMAL: + /* TODO. */ + break; + default: + break; + } + + return 0; +} + +int exynos_mipi_dsi_register_lcd_device(struct mipi_dsim_lcd_device *lcd_dev) +{ + struct mipi_dsim_ddi *dsim_ddi; + + if (!lcd_dev->name) { + pr_err("dsim_lcd_device name is NULL.\n"); + return -EFAULT; + } + + dsim_ddi = kzalloc(sizeof(struct mipi_dsim_ddi), GFP_KERNEL); + if (!dsim_ddi) { + pr_err("failed to allocate dsim_ddi object.\n"); + return -ENOMEM; + } + + dsim_ddi->dsim_lcd_dev = lcd_dev; + + mutex_lock(&mipi_dsim_lock); + list_add_tail(&dsim_ddi->list, &dsim_ddi_list); + mutex_unlock(&mipi_dsim_lock); + + return 0; +} + +struct mipi_dsim_ddi *exynos_mipi_dsi_find_lcd_device(struct mipi_dsim_lcd_driver *lcd_drv) +{ + struct mipi_dsim_ddi *dsim_ddi, *next; + struct mipi_dsim_lcd_device *lcd_dev; + + mutex_lock(&mipi_dsim_lock); + + list_for_each_entry_safe(dsim_ddi, next, &dsim_ddi_list, list) { + if (!dsim_ddi) + goto out; + + lcd_dev = dsim_ddi->dsim_lcd_dev; + if (!lcd_dev) + continue; + + if ((strcmp(lcd_drv->name, lcd_dev->name)) == 0) { + /** + * bus_id would be used to identify + * connected bus. + */ + dsim_ddi->bus_id = lcd_dev->bus_id; + mutex_unlock(&mipi_dsim_lock); + + return dsim_ddi; + } + + list_del(&dsim_ddi->list); + kfree(dsim_ddi); + } + +out: + mutex_unlock(&mipi_dsim_lock); + + return NULL; +} + +int exynos_mipi_dsi_register_lcd_driver(struct mipi_dsim_lcd_driver *lcd_drv) +{ + struct mipi_dsim_ddi *dsim_ddi; + + if (!lcd_drv->name) { + pr_err("dsim_lcd_driver name is NULL.\n"); + return -EFAULT; + } + + dsim_ddi = exynos_mipi_dsi_find_lcd_device(lcd_drv); + if (!dsim_ddi) { + pr_err("mipi_dsim_ddi object not found.\n"); + return -EFAULT; + } + + dsim_ddi->dsim_lcd_drv = lcd_drv; + + pr_info("registered panel driver(%s) to mipi-dsi driver.\n", + lcd_drv->name); + + return 0; + +} + +struct mipi_dsim_ddi *exynos_mipi_dsi_bind_lcd_ddi(struct mipi_dsim_device *dsim, + const char *name) +{ + struct mipi_dsim_ddi *dsim_ddi, *next; + struct mipi_dsim_lcd_driver *lcd_drv; + struct mipi_dsim_lcd_device *lcd_dev; + int ret; + + mutex_lock(&dsim->lock); + + list_for_each_entry_safe(dsim_ddi, next, &dsim_ddi_list, list) { + lcd_drv = dsim_ddi->dsim_lcd_drv; + lcd_dev = dsim_ddi->dsim_lcd_dev; + if (!lcd_drv || !lcd_dev || + (dsim->id != dsim_ddi->bus_id)) + continue; + + dev_dbg(dsim->dev, "lcd_drv->id = %d, lcd_dev->id = %d\n", + lcd_drv->id, lcd_dev->id); + dev_dbg(dsim->dev, "lcd_dev->bus_id = %d, dsim->id = %d\n", + lcd_dev->bus_id, dsim->id); + + if ((strcmp(lcd_drv->name, name) == 0)) { + lcd_dev->master = dsim; + + lcd_dev->dev.parent = dsim->dev; + dev_set_name(&lcd_dev->dev, "%s", lcd_drv->name); + + ret = device_register(&lcd_dev->dev); + if (ret < 0) { + dev_err(dsim->dev, + "can't register %s, status %d\n", + dev_name(&lcd_dev->dev), ret); + mutex_unlock(&dsim->lock); + + return NULL; + } + + dsim->dsim_lcd_dev = lcd_dev; + dsim->dsim_lcd_drv = lcd_drv; + + mutex_unlock(&dsim->lock); + + return dsim_ddi; + } + } + + mutex_unlock(&dsim->lock); + + return NULL; +} + +/* define MIPI-DSI Master operations. */ +static struct mipi_dsim_master_ops master_ops = { + .cmd_read = exynos_mipi_dsi_rd_data, + .cmd_write = exynos_mipi_dsi_wr_data, + .get_dsim_frame_done = exynos_mipi_dsi_get_frame_done_status, + .clear_dsim_frame_done = exynos_mipi_dsi_clear_frame_done, + .set_early_blank_mode = exynos_mipi_dsi_early_blank_mode, + .set_blank_mode = exynos_mipi_dsi_blank_mode, +}; + +static int exynos_mipi_dsi_probe(struct platform_device *pdev) +{ + struct resource *res; + struct mipi_dsim_device *dsim; + struct mipi_dsim_config *dsim_config; + struct mipi_dsim_platform_data *dsim_pd; + struct mipi_dsim_ddi *dsim_ddi; + int ret = -EINVAL; + + dsim = kzalloc(sizeof(struct mipi_dsim_device), GFP_KERNEL); + if (!dsim) { + dev_err(&pdev->dev, "failed to allocate dsim object.\n"); + return -ENOMEM; + } + + dsim->pd = to_dsim_plat(pdev); + dsim->dev = &pdev->dev; + dsim->id = pdev->id; + + /* get mipi_dsim_platform_data. */ + dsim_pd = (struct mipi_dsim_platform_data *)dsim->pd; + if (dsim_pd == NULL) { + dev_err(&pdev->dev, "failed to get platform data for dsim.\n"); + goto err_clock_get; + } + /* get mipi_dsim_config. */ + dsim_config = dsim_pd->dsim_config; + if (dsim_config == NULL) { + dev_err(&pdev->dev, "failed to get dsim config data.\n"); + goto err_clock_get; + } + + dsim->dsim_config = dsim_config; + dsim->master_ops = &master_ops; + + mutex_init(&dsim->lock); + + ret = regulator_bulk_get(&pdev->dev, ARRAY_SIZE(supplies), supplies); + if (ret) { + dev_err(&pdev->dev, "Failed to get regulators: %d\n", ret); + goto err_clock_get; + } + + dsim->clock = clk_get(&pdev->dev, "dsim0"); + if (IS_ERR(dsim->clock)) { + dev_err(&pdev->dev, "failed to get dsim clock source\n"); + goto err_clock_get; + } + + clk_enable(dsim->clock); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to get io memory region\n"); + goto err_platform_get; + } + + dsim->res = request_mem_region(res->start, resource_size(res), + dev_name(&pdev->dev)); + if (!dsim->res) { + dev_err(&pdev->dev, "failed to request io memory region\n"); + ret = -ENOMEM; + goto err_mem_region; + } + + dsim->reg_base = ioremap(res->start, resource_size(res)); + if (!dsim->reg_base) { + dev_err(&pdev->dev, "failed to remap io region\n"); + ret = -ENOMEM; + goto err_ioremap; + } + + mutex_init(&dsim->lock); + + /* bind lcd ddi matched with panel name. */ + dsim_ddi = exynos_mipi_dsi_bind_lcd_ddi(dsim, dsim_pd->lcd_panel_name); + if (!dsim_ddi) { + dev_err(&pdev->dev, "mipi_dsim_ddi object not found.\n"); + goto err_bind; + } + + dsim->irq = platform_get_irq(pdev, 0); + if (dsim->irq < 0) { + dev_err(&pdev->dev, "failed to request dsim irq resource\n"); + ret = -EINVAL; + goto err_platform_get_irq; + } + + ret = request_irq(dsim->irq, exynos_mipi_dsi_interrupt_handler, + IRQF_SHARED, pdev->name, dsim); + if (ret != 0) { + dev_err(&pdev->dev, "failed to request dsim irq\n"); + ret = -EINVAL; + goto err_bind; + } + + init_completion(&dsim_wr_comp); + init_completion(&dsim_rd_comp); + + /* enable interrupt */ + exynos_mipi_dsi_init_interrupt(dsim); + + /* initialize mipi-dsi client(lcd panel). */ + if (dsim_ddi->dsim_lcd_drv && dsim_ddi->dsim_lcd_drv->probe) + dsim_ddi->dsim_lcd_drv->probe(dsim_ddi->dsim_lcd_dev); + + /* in case that mipi got enabled at bootloader. */ + if (dsim_pd->enabled) + goto out; + + /* lcd panel power on. */ + if (dsim_ddi->dsim_lcd_drv && dsim_ddi->dsim_lcd_drv->power_on) + dsim_ddi->dsim_lcd_drv->power_on(dsim_ddi->dsim_lcd_dev, 1); + + exynos_mipi_regulator_enable(dsim); + + /* enable MIPI-DSI PHY. */ + if (dsim->pd->phy_enable) + dsim->pd->phy_enable(pdev, true); + + exynos_mipi_update_cfg(dsim); + + /* set lcd panel sequence commands. */ + if (dsim_ddi->dsim_lcd_drv && dsim_ddi->dsim_lcd_drv->set_sequence) + dsim_ddi->dsim_lcd_drv->set_sequence(dsim_ddi->dsim_lcd_dev); + + dsim->suspended = false; + +out: + platform_set_drvdata(pdev, dsim); + + dev_dbg(&pdev->dev, "mipi-dsi driver(%s mode) has been probed.\n", + (dsim_config->e_interface == DSIM_COMMAND) ? + "CPU" : "RGB"); + + return 0; + +err_bind: + iounmap(dsim->reg_base); + +err_ioremap: + release_mem_region(dsim->res->start, resource_size(dsim->res)); + +err_mem_region: + release_resource(dsim->res); + +err_platform_get: + clk_disable(dsim->clock); + clk_put(dsim->clock); +err_clock_get: + kfree(dsim); + +err_platform_get_irq: + return ret; +} + +static int __devexit exynos_mipi_dsi_remove(struct platform_device *pdev) +{ + struct mipi_dsim_device *dsim = platform_get_drvdata(pdev); + struct mipi_dsim_ddi *dsim_ddi, *next; + struct mipi_dsim_lcd_driver *dsim_lcd_drv; + + iounmap(dsim->reg_base); + + clk_disable(dsim->clock); + clk_put(dsim->clock); + + release_resource(dsim->res); + release_mem_region(dsim->res->start, resource_size(dsim->res)); + + list_for_each_entry_safe(dsim_ddi, next, &dsim_ddi_list, list) { + if (dsim_ddi) { + if (dsim->id != dsim_ddi->bus_id) + continue; + + dsim_lcd_drv = dsim_ddi->dsim_lcd_drv; + + if (dsim_lcd_drv->remove) + dsim_lcd_drv->remove(dsim_ddi->dsim_lcd_dev); + + kfree(dsim_ddi); + } + } + + regulator_bulk_free(ARRAY_SIZE(supplies), supplies); + kfree(dsim); + + return 0; +} + +#ifdef CONFIG_PM +static int exynos_mipi_dsi_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct mipi_dsim_device *dsim = platform_get_drvdata(pdev); + struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv; + struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev; + + disable_irq(dsim->irq); + + if (dsim->suspended) + return 0; + + if (client_drv && client_drv->suspend) + client_drv->suspend(client_dev); + + /* enable MIPI-DSI PHY. */ + if (dsim->pd->phy_enable) + dsim->pd->phy_enable(pdev, false); + + clk_disable(dsim->clock); + + exynos_mipi_regulator_disable(dsim); + + dsim->suspended = true; + + return 0; +} + +static int exynos_mipi_dsi_resume(struct platform_device *pdev) +{ + struct mipi_dsim_device *dsim = platform_get_drvdata(pdev); + struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv; + struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev; + + enable_irq(dsim->irq); + + if (!dsim->suspended) + return 0; + + /* lcd panel power on. */ + if (client_drv && client_drv->power_on) + client_drv->power_on(client_dev, 1); + + exynos_mipi_regulator_enable(dsim); + + /* enable MIPI-DSI PHY. */ + if (dsim->pd->phy_enable) + dsim->pd->phy_enable(pdev, true); + + clk_enable(dsim->clock); + + exynos_mipi_update_cfg(dsim); + + /* set lcd panel sequence commands. */ + if (client_drv && client_drv->set_sequence) + client_drv->set_sequence(client_dev); + + dsim->suspended = false; + + return 0; +} +#else +#define exynos_mipi_dsi_suspend NULL +#define exynos_mipi_dsi_resume NULL +#endif + +static struct platform_driver exynos_mipi_dsi_driver = { + .probe = exynos_mipi_dsi_probe, + .remove = __devexit_p(exynos_mipi_dsi_remove), + .suspend = exynos_mipi_dsi_suspend, + .resume = exynos_mipi_dsi_resume, + .driver = { + .name = "exynos-mipi-dsim", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(exynos_mipi_dsi_driver); + +MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>"); +MODULE_DESCRIPTION("Samusung SoC MIPI-DSI driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/exynos/exynos_mipi_dsi_common.c b/drivers/video/exynos/exynos_mipi_dsi_common.c new file mode 100644 index 000000000000..14909c1d3832 --- /dev/null +++ b/drivers/video/exynos/exynos_mipi_dsi_common.c @@ -0,0 +1,896 @@ +/* linux/drivers/video/exynos/exynos_mipi_dsi_common.c + * + * Samsung SoC MIPI-DSI common driver. + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd + * + * InKi Dae, <inki.dae@samsung.com> + * Donghwa Lee, <dh09.lee@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/mutex.h> +#include <linux/wait.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/fb.h> +#include <linux/ctype.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/memory.h> +#include <linux/delay.h> +#include <linux/kthread.h> + +#include <video/mipi_display.h> +#include <video/exynos_mipi_dsim.h> + +#include <mach/map.h> + +#include "exynos_mipi_dsi_regs.h" +#include "exynos_mipi_dsi_lowlevel.h" +#include "exynos_mipi_dsi_common.h" + +#define MIPI_FIFO_TIMEOUT msecs_to_jiffies(250) +#define MIPI_RX_FIFO_READ_DONE 0x30800002 +#define MIPI_MAX_RX_FIFO 20 +#define MHZ (1000 * 1000) +#define FIN_HZ (24 * MHZ) + +#define DFIN_PLL_MIN_HZ (6 * MHZ) +#define DFIN_PLL_MAX_HZ (12 * MHZ) + +#define DFVCO_MIN_HZ (500 * MHZ) +#define DFVCO_MAX_HZ (1000 * MHZ) + +#define TRY_GET_FIFO_TIMEOUT (5000 * 2) +#define TRY_FIFO_CLEAR (10) + +/* MIPI-DSIM status types. */ +enum { + DSIM_STATE_INIT, /* should be initialized. */ + DSIM_STATE_STOP, /* CPU and LCDC are LP mode. */ + DSIM_STATE_HSCLKEN, /* HS clock was enabled. */ + DSIM_STATE_ULPS +}; + +/* define DSI lane types. */ +enum { + DSIM_LANE_CLOCK = (1 << 0), + DSIM_LANE_DATA0 = (1 << 1), + DSIM_LANE_DATA1 = (1 << 2), + DSIM_LANE_DATA2 = (1 << 3), + DSIM_LANE_DATA3 = (1 << 4) +}; + +static unsigned int dpll_table[15] = { + 100, 120, 170, 220, 270, + 320, 390, 450, 510, 560, + 640, 690, 770, 870, 950 +}; + +irqreturn_t exynos_mipi_dsi_interrupt_handler(int irq, void *dev_id) +{ + unsigned int intsrc = 0; + unsigned int intmsk = 0; + struct mipi_dsim_device *dsim = NULL; + + dsim = dev_id; + if (!dsim) { + dev_dbg(dsim->dev, KERN_ERR "%s:error: wrong parameter\n", + __func__); + return IRQ_HANDLED; + } + + intsrc = exynos_mipi_dsi_read_interrupt(dsim); + intmsk = exynos_mipi_dsi_read_interrupt_mask(dsim); + + intmsk = ~(intmsk) & intsrc; + + switch (intmsk) { + case INTMSK_RX_DONE: + complete(&dsim_rd_comp); + dev_dbg(dsim->dev, "MIPI INTMSK_RX_DONE\n"); + break; + case INTMSK_FIFO_EMPTY: + complete(&dsim_wr_comp); + dev_dbg(dsim->dev, "MIPI INTMSK_FIFO_EMPTY\n"); + break; + default: + break; + } + + exynos_mipi_dsi_clear_interrupt(dsim, intmsk); + + return IRQ_HANDLED; +} + +/* + * write long packet to mipi dsi slave + * @dsim: mipi dsim device structure. + * @data0: packet data to send. + * @data1: size of packet data + */ +static void exynos_mipi_dsi_long_data_wr(struct mipi_dsim_device *dsim, + const unsigned char *data0, unsigned int data_size) +{ + unsigned int data_cnt = 0, payload = 0; + + /* in case that data count is more then 4 */ + for (data_cnt = 0; data_cnt < data_size; data_cnt += 4) { + /* + * after sending 4bytes per one time, + * send remainder data less then 4. + */ + if ((data_size - data_cnt) < 4) { + if ((data_size - data_cnt) == 3) { + payload = data0[data_cnt] | + data0[data_cnt + 1] << 8 | + data0[data_cnt + 2] << 16; + dev_dbg(dsim->dev, "count = 3 payload = %x, %x %x %x\n", + payload, data0[data_cnt], + data0[data_cnt + 1], + data0[data_cnt + 2]); + } else if ((data_size - data_cnt) == 2) { + payload = data0[data_cnt] | + data0[data_cnt + 1] << 8; + dev_dbg(dsim->dev, + "count = 2 payload = %x, %x %x\n", payload, + data0[data_cnt], + data0[data_cnt + 1]); + } else if ((data_size - data_cnt) == 1) { + payload = data0[data_cnt]; + } + + exynos_mipi_dsi_wr_tx_data(dsim, payload); + /* send 4bytes per one time. */ + } else { + payload = data0[data_cnt] | + data0[data_cnt + 1] << 8 | + data0[data_cnt + 2] << 16 | + data0[data_cnt + 3] << 24; + + dev_dbg(dsim->dev, + "count = 4 payload = %x, %x %x %x %x\n", + payload, *(u8 *)(data0 + data_cnt), + data0[data_cnt + 1], + data0[data_cnt + 2], + data0[data_cnt + 3]); + + exynos_mipi_dsi_wr_tx_data(dsim, payload); + } + } +} + +int exynos_mipi_dsi_wr_data(struct mipi_dsim_device *dsim, unsigned int data_id, + const unsigned char *data0, unsigned int data_size) +{ + unsigned int check_rx_ack = 0; + + if (dsim->state == DSIM_STATE_ULPS) { + dev_err(dsim->dev, "state is ULPS.\n"); + + return -EINVAL; + } + + /* FIXME!!! why does it need this delay? */ + msleep(20); + + mutex_lock(&dsim->lock); + + switch (data_id) { + /* short packet types of packet types for command. */ + case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM: + case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM: + case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM: + case MIPI_DSI_DCS_SHORT_WRITE: + case MIPI_DSI_DCS_SHORT_WRITE_PARAM: + case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE: + exynos_mipi_dsi_wr_tx_header(dsim, data_id, data0[0], data0[1]); + if (check_rx_ack) { + /* process response func should be implemented */ + mutex_unlock(&dsim->lock); + return 0; + } else { + mutex_unlock(&dsim->lock); + return -EINVAL; + } + + /* general command */ + case MIPI_DSI_COLOR_MODE_OFF: + case MIPI_DSI_COLOR_MODE_ON: + case MIPI_DSI_SHUTDOWN_PERIPHERAL: + case MIPI_DSI_TURN_ON_PERIPHERAL: + exynos_mipi_dsi_wr_tx_header(dsim, data_id, data0[0], data0[1]); + if (check_rx_ack) { + /* process response func should be implemented. */ + mutex_unlock(&dsim->lock); + return 0; + } else { + mutex_unlock(&dsim->lock); + return -EINVAL; + } + + /* packet types for video data */ + case MIPI_DSI_V_SYNC_START: + case MIPI_DSI_V_SYNC_END: + case MIPI_DSI_H_SYNC_START: + case MIPI_DSI_H_SYNC_END: + case MIPI_DSI_END_OF_TRANSMISSION: + mutex_unlock(&dsim->lock); + return 0; + + /* long packet type and null packet */ + case MIPI_DSI_NULL_PACKET: + case MIPI_DSI_BLANKING_PACKET: + mutex_unlock(&dsim->lock); + return 0; + case MIPI_DSI_GENERIC_LONG_WRITE: + case MIPI_DSI_DCS_LONG_WRITE: + { + unsigned int size, payload = 0; + INIT_COMPLETION(dsim_wr_comp); + + size = data_size * 4; + + /* if data count is less then 4, then send 3bytes data. */ + if (data_size < 4) { + payload = data0[0] | + data0[1] << 8 | + data0[2] << 16; + + exynos_mipi_dsi_wr_tx_data(dsim, payload); + + dev_dbg(dsim->dev, "count = %d payload = %x,%x %x %x\n", + data_size, payload, data0[0], + data0[1], data0[2]); + + /* in case that data count is more then 4 */ + } else + exynos_mipi_dsi_long_data_wr(dsim, data0, data_size); + + /* put data into header fifo */ + exynos_mipi_dsi_wr_tx_header(dsim, data_id, data_size & 0xff, + (data_size & 0xff00) >> 8); + + if (!wait_for_completion_interruptible_timeout(&dsim_wr_comp, + MIPI_FIFO_TIMEOUT)) { + dev_warn(dsim->dev, "command write timeout.\n"); + mutex_unlock(&dsim->lock); + return -EAGAIN; + } + + if (check_rx_ack) { + /* process response func should be implemented. */ + mutex_unlock(&dsim->lock); + return 0; + } else { + mutex_unlock(&dsim->lock); + return -EINVAL; + } + } + + /* packet typo for video data */ + case MIPI_DSI_PACKED_PIXEL_STREAM_16: + case MIPI_DSI_PACKED_PIXEL_STREAM_18: + case MIPI_DSI_PIXEL_STREAM_3BYTE_18: + case MIPI_DSI_PACKED_PIXEL_STREAM_24: + if (check_rx_ack) { + /* process response func should be implemented. */ + mutex_unlock(&dsim->lock); + return 0; + } else { + mutex_unlock(&dsim->lock); + return -EINVAL; + } + default: + dev_warn(dsim->dev, + "data id %x is not supported current DSI spec.\n", + data_id); + + mutex_unlock(&dsim->lock); + return -EINVAL; + } + + mutex_unlock(&dsim->lock); + return 0; +} + +static unsigned int exynos_mipi_dsi_long_data_rd(struct mipi_dsim_device *dsim, + unsigned int req_size, unsigned int rx_data, u8 *rx_buf) +{ + unsigned int rcv_pkt, i, j; + u16 rxsize; + + /* for long packet */ + rxsize = (u16)((rx_data & 0x00ffff00) >> 8); + dev_dbg(dsim->dev, "mipi dsi rx size : %d\n", rxsize); + if (rxsize != req_size) { + dev_dbg(dsim->dev, + "received size mismatch received: %d, requested: %d\n", + rxsize, req_size); + goto err; + } + + for (i = 0; i < (rxsize >> 2); i++) { + rcv_pkt = exynos_mipi_dsi_rd_rx_fifo(dsim); + dev_dbg(dsim->dev, "received pkt : %08x\n", rcv_pkt); + for (j = 0; j < 4; j++) { + rx_buf[(i * 4) + j] = + (u8)(rcv_pkt >> (j * 8)) & 0xff; + dev_dbg(dsim->dev, "received value : %02x\n", + (rcv_pkt >> (j * 8)) & 0xff); + } + } + if (rxsize % 4) { + rcv_pkt = exynos_mipi_dsi_rd_rx_fifo(dsim); + dev_dbg(dsim->dev, "received pkt : %08x\n", rcv_pkt); + for (j = 0; j < (rxsize % 4); j++) { + rx_buf[(i * 4) + j] = + (u8)(rcv_pkt >> (j * 8)) & 0xff; + dev_dbg(dsim->dev, "received value : %02x\n", + (rcv_pkt >> (j * 8)) & 0xff); + } + } + + return rxsize; + +err: + return -EINVAL; +} + +static unsigned int exynos_mipi_dsi_response_size(unsigned int req_size) +{ + switch (req_size) { + case 1: + return MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE; + case 2: + return MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE; + default: + return MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE; + } +} + +int exynos_mipi_dsi_rd_data(struct mipi_dsim_device *dsim, unsigned int data_id, + unsigned int data0, unsigned int req_size, u8 *rx_buf) +{ + unsigned int rx_data, rcv_pkt, i; + u8 response = 0; + u16 rxsize; + + if (dsim->state == DSIM_STATE_ULPS) { + dev_err(dsim->dev, "state is ULPS.\n"); + + return -EINVAL; + } + + /* FIXME!!! */ + msleep(20); + + mutex_lock(&dsim->lock); + INIT_COMPLETION(dsim_rd_comp); + exynos_mipi_dsi_rd_tx_header(dsim, + MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, req_size); + + response = exynos_mipi_dsi_response_size(req_size); + + switch (data_id) { + case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM: + case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM: + case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM: + case MIPI_DSI_DCS_READ: + exynos_mipi_dsi_rd_tx_header(dsim, + data_id, data0); + /* process response func should be implemented. */ + break; + default: + dev_warn(dsim->dev, + "data id %x is not supported current DSI spec.\n", + data_id); + + return -EINVAL; + } + + if (!wait_for_completion_interruptible_timeout(&dsim_rd_comp, + MIPI_FIFO_TIMEOUT)) { + pr_err("RX done interrupt timeout\n"); + mutex_unlock(&dsim->lock); + return 0; + } + + msleep(20); + + rx_data = exynos_mipi_dsi_rd_rx_fifo(dsim); + + if ((u8)(rx_data & 0xff) != response) { + printk(KERN_ERR + "mipi dsi wrong response rx_data : %x, response:%x\n", + rx_data, response); + goto clear_rx_fifo; + } + + if (req_size <= 2) { + /* for short packet */ + for (i = 0; i < req_size; i++) + rx_buf[i] = (rx_data >> (8 + (i * 8))) & 0xff; + rxsize = req_size; + } else { + /* for long packet */ + rxsize = exynos_mipi_dsi_long_data_rd(dsim, req_size, rx_data, + rx_buf); + if (rxsize != req_size) + goto clear_rx_fifo; + } + + rcv_pkt = exynos_mipi_dsi_rd_rx_fifo(dsim); + + msleep(20); + + if (rcv_pkt != MIPI_RX_FIFO_READ_DONE) { + dev_info(dsim->dev, + "Can't found RX FIFO READ DONE FLAG : %x\n", rcv_pkt); + goto clear_rx_fifo; + } + + mutex_unlock(&dsim->lock); + + return rxsize; + +clear_rx_fifo: + i = 0; + while (1) { + rcv_pkt = exynos_mipi_dsi_rd_rx_fifo(dsim); + if ((rcv_pkt == MIPI_RX_FIFO_READ_DONE) + || (i > MIPI_MAX_RX_FIFO)) + break; + dev_dbg(dsim->dev, + "mipi dsi clear rx fifo : %08x\n", rcv_pkt); + i++; + } + dev_info(dsim->dev, + "mipi dsi rx done count : %d, rcv_pkt : %08x\n", i, rcv_pkt); + + mutex_unlock(&dsim->lock); + + return 0; +} + +static int exynos_mipi_dsi_pll_on(struct mipi_dsim_device *dsim, + unsigned int enable) +{ + int sw_timeout; + + if (enable) { + sw_timeout = 1000; + + exynos_mipi_dsi_enable_pll(dsim, 1); + while (1) { + sw_timeout--; + if (exynos_mipi_dsi_is_pll_stable(dsim)) + return 0; + if (sw_timeout == 0) + return -EINVAL; + } + } else + exynos_mipi_dsi_enable_pll(dsim, 0); + + return 0; +} + +static unsigned long exynos_mipi_dsi_change_pll(struct mipi_dsim_device *dsim, + unsigned int pre_divider, unsigned int main_divider, + unsigned int scaler) +{ + unsigned long dfin_pll, dfvco, dpll_out; + unsigned int i, freq_band = 0xf; + + dfin_pll = (FIN_HZ / pre_divider); + + /****************************************************** + * Serial Clock(=ByteClk X 8) FreqBand[3:0] * + ****************************************************** + * ~ 99.99 MHz 0000 + * 100 ~ 119.99 MHz 0001 + * 120 ~ 159.99 MHz 0010 + * 160 ~ 199.99 MHz 0011 + * 200 ~ 239.99 MHz 0100 + * 140 ~ 319.99 MHz 0101 + * 320 ~ 389.99 MHz 0110 + * 390 ~ 449.99 MHz 0111 + * 450 ~ 509.99 MHz 1000 + * 510 ~ 559.99 MHz 1001 + * 560 ~ 639.99 MHz 1010 + * 640 ~ 689.99 MHz 1011 + * 690 ~ 769.99 MHz 1100 + * 770 ~ 869.99 MHz 1101 + * 870 ~ 949.99 MHz 1110 + * 950 ~ 1000 MHz 1111 + ******************************************************/ + if (dfin_pll < DFIN_PLL_MIN_HZ || dfin_pll > DFIN_PLL_MAX_HZ) { + dev_warn(dsim->dev, "fin_pll range should be 6MHz ~ 12MHz\n"); + exynos_mipi_dsi_enable_afc(dsim, 0, 0); + } else { + if (dfin_pll < 7 * MHZ) + exynos_mipi_dsi_enable_afc(dsim, 1, 0x1); + else if (dfin_pll < 8 * MHZ) + exynos_mipi_dsi_enable_afc(dsim, 1, 0x0); + else if (dfin_pll < 9 * MHZ) + exynos_mipi_dsi_enable_afc(dsim, 1, 0x3); + else if (dfin_pll < 10 * MHZ) + exynos_mipi_dsi_enable_afc(dsim, 1, 0x2); + else if (dfin_pll < 11 * MHZ) + exynos_mipi_dsi_enable_afc(dsim, 1, 0x5); + else + exynos_mipi_dsi_enable_afc(dsim, 1, 0x4); + } + + dfvco = dfin_pll * main_divider; + dev_dbg(dsim->dev, "dfvco = %lu, dfin_pll = %lu, main_divider = %d\n", + dfvco, dfin_pll, main_divider); + if (dfvco < DFVCO_MIN_HZ || dfvco > DFVCO_MAX_HZ) + dev_warn(dsim->dev, "fvco range should be 500MHz ~ 1000MHz\n"); + + dpll_out = dfvco / (1 << scaler); + dev_dbg(dsim->dev, "dpll_out = %lu, dfvco = %lu, scaler = %d\n", + dpll_out, dfvco, scaler); + + for (i = 0; i < ARRAY_SIZE(dpll_table); i++) { + if (dpll_out < dpll_table[i] * MHZ) { + freq_band = i; + break; + } + } + + dev_dbg(dsim->dev, "freq_band = %d\n", freq_band); + + exynos_mipi_dsi_pll_freq(dsim, pre_divider, main_divider, scaler); + + exynos_mipi_dsi_hs_zero_ctrl(dsim, 0); + exynos_mipi_dsi_prep_ctrl(dsim, 0); + + /* Freq Band */ + exynos_mipi_dsi_pll_freq_band(dsim, freq_band); + + /* Stable time */ + exynos_mipi_dsi_pll_stable_time(dsim, dsim->dsim_config->pll_stable_time); + + /* Enable PLL */ + dev_dbg(dsim->dev, "FOUT of mipi dphy pll is %luMHz\n", + (dpll_out / MHZ)); + + return dpll_out; +} + +static int exynos_mipi_dsi_set_clock(struct mipi_dsim_device *dsim, + unsigned int byte_clk_sel, unsigned int enable) +{ + unsigned int esc_div; + unsigned long esc_clk_error_rate; + unsigned long hs_clk = 0, byte_clk = 0, escape_clk = 0; + + if (enable) { + dsim->e_clk_src = byte_clk_sel; + + /* Escape mode clock and byte clock source */ + exynos_mipi_dsi_set_byte_clock_src(dsim, byte_clk_sel); + + /* DPHY, DSIM Link : D-PHY clock out */ + if (byte_clk_sel == DSIM_PLL_OUT_DIV8) { + hs_clk = exynos_mipi_dsi_change_pll(dsim, + dsim->dsim_config->p, dsim->dsim_config->m, + dsim->dsim_config->s); + if (hs_clk == 0) { + dev_err(dsim->dev, + "failed to get hs clock.\n"); + return -EINVAL; + } + + byte_clk = hs_clk / 8; + exynos_mipi_dsi_enable_pll_bypass(dsim, 0); + exynos_mipi_dsi_pll_on(dsim, 1); + /* DPHY : D-PHY clock out, DSIM link : external clock out */ + } else if (byte_clk_sel == DSIM_EXT_CLK_DIV8) { + dev_warn(dsim->dev, "this project is not support\n"); + dev_warn(dsim->dev, + "external clock source for MIPI DSIM.\n"); + } else if (byte_clk_sel == DSIM_EXT_CLK_BYPASS) { + dev_warn(dsim->dev, "this project is not support\n"); + dev_warn(dsim->dev, + "external clock source for MIPI DSIM\n"); + } + + /* escape clock divider */ + esc_div = byte_clk / (dsim->dsim_config->esc_clk); + dev_dbg(dsim->dev, + "esc_div = %d, byte_clk = %lu, esc_clk = %lu\n", + esc_div, byte_clk, dsim->dsim_config->esc_clk); + if ((byte_clk / esc_div) >= (20 * MHZ) || + (byte_clk / esc_div) > + dsim->dsim_config->esc_clk) + esc_div += 1; + + escape_clk = byte_clk / esc_div; + dev_dbg(dsim->dev, + "escape_clk = %lu, byte_clk = %lu, esc_div = %d\n", + escape_clk, byte_clk, esc_div); + + /* enable escape clock. */ + exynos_mipi_dsi_enable_byte_clock(dsim, 1); + + /* enable byte clk and escape clock */ + exynos_mipi_dsi_set_esc_clk_prs(dsim, 1, esc_div); + /* escape clock on lane */ + exynos_mipi_dsi_enable_esc_clk_on_lane(dsim, + (DSIM_LANE_CLOCK | dsim->data_lane), 1); + + dev_dbg(dsim->dev, "byte clock is %luMHz\n", + (byte_clk / MHZ)); + dev_dbg(dsim->dev, "escape clock that user's need is %lu\n", + (dsim->dsim_config->esc_clk / MHZ)); + dev_dbg(dsim->dev, "escape clock divider is %x\n", esc_div); + dev_dbg(dsim->dev, "escape clock is %luMHz\n", + ((byte_clk / esc_div) / MHZ)); + + if ((byte_clk / esc_div) > escape_clk) { + esc_clk_error_rate = escape_clk / + (byte_clk / esc_div); + dev_warn(dsim->dev, "error rate is %lu over.\n", + (esc_clk_error_rate / 100)); + } else if ((byte_clk / esc_div) < (escape_clk)) { + esc_clk_error_rate = (byte_clk / esc_div) / + escape_clk; + dev_warn(dsim->dev, "error rate is %lu under.\n", + (esc_clk_error_rate / 100)); + } + } else { + exynos_mipi_dsi_enable_esc_clk_on_lane(dsim, + (DSIM_LANE_CLOCK | dsim->data_lane), 0); + exynos_mipi_dsi_set_esc_clk_prs(dsim, 0, 0); + + /* disable escape clock. */ + exynos_mipi_dsi_enable_byte_clock(dsim, 0); + + if (byte_clk_sel == DSIM_PLL_OUT_DIV8) + exynos_mipi_dsi_pll_on(dsim, 0); + } + + return 0; +} + +int exynos_mipi_dsi_init_dsim(struct mipi_dsim_device *dsim) +{ + dsim->state = DSIM_STATE_INIT; + + switch (dsim->dsim_config->e_no_data_lane) { + case DSIM_DATA_LANE_1: + dsim->data_lane = DSIM_LANE_DATA0; + break; + case DSIM_DATA_LANE_2: + dsim->data_lane = DSIM_LANE_DATA0 | DSIM_LANE_DATA1; + break; + case DSIM_DATA_LANE_3: + dsim->data_lane = DSIM_LANE_DATA0 | DSIM_LANE_DATA1 | + DSIM_LANE_DATA2; + break; + case DSIM_DATA_LANE_4: + dsim->data_lane = DSIM_LANE_DATA0 | DSIM_LANE_DATA1 | + DSIM_LANE_DATA2 | DSIM_LANE_DATA3; + break; + default: + dev_info(dsim->dev, "data lane is invalid.\n"); + return -EINVAL; + }; + + exynos_mipi_dsi_sw_reset(dsim); + exynos_mipi_dsi_func_reset(dsim); + + exynos_mipi_dsi_dp_dn_swap(dsim, 0); + + return 0; +} + +void exynos_mipi_dsi_init_interrupt(struct mipi_dsim_device *dsim) +{ + unsigned int src = 0; + + src = (INTSRC_SFR_FIFO_EMPTY | INTSRC_RX_DATA_DONE); + exynos_mipi_dsi_set_interrupt(dsim, src, 1); + + src = 0; + src = ~(INTMSK_RX_DONE | INTMSK_FIFO_EMPTY); + exynos_mipi_dsi_set_interrupt_mask(dsim, src, 1); +} + +int exynos_mipi_dsi_enable_frame_done_int(struct mipi_dsim_device *dsim, + unsigned int enable) +{ + /* enable only frame done interrupt */ + exynos_mipi_dsi_set_interrupt_mask(dsim, INTMSK_FRAME_DONE, enable); + + return 0; +} + +void exynos_mipi_dsi_stand_by(struct mipi_dsim_device *dsim, + unsigned int enable) +{ + + /* consider Main display and Sub display. */ + + exynos_mipi_dsi_set_main_stand_by(dsim, enable); +} + +int exynos_mipi_dsi_set_display_mode(struct mipi_dsim_device *dsim, + struct mipi_dsim_config *dsim_config) +{ + struct mipi_dsim_platform_data *dsim_pd; + struct fb_videomode *timing; + + dsim_pd = (struct mipi_dsim_platform_data *)dsim->pd; + timing = (struct fb_videomode *)dsim_pd->lcd_panel_info; + + /* in case of VIDEO MODE (RGB INTERFACE), it sets polarities. */ + if (dsim_config->e_interface == (u32) DSIM_VIDEO) { + if (dsim_config->auto_vertical_cnt == 0) { + exynos_mipi_dsi_set_main_disp_vporch(dsim, + dsim_config->cmd_allow, + timing->upper_margin, + timing->lower_margin); + exynos_mipi_dsi_set_main_disp_hporch(dsim, + timing->left_margin, + timing->right_margin); + exynos_mipi_dsi_set_main_disp_sync_area(dsim, + timing->vsync_len, + timing->hsync_len); + } + } + + exynos_mipi_dsi_set_main_disp_resol(dsim, timing->xres, + timing->yres); + + exynos_mipi_dsi_display_config(dsim, dsim_config); + + dev_info(dsim->dev, "lcd panel ==> width = %d, height = %d\n", + timing->xres, timing->yres); + + return 0; +} + +int exynos_mipi_dsi_init_link(struct mipi_dsim_device *dsim) +{ + unsigned int time_out = 100; + + switch (dsim->state) { + case DSIM_STATE_INIT: + exynos_mipi_dsi_init_fifo_pointer(dsim, 0x1f); + + /* dsi configuration */ + exynos_mipi_dsi_init_config(dsim); + exynos_mipi_dsi_enable_lane(dsim, DSIM_LANE_CLOCK, 1); + exynos_mipi_dsi_enable_lane(dsim, dsim->data_lane, 1); + + /* set clock configuration */ + exynos_mipi_dsi_set_clock(dsim, dsim->dsim_config->e_byte_clk, 1); + + /* check clock and data lane state are stop state */ + while (!(exynos_mipi_dsi_is_lane_state(dsim))) { + time_out--; + if (time_out == 0) { + dev_err(dsim->dev, + "DSI Master is not stop state.\n"); + dev_err(dsim->dev, + "Check initialization process\n"); + + return -EINVAL; + } + } + if (time_out != 0) { + dev_info(dsim->dev, + "DSI Master driver has been completed.\n"); + dev_info(dsim->dev, "DSI Master state is stop state\n"); + } + + dsim->state = DSIM_STATE_STOP; + + /* BTA sequence counters */ + exynos_mipi_dsi_set_stop_state_counter(dsim, + dsim->dsim_config->stop_holding_cnt); + exynos_mipi_dsi_set_bta_timeout(dsim, + dsim->dsim_config->bta_timeout); + exynos_mipi_dsi_set_lpdr_timeout(dsim, + dsim->dsim_config->rx_timeout); + + return 0; + default: + dev_info(dsim->dev, "DSI Master is already init.\n"); + return 0; + } + + return 0; +} + +int exynos_mipi_dsi_set_hs_enable(struct mipi_dsim_device *dsim) +{ + if (dsim->state != DSIM_STATE_STOP) { + dev_warn(dsim->dev, "DSIM is not in stop state.\n"); + return 0; + } + + if (dsim->e_clk_src == DSIM_EXT_CLK_BYPASS) { + dev_warn(dsim->dev, "clock source is external bypass.\n"); + return 0; + } + + dsim->state = DSIM_STATE_HSCLKEN; + + /* set LCDC and CPU transfer mode to HS. */ + exynos_mipi_dsi_set_lcdc_transfer_mode(dsim, 0); + exynos_mipi_dsi_set_cpu_transfer_mode(dsim, 0); + exynos_mipi_dsi_enable_hs_clock(dsim, 1); + + return 0; +} + +int exynos_mipi_dsi_set_data_transfer_mode(struct mipi_dsim_device *dsim, + unsigned int mode) +{ + if (mode) { + if (dsim->state != DSIM_STATE_HSCLKEN) { + dev_err(dsim->dev, "HS Clock lane is not enabled.\n"); + return -EINVAL; + } + + exynos_mipi_dsi_set_lcdc_transfer_mode(dsim, 0); + } else { + if (dsim->state == DSIM_STATE_INIT || dsim->state == + DSIM_STATE_ULPS) { + dev_err(dsim->dev, + "DSI Master is not STOP or HSDT state.\n"); + return -EINVAL; + } + + exynos_mipi_dsi_set_cpu_transfer_mode(dsim, 0); + } + + return 0; +} + +int exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim) +{ + return _exynos_mipi_dsi_get_frame_done_status(dsim); +} + +int exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim) +{ + _exynos_mipi_dsi_clear_frame_done(dsim); + + return 0; +} + +int exynos_mipi_dsi_fifo_clear(struct mipi_dsim_device *dsim, + unsigned int val) +{ + int try = TRY_FIFO_CLEAR; + + exynos_mipi_dsi_sw_reset_release(dsim); + exynos_mipi_dsi_func_reset(dsim); + + do { + if (exynos_mipi_dsi_get_sw_reset_release(dsim)) { + exynos_mipi_dsi_init_interrupt(dsim); + dev_dbg(dsim->dev, "reset release done.\n"); + return 0; + } + } while (--try); + + dev_err(dsim->dev, "failed to clear dsim fifo.\n"); + return -EAGAIN; +} + +MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>"); +MODULE_DESCRIPTION("Samusung SoC MIPI-DSI common driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/exynos/exynos_mipi_dsi_common.h b/drivers/video/exynos/exynos_mipi_dsi_common.h new file mode 100644 index 000000000000..412552274df3 --- /dev/null +++ b/drivers/video/exynos/exynos_mipi_dsi_common.h @@ -0,0 +1,46 @@ +/* linux/drivers/video/exynos_mipi_dsi_common.h + * + * Header file for Samsung SoC MIPI-DSI common driver. + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd + * + * InKi Dae <inki.dae@samsung.com> + * Donghwa Lee <dh09.lee@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef _EXYNOS_MIPI_DSI_COMMON_H +#define _EXYNOS_MIPI_DSI_COMMON_H + +static DECLARE_COMPLETION(dsim_rd_comp); +static DECLARE_COMPLETION(dsim_wr_comp); + +int exynos_mipi_dsi_wr_data(struct mipi_dsim_device *dsim, unsigned int data_id, + const unsigned char *data0, unsigned int data_size); +int exynos_mipi_dsi_rd_data(struct mipi_dsim_device *dsim, unsigned int data_id, + unsigned int data0, unsigned int req_size, u8 *rx_buf); +irqreturn_t exynos_mipi_dsi_interrupt_handler(int irq, void *dev_id); +void exynos_mipi_dsi_init_interrupt(struct mipi_dsim_device *dsim); +int exynos_mipi_dsi_init_dsim(struct mipi_dsim_device *dsim); +void exynos_mipi_dsi_stand_by(struct mipi_dsim_device *dsim, + unsigned int enable); +int exynos_mipi_dsi_set_display_mode(struct mipi_dsim_device *dsim, + struct mipi_dsim_config *dsim_info); +int exynos_mipi_dsi_init_link(struct mipi_dsim_device *dsim); +int exynos_mipi_dsi_set_hs_enable(struct mipi_dsim_device *dsim); +int exynos_mipi_dsi_set_data_transfer_mode(struct mipi_dsim_device *dsim, + unsigned int mode); +int exynos_mipi_dsi_enable_frame_done_int(struct mipi_dsim_device *dsim, + unsigned int enable); +int exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim); +int exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim); + +extern struct fb_info *registered_fb[FB_MAX] __read_mostly; + +int exynos_mipi_dsi_fifo_clear(struct mipi_dsim_device *dsim, + unsigned int val); + +#endif /* _EXYNOS_MIPI_DSI_COMMON_H */ diff --git a/drivers/video/exynos/exynos_mipi_dsi_lowlevel.c b/drivers/video/exynos/exynos_mipi_dsi_lowlevel.c new file mode 100644 index 000000000000..0ef38ce72af6 --- /dev/null +++ b/drivers/video/exynos/exynos_mipi_dsi_lowlevel.c @@ -0,0 +1,618 @@ +/* linux/drivers/video/exynos/exynos_mipi_dsi_lowlevel.c + * + * Samsung SoC MIPI-DSI lowlevel driver. + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd + * + * InKi Dae, <inki.dae@samsung.com> + * Donghwa Lee, <dh09.lee@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/mutex.h> +#include <linux/wait.h> +#include <linux/delay.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/ctype.h> +#include <linux/io.h> + +#include <video/exynos_mipi_dsim.h> + +#include <mach/map.h> + +#include "exynos_mipi_dsi_regs.h" + +void exynos_mipi_dsi_func_reset(struct mipi_dsim_device *dsim) +{ + unsigned int reg; + + reg = readl(dsim->reg_base + EXYNOS_DSIM_SWRST); + + reg |= DSIM_FUNCRST; + + writel(reg, dsim->reg_base + EXYNOS_DSIM_SWRST); +} + +void exynos_mipi_dsi_sw_reset(struct mipi_dsim_device *dsim) +{ + unsigned int reg; + + reg = readl(dsim->reg_base + EXYNOS_DSIM_SWRST); + + reg |= DSIM_SWRST; + + writel(reg, dsim->reg_base + EXYNOS_DSIM_SWRST); +} + +void exynos_mipi_dsi_sw_reset_release(struct mipi_dsim_device *dsim) +{ + unsigned int reg; + + reg = readl(dsim->reg_base + EXYNOS_DSIM_INTSRC); + + reg |= INTSRC_SW_RST_RELEASE; + + writel(reg, dsim->reg_base + EXYNOS_DSIM_INTSRC); +} + +int exynos_mipi_dsi_get_sw_reset_release(struct mipi_dsim_device *dsim) +{ + return (readl(dsim->reg_base + EXYNOS_DSIM_INTSRC)) & + INTSRC_SW_RST_RELEASE; +} + +unsigned int exynos_mipi_dsi_read_interrupt_mask(struct mipi_dsim_device *dsim) +{ + unsigned int reg; + + reg = readl(dsim->reg_base + EXYNOS_DSIM_INTMSK); + + return reg; +} + +void exynos_mipi_dsi_set_interrupt_mask(struct mipi_dsim_device *dsim, + unsigned int mode, unsigned int mask) +{ + unsigned int reg = 0; + + if (mask) + reg |= mode; + else + reg &= ~mode; + + writel(reg, dsim->reg_base + EXYNOS_DSIM_INTMSK); +} + +void exynos_mipi_dsi_init_fifo_pointer(struct mipi_dsim_device *dsim, + unsigned int cfg) +{ + unsigned int reg; + + reg = readl(dsim->reg_base + EXYNOS_DSIM_FIFOCTRL); + + writel(reg & ~(cfg), dsim->reg_base + EXYNOS_DSIM_FIFOCTRL); + mdelay(10); + reg |= cfg; + + writel(reg, dsim->reg_base + EXYNOS_DSIM_FIFOCTRL); +} + +/* + * this function set PLL P, M and S value in D-PHY + */ +void exynos_mipi_dsi_set_phy_tunning(struct mipi_dsim_device *dsim, + unsigned int value) +{ + writel(DSIM_AFC_CTL(value), dsim->reg_base + EXYNOS_DSIM_PHYACCHR); +} + +void exynos_mipi_dsi_set_main_stand_by(struct mipi_dsim_device *dsim, + unsigned int enable) +{ + unsigned int reg; + + reg = readl(dsim->reg_base + EXYNOS_DSIM_MDRESOL); + + reg &= ~DSIM_MAIN_STAND_BY; + + if (enable) + reg |= DSIM_MAIN_STAND_BY; + + writel(reg, dsim->reg_base + EXYNOS_DSIM_MDRESOL); +} + +void exynos_mipi_dsi_set_main_disp_resol(struct mipi_dsim_device *dsim, + unsigned int width_resol, unsigned int height_resol) +{ + unsigned int reg; + + /* standby should be set after configuration so set to not ready*/ + reg = (readl(dsim->reg_base + EXYNOS_DSIM_MDRESOL)) & + ~(DSIM_MAIN_STAND_BY); + writel(reg, dsim->reg_base + EXYNOS_DSIM_MDRESOL); + + reg &= ~((0x7ff << 16) | (0x7ff << 0)); + reg |= DSIM_MAIN_VRESOL(height_resol) | DSIM_MAIN_HRESOL(width_resol); + + reg |= DSIM_MAIN_STAND_BY; + writel(reg, dsim->reg_base + EXYNOS_DSIM_MDRESOL); +} + +void exynos_mipi_dsi_set_main_disp_vporch(struct mipi_dsim_device *dsim, + unsigned int cmd_allow, unsigned int vfront, unsigned int vback) +{ + unsigned int reg; + + reg = (readl(dsim->reg_base + EXYNOS_DSIM_MVPORCH)) & + ~((DSIM_CMD_ALLOW_MASK) | (DSIM_STABLE_VFP_MASK) | + (DSIM_MAIN_VBP_MASK)); + + reg |= (DSIM_CMD_ALLOW_SHIFT(cmd_allow & 0xf) | + DSIM_STABLE_VFP_SHIFT(vfront & 0x7ff) | + DSIM_MAIN_VBP_SHIFT(vback & 0x7ff)); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_MVPORCH); +} + +void exynos_mipi_dsi_set_main_disp_hporch(struct mipi_dsim_device *dsim, + unsigned int front, unsigned int back) +{ + unsigned int reg; + + reg = (readl(dsim->reg_base + EXYNOS_DSIM_MHPORCH)) & + ~((DSIM_MAIN_HFP_MASK) | (DSIM_MAIN_HBP_MASK)); + + reg |= DSIM_MAIN_HFP_SHIFT(front) | DSIM_MAIN_HBP_SHIFT(back); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_MHPORCH); +} + +void exynos_mipi_dsi_set_main_disp_sync_area(struct mipi_dsim_device *dsim, + unsigned int vert, unsigned int hori) +{ + unsigned int reg; + + reg = (readl(dsim->reg_base + EXYNOS_DSIM_MSYNC)) & + ~((DSIM_MAIN_VSA_MASK) | (DSIM_MAIN_HSA_MASK)); + + reg |= (DSIM_MAIN_VSA_SHIFT(vert & 0x3ff) | + DSIM_MAIN_HSA_SHIFT(hori)); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_MSYNC); +} + +void exynos_mipi_dsi_set_sub_disp_resol(struct mipi_dsim_device *dsim, + unsigned int vert, unsigned int hori) +{ + unsigned int reg; + + reg = (readl(dsim->reg_base + EXYNOS_DSIM_SDRESOL)) & + ~(DSIM_SUB_STANDY_MASK); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_SDRESOL); + + reg &= ~(DSIM_SUB_VRESOL_MASK) | ~(DSIM_SUB_HRESOL_MASK); + reg |= (DSIM_SUB_VRESOL_SHIFT(vert & 0x7ff) | + DSIM_SUB_HRESOL_SHIFT(hori & 0x7ff)); + writel(reg, dsim->reg_base + EXYNOS_DSIM_SDRESOL); + + reg |= DSIM_SUB_STANDY_SHIFT(1); + writel(reg, dsim->reg_base + EXYNOS_DSIM_SDRESOL); +} + +void exynos_mipi_dsi_init_config(struct mipi_dsim_device *dsim) +{ + struct mipi_dsim_config *dsim_config = dsim->dsim_config; + + unsigned int cfg = (readl(dsim->reg_base + EXYNOS_DSIM_CONFIG)) & + ~((1 << 28) | (0x1f << 20) | (0x3 << 5)); + + cfg = ((DSIM_AUTO_FLUSH(dsim_config->auto_flush)) | + (DSIM_EOT_DISABLE(dsim_config->eot_disable)) | + (DSIM_AUTO_MODE_SHIFT(dsim_config->auto_vertical_cnt)) | + (DSIM_HSE_MODE_SHIFT(dsim_config->hse)) | + (DSIM_HFP_MODE_SHIFT(dsim_config->hfp)) | + (DSIM_HBP_MODE_SHIFT(dsim_config->hbp)) | + (DSIM_HSA_MODE_SHIFT(dsim_config->hsa)) | + (DSIM_NUM_OF_DATALANE_SHIFT(dsim_config->e_no_data_lane))); + + writel(cfg, dsim->reg_base + EXYNOS_DSIM_CONFIG); +} + +void exynos_mipi_dsi_display_config(struct mipi_dsim_device *dsim, + struct mipi_dsim_config *dsim_config) +{ + u32 reg = (readl(dsim->reg_base + EXYNOS_DSIM_CONFIG)) & + ~((0x3 << 26) | (1 << 25) | (0x3 << 18) | (0x7 << 12) | + (0x3 << 16) | (0x7 << 8)); + + if (dsim_config->e_interface == DSIM_VIDEO) + reg |= (1 << 25); + else if (dsim_config->e_interface == DSIM_COMMAND) + reg &= ~(1 << 25); + else { + dev_err(dsim->dev, "unknown lcd type.\n"); + return; + } + + /* main lcd */ + reg |= ((u8) (dsim_config->e_burst_mode) & 0x3) << 26 | + ((u8) (dsim_config->e_virtual_ch) & 0x3) << 18 | + ((u8) (dsim_config->e_pixel_format) & 0x7) << 12; + + writel(reg, dsim->reg_base + EXYNOS_DSIM_CONFIG); +} + +void exynos_mipi_dsi_enable_lane(struct mipi_dsim_device *dsim, unsigned int lane, + unsigned int enable) +{ + unsigned int reg; + + reg = readl(dsim->reg_base + EXYNOS_DSIM_CONFIG); + + if (enable) + reg |= DSIM_LANE_ENx(lane); + else + reg &= ~DSIM_LANE_ENx(lane); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_CONFIG); +} + + +void exynos_mipi_dsi_set_data_lane_number(struct mipi_dsim_device *dsim, + unsigned int count) +{ + unsigned int cfg; + + /* get the data lane number. */ + cfg = DSIM_NUM_OF_DATALANE_SHIFT(count); + + writel(cfg, dsim->reg_base + EXYNOS_DSIM_CONFIG); +} + +void exynos_mipi_dsi_enable_afc(struct mipi_dsim_device *dsim, unsigned int enable, + unsigned int afc_code) +{ + unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_PHYACCHR); + + if (enable) { + reg |= (1 << 14); + reg &= ~(0x7 << 5); + reg |= (afc_code & 0x7) << 5; + } else + reg &= ~(1 << 14); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_PHYACCHR); +} + +void exynos_mipi_dsi_enable_pll_bypass(struct mipi_dsim_device *dsim, + unsigned int enable) +{ + unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL)) & + ~(DSIM_PLL_BYPASS_SHIFT(0x1)); + + reg |= DSIM_PLL_BYPASS_SHIFT(enable); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL); +} + +void exynos_mipi_dsi_set_pll_pms(struct mipi_dsim_device *dsim, unsigned int p, + unsigned int m, unsigned int s) +{ + unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL); + + reg |= ((p & 0x3f) << 13) | ((m & 0x1ff) << 4) | ((s & 0x7) << 1); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL); +} + +void exynos_mipi_dsi_pll_freq_band(struct mipi_dsim_device *dsim, + unsigned int freq_band) +{ + unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL)) & + ~(DSIM_FREQ_BAND_SHIFT(0x1f)); + + reg |= DSIM_FREQ_BAND_SHIFT(freq_band & 0x1f); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL); +} + +void exynos_mipi_dsi_pll_freq(struct mipi_dsim_device *dsim, + unsigned int pre_divider, unsigned int main_divider, + unsigned int scaler) +{ + unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL)) & + ~(0x7ffff << 1); + + reg |= (pre_divider & 0x3f) << 13 | (main_divider & 0x1ff) << 4 | + (scaler & 0x7) << 1; + + writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL); +} + +void exynos_mipi_dsi_pll_stable_time(struct mipi_dsim_device *dsim, + unsigned int lock_time) +{ + writel(lock_time, dsim->reg_base + EXYNOS_DSIM_PLLTMR); +} + +void exynos_mipi_dsi_enable_pll(struct mipi_dsim_device *dsim, unsigned int enable) +{ + unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL)) & + ~(DSIM_PLL_EN_SHIFT(0x1)); + + reg |= DSIM_PLL_EN_SHIFT(enable & 0x1); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL); +} + +void exynos_mipi_dsi_set_byte_clock_src(struct mipi_dsim_device *dsim, + unsigned int src) +{ + unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL)) & + ~(DSIM_BYTE_CLK_SRC_SHIFT(0x3)); + + reg |= (DSIM_BYTE_CLK_SRC_SHIFT(src)); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL); +} + +void exynos_mipi_dsi_enable_byte_clock(struct mipi_dsim_device *dsim, + unsigned int enable) +{ + unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL)) & + ~(DSIM_BYTE_CLKEN_SHIFT(0x1)); + + reg |= DSIM_BYTE_CLKEN_SHIFT(enable); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL); +} + +void exynos_mipi_dsi_set_esc_clk_prs(struct mipi_dsim_device *dsim, + unsigned int enable, unsigned int prs_val) +{ + unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL)) & + ~(DSIM_ESC_CLKEN_SHIFT(0x1) | 0xffff); + + reg |= DSIM_ESC_CLKEN_SHIFT(enable); + if (enable) + reg |= prs_val; + + writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL); +} + +void exynos_mipi_dsi_enable_esc_clk_on_lane(struct mipi_dsim_device *dsim, + unsigned int lane_sel, unsigned int enable) +{ + unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL); + + if (enable) + reg |= DSIM_LANE_ESC_CLKEN(lane_sel); + else + + reg &= ~DSIM_LANE_ESC_CLKEN(lane_sel); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL); +} + +void exynos_mipi_dsi_force_dphy_stop_state(struct mipi_dsim_device *dsim, + unsigned int enable) +{ + unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_ESCMODE)) & + ~(DSIM_FORCE_STOP_STATE_SHIFT(0x1)); + + reg |= (DSIM_FORCE_STOP_STATE_SHIFT(enable & 0x1)); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_ESCMODE); +} + +unsigned int exynos_mipi_dsi_is_lane_state(struct mipi_dsim_device *dsim) +{ + unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_STATUS); + + /** + * check clock and data lane states. + * if MIPI-DSI controller was enabled at bootloader then + * TX_READY_HS_CLK is enabled otherwise STOP_STATE_CLK. + * so it should be checked for two case. + */ + if ((reg & DSIM_STOP_STATE_DAT(0xf)) && + ((reg & DSIM_STOP_STATE_CLK) || + (reg & DSIM_TX_READY_HS_CLK))) + return 1; + + return 0; +} + +void exynos_mipi_dsi_set_stop_state_counter(struct mipi_dsim_device *dsim, + unsigned int cnt_val) +{ + unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_ESCMODE)) & + ~(DSIM_STOP_STATE_CNT_SHIFT(0x7ff)); + + reg |= (DSIM_STOP_STATE_CNT_SHIFT(cnt_val & 0x7ff)); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_ESCMODE); +} + +void exynos_mipi_dsi_set_bta_timeout(struct mipi_dsim_device *dsim, + unsigned int timeout) +{ + unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_TIMEOUT)) & + ~(DSIM_BTA_TOUT_SHIFT(0xff)); + + reg |= (DSIM_BTA_TOUT_SHIFT(timeout)); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_TIMEOUT); +} + +void exynos_mipi_dsi_set_lpdr_timeout(struct mipi_dsim_device *dsim, + unsigned int timeout) +{ + unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_TIMEOUT)) & + ~(DSIM_LPDR_TOUT_SHIFT(0xffff)); + + reg |= (DSIM_LPDR_TOUT_SHIFT(timeout)); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_TIMEOUT); +} + +void exynos_mipi_dsi_set_cpu_transfer_mode(struct mipi_dsim_device *dsim, + unsigned int lp) +{ + unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_ESCMODE); + + reg &= ~DSIM_CMD_LPDT_LP; + + if (lp) + reg |= DSIM_CMD_LPDT_LP; + + writel(reg, dsim->reg_base + EXYNOS_DSIM_ESCMODE); +} + +void exynos_mipi_dsi_set_lcdc_transfer_mode(struct mipi_dsim_device *dsim, + unsigned int lp) +{ + unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_ESCMODE); + + reg &= ~DSIM_TX_LPDT_LP; + + if (lp) + reg |= DSIM_TX_LPDT_LP; + + writel(reg, dsim->reg_base + EXYNOS_DSIM_ESCMODE); +} + +void exynos_mipi_dsi_enable_hs_clock(struct mipi_dsim_device *dsim, + unsigned int enable) +{ + unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL)) & + ~(DSIM_TX_REQUEST_HSCLK_SHIFT(0x1)); + + reg |= DSIM_TX_REQUEST_HSCLK_SHIFT(enable); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL); +} + +void exynos_mipi_dsi_dp_dn_swap(struct mipi_dsim_device *dsim, + unsigned int swap_en) +{ + unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_PHYACCHR1); + + reg &= ~(0x3 << 0); + reg |= (swap_en & 0x3) << 0; + + writel(reg, dsim->reg_base + EXYNOS_DSIM_PHYACCHR1); +} + +void exynos_mipi_dsi_hs_zero_ctrl(struct mipi_dsim_device *dsim, + unsigned int hs_zero) +{ + unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL)) & + ~(0xf << 28); + + reg |= ((hs_zero & 0xf) << 28); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL); +} + +void exynos_mipi_dsi_prep_ctrl(struct mipi_dsim_device *dsim, unsigned int prep) +{ + unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL)) & + ~(0x7 << 20); + + reg |= ((prep & 0x7) << 20); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL); +} + +unsigned int exynos_mipi_dsi_read_interrupt(struct mipi_dsim_device *dsim) +{ + return readl(dsim->reg_base + EXYNOS_DSIM_INTSRC); +} + +void exynos_mipi_dsi_clear_interrupt(struct mipi_dsim_device *dsim, + unsigned int src) +{ + unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_INTSRC); + + reg |= src; + + writel(reg, dsim->reg_base + EXYNOS_DSIM_INTSRC); +} + +void exynos_mipi_dsi_set_interrupt(struct mipi_dsim_device *dsim, + unsigned int src, unsigned int enable) +{ + unsigned int reg = 0; + + if (enable) + reg |= src; + else + reg &= ~src; + + writel(reg, dsim->reg_base + EXYNOS_DSIM_INTSRC); +} + +unsigned int exynos_mipi_dsi_is_pll_stable(struct mipi_dsim_device *dsim) +{ + unsigned int reg; + + reg = readl(dsim->reg_base + EXYNOS_DSIM_STATUS); + + return reg & (1 << 31) ? 1 : 0; +} + +unsigned int exynos_mipi_dsi_get_fifo_state(struct mipi_dsim_device *dsim) +{ + return readl(dsim->reg_base + EXYNOS_DSIM_FIFOCTRL) & ~(0x1f); +} + +void exynos_mipi_dsi_wr_tx_header(struct mipi_dsim_device *dsim, + unsigned int di, unsigned int data0, unsigned int data1) +{ + unsigned int reg = (data1 << 16) | (data0 << 8) | ((di & 0x3f) << 0); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_PKTHDR); +} + +void exynos_mipi_dsi_rd_tx_header(struct mipi_dsim_device *dsim, + unsigned int di, unsigned int data0) +{ + unsigned int reg = (data0 << 8) | (di << 0); + + writel(reg, dsim->reg_base + EXYNOS_DSIM_PKTHDR); +} + +unsigned int exynos_mipi_dsi_rd_rx_fifo(struct mipi_dsim_device *dsim) +{ + return readl(dsim->reg_base + EXYNOS_DSIM_RXFIFO); +} + +unsigned int _exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim) +{ + unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_INTSRC); + + return (reg & INTSRC_FRAME_DONE) ? 1 : 0; +} + +void _exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim) +{ + unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_INTSRC); + + writel(reg | INTSRC_FRAME_DONE, dsim->reg_base + + EXYNOS_DSIM_INTSRC); +} + +void exynos_mipi_dsi_wr_tx_data(struct mipi_dsim_device *dsim, + unsigned int tx_data) +{ + writel(tx_data, dsim->reg_base + EXYNOS_DSIM_PAYLOAD); +} diff --git a/drivers/video/exynos/exynos_mipi_dsi_lowlevel.h b/drivers/video/exynos/exynos_mipi_dsi_lowlevel.h new file mode 100644 index 000000000000..85460701c7ea --- /dev/null +++ b/drivers/video/exynos/exynos_mipi_dsi_lowlevel.h @@ -0,0 +1,112 @@ +/* linux/drivers/video/exynos/exynos_mipi_dsi_lowlevel.h + * + * Header file for Samsung SoC MIPI-DSI lowlevel driver. + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd + * + * InKi Dae <inki.dae@samsung.com> + * Donghwa Lee <dh09.lee@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef _EXYNOS_MIPI_DSI_LOWLEVEL_H +#define _EXYNOS_MIPI_DSI_LOWLEVEL_H + +void exynos_mipi_dsi_func_reset(struct mipi_dsim_device *dsim); +void exynos_mipi_dsi_sw_reset(struct mipi_dsim_device *dsim); +void exynos_mipi_dsi_sw_reset_release(struct mipi_dsim_device *dsim); +int exynos_mipi_dsi_get_sw_reset_release(struct mipi_dsim_device *dsim); +void exynos_mipi_dsi_set_interrupt_mask(struct mipi_dsim_device *dsim, + unsigned int mode, unsigned int mask); +void exynos_mipi_dsi_set_data_lane_number(struct mipi_dsim_device *dsim, + unsigned int count); +void exynos_mipi_dsi_init_fifo_pointer(struct mipi_dsim_device *dsim, + unsigned int cfg); +void exynos_mipi_dsi_set_phy_tunning(struct mipi_dsim_device *dsim, + unsigned int value); +void exynos_mipi_dsi_set_phy_tunning(struct mipi_dsim_device *dsim, + unsigned int value); +void exynos_mipi_dsi_set_main_stand_by(struct mipi_dsim_device *dsim, + unsigned int enable); +void exynos_mipi_dsi_set_main_disp_resol(struct mipi_dsim_device *dsim, + unsigned int width_resol, unsigned int height_resol); +void exynos_mipi_dsi_set_main_disp_vporch(struct mipi_dsim_device *dsim, + unsigned int cmd_allow, unsigned int vfront, unsigned int vback); +void exynos_mipi_dsi_set_main_disp_hporch(struct mipi_dsim_device *dsim, + unsigned int front, unsigned int back); +void exynos_mipi_dsi_set_main_disp_sync_area(struct mipi_dsim_device *dsim, + unsigned int vert, unsigned int hori); +void exynos_mipi_dsi_set_sub_disp_resol(struct mipi_dsim_device *dsim, + unsigned int vert, unsigned int hori); +void exynos_mipi_dsi_init_config(struct mipi_dsim_device *dsim); +void exynos_mipi_dsi_display_config(struct mipi_dsim_device *dsim, + struct mipi_dsim_config *dsim_config); +void exynos_mipi_dsi_set_data_lane_number(struct mipi_dsim_device *dsim, + unsigned int count); +void exynos_mipi_dsi_enable_lane(struct mipi_dsim_device *dsim, unsigned int lane, + unsigned int enable); +void exynos_mipi_dsi_enable_afc(struct mipi_dsim_device *dsim, unsigned int enable, + unsigned int afc_code); +void exynos_mipi_dsi_enable_pll_bypass(struct mipi_dsim_device *dsim, + unsigned int enable); +void exynos_mipi_dsi_set_pll_pms(struct mipi_dsim_device *dsim, unsigned int p, + unsigned int m, unsigned int s); +void exynos_mipi_dsi_pll_freq_band(struct mipi_dsim_device *dsim, + unsigned int freq_band); +void exynos_mipi_dsi_pll_freq(struct mipi_dsim_device *dsim, + unsigned int pre_divider, unsigned int main_divider, + unsigned int scaler); +void exynos_mipi_dsi_pll_stable_time(struct mipi_dsim_device *dsim, + unsigned int lock_time); +void exynos_mipi_dsi_enable_pll(struct mipi_dsim_device *dsim, + unsigned int enable); +void exynos_mipi_dsi_set_byte_clock_src(struct mipi_dsim_device *dsim, + unsigned int src); +void exynos_mipi_dsi_enable_byte_clock(struct mipi_dsim_device *dsim, + unsigned int enable); +void exynos_mipi_dsi_set_esc_clk_prs(struct mipi_dsim_device *dsim, + unsigned int enable, unsigned int prs_val); +void exynos_mipi_dsi_enable_esc_clk_on_lane(struct mipi_dsim_device *dsim, + unsigned int lane_sel, unsigned int enable); +void exynos_mipi_dsi_force_dphy_stop_state(struct mipi_dsim_device *dsim, + unsigned int enable); +unsigned int exynos_mipi_dsi_is_lane_state(struct mipi_dsim_device *dsim); +void exynos_mipi_dsi_set_stop_state_counter(struct mipi_dsim_device *dsim, + unsigned int cnt_val); +void exynos_mipi_dsi_set_bta_timeout(struct mipi_dsim_device *dsim, + unsigned int timeout); +void exynos_mipi_dsi_set_lpdr_timeout(struct mipi_dsim_device *dsim, + unsigned int timeout); +void exynos_mipi_dsi_set_lcdc_transfer_mode(struct mipi_dsim_device *dsim, + unsigned int lp); +void exynos_mipi_dsi_set_cpu_transfer_mode(struct mipi_dsim_device *dsim, + unsigned int lp); +void exynos_mipi_dsi_enable_hs_clock(struct mipi_dsim_device *dsim, + unsigned int enable); +void exynos_mipi_dsi_dp_dn_swap(struct mipi_dsim_device *dsim, + unsigned int swap_en); +void exynos_mipi_dsi_hs_zero_ctrl(struct mipi_dsim_device *dsim, + unsigned int hs_zero); +void exynos_mipi_dsi_prep_ctrl(struct mipi_dsim_device *dsim, unsigned int prep); +unsigned int exynos_mipi_dsi_read_interrupt(struct mipi_dsim_device *dsim); +unsigned int exynos_mipi_dsi_read_interrupt_mask(struct mipi_dsim_device *dsim); +void exynos_mipi_dsi_clear_interrupt(struct mipi_dsim_device *dsim, + unsigned int src); +void exynos_mipi_dsi_set_interrupt(struct mipi_dsim_device *dsim, + unsigned int src, unsigned int enable); +unsigned int exynos_mipi_dsi_is_pll_stable(struct mipi_dsim_device *dsim); +unsigned int exynos_mipi_dsi_get_fifo_state(struct mipi_dsim_device *dsim); +unsigned int _exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim); +void _exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim); +void exynos_mipi_dsi_wr_tx_header(struct mipi_dsim_device *dsim, unsigned int di, + unsigned int data0, unsigned int data1); +void exynos_mipi_dsi_wr_tx_data(struct mipi_dsim_device *dsim, + unsigned int tx_data); +void exynos_mipi_dsi_rd_tx_header(struct mipi_dsim_device *dsim, + unsigned int data0, unsigned int data1); +unsigned int exynos_mipi_dsi_rd_rx_fifo(struct mipi_dsim_device *dsim); + +#endif /* _EXYNOS_MIPI_DSI_LOWLEVEL_H */ diff --git a/drivers/video/exynos/exynos_mipi_dsi_regs.h b/drivers/video/exynos/exynos_mipi_dsi_regs.h new file mode 100644 index 000000000000..4227106d3fd0 --- /dev/null +++ b/drivers/video/exynos/exynos_mipi_dsi_regs.h @@ -0,0 +1,149 @@ +/* linux/driver/video/exynos/exynos_mipi_dsi_regs.h + * + * Register definition file for Samsung MIPI-DSIM driver + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd + * + * InKi Dae <inki.dae@samsung.com> + * Donghwa Lee <dh09.lee@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef _EXYNOS_MIPI_DSI_REGS_H +#define _EXYNOS_MIPI_DSI_REGS_H + +#define EXYNOS_DSIM_STATUS 0x0 /* Status register */ +#define EXYNOS_DSIM_SWRST 0x4 /* Software reset register */ +#define EXYNOS_DSIM_CLKCTRL 0x8 /* Clock control register */ +#define EXYNOS_DSIM_TIMEOUT 0xc /* Time out register */ +#define EXYNOS_DSIM_CONFIG 0x10 /* Configuration register */ +#define EXYNOS_DSIM_ESCMODE 0x14 /* Escape mode register */ + +/* Main display image resolution register */ +#define EXYNOS_DSIM_MDRESOL 0x18 +#define EXYNOS_DSIM_MVPORCH 0x1c /* Main display Vporch register */ +#define EXYNOS_DSIM_MHPORCH 0x20 /* Main display Hporch register */ +#define EXYNOS_DSIM_MSYNC 0x24 /* Main display sync area register */ + +/* Sub display image resolution register */ +#define EXYNOS_DSIM_SDRESOL 0x28 +#define EXYNOS_DSIM_INTSRC 0x2c /* Interrupt source register */ +#define EXYNOS_DSIM_INTMSK 0x30 /* Interrupt mask register */ +#define EXYNOS_DSIM_PKTHDR 0x34 /* Packet Header FIFO register */ +#define EXYNOS_DSIM_PAYLOAD 0x38 /* Payload FIFO register */ +#define EXYNOS_DSIM_RXFIFO 0x3c /* Read FIFO register */ +#define EXYNOS_DSIM_FIFOTHLD 0x40 /* FIFO threshold level register */ +#define EXYNOS_DSIM_FIFOCTRL 0x44 /* FIFO status and control register */ + +/* FIFO memory AC characteristic register */ +#define EXYNOS_DSIM_PLLCTRL 0x4c /* PLL control register */ +#define EXYNOS_DSIM_PLLTMR 0x50 /* PLL timer register */ +#define EXYNOS_DSIM_PHYACCHR 0x54 /* D-PHY AC characteristic register */ +#define EXYNOS_DSIM_PHYACCHR1 0x58 /* D-PHY AC characteristic register1 */ + +/* DSIM_STATUS */ +#define DSIM_STOP_STATE_DAT(x) (((x) & 0xf) << 0) +#define DSIM_STOP_STATE_CLK (1 << 8) +#define DSIM_TX_READY_HS_CLK (1 << 10) + +/* DSIM_SWRST */ +#define DSIM_FUNCRST (1 << 16) +#define DSIM_SWRST (1 << 0) + +/* EXYNOS_DSIM_TIMEOUT */ +#define DSIM_LPDR_TOUT_SHIFT(x) ((x) << 0) +#define DSIM_BTA_TOUT_SHIFT(x) ((x) << 16) + +/* EXYNOS_DSIM_CLKCTRL */ +#define DSIM_LANE_ESC_CLKEN(x) (((x) & 0x1f) << 19) +#define DSIM_BYTE_CLKEN_SHIFT(x) ((x) << 24) +#define DSIM_BYTE_CLK_SRC_SHIFT(x) ((x) << 25) +#define DSIM_PLL_BYPASS_SHIFT(x) ((x) << 27) +#define DSIM_ESC_CLKEN_SHIFT(x) ((x) << 28) +#define DSIM_TX_REQUEST_HSCLK_SHIFT(x) ((x) << 31) + +/* EXYNOS_DSIM_CONFIG */ +#define DSIM_LANE_ENx(x) (((x) & 0x1f) << 0) +#define DSIM_NUM_OF_DATALANE_SHIFT(x) ((x) << 5) +#define DSIM_HSA_MODE_SHIFT(x) ((x) << 20) +#define DSIM_HBP_MODE_SHIFT(x) ((x) << 21) +#define DSIM_HFP_MODE_SHIFT(x) ((x) << 22) +#define DSIM_HSE_MODE_SHIFT(x) ((x) << 23) +#define DSIM_AUTO_MODE_SHIFT(x) ((x) << 24) +#define DSIM_EOT_DISABLE(x) ((x) << 28) +#define DSIM_AUTO_FLUSH(x) ((x) << 29) + +#define DSIM_NUM_OF_DATA_LANE(x) ((x) << DSIM_NUM_OF_DATALANE_SHIFT) + +/* EXYNOS_DSIM_ESCMODE */ +#define DSIM_TX_LPDT_LP (1 << 6) +#define DSIM_CMD_LPDT_LP (1 << 7) +#define DSIM_FORCE_STOP_STATE_SHIFT(x) ((x) << 20) +#define DSIM_STOP_STATE_CNT_SHIFT(x) ((x) << 21) + +/* EXYNOS_DSIM_MDRESOL */ +#define DSIM_MAIN_STAND_BY (1 << 31) +#define DSIM_MAIN_VRESOL(x) (((x) & 0x7ff) << 16) +#define DSIM_MAIN_HRESOL(x) (((x) & 0X7ff) << 0) + +/* EXYNOS_DSIM_MVPORCH */ +#define DSIM_CMD_ALLOW_SHIFT(x) ((x) << 28) +#define DSIM_STABLE_VFP_SHIFT(x) ((x) << 16) +#define DSIM_MAIN_VBP_SHIFT(x) ((x) << 0) +#define DSIM_CMD_ALLOW_MASK (0xf << 28) +#define DSIM_STABLE_VFP_MASK (0x7ff << 16) +#define DSIM_MAIN_VBP_MASK (0x7ff << 0) + +/* EXYNOS_DSIM_MHPORCH */ +#define DSIM_MAIN_HFP_SHIFT(x) ((x) << 16) +#define DSIM_MAIN_HBP_SHIFT(x) ((x) << 0) +#define DSIM_MAIN_HFP_MASK ((0xffff) << 16) +#define DSIM_MAIN_HBP_MASK ((0xffff) << 0) + +/* EXYNOS_DSIM_MSYNC */ +#define DSIM_MAIN_VSA_SHIFT(x) ((x) << 22) +#define DSIM_MAIN_HSA_SHIFT(x) ((x) << 0) +#define DSIM_MAIN_VSA_MASK ((0x3ff) << 22) +#define DSIM_MAIN_HSA_MASK ((0xffff) << 0) + +/* EXYNOS_DSIM_SDRESOL */ +#define DSIM_SUB_STANDY_SHIFT(x) ((x) << 31) +#define DSIM_SUB_VRESOL_SHIFT(x) ((x) << 16) +#define DSIM_SUB_HRESOL_SHIFT(x) ((x) << 0) +#define DSIM_SUB_STANDY_MASK ((0x1) << 31) +#define DSIM_SUB_VRESOL_MASK ((0x7ff) << 16) +#define DSIM_SUB_HRESOL_MASK ((0x7ff) << 0) + +/* EXYNOS_DSIM_INTSRC */ +#define INTSRC_PLL_STABLE (1 << 31) +#define INTSRC_SW_RST_RELEASE (1 << 30) +#define INTSRC_SFR_FIFO_EMPTY (1 << 29) +#define INTSRC_FRAME_DONE (1 << 24) +#define INTSRC_RX_DATA_DONE (1 << 18) + +/* EXYNOS_DSIM_INTMSK */ +#define INTMSK_FIFO_EMPTY (1 << 29) +#define INTMSK_BTA (1 << 25) +#define INTMSK_FRAME_DONE (1 << 24) +#define INTMSK_RX_TIMEOUT (1 << 21) +#define INTMSK_BTA_TIMEOUT (1 << 20) +#define INTMSK_RX_DONE (1 << 18) +#define INTMSK_RX_TE (1 << 17) +#define INTMSK_RX_ACK (1 << 16) +#define INTMSK_RX_ECC_ERR (1 << 15) +#define INTMSK_RX_CRC_ERR (1 << 14) + +/* EXYNOS_DSIM_FIFOCTRL */ +#define SFR_HEADER_EMPTY (1 << 22) + +/* EXYNOS_DSIM_PHYACCHR */ +#define DSIM_AFC_CTL(x) (((x) & 0x7) << 5) + +/* EXYNOS_DSIM_PLLCTRL */ +#define DSIM_PLL_EN_SHIFT(x) ((x) << 23) +#define DSIM_FREQ_BAND_SHIFT(x) ((x) << 24) + +#endif /* _EXYNOS_MIPI_DSI_REGS_H */ diff --git a/drivers/video/exynos/s6e8ax0.c b/drivers/video/exynos/s6e8ax0.c new file mode 100644 index 000000000000..4aa9ac6218bf --- /dev/null +++ b/drivers/video/exynos/s6e8ax0.c @@ -0,0 +1,898 @@ +/* linux/drivers/video/exynos/s6e8ax0.c + * + * MIPI-DSI based s6e8ax0 AMOLED lcd 4.65 inch panel driver. + * + * Inki Dae, <inki.dae@samsung.com> + * Donghwa Lee, <dh09.lee@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/mutex.h> +#include <linux/wait.h> +#include <linux/ctype.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/lcd.h> +#include <linux/fb.h> +#include <linux/backlight.h> +#include <linux/regulator/consumer.h> + +#include <video/mipi_display.h> +#include <video/exynos_mipi_dsim.h> + +#define LDI_MTP_LENGTH 24 +#define DSIM_PM_STABLE_TIME 10 +#define MIN_BRIGHTNESS 0 +#define MAX_BRIGHTNESS 24 +#define GAMMA_TABLE_COUNT 26 + +#define POWER_IS_ON(pwr) ((pwr) == FB_BLANK_UNBLANK) +#define POWER_IS_OFF(pwr) ((pwr) == FB_BLANK_POWERDOWN) +#define POWER_IS_NRM(pwr) ((pwr) == FB_BLANK_NORMAL) + +#define lcd_to_master(a) (a->dsim_dev->master) +#define lcd_to_master_ops(a) ((lcd_to_master(a))->master_ops) + +enum { + DSIM_NONE_STATE = 0, + DSIM_RESUME_COMPLETE = 1, + DSIM_FRAME_DONE = 2, +}; + +struct s6e8ax0 { + struct device *dev; + unsigned int power; + unsigned int id; + unsigned int gamma; + unsigned int acl_enable; + unsigned int cur_acl; + + struct lcd_device *ld; + struct backlight_device *bd; + + struct mipi_dsim_lcd_device *dsim_dev; + struct lcd_platform_data *ddi_pd; + struct mutex lock; + bool enabled; +}; + + +static struct regulator_bulk_data supplies[] = { + { .supply = "vdd3", }, + { .supply = "vci", }, +}; + +static void s6e8ax0_regulator_enable(struct s6e8ax0 *lcd) +{ + int ret = 0; + struct lcd_platform_data *pd = NULL; + + pd = lcd->ddi_pd; + mutex_lock(&lcd->lock); + if (!lcd->enabled) { + ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies); + if (ret) + goto out; + + lcd->enabled = true; + } + msleep(pd->power_on_delay); +out: + mutex_unlock(&lcd->lock); +} + +static void s6e8ax0_regulator_disable(struct s6e8ax0 *lcd) +{ + int ret = 0; + + mutex_lock(&lcd->lock); + if (lcd->enabled) { + ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies); + if (ret) + goto out; + + lcd->enabled = false; + } +out: + mutex_unlock(&lcd->lock); +} + +static const unsigned char s6e8ax0_22_gamma_30[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xf5, 0x00, 0xff, 0xad, 0xaf, + 0xbA, 0xc3, 0xd8, 0xc5, 0x9f, 0xc6, 0x9e, 0xc1, 0xdc, 0xc0, + 0x00, 0x61, 0x00, 0x5a, 0x00, 0x74, +}; + +static const unsigned char s6e8ax0_22_gamma_50[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xe8, 0x1f, 0xf7, 0xad, 0xc0, + 0xb5, 0xc4, 0xdc, 0xc4, 0x9e, 0xc6, 0x9c, 0xbb, 0xd8, 0xbb, + 0x00, 0x70, 0x00, 0x68, 0x00, 0x86, +}; + +static const unsigned char s6e8ax0_22_gamma_60[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xde, 0x1f, 0xef, 0xad, 0xc4, + 0xb3, 0xc3, 0xdd, 0xc4, 0x9e, 0xc6, 0x9c, 0xbc, 0xd6, 0xba, + 0x00, 0x75, 0x00, 0x6e, 0x00, 0x8d, +}; + +static const unsigned char s6e8ax0_22_gamma_70[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xd8, 0x1f, 0xe7, 0xaf, 0xc8, + 0xb4, 0xc4, 0xdd, 0xc3, 0x9d, 0xc6, 0x9c, 0xbb, 0xd6, 0xb9, + 0x00, 0x7a, 0x00, 0x72, 0x00, 0x93, +}; + +static const unsigned char s6e8ax0_22_gamma_80[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xc9, 0x1f, 0xde, 0xae, 0xc9, + 0xb1, 0xc3, 0xdd, 0xc2, 0x9d, 0xc5, 0x9b, 0xbc, 0xd6, 0xbb, + 0x00, 0x7f, 0x00, 0x77, 0x00, 0x99, +}; + +static const unsigned char s6e8ax0_22_gamma_90[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xc7, 0x1f, 0xd9, 0xb0, 0xcc, + 0xb2, 0xc3, 0xdc, 0xc1, 0x9c, 0xc6, 0x9c, 0xbc, 0xd4, 0xb9, + 0x00, 0x83, 0x00, 0x7b, 0x00, 0x9e, +}; + +static const unsigned char s6e8ax0_22_gamma_100[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xbd, 0x80, 0xcd, 0xba, 0xce, + 0xb3, 0xc4, 0xde, 0xc3, 0x9c, 0xc4, 0x9, 0xb8, 0xd3, 0xb6, + 0x00, 0x88, 0x00, 0x80, 0x00, 0xa5, +}; + +static const unsigned char s6e8ax0_22_gamma_120[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb9, 0x95, 0xc8, 0xb1, 0xcf, + 0xb2, 0xc6, 0xdf, 0xc5, 0x9b, 0xc3, 0x99, 0xb6, 0xd2, 0xb6, + 0x00, 0x8f, 0x00, 0x86, 0x00, 0xac, +}; + +static const unsigned char s6e8ax0_22_gamma_130[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb7, 0xa0, 0xc7, 0xb1, 0xd0, + 0xb2, 0xc4, 0xdd, 0xc3, 0x9a, 0xc3, 0x98, 0xb6, 0xd0, 0xb4, + 0x00, 0x92, 0x00, 0x8a, 0x00, 0xb1, +}; + +static const unsigned char s6e8ax0_22_gamma_140[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb7, 0xa0, 0xc5, 0xb2, 0xd0, + 0xb3, 0xc3, 0xde, 0xc3, 0x9b, 0xc2, 0x98, 0xb6, 0xd0, 0xb4, + 0x00, 0x95, 0x00, 0x8d, 0x00, 0xb5, +}; + +static const unsigned char s6e8ax0_22_gamma_150[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xa0, 0xc2, 0xb2, 0xd0, + 0xb2, 0xc1, 0xdd, 0xc2, 0x9b, 0xc2, 0x98, 0xb4, 0xcf, 0xb1, + 0x00, 0x99, 0x00, 0x90, 0x00, 0xba, +}; + +static const unsigned char s6e8ax0_22_gamma_160[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xa5, 0xbf, 0xb0, 0xd0, + 0xb1, 0xc3, 0xde, 0xc2, 0x99, 0xc1, 0x97, 0xb4, 0xce, 0xb1, + 0x00, 0x9c, 0x00, 0x93, 0x00, 0xbe, +}; + +static const unsigned char s6e8ax0_22_gamma_170[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb5, 0xbf, 0xb1, 0xd1, + 0xb1, 0xc3, 0xde, 0xc3, 0x99, 0xc0, 0x96, 0xb4, 0xce, 0xb1, + 0x00, 0x9f, 0x00, 0x96, 0x00, 0xc2, +}; + +static const unsigned char s6e8ax0_22_gamma_180[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb7, 0xbe, 0xb3, 0xd2, + 0xb3, 0xc3, 0xde, 0xc2, 0x97, 0xbf, 0x95, 0xb4, 0xcd, 0xb1, + 0x00, 0xa2, 0x00, 0x99, 0x00, 0xc5, +}; + +static const unsigned char s6e8ax0_22_gamma_190[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb9, 0xbe, 0xb2, 0xd2, + 0xb2, 0xc3, 0xdd, 0xc3, 0x98, 0xbf, 0x95, 0xb2, 0xcc, 0xaf, + 0x00, 0xa5, 0x00, 0x9c, 0x00, 0xc9, +}; + +static const unsigned char s6e8ax0_22_gamma_200[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb9, 0xbc, 0xb2, 0xd2, + 0xb1, 0xc4, 0xdd, 0xc3, 0x97, 0xbe, 0x95, 0xb1, 0xcb, 0xae, + 0x00, 0xa8, 0x00, 0x9f, 0x00, 0xcd, +}; + +static const unsigned char s6e8ax0_22_gamma_210[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc1, 0xbd, 0xb1, 0xd1, + 0xb1, 0xc2, 0xde, 0xc2, 0x97, 0xbe, 0x94, 0xB0, 0xc9, 0xad, + 0x00, 0xae, 0x00, 0xa4, 0x00, 0xd4, +}; + +static const unsigned char s6e8ax0_22_gamma_220[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc7, 0xbd, 0xb1, 0xd1, + 0xb1, 0xc2, 0xdd, 0xc2, 0x97, 0xbd, 0x94, 0xb0, 0xc9, 0xad, + 0x00, 0xad, 0x00, 0xa2, 0x00, 0xd3, +}; + +static const unsigned char s6e8ax0_22_gamma_230[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc3, 0xbd, 0xb2, 0xd1, + 0xb1, 0xc3, 0xdd, 0xc1, 0x96, 0xbd, 0x94, 0xb0, 0xc9, 0xad, + 0x00, 0xb0, 0x00, 0xa7, 0x00, 0xd7, +}; + +static const unsigned char s6e8ax0_22_gamma_240[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xcb, 0xbd, 0xb1, 0xd2, + 0xb1, 0xc3, 0xdD, 0xc2, 0x95, 0xbd, 0x93, 0xaf, 0xc8, 0xab, + 0x00, 0xb3, 0x00, 0xa9, 0x00, 0xdb, +}; + +static const unsigned char s6e8ax0_22_gamma_250[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xcc, 0xbe, 0xb0, 0xd2, + 0xb0, 0xc3, 0xdD, 0xc2, 0x94, 0xbc, 0x92, 0xae, 0xc8, 0xab, + 0x00, 0xb6, 0x00, 0xab, 0x00, 0xde, +}; + +static const unsigned char s6e8ax0_22_gamma_260[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xd0, 0xbe, 0xaf, 0xd1, + 0xaf, 0xc2, 0xdd, 0xc1, 0x96, 0xbc, 0x93, 0xaf, 0xc8, 0xac, + 0x00, 0xb7, 0x00, 0xad, 0x00, 0xe0, +}; + +static const unsigned char s6e8ax0_22_gamma_270[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb2, 0xcF, 0xbd, 0xb0, 0xd2, + 0xaf, 0xc2, 0xdc, 0xc1, 0x95, 0xbd, 0x93, 0xae, 0xc6, 0xaa, + 0x00, 0xba, 0x00, 0xb0, 0x00, 0xe4, +}; + +static const unsigned char s6e8ax0_22_gamma_280[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb2, 0xd0, 0xbd, 0xaf, 0xd0, + 0xad, 0xc4, 0xdd, 0xc3, 0x95, 0xbd, 0x93, 0xac, 0xc5, 0xa9, + 0x00, 0xbd, 0x00, 0xb2, 0x00, 0xe7, +}; + +static const unsigned char s6e8ax0_22_gamma_300[] = { + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb5, 0xd3, 0xbd, 0xb1, 0xd2, + 0xb0, 0xc0, 0xdc, 0xc0, 0x94, 0xba, 0x91, 0xac, 0xc5, 0xa9, + 0x00, 0xc2, 0x00, 0xb7, 0x00, 0xed, +}; + +static const unsigned char *s6e8ax0_22_gamma_table[] = { + s6e8ax0_22_gamma_30, + s6e8ax0_22_gamma_50, + s6e8ax0_22_gamma_60, + s6e8ax0_22_gamma_70, + s6e8ax0_22_gamma_80, + s6e8ax0_22_gamma_90, + s6e8ax0_22_gamma_100, + s6e8ax0_22_gamma_120, + s6e8ax0_22_gamma_130, + s6e8ax0_22_gamma_140, + s6e8ax0_22_gamma_150, + s6e8ax0_22_gamma_160, + s6e8ax0_22_gamma_170, + s6e8ax0_22_gamma_180, + s6e8ax0_22_gamma_190, + s6e8ax0_22_gamma_200, + s6e8ax0_22_gamma_210, + s6e8ax0_22_gamma_220, + s6e8ax0_22_gamma_230, + s6e8ax0_22_gamma_240, + s6e8ax0_22_gamma_250, + s6e8ax0_22_gamma_260, + s6e8ax0_22_gamma_270, + s6e8ax0_22_gamma_280, + s6e8ax0_22_gamma_300, +}; + +static void s6e8ax0_panel_cond(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + + static const unsigned char data_to_send[] = { + 0xf8, 0x3d, 0x35, 0x00, 0x00, 0x00, 0x93, 0x00, 0x3c, 0x7d, + 0x08, 0x27, 0x7d, 0x3f, 0x00, 0x00, 0x00, 0x20, 0x04, 0x08, + 0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, 0x23, 0x23, 0xc0, + 0xc8, 0x08, 0x48, 0xc1, 0x00, 0xc1, 0xff, 0xff, 0xc8 + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_display_cond(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + static const unsigned char data_to_send[] = { + 0xf2, 0x80, 0x03, 0x0d + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +/* Gamma 2.2 Setting (200cd, 7500K, 10MPCD) */ +static void s6e8ax0_gamma_cond(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + unsigned int gamma = lcd->bd->props.brightness; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + s6e8ax0_22_gamma_table[gamma], + GAMMA_TABLE_COUNT); +} + +static void s6e8ax0_gamma_update(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + static const unsigned char data_to_send[] = { + 0xf7, 0x03 + }; + + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE_PARAM, data_to_send, + ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_etc_cond1(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + static const unsigned char data_to_send[] = { + 0xd1, 0xfe, 0x80, 0x00, 0x01, 0x0b, 0x00, 0x00, 0x40, + 0x0d, 0x00, 0x00 + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_etc_cond2(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + static const unsigned char data_to_send[] = { + 0xb6, 0x0c, 0x02, 0x03, 0x32, 0xff, 0x44, 0x44, 0xc0, + 0x00 + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_etc_cond3(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + static const unsigned char data_to_send[] = { + 0xe1, 0x10, 0x1c, 0x17, 0x08, 0x1d + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_etc_cond4(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + static const unsigned char data_to_send[] = { + 0xe2, 0xed, 0x07, 0xc3, 0x13, 0x0d, 0x03 + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_etc_cond5(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + static const unsigned char data_to_send[] = { + 0xf4, 0xcf, 0x0a, 0x12, 0x10, 0x19, 0x33, 0x02 + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} +static void s6e8ax0_etc_cond6(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + static const unsigned char data_to_send[] = { + 0xe3, 0x40 + }; + + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE_PARAM, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_etc_cond7(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + static const unsigned char data_to_send[] = { + 0xe4, 0x00, 0x00, 0x14, 0x80, 0x00, 0x00, 0x00 + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_elvss_set(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + static const unsigned char data_to_send[] = { + 0xb1, 0x04, 0x00 + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_elvss_nvm_set(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + static const unsigned char data_to_send[] = { + 0xd9, 0x5c, 0x20, 0x0c, 0x0f, 0x41, 0x00, 0x10, 0x11, + 0x12, 0xd1, 0x00, 0x00, 0x00, 0x00, 0x80, 0xcb, 0xed, + 0x64, 0xaf + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_sleep_in(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + static const unsigned char data_to_send[] = { + 0x10, 0x00 + }; + + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_sleep_out(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + static const unsigned char data_to_send[] = { + 0x11, 0x00 + }; + + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_display_on(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + static const unsigned char data_to_send[] = { + 0x29, 0x00 + }; + + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_display_off(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + static const unsigned char data_to_send[] = { + 0x28, 0x00 + }; + + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_apply_level2_key(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + static const unsigned char data_to_send[] = { + 0xf0, 0x5a, 0x5a + }; + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_acl_on(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + static const unsigned char data_to_send[] = { + 0xc0, 0x01 + }; + + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +static void s6e8ax0_acl_off(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + static const unsigned char data_to_send[] = { + 0xc0, 0x00 + }; + + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_SHORT_WRITE, + data_to_send, ARRAY_SIZE(data_to_send)); +} + +/* Full white 50% reducing setting */ +static void s6e8ax0_acl_ctrl_set(struct s6e8ax0 *lcd) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + /* Full white 50% reducing setting */ + static const unsigned char cutoff_50[] = { + 0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf, + 0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x08, 0x0f, 0x16, 0x1d, 0x24, 0x2a, 0x31, 0x38, + 0x3f, 0x46 + }; + /* Full white 45% reducing setting */ + static const unsigned char cutoff_45[] = { + 0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf, + 0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x07, 0x0d, 0x13, 0x19, 0x1f, 0x25, 0x2b, 0x31, + 0x37, 0x3d + }; + /* Full white 40% reducing setting */ + static const unsigned char cutoff_40[] = { + 0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf, + 0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x06, 0x0c, 0x11, 0x16, 0x1c, 0x21, 0x26, 0x2b, + 0x31, 0x36 + }; + + if (lcd->acl_enable) { + if (lcd->cur_acl == 0) { + if (lcd->gamma == 0 || lcd->gamma == 1) { + s6e8ax0_acl_off(lcd); + dev_dbg(&lcd->ld->dev, + "cur_acl=%d\n", lcd->cur_acl); + } else + s6e8ax0_acl_on(lcd); + } + switch (lcd->gamma) { + case 0: /* 30cd */ + s6e8ax0_acl_off(lcd); + lcd->cur_acl = 0; + break; + case 1 ... 3: /* 50cd ~ 90cd */ + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_LONG_WRITE, + cutoff_40, + ARRAY_SIZE(cutoff_40)); + lcd->cur_acl = 40; + break; + case 4 ... 7: /* 120cd ~ 210cd */ + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_LONG_WRITE, + cutoff_45, + ARRAY_SIZE(cutoff_45)); + lcd->cur_acl = 45; + break; + case 8 ... 10: /* 220cd ~ 300cd */ + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_LONG_WRITE, + cutoff_50, + ARRAY_SIZE(cutoff_50)); + lcd->cur_acl = 50; + break; + default: + break; + } + } else { + s6e8ax0_acl_off(lcd); + lcd->cur_acl = 0; + dev_dbg(&lcd->ld->dev, "cur_acl = %d\n", lcd->cur_acl); + } +} + +static void s6e8ax0_read_id(struct s6e8ax0 *lcd, u8 *mtp_id) +{ + unsigned int ret; + unsigned int addr = 0xd1; /* MTP ID */ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + + ret = ops->cmd_read(lcd_to_master(lcd), + MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM, + addr, 3, mtp_id); +} + +static int s6e8ax0_panel_init(struct s6e8ax0 *lcd) +{ + s6e8ax0_apply_level2_key(lcd); + s6e8ax0_sleep_out(lcd); + msleep(1); + s6e8ax0_panel_cond(lcd); + s6e8ax0_display_cond(lcd); + s6e8ax0_gamma_cond(lcd); + s6e8ax0_gamma_update(lcd); + + s6e8ax0_etc_cond1(lcd); + s6e8ax0_etc_cond2(lcd); + s6e8ax0_etc_cond3(lcd); + s6e8ax0_etc_cond4(lcd); + s6e8ax0_etc_cond5(lcd); + s6e8ax0_etc_cond6(lcd); + s6e8ax0_etc_cond7(lcd); + + s6e8ax0_elvss_nvm_set(lcd); + s6e8ax0_elvss_set(lcd); + + s6e8ax0_acl_ctrl_set(lcd); + s6e8ax0_acl_on(lcd); + + /* if ID3 value is not 33h, branch private elvss mode */ + msleep(lcd->ddi_pd->power_on_delay); + + return 0; +} + +static int s6e8ax0_update_gamma_ctrl(struct s6e8ax0 *lcd, int brightness) +{ + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + + ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, + s6e8ax0_22_gamma_table[brightness], + ARRAY_SIZE(s6e8ax0_22_gamma_table)); + + /* update gamma table. */ + s6e8ax0_gamma_update(lcd); + lcd->gamma = brightness; + + return 0; +} + +static int s6e8ax0_gamma_ctrl(struct s6e8ax0 *lcd, int gamma) +{ + s6e8ax0_update_gamma_ctrl(lcd, gamma); + + return 0; +} + +static int s6e8ax0_set_power(struct lcd_device *ld, int power) +{ + struct s6e8ax0 *lcd = lcd_get_data(ld); + struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); + int ret = 0; + + if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN && + power != FB_BLANK_NORMAL) { + dev_err(lcd->dev, "power value should be 0, 1 or 4.\n"); + return -EINVAL; + } + + if ((power == FB_BLANK_UNBLANK) && ops->set_blank_mode) { + /* LCD power on */ + if ((POWER_IS_ON(power) && POWER_IS_OFF(lcd->power)) + || (POWER_IS_ON(power) && POWER_IS_NRM(lcd->power))) { + ret = ops->set_blank_mode(lcd_to_master(lcd), power); + if (!ret && lcd->power != power) + lcd->power = power; + } + } else if ((power == FB_BLANK_POWERDOWN) && ops->set_early_blank_mode) { + /* LCD power off */ + if ((POWER_IS_OFF(power) && POWER_IS_ON(lcd->power)) || + (POWER_IS_ON(lcd->power) && POWER_IS_NRM(power))) { + ret = ops->set_early_blank_mode(lcd_to_master(lcd), + power); + if (!ret && lcd->power != power) + lcd->power = power; + } + } + + return ret; +} + +static int s6e8ax0_get_power(struct lcd_device *ld) +{ + struct s6e8ax0 *lcd = lcd_get_data(ld); + + return lcd->power; +} + +static int s6e8ax0_get_brightness(struct backlight_device *bd) +{ + return bd->props.brightness; +} + +static int s6e8ax0_set_brightness(struct backlight_device *bd) +{ + int ret = 0, brightness = bd->props.brightness; + struct s6e8ax0 *lcd = bl_get_data(bd); + + if (brightness < MIN_BRIGHTNESS || + brightness > bd->props.max_brightness) { + dev_err(lcd->dev, "lcd brightness should be %d to %d.\n", + MIN_BRIGHTNESS, MAX_BRIGHTNESS); + return -EINVAL; + } + + ret = s6e8ax0_gamma_ctrl(lcd, brightness); + if (ret) { + dev_err(&bd->dev, "lcd brightness setting failed.\n"); + return -EIO; + } + + return ret; +} + +static struct lcd_ops s6e8ax0_lcd_ops = { + .set_power = s6e8ax0_set_power, + .get_power = s6e8ax0_get_power, +}; + +static const struct backlight_ops s6e8ax0_backlight_ops = { + .get_brightness = s6e8ax0_get_brightness, + .update_status = s6e8ax0_set_brightness, +}; + +static void s6e8ax0_power_on(struct mipi_dsim_lcd_device *dsim_dev, int power) +{ + struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev); + + msleep(lcd->ddi_pd->power_on_delay); + + /* lcd power on */ + if (power) + s6e8ax0_regulator_enable(lcd); + else + s6e8ax0_regulator_disable(lcd); + + msleep(lcd->ddi_pd->reset_delay); + + /* lcd reset */ + if (lcd->ddi_pd->reset) + lcd->ddi_pd->reset(lcd->ld); + msleep(5); +} + +static void s6e8ax0_set_sequence(struct mipi_dsim_lcd_device *dsim_dev) +{ + struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev); + + s6e8ax0_panel_init(lcd); + s6e8ax0_display_on(lcd); + + lcd->power = FB_BLANK_UNBLANK; +} + +static int s6e8ax0_probe(struct mipi_dsim_lcd_device *dsim_dev) +{ + struct s6e8ax0 *lcd; + int ret; + u8 mtp_id[3] = {0, }; + + lcd = kzalloc(sizeof(struct s6e8ax0), GFP_KERNEL); + if (!lcd) { + dev_err(&dsim_dev->dev, "failed to allocate s6e8ax0 structure.\n"); + return -ENOMEM; + } + + lcd->dsim_dev = dsim_dev; + lcd->ddi_pd = (struct lcd_platform_data *)dsim_dev->platform_data; + lcd->dev = &dsim_dev->dev; + + mutex_init(&lcd->lock); + + ret = regulator_bulk_get(lcd->dev, ARRAY_SIZE(supplies), supplies); + if (ret) { + dev_err(lcd->dev, "Failed to get regulators: %d\n", ret); + goto err_lcd_register; + } + + lcd->ld = lcd_device_register("s6e8ax0", lcd->dev, lcd, + &s6e8ax0_lcd_ops); + if (IS_ERR(lcd->ld)) { + dev_err(lcd->dev, "failed to register lcd ops.\n"); + ret = PTR_ERR(lcd->ld); + goto err_lcd_register; + } + + lcd->bd = backlight_device_register("s6e8ax0-bl", lcd->dev, lcd, + &s6e8ax0_backlight_ops, NULL); + if (IS_ERR(lcd->bd)) { + dev_err(lcd->dev, "failed to register backlight ops.\n"); + ret = PTR_ERR(lcd->bd); + goto err_backlight_register; + } + + lcd->bd->props.max_brightness = MAX_BRIGHTNESS; + lcd->bd->props.brightness = MAX_BRIGHTNESS; + + s6e8ax0_read_id(lcd, mtp_id); + if (mtp_id[0] == 0x00) + dev_err(lcd->dev, "read id failed\n"); + + dev_info(lcd->dev, "Read ID : %x, %x, %x\n", + mtp_id[0], mtp_id[1], mtp_id[2]); + + if (mtp_id[2] == 0x33) + dev_info(lcd->dev, + "ID-3 is 0xff does not support dynamic elvss\n"); + else + dev_info(lcd->dev, + "ID-3 is 0x%x support dynamic elvss\n", mtp_id[2]); + + lcd->acl_enable = 1; + lcd->cur_acl = 0; + + dev_set_drvdata(&dsim_dev->dev, lcd); + + dev_dbg(lcd->dev, "probed s6e8ax0 panel driver.\n"); + + return 0; + +err_backlight_register: + lcd_device_unregister(lcd->ld); + +err_lcd_register: + regulator_bulk_free(ARRAY_SIZE(supplies), supplies); + kfree(lcd); + + return ret; +} + +#ifdef CONFIG_PM +static int s6e8ax0_suspend(struct mipi_dsim_lcd_device *dsim_dev) +{ + struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev); + + s6e8ax0_sleep_in(lcd); + msleep(lcd->ddi_pd->power_off_delay); + s6e8ax0_display_off(lcd); + + s6e8ax0_regulator_disable(lcd); + + return 0; +} + +static int s6e8ax0_resume(struct mipi_dsim_lcd_device *dsim_dev) +{ + struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev); + + s6e8ax0_sleep_out(lcd); + msleep(lcd->ddi_pd->power_on_delay); + + s6e8ax0_regulator_enable(lcd); + s6e8ax0_set_sequence(dsim_dev); + + return 0; +} +#else +#define s6e8ax0_suspend NULL +#define s6e8ax0_resume NULL +#endif + +static struct mipi_dsim_lcd_driver s6e8ax0_dsim_ddi_driver = { + .name = "s6e8ax0", + .id = -1, + + .power_on = s6e8ax0_power_on, + .set_sequence = s6e8ax0_set_sequence, + .probe = s6e8ax0_probe, + .suspend = s6e8ax0_suspend, + .resume = s6e8ax0_resume, +}; + +static int s6e8ax0_init(void) +{ + exynos_mipi_dsi_register_lcd_driver(&s6e8ax0_dsim_ddi_driver); + + return 0; +} + +static void s6e8ax0_exit(void) +{ + return; +} + +module_init(s6e8ax0_init); +module_exit(s6e8ax0_exit); + +MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>"); +MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); +MODULE_DESCRIPTION("MIPI-DSI based s6e8ax0 AMOLED LCD Panel Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/exynos/s6e8ax0.h b/drivers/video/exynos/s6e8ax0.h new file mode 100644 index 000000000000..1f1b270484b0 --- /dev/null +++ b/drivers/video/exynos/s6e8ax0.h @@ -0,0 +1,21 @@ +/* linux/drivers/video/backlight/s6e8ax0.h + * + * MIPI-DSI based s6e8ax0 AMOLED LCD Panel definitions. + * + * Copyright (c) 2011 Samsung Electronics + * + * Inki Dae, <inki.dae@samsung.com> + * Donghwa Lee <dh09.lee@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef _S6E8AX0_H +#define _S6E8AX0_H + +extern void s6e8ax0_init(void); + +#endif + diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index ac9141b85356..c6ce416ab587 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -1665,6 +1665,7 @@ static int do_unregister_framebuffer(struct fb_info *fb_info) if (ret) return -EINVAL; + unlink_framebuffer(fb_info); if (fb_info->pixmap.addr && (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT)) kfree(fb_info->pixmap.addr); @@ -1672,7 +1673,6 @@ static int do_unregister_framebuffer(struct fb_info *fb_info) registered_fb[i] = NULL; num_registered_fb--; fb_cleanup_device(fb_info); - device_destroy(fb_class, MKDEV(FB_MAJOR, i)); event.info = fb_info; fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event); @@ -1681,6 +1681,22 @@ static int do_unregister_framebuffer(struct fb_info *fb_info) return 0; } +int unlink_framebuffer(struct fb_info *fb_info) +{ + int i; + + i = fb_info->node; + if (i < 0 || i >= FB_MAX || registered_fb[i] != fb_info) + return -EINVAL; + + if (fb_info->dev) { + device_destroy(fb_class, MKDEV(FB_MAJOR, i)); + fb_info->dev = NULL; + } + return 0; +} +EXPORT_SYMBOL(unlink_framebuffer); + void remove_conflicting_framebuffers(struct apertures_struct *a, const char *name, bool primary) { diff --git a/drivers/video/i740_reg.h b/drivers/video/i740_reg.h new file mode 100644 index 000000000000..91bac76549d7 --- /dev/null +++ b/drivers/video/i740_reg.h @@ -0,0 +1,309 @@ +/************************************************************************** + +Copyright 1998-1999 Precision Insight, Inc., Cedar Park, Texas. +All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sub license, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice (including the +next paragraph) shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. +IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +**************************************************************************/ + +/* + * Authors: + * Kevin E. Martin <kevin@precisioninsight.com> + */ + +/* I/O register offsets */ +#define SRX VGA_SEQ_I +#define GRX VGA_GFX_I +#define ARX VGA_ATT_IW +#define XRX 0x3D6 +#define MRX 0x3D2 + +/* VGA Color Palette Registers */ +#define DACMASK 0x3C6 +#define DACSTATE 0x3C7 +#define DACRX 0x3C7 +#define DACWX 0x3C8 +#define DACDATA 0x3C9 + +/* CRT Controller Registers (CRX) */ +#define START_ADDR_HI 0x0C +#define START_ADDR_LO 0x0D +#define VERT_SYNC_END 0x11 +#define EXT_VERT_TOTAL 0x30 +#define EXT_VERT_DISPLAY 0x31 +#define EXT_VERT_SYNC_START 0x32 +#define EXT_VERT_BLANK_START 0x33 +#define EXT_HORIZ_TOTAL 0x35 +#define EXT_HORIZ_BLANK 0x39 +#define EXT_START_ADDR 0x40 +#define EXT_START_ADDR_ENABLE 0x80 +#define EXT_OFFSET 0x41 +#define EXT_START_ADDR_HI 0x42 +#define INTERLACE_CNTL 0x70 +#define INTERLACE_ENABLE 0x80 +#define INTERLACE_DISABLE 0x00 + +/* Miscellaneous Output Register */ +#define MSR_R 0x3CC +#define MSR_W 0x3C2 +#define IO_ADDR_SELECT 0x01 + +#define MDA_BASE 0x3B0 +#define CGA_BASE 0x3D0 + +/* System Configuration Extension Registers (XRX) */ +#define IO_CTNL 0x09 +#define EXTENDED_ATTR_CNTL 0x02 +#define EXTENDED_CRTC_CNTL 0x01 + +#define ADDRESS_MAPPING 0x0A +#define PACKED_MODE_ENABLE 0x04 +#define LINEAR_MODE_ENABLE 0x02 +#define PAGE_MAPPING_ENABLE 0x01 + +#define BITBLT_CNTL 0x20 +#define COLEXP_MODE 0x30 +#define COLEXP_8BPP 0x00 +#define COLEXP_16BPP 0x10 +#define COLEXP_24BPP 0x20 +#define COLEXP_RESERVED 0x30 +#define CHIP_RESET 0x02 +#define BITBLT_STATUS 0x01 + +#define DISPLAY_CNTL 0x40 +#define VGA_WRAP_MODE 0x02 +#define VGA_WRAP_AT_256KB 0x00 +#define VGA_NO_WRAP 0x02 +#define GUI_MODE 0x01 +#define STANDARD_VGA_MODE 0x00 +#define HIRES_MODE 0x01 + +#define DRAM_ROW_TYPE 0x50 +#define DRAM_ROW_0 0x07 +#define DRAM_ROW_0_SDRAM 0x00 +#define DRAM_ROW_0_EMPTY 0x07 +#define DRAM_ROW_1 0x38 +#define DRAM_ROW_1_SDRAM 0x00 +#define DRAM_ROW_1_EMPTY 0x38 +#define DRAM_ROW_CNTL_LO 0x51 +#define DRAM_CAS_LATENCY 0x10 +#define DRAM_RAS_TIMING 0x08 +#define DRAM_RAS_PRECHARGE 0x04 +#define DRAM_ROW_CNTL_HI 0x52 +#define DRAM_EXT_CNTL 0x53 +#define DRAM_REFRESH_RATE 0x03 +#define DRAM_REFRESH_DISABLE 0x00 +#define DRAM_REFRESH_60HZ 0x01 +#define DRAM_REFRESH_FAST_TEST 0x02 +#define DRAM_REFRESH_RESERVED 0x03 +#define DRAM_TIMING 0x54 +#define DRAM_ROW_BNDRY_0 0x55 +#define DRAM_ROW_BNDRY_1 0x56 + +#define DPMS_SYNC_SELECT 0x61 +#define VSYNC_CNTL 0x08 +#define VSYNC_ON 0x00 +#define VSYNC_OFF 0x08 +#define HSYNC_CNTL 0x02 +#define HSYNC_ON 0x00 +#define HSYNC_OFF 0x02 + +#define PIXPIPE_CONFIG_0 0x80 +#define DAC_8_BIT 0x80 +#define DAC_6_BIT 0x00 +#define HW_CURSOR_ENABLE 0x10 +#define EXTENDED_PALETTE 0x01 + +#define PIXPIPE_CONFIG_1 0x81 +#define DISPLAY_COLOR_MODE 0x0F +#define DISPLAY_VGA_MODE 0x00 +#define DISPLAY_8BPP_MODE 0x02 +#define DISPLAY_15BPP_MODE 0x04 +#define DISPLAY_16BPP_MODE 0x05 +#define DISPLAY_24BPP_MODE 0x06 +#define DISPLAY_32BPP_MODE 0x07 + +#define PIXPIPE_CONFIG_2 0x82 +#define DISPLAY_GAMMA_ENABLE 0x08 +#define DISPLAY_GAMMA_DISABLE 0x00 +#define OVERLAY_GAMMA_ENABLE 0x04 +#define OVERLAY_GAMMA_DISABLE 0x00 + +#define CURSOR_CONTROL 0xA0 +#define CURSOR_ORIGIN_SCREEN 0x00 +#define CURSOR_ORIGIN_DISPLAY 0x10 +#define CURSOR_MODE 0x07 +#define CURSOR_MODE_DISABLE 0x00 +#define CURSOR_MODE_32_4C_AX 0x01 +#define CURSOR_MODE_128_2C 0x02 +#define CURSOR_MODE_128_1C 0x03 +#define CURSOR_MODE_64_3C 0x04 +#define CURSOR_MODE_64_4C_AX 0x05 +#define CURSOR_MODE_64_4C 0x06 +#define CURSOR_MODE_RESERVED 0x07 +#define CURSOR_BASEADDR_LO 0xA2 +#define CURSOR_BASEADDR_HI 0xA3 +#define CURSOR_X_LO 0xA4 +#define CURSOR_X_HI 0xA5 +#define CURSOR_X_POS 0x00 +#define CURSOR_X_NEG 0x80 +#define CURSOR_Y_LO 0xA6 +#define CURSOR_Y_HI 0xA7 +#define CURSOR_Y_POS 0x00 +#define CURSOR_Y_NEG 0x80 + +#define VCLK2_VCO_M 0xC8 +#define VCLK2_VCO_N 0xC9 +#define VCLK2_VCO_MN_MSBS 0xCA +#define VCO_N_MSBS 0x30 +#define VCO_M_MSBS 0x03 +#define VCLK2_VCO_DIV_SEL 0xCB +#define POST_DIV_SELECT 0x70 +#define POST_DIV_1 0x00 +#define POST_DIV_2 0x10 +#define POST_DIV_4 0x20 +#define POST_DIV_8 0x30 +#define POST_DIV_16 0x40 +#define POST_DIV_32 0x50 +#define VCO_LOOP_DIV_BY_4M 0x00 +#define VCO_LOOP_DIV_BY_16M 0x04 +#define REF_CLK_DIV_BY_5 0x02 +#define REF_DIV_4 0x00 +#define REF_DIV_1 0x01 + +#define PLL_CNTL 0xCE +#define PLL_MEMCLK_SEL 0x03 +#define PLL_MEMCLK__66667KHZ 0x00 +#define PLL_MEMCLK__75000KHZ 0x01 +#define PLL_MEMCLK__88889KHZ 0x02 +#define PLL_MEMCLK_100000KHZ 0x03 + +/* Multimedia Extension Registers (MRX) */ +#define ACQ_CNTL_1 0x02 +#define ACQ_CNTL_2 0x03 +#define FRAME_CAP_MODE 0x01 +#define CONT_CAP_MODE 0x00 +#define SINGLE_CAP_MODE 0x01 +#define ACQ_CNTL_3 0x04 +#define COL_KEY_CNTL_1 0x3C +#define BLANK_DISP_OVERLAY 0x20 + +/* FIFOs */ +#define LP_FIFO 0x1000 +#define HP_FIFO 0x2000 +#define INSTPNT 0x3040 +#define LP_FIFO_COUNT 0x3040 +#define HP_FIFO_COUNT 0x3041 + +/* FIFO Commands */ +#define CLIENT 0xE0000000 +#define CLIENT_2D 0x60000000 + +/* Command Parser Mode Register */ +#define COMPARS 0x3038 +#define TWO_D_INST_DISABLE 0x08 +#define THREE_D_INST_DISABLE 0x04 +#define STATE_VAR_UPDATE_DISABLE 0x02 +#define PAL_STIP_DISABLE 0x01 + +/* Interrupt Control Registers */ +#define IER 0x3030 +#define IIR 0x3032 +#define IMR 0x3034 +#define ISR 0x3036 +#define VMIINTB_EVENT 0x2000 +#define GPIO4_INT 0x1000 +#define DISP_FLIP_EVENT 0x0800 +#define DVD_PORT_DMA 0x0400 +#define DISP_VBLANK 0x0200 +#define FIFO_EMPTY_DMA_DONE 0x0100 +#define INST_PARSER_ERROR 0x0080 +#define USER_DEFINED 0x0040 +#define BREAKPOINT 0x0020 +#define DISP_HORIZ_COUNT 0x0010 +#define DISP_VSYNC 0x0008 +#define CAPTURE_HORIZ_COUNT 0x0004 +#define CAPTURE_VSYNC 0x0002 +#define THREE_D_PIPE_FLUSHED 0x0001 + +/* FIFO Watermark and Burst Length Control Register */ +#define FWATER_BLC 0x00006000 +#define LMI_BURST_LENGTH 0x7F000000 +#define LMI_FIFO_WATERMARK 0x003F0000 +#define AGP_BURST_LENGTH 0x00007F00 +#define AGP_FIFO_WATERMARK 0x0000003F + +/* BitBLT Registers */ +#define SRC_DST_PITCH 0x00040000 +#define DST_PITCH 0x1FFF0000 +#define SRC_PITCH 0x00001FFF +#define COLEXP_BG_COLOR 0x00040004 +#define COLEXP_FG_COLOR 0x00040008 +#define MONO_SRC_CNTL 0x0004000C +#define MONO_USE_COLEXP 0x00000000 +#define MONO_USE_SRCEXP 0x08000000 +#define MONO_DATA_ALIGN 0x07000000 +#define MONO_BIT_ALIGN 0x01000000 +#define MONO_BYTE_ALIGN 0x02000000 +#define MONO_WORD_ALIGN 0x03000000 +#define MONO_DWORD_ALIGN 0x04000000 +#define MONO_QWORD_ALIGN 0x05000000 +#define MONO_SRC_INIT_DSCRD 0x003F0000 +#define MONO_SRC_RIGHT_CLIP 0x00003F00 +#define MONO_SRC_LEFT_CLIP 0x0000003F +#define BITBLT_CONTROL 0x00040010 +#define BLTR_STATUS 0x80000000 +#define DYN_DEPTH 0x03000000 +#define DYN_DEPTH_8BPP 0x00000000 +#define DYN_DEPTH_16BPP 0x01000000 +#define DYN_DEPTH_24BPP 0x02000000 +#define DYN_DEPTH_32BPP 0x03000000 /* Unimplemented on the i740 */ +#define DYN_DEPTH_ENABLE 0x00800000 +#define PAT_VERT_ALIGN 0x00700000 +#define SOLID_PAT_SELECT 0x00080000 +#define PAT_IS_IN_COLOR 0x00000000 +#define PAT_IS_MONO 0x00040000 +#define MONO_PAT_TRANSP 0x00020000 +#define COLOR_TRANSP_ROP 0x00000000 +#define COLOR_TRANSP_DST 0x00008000 +#define COLOR_TRANSP_EQ 0x00000000 +#define COLOR_TRANSP_NOT_EQ 0x00010000 +#define COLOR_TRANSP_ENABLE 0x00004000 +#define MONO_SRC_TRANSP 0x00002000 +#define SRC_IS_IN_COLOR 0x00000000 +#define SRC_IS_MONO 0x00001000 +#define SRC_USE_SRC_ADDR 0x00000000 +#define SRC_USE_BLTDATA 0x00000400 +#define BLT_TOP_TO_BOT 0x00000000 +#define BLT_BOT_TO_TOP 0x00000200 +#define BLT_LEFT_TO_RIGHT 0x00000000 +#define BLT_RIGHT_TO_LEFT 0x00000100 +#define BLT_ROP 0x000000FF +#define BLT_PAT_ADDR 0x00040014 +#define BLT_SRC_ADDR 0x00040018 +#define BLT_DST_ADDR 0x0004001C +#define BLT_DST_H_W 0x00040020 +#define BLT_DST_HEIGHT 0x1FFF0000 +#define BLT_DST_WIDTH 0x00001FFF +#define SRCEXP_BG_COLOR 0x00040024 +#define SRCEXP_FG_COLOR 0x00040028 +#define BLTDATA 0x00050000 diff --git a/drivers/video/i740fb.c b/drivers/video/i740fb.c new file mode 100644 index 000000000000..fe574d84ed99 --- /dev/null +++ b/drivers/video/i740fb.c @@ -0,0 +1,1337 @@ +/* + * i740fb - framebuffer driver for Intel740 + * Copyright (c) 2011 Ondrej Zary + * + * Based on old i740fb driver (c) 2001-2002 Andrey Ulanov <drey@rt.mipt.ru> + * which was partially based on: + * VGA 16-color framebuffer driver (c) 1999 Ben Pfaff <pfaffben@debian.org> + * and Petr Vandrovec <VANDROVE@vc.cvut.cz> + * i740 driver from XFree86 (c) 1998-1999 Precision Insight, Inc., Cedar Park, + * Texas. + * i740fb by Patrick LERDA, v0.9 + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/pci_ids.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> +#include <linux/console.h> +#include <video/vga.h> + +#ifdef CONFIG_MTRR +#include <asm/mtrr.h> +#endif + +#include "i740_reg.h" + +static char *mode_option __devinitdata; + +#ifdef CONFIG_MTRR +static int mtrr __devinitdata = 1; +#endif + +struct i740fb_par { + unsigned char __iomem *regs; + bool has_sgram; +#ifdef CONFIG_MTRR + int mtrr_reg; +#endif + bool ddc_registered; + struct i2c_adapter ddc_adapter; + struct i2c_algo_bit_data ddc_algo; + u32 pseudo_palette[16]; + struct mutex open_lock; + unsigned int ref_count; + + u8 crtc[VGA_CRT_C]; + u8 atc[VGA_ATT_C]; + u8 gdc[VGA_GFX_C]; + u8 seq[VGA_SEQ_C]; + u8 misc; + u8 vss; + + /* i740 specific registers */ + u8 display_cntl; + u8 pixelpipe_cfg0; + u8 pixelpipe_cfg1; + u8 pixelpipe_cfg2; + u8 video_clk2_m; + u8 video_clk2_n; + u8 video_clk2_mn_msbs; + u8 video_clk2_div_sel; + u8 pll_cntl; + u8 address_mapping; + u8 io_cntl; + u8 bitblt_cntl; + u8 ext_vert_total; + u8 ext_vert_disp_end; + u8 ext_vert_sync_start; + u8 ext_vert_blank_start; + u8 ext_horiz_total; + u8 ext_horiz_blank; + u8 ext_offset; + u8 interlace_cntl; + u32 lmi_fifo_watermark; + u8 ext_start_addr; + u8 ext_start_addr_hi; +}; + +#define DACSPEED8 203 +#define DACSPEED16 163 +#define DACSPEED24_SG 136 +#define DACSPEED24_SD 128 +#define DACSPEED32 86 + +static struct fb_fix_screeninfo i740fb_fix __devinitdata = { + .id = "i740fb", + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_TRUECOLOR, + .xpanstep = 8, + .ypanstep = 1, + .accel = FB_ACCEL_NONE, +}; + +static inline void i740outb(struct i740fb_par *par, u16 port, u8 val) +{ + vga_mm_w(par->regs, port, val); +} +static inline u8 i740inb(struct i740fb_par *par, u16 port) +{ + return vga_mm_r(par->regs, port); +} +static inline void i740outreg(struct i740fb_par *par, u16 port, u8 reg, u8 val) +{ + vga_mm_w_fast(par->regs, port, reg, val); +} +static inline u8 i740inreg(struct i740fb_par *par, u16 port, u8 reg) +{ + vga_mm_w(par->regs, port, reg); + return vga_mm_r(par->regs, port+1); +} +static inline void i740outreg_mask(struct i740fb_par *par, u16 port, u8 reg, + u8 val, u8 mask) +{ + vga_mm_w_fast(par->regs, port, reg, (val & mask) + | (i740inreg(par, port, reg) & ~mask)); +} + +#define REG_DDC_DRIVE 0x62 +#define REG_DDC_STATE 0x63 +#define DDC_SCL (1 << 3) +#define DDC_SDA (1 << 2) + +static void i740fb_ddc_setscl(void *data, int val) +{ + struct i740fb_par *par = data; + + i740outreg_mask(par, XRX, REG_DDC_DRIVE, DDC_SCL, DDC_SCL); + i740outreg_mask(par, XRX, REG_DDC_STATE, val ? DDC_SCL : 0, DDC_SCL); +} + +static void i740fb_ddc_setsda(void *data, int val) +{ + struct i740fb_par *par = data; + + i740outreg_mask(par, XRX, REG_DDC_DRIVE, DDC_SDA, DDC_SDA); + i740outreg_mask(par, XRX, REG_DDC_STATE, val ? DDC_SDA : 0, DDC_SDA); +} + +static int i740fb_ddc_getscl(void *data) +{ + struct i740fb_par *par = data; + + i740outreg_mask(par, XRX, REG_DDC_DRIVE, 0, DDC_SCL); + + return !!(i740inreg(par, XRX, REG_DDC_STATE) & DDC_SCL); +} + +static int i740fb_ddc_getsda(void *data) +{ + struct i740fb_par *par = data; + + i740outreg_mask(par, XRX, REG_DDC_DRIVE, 0, DDC_SDA); + + return !!(i740inreg(par, XRX, REG_DDC_STATE) & DDC_SDA); +} + +static int __devinit i740fb_setup_ddc_bus(struct fb_info *info) +{ + struct i740fb_par *par = info->par; + + strlcpy(par->ddc_adapter.name, info->fix.id, + sizeof(par->ddc_adapter.name)); + par->ddc_adapter.owner = THIS_MODULE; + par->ddc_adapter.class = I2C_CLASS_DDC; + par->ddc_adapter.algo_data = &par->ddc_algo; + par->ddc_adapter.dev.parent = info->device; + par->ddc_algo.setsda = i740fb_ddc_setsda; + par->ddc_algo.setscl = i740fb_ddc_setscl; + par->ddc_algo.getsda = i740fb_ddc_getsda; + par->ddc_algo.getscl = i740fb_ddc_getscl; + par->ddc_algo.udelay = 10; + par->ddc_algo.timeout = 20; + par->ddc_algo.data = par; + + i2c_set_adapdata(&par->ddc_adapter, par); + + return i2c_bit_add_bus(&par->ddc_adapter); +} + +static int i740fb_open(struct fb_info *info, int user) +{ + struct i740fb_par *par = info->par; + + mutex_lock(&(par->open_lock)); + par->ref_count++; + mutex_unlock(&(par->open_lock)); + + return 0; +} + +static int i740fb_release(struct fb_info *info, int user) +{ + struct i740fb_par *par = info->par; + + mutex_lock(&(par->open_lock)); + if (par->ref_count == 0) { + printk(KERN_ERR "fb%d: release called with zero refcount\n", + info->node); + mutex_unlock(&(par->open_lock)); + return -EINVAL; + } + + par->ref_count--; + mutex_unlock(&(par->open_lock)); + + return 0; +} + +static u32 i740_calc_fifo(struct i740fb_par *par, u32 freq, int bpp) +{ + /* + * Would like to calculate these values automatically, but a generic + * algorithm does not seem possible. Note: These FIFO water mark + * values were tested on several cards and seem to eliminate the + * all of the snow and vertical banding, but fine adjustments will + * probably be required for other cards. + */ + + u32 wm; + + switch (bpp) { + case 8: + if (freq > 200) + wm = 0x18120000; + else if (freq > 175) + wm = 0x16110000; + else if (freq > 135) + wm = 0x120E0000; + else + wm = 0x100D0000; + break; + case 15: + case 16: + if (par->has_sgram) { + if (freq > 140) + wm = 0x2C1D0000; + else if (freq > 120) + wm = 0x2C180000; + else if (freq > 100) + wm = 0x24160000; + else if (freq > 90) + wm = 0x18120000; + else if (freq > 50) + wm = 0x16110000; + else if (freq > 32) + wm = 0x13100000; + else + wm = 0x120E0000; + } else { + if (freq > 160) + wm = 0x28200000; + else if (freq > 140) + wm = 0x2A1E0000; + else if (freq > 130) + wm = 0x2B1A0000; + else if (freq > 120) + wm = 0x2C180000; + else if (freq > 100) + wm = 0x24180000; + else if (freq > 90) + wm = 0x18120000; + else if (freq > 50) + wm = 0x16110000; + else if (freq > 32) + wm = 0x13100000; + else + wm = 0x120E0000; + } + break; + case 24: + if (par->has_sgram) { + if (freq > 130) + wm = 0x31200000; + else if (freq > 120) + wm = 0x2E200000; + else if (freq > 100) + wm = 0x2C1D0000; + else if (freq > 80) + wm = 0x25180000; + else if (freq > 64) + wm = 0x24160000; + else if (freq > 49) + wm = 0x18120000; + else if (freq > 32) + wm = 0x16110000; + else + wm = 0x13100000; + } else { + if (freq > 120) + wm = 0x311F0000; + else if (freq > 100) + wm = 0x2C1D0000; + else if (freq > 80) + wm = 0x25180000; + else if (freq > 64) + wm = 0x24160000; + else if (freq > 49) + wm = 0x18120000; + else if (freq > 32) + wm = 0x16110000; + else + wm = 0x13100000; + } + break; + case 32: + if (par->has_sgram) { + if (freq > 80) + wm = 0x2A200000; + else if (freq > 60) + wm = 0x281A0000; + else if (freq > 49) + wm = 0x25180000; + else if (freq > 32) + wm = 0x18120000; + else + wm = 0x16110000; + } else { + if (freq > 80) + wm = 0x29200000; + else if (freq > 60) + wm = 0x281A0000; + else if (freq > 49) + wm = 0x25180000; + else if (freq > 32) + wm = 0x18120000; + else + wm = 0x16110000; + } + break; + } + + return wm; +} + +/* clock calculation from i740fb by Patrick LERDA */ + +#define I740_RFREQ 1000000 +#define TARGET_MAX_N 30 +#define I740_FFIX (1 << 8) +#define I740_RFREQ_FIX (I740_RFREQ / I740_FFIX) +#define I740_REF_FREQ (6667 * I740_FFIX / 100) /* 66.67 MHz */ +#define I740_MAX_VCO_FREQ (450 * I740_FFIX) /* 450 MHz */ + +static void i740_calc_vclk(u32 freq, struct i740fb_par *par) +{ + const u32 err_max = freq / (200 * I740_RFREQ / I740_FFIX); + const u32 err_target = freq / (1000 * I740_RFREQ / I740_FFIX); + u32 err_best = 512 * I740_FFIX; + u32 f_err, f_vco; + int m_best = 0, n_best = 0, p_best = 0, d_best = 0; + int m, n; + + p_best = min(15, ilog2(I740_MAX_VCO_FREQ / (freq / I740_RFREQ_FIX))); + d_best = 0; + f_vco = (freq * (1 << p_best)) / I740_RFREQ_FIX; + freq = freq / I740_RFREQ_FIX; + + n = 2; + do { + n++; + m = ((f_vco * n) / I740_REF_FREQ + 2) / 4; + + if (m < 3) + m = 3; + + { + u32 f_out = (((m * I740_REF_FREQ * (4 << 2 * d_best)) + / n) + ((1 << p_best) / 2)) / (1 << p_best); + + f_err = (freq - f_out); + + if (abs(f_err) < err_max) { + m_best = m; + n_best = n; + err_best = f_err; + } + } + } while ((abs(f_err) >= err_target) && + ((n <= TARGET_MAX_N) || (abs(err_best) > err_max))); + + if (abs(f_err) < err_target) { + m_best = m; + n_best = n; + } + + par->video_clk2_m = (m_best - 2) & 0xFF; + par->video_clk2_n = (n_best - 2) & 0xFF; + par->video_clk2_mn_msbs = ((((n_best - 2) >> 4) & VCO_N_MSBS) + | (((m_best - 2) >> 8) & VCO_M_MSBS)); + par->video_clk2_div_sel = + ((p_best << 4) | (d_best ? 4 : 0) | REF_DIV_1); +} + +static int i740fb_decode_var(const struct fb_var_screeninfo *var, + struct i740fb_par *par, struct fb_info *info) +{ + /* + * Get the video params out of 'var'. + * If a value doesn't fit, round it up, if it's too big, return -EINVAL. + */ + + u32 xres, right, hslen, left, xtotal; + u32 yres, lower, vslen, upper, ytotal; + u32 vxres, xoffset, vyres, yoffset; + u32 bpp, base, dacspeed24, mem; + u8 r7; + int i; + + dev_dbg(info->device, "decode_var: xres: %i, yres: %i, xres_v: %i, xres_v: %i\n", + var->xres, var->yres, var->xres_virtual, var->xres_virtual); + dev_dbg(info->device, " xoff: %i, yoff: %i, bpp: %i, graysc: %i\n", + var->xoffset, var->yoffset, var->bits_per_pixel, + var->grayscale); + dev_dbg(info->device, " activate: %i, nonstd: %i, vmode: %i\n", + var->activate, var->nonstd, var->vmode); + dev_dbg(info->device, " pixclock: %i, hsynclen:%i, vsynclen:%i\n", + var->pixclock, var->hsync_len, var->vsync_len); + dev_dbg(info->device, " left: %i, right: %i, up:%i, lower:%i\n", + var->left_margin, var->right_margin, var->upper_margin, + var->lower_margin); + + + bpp = var->bits_per_pixel; + switch (bpp) { + case 1 ... 8: + bpp = 8; + if ((1000000 / var->pixclock) > DACSPEED8) { + dev_err(info->device, "requested pixclock %i MHz out of range (max. %i MHz at 8bpp)\n", + 1000000 / var->pixclock, DACSPEED8); + return -EINVAL; + } + break; + case 9 ... 15: + bpp = 15; + case 16: + if ((1000000 / var->pixclock) > DACSPEED16) { + dev_err(info->device, "requested pixclock %i MHz out of range (max. %i MHz at 15/16bpp)\n", + 1000000 / var->pixclock, DACSPEED16); + return -EINVAL; + } + break; + case 17 ... 24: + bpp = 24; + dacspeed24 = par->has_sgram ? DACSPEED24_SG : DACSPEED24_SD; + if ((1000000 / var->pixclock) > dacspeed24) { + dev_err(info->device, "requested pixclock %i MHz out of range (max. %i MHz at 24bpp)\n", + 1000000 / var->pixclock, dacspeed24); + return -EINVAL; + } + break; + case 25 ... 32: + bpp = 32; + if ((1000000 / var->pixclock) > DACSPEED32) { + dev_err(info->device, "requested pixclock %i MHz out of range (max. %i MHz at 32bpp)\n", + 1000000 / var->pixclock, DACSPEED32); + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + xres = ALIGN(var->xres, 8); + vxres = ALIGN(var->xres_virtual, 16); + if (vxres < xres) + vxres = xres; + + xoffset = ALIGN(var->xoffset, 8); + if (xres + xoffset > vxres) + xoffset = vxres - xres; + + left = ALIGN(var->left_margin, 8); + right = ALIGN(var->right_margin, 8); + hslen = ALIGN(var->hsync_len, 8); + + yres = var->yres; + vyres = var->yres_virtual; + if (yres > vyres) + vyres = yres; + + yoffset = var->yoffset; + if (yres + yoffset > vyres) + yoffset = vyres - yres; + + lower = var->lower_margin; + vslen = var->vsync_len; + upper = var->upper_margin; + + mem = vxres * vyres * ((bpp + 1) / 8); + if (mem > info->screen_size) { + dev_err(info->device, "not enough video memory (%d KB requested, %ld KB avaliable)\n", + mem >> 10, info->screen_size >> 10); + return -ENOMEM; + } + + if (yoffset + yres > vyres) + yoffset = vyres - yres; + + xtotal = xres + right + hslen + left; + ytotal = yres + lower + vslen + upper; + + par->crtc[VGA_CRTC_H_TOTAL] = (xtotal >> 3) - 5; + par->crtc[VGA_CRTC_H_DISP] = (xres >> 3) - 1; + par->crtc[VGA_CRTC_H_BLANK_START] = ((xres + right) >> 3) - 1; + par->crtc[VGA_CRTC_H_SYNC_START] = (xres + right) >> 3; + par->crtc[VGA_CRTC_H_SYNC_END] = (((xres + right + hslen) >> 3) & 0x1F) + | ((((xres + right + hslen) >> 3) & 0x20) << 2); + par->crtc[VGA_CRTC_H_BLANK_END] = ((xres + right + hslen) >> 3 & 0x1F) + | 0x80; + + par->crtc[VGA_CRTC_V_TOTAL] = ytotal - 2; + + r7 = 0x10; /* disable linecompare */ + if (ytotal & 0x100) + r7 |= 0x01; + if (ytotal & 0x200) + r7 |= 0x20; + + par->crtc[VGA_CRTC_PRESET_ROW] = 0; + par->crtc[VGA_CRTC_MAX_SCAN] = 0x40; /* 1 scanline, no linecmp */ + if (var->vmode & FB_VMODE_DOUBLE) + par->crtc[VGA_CRTC_MAX_SCAN] |= 0x80; + par->crtc[VGA_CRTC_CURSOR_START] = 0x00; + par->crtc[VGA_CRTC_CURSOR_END] = 0x00; + par->crtc[VGA_CRTC_CURSOR_HI] = 0x00; + par->crtc[VGA_CRTC_CURSOR_LO] = 0x00; + par->crtc[VGA_CRTC_V_DISP_END] = yres-1; + if ((yres-1) & 0x100) + r7 |= 0x02; + if ((yres-1) & 0x200) + r7 |= 0x40; + + par->crtc[VGA_CRTC_V_BLANK_START] = yres + lower - 1; + par->crtc[VGA_CRTC_V_SYNC_START] = yres + lower - 1; + if ((yres + lower - 1) & 0x100) + r7 |= 0x0C; + if ((yres + lower - 1) & 0x200) { + par->crtc[VGA_CRTC_MAX_SCAN] |= 0x20; + r7 |= 0x80; + } + + /* disabled IRQ */ + par->crtc[VGA_CRTC_V_SYNC_END] = + ((yres + lower - 1 + vslen) & 0x0F) & ~0x10; + /* 0x7F for VGA, but some SVGA chips require all 8 bits to be set */ + par->crtc[VGA_CRTC_V_BLANK_END] = (yres + lower - 1 + vslen) & 0xFF; + + par->crtc[VGA_CRTC_UNDERLINE] = 0x00; + par->crtc[VGA_CRTC_MODE] = 0xC3 ; + par->crtc[VGA_CRTC_LINE_COMPARE] = 0xFF; + par->crtc[VGA_CRTC_OVERFLOW] = r7; + + par->vss = 0x00; /* 3DA */ + + for (i = 0x00; i < 0x10; i++) + par->atc[i] = i; + par->atc[VGA_ATC_MODE] = 0x81; + par->atc[VGA_ATC_OVERSCAN] = 0x00; /* 0 for EGA, 0xFF for VGA */ + par->atc[VGA_ATC_PLANE_ENABLE] = 0x0F; + par->atc[VGA_ATC_COLOR_PAGE] = 0x00; + + par->misc = 0xC3; + if (var->sync & FB_SYNC_HOR_HIGH_ACT) + par->misc &= ~0x40; + if (var->sync & FB_SYNC_VERT_HIGH_ACT) + par->misc &= ~0x80; + + par->seq[VGA_SEQ_CLOCK_MODE] = 0x01; + par->seq[VGA_SEQ_PLANE_WRITE] = 0x0F; + par->seq[VGA_SEQ_CHARACTER_MAP] = 0x00; + par->seq[VGA_SEQ_MEMORY_MODE] = 0x06; + + par->gdc[VGA_GFX_SR_VALUE] = 0x00; + par->gdc[VGA_GFX_SR_ENABLE] = 0x00; + par->gdc[VGA_GFX_COMPARE_VALUE] = 0x00; + par->gdc[VGA_GFX_DATA_ROTATE] = 0x00; + par->gdc[VGA_GFX_PLANE_READ] = 0; + par->gdc[VGA_GFX_MODE] = 0x02; + par->gdc[VGA_GFX_MISC] = 0x05; + par->gdc[VGA_GFX_COMPARE_MASK] = 0x0F; + par->gdc[VGA_GFX_BIT_MASK] = 0xFF; + + base = (yoffset * vxres + (xoffset & ~7)) >> 2; + switch (bpp) { + case 8: + par->crtc[VGA_CRTC_OFFSET] = vxres >> 3; + par->ext_offset = vxres >> 11; + par->pixelpipe_cfg1 = DISPLAY_8BPP_MODE; + par->bitblt_cntl = COLEXP_8BPP; + break; + case 15: /* 0rrrrrgg gggbbbbb */ + case 16: /* rrrrrggg gggbbbbb */ + par->pixelpipe_cfg1 = (var->green.length == 6) ? + DISPLAY_16BPP_MODE : DISPLAY_15BPP_MODE; + par->crtc[VGA_CRTC_OFFSET] = vxres >> 2; + par->ext_offset = vxres >> 10; + par->bitblt_cntl = COLEXP_16BPP; + base *= 2; + break; + case 24: + par->crtc[VGA_CRTC_OFFSET] = (vxres * 3) >> 3; + par->ext_offset = (vxres * 3) >> 11; + par->pixelpipe_cfg1 = DISPLAY_24BPP_MODE; + par->bitblt_cntl = COLEXP_24BPP; + base &= 0xFFFFFFFE; /* ...ignore the last bit. */ + base *= 3; + break; + case 32: + par->crtc[VGA_CRTC_OFFSET] = vxres >> 1; + par->ext_offset = vxres >> 9; + par->pixelpipe_cfg1 = DISPLAY_32BPP_MODE; + par->bitblt_cntl = COLEXP_RESERVED; /* Unimplemented on i740 */ + base *= 4; + break; + } + + par->crtc[VGA_CRTC_START_LO] = base & 0x000000FF; + par->crtc[VGA_CRTC_START_HI] = (base & 0x0000FF00) >> 8; + par->ext_start_addr = + ((base & 0x003F0000) >> 16) | EXT_START_ADDR_ENABLE; + par->ext_start_addr_hi = (base & 0x3FC00000) >> 22; + + par->pixelpipe_cfg0 = DAC_8_BIT; + + par->pixelpipe_cfg2 = DISPLAY_GAMMA_ENABLE | OVERLAY_GAMMA_ENABLE; + par->io_cntl = EXTENDED_CRTC_CNTL; + par->address_mapping = LINEAR_MODE_ENABLE | PAGE_MAPPING_ENABLE; + par->display_cntl = HIRES_MODE; + + /* Set the MCLK freq */ + par->pll_cntl = PLL_MEMCLK_100000KHZ; /* 100 MHz -- use as default */ + + /* Calculate the extended CRTC regs */ + par->ext_vert_total = (ytotal - 2) >> 8; + par->ext_vert_disp_end = (yres - 1) >> 8; + par->ext_vert_sync_start = (yres + lower) >> 8; + par->ext_vert_blank_start = (yres + lower) >> 8; + par->ext_horiz_total = ((xtotal >> 3) - 5) >> 8; + par->ext_horiz_blank = (((xres + right) >> 3) & 0x40) >> 6; + + par->interlace_cntl = INTERLACE_DISABLE; + + /* Set the overscan color to 0. (NOTE: This only affects >8bpp mode) */ + par->atc[VGA_ATC_OVERSCAN] = 0; + + /* Calculate VCLK that most closely matches the requested dot clock */ + i740_calc_vclk((((u32)1e9) / var->pixclock) * (u32)(1e3), par); + + /* Since we program the clocks ourselves, always use VCLK2. */ + par->misc |= 0x0C; + + /* Calculate the FIFO Watermark and Burst Length. */ + par->lmi_fifo_watermark = + i740_calc_fifo(par, 1000000 / var->pixclock, bpp); + + return 0; +} + +static int i740fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + switch (var->bits_per_pixel) { + case 8: + var->red.offset = var->green.offset = var->blue.offset = 0; + var->red.length = var->green.length = var->blue.length = 8; + break; + case 16: + switch (var->green.length) { + default: + case 5: + var->red.offset = 10; + var->green.offset = 5; + var->blue.offset = 0; + var->red.length = 5; + var->green.length = 5; + var->blue.length = 5; + break; + case 6: + var->red.offset = 11; + var->green.offset = 5; + var->blue.offset = 0; + var->red.length = var->blue.length = 5; + break; + } + break; + case 24: + var->red.offset = 16; + var->green.offset = 8; + var->blue.offset = 0; + var->red.length = var->green.length = var->blue.length = 8; + break; + case 32: + var->transp.offset = 24; + var->red.offset = 16; + var->green.offset = 8; + var->blue.offset = 0; + var->transp.length = 8; + var->red.length = var->green.length = var->blue.length = 8; + break; + default: + return -EINVAL; + } + + if (var->xres > var->xres_virtual) + var->xres_virtual = var->xres; + + if (var->yres > var->yres_virtual) + var->yres_virtual = var->yres; + + if (info->monspecs.hfmax && info->monspecs.vfmax && + info->monspecs.dclkmax && fb_validate_mode(var, info) < 0) + return -EINVAL; + + return 0; +} + +static void vga_protect(struct i740fb_par *par) +{ + /* disable the display */ + i740outreg_mask(par, VGA_SEQ_I, VGA_SEQ_CLOCK_MODE, 0x20, 0x20); + + i740inb(par, 0x3DA); + i740outb(par, VGA_ATT_W, 0x00); /* enable pallete access */ +} + +static void vga_unprotect(struct i740fb_par *par) +{ + /* reenable display */ + i740outreg_mask(par, VGA_SEQ_I, VGA_SEQ_CLOCK_MODE, 0, 0x20); + + i740inb(par, 0x3DA); + i740outb(par, VGA_ATT_W, 0x20); /* disable pallete access */ +} + +static int i740fb_set_par(struct fb_info *info) +{ + struct i740fb_par *par = info->par; + u32 itemp; + int i; + + i = i740fb_decode_var(&info->var, par, info); + if (i) + return i; + + memset(info->screen_base, 0, info->screen_size); + + vga_protect(par); + + i740outreg(par, XRX, DRAM_EXT_CNTL, DRAM_REFRESH_DISABLE); + + mdelay(1); + + i740outreg(par, XRX, VCLK2_VCO_M, par->video_clk2_m); + i740outreg(par, XRX, VCLK2_VCO_N, par->video_clk2_n); + i740outreg(par, XRX, VCLK2_VCO_MN_MSBS, par->video_clk2_mn_msbs); + i740outreg(par, XRX, VCLK2_VCO_DIV_SEL, par->video_clk2_div_sel); + + i740outreg_mask(par, XRX, PIXPIPE_CONFIG_0, + par->pixelpipe_cfg0 & DAC_8_BIT, 0x80); + + i740inb(par, 0x3DA); + i740outb(par, 0x3C0, 0x00); + + /* update misc output register */ + i740outb(par, VGA_MIS_W, par->misc | 0x01); + + /* synchronous reset on */ + i740outreg(par, VGA_SEQ_I, VGA_SEQ_RESET, 0x01); + /* write sequencer registers */ + i740outreg(par, VGA_SEQ_I, VGA_SEQ_CLOCK_MODE, + par->seq[VGA_SEQ_CLOCK_MODE] | 0x20); + for (i = 2; i < VGA_SEQ_C; i++) + i740outreg(par, VGA_SEQ_I, i, par->seq[i]); + + /* synchronous reset off */ + i740outreg(par, VGA_SEQ_I, VGA_SEQ_RESET, 0x03); + + /* deprotect CRT registers 0-7 */ + i740outreg(par, VGA_CRT_IC, VGA_CRTC_V_SYNC_END, + par->crtc[VGA_CRTC_V_SYNC_END]); + + /* write CRT registers */ + for (i = 0; i < VGA_CRT_C; i++) + i740outreg(par, VGA_CRT_IC, i, par->crtc[i]); + + /* write graphics controller registers */ + for (i = 0; i < VGA_GFX_C; i++) + i740outreg(par, VGA_GFX_I, i, par->gdc[i]); + + /* write attribute controller registers */ + for (i = 0; i < VGA_ATT_C; i++) { + i740inb(par, VGA_IS1_RC); /* reset flip-flop */ + i740outb(par, VGA_ATT_IW, i); + i740outb(par, VGA_ATT_IW, par->atc[i]); + } + + i740inb(par, VGA_IS1_RC); + i740outb(par, VGA_ATT_IW, 0x20); + + i740outreg(par, VGA_CRT_IC, EXT_VERT_TOTAL, par->ext_vert_total); + i740outreg(par, VGA_CRT_IC, EXT_VERT_DISPLAY, par->ext_vert_disp_end); + i740outreg(par, VGA_CRT_IC, EXT_VERT_SYNC_START, + par->ext_vert_sync_start); + i740outreg(par, VGA_CRT_IC, EXT_VERT_BLANK_START, + par->ext_vert_blank_start); + i740outreg(par, VGA_CRT_IC, EXT_HORIZ_TOTAL, par->ext_horiz_total); + i740outreg(par, VGA_CRT_IC, EXT_HORIZ_BLANK, par->ext_horiz_blank); + i740outreg(par, VGA_CRT_IC, EXT_OFFSET, par->ext_offset); + i740outreg(par, VGA_CRT_IC, EXT_START_ADDR_HI, par->ext_start_addr_hi); + i740outreg(par, VGA_CRT_IC, EXT_START_ADDR, par->ext_start_addr); + + i740outreg_mask(par, VGA_CRT_IC, INTERLACE_CNTL, + par->interlace_cntl, INTERLACE_ENABLE); + i740outreg_mask(par, XRX, ADDRESS_MAPPING, par->address_mapping, 0x1F); + i740outreg_mask(par, XRX, BITBLT_CNTL, par->bitblt_cntl, COLEXP_MODE); + i740outreg_mask(par, XRX, DISPLAY_CNTL, + par->display_cntl, VGA_WRAP_MODE | GUI_MODE); + i740outreg_mask(par, XRX, PIXPIPE_CONFIG_0, par->pixelpipe_cfg0, 0x9B); + i740outreg_mask(par, XRX, PIXPIPE_CONFIG_2, par->pixelpipe_cfg2, 0x0C); + + i740outreg(par, XRX, PLL_CNTL, par->pll_cntl); + + i740outreg_mask(par, XRX, PIXPIPE_CONFIG_1, + par->pixelpipe_cfg1, DISPLAY_COLOR_MODE); + + itemp = readl(par->regs + FWATER_BLC); + itemp &= ~(LMI_BURST_LENGTH | LMI_FIFO_WATERMARK); + itemp |= par->lmi_fifo_watermark; + writel(itemp, par->regs + FWATER_BLC); + + i740outreg(par, XRX, DRAM_EXT_CNTL, DRAM_REFRESH_60HZ); + + i740outreg_mask(par, MRX, COL_KEY_CNTL_1, 0, BLANK_DISP_OVERLAY); + i740outreg_mask(par, XRX, IO_CTNL, + par->io_cntl, EXTENDED_ATTR_CNTL | EXTENDED_CRTC_CNTL); + + if (par->pixelpipe_cfg1 != DISPLAY_8BPP_MODE) { + i740outb(par, VGA_PEL_MSK, 0xFF); + i740outb(par, VGA_PEL_IW, 0x00); + for (i = 0; i < 256; i++) { + itemp = (par->pixelpipe_cfg0 & DAC_8_BIT) ? i : i >> 2; + i740outb(par, VGA_PEL_D, itemp); + i740outb(par, VGA_PEL_D, itemp); + i740outb(par, VGA_PEL_D, itemp); + } + } + + /* Wait for screen to stabilize. */ + mdelay(50); + vga_unprotect(par); + + info->fix.line_length = + info->var.xres_virtual * info->var.bits_per_pixel / 8; + if (info->var.bits_per_pixel == 8) + info->fix.visual = FB_VISUAL_PSEUDOCOLOR; + else + info->fix.visual = FB_VISUAL_TRUECOLOR; + + return 0; +} + +static int i740fb_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, + struct fb_info *info) +{ + u32 r, g, b; + + dev_dbg(info->device, "setcolreg: regno: %i, red=%d, green=%d, blue=%d, transp=%d, bpp=%d\n", + regno, red, green, blue, transp, info->var.bits_per_pixel); + + switch (info->fix.visual) { + case FB_VISUAL_PSEUDOCOLOR: + if (regno >= 256) + return -EINVAL; + i740outb(info->par, VGA_PEL_IW, regno); + i740outb(info->par, VGA_PEL_D, red >> 8); + i740outb(info->par, VGA_PEL_D, green >> 8); + i740outb(info->par, VGA_PEL_D, blue >> 8); + break; + case FB_VISUAL_TRUECOLOR: + if (regno >= 16) + return -EINVAL; + r = (red >> (16 - info->var.red.length)) + << info->var.red.offset; + b = (blue >> (16 - info->var.blue.length)) + << info->var.blue.offset; + g = (green >> (16 - info->var.green.length)) + << info->var.green.offset; + ((u32 *) info->pseudo_palette)[regno] = r | g | b; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int i740fb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct i740fb_par *par = info->par; + u32 base = (var->yoffset * info->var.xres_virtual + + (var->xoffset & ~7)) >> 2; + + dev_dbg(info->device, "pan_display: xoffset: %i yoffset: %i base: %i\n", + var->xoffset, var->yoffset, base); + + switch (info->var.bits_per_pixel) { + case 8: + break; + case 15: + case 16: + base *= 2; + break; + case 24: + /* + * The last bit does not seem to have any effect on the start + * address register in 24bpp mode, so... + */ + base &= 0xFFFFFFFE; /* ...ignore the last bit. */ + base *= 3; + break; + case 32: + base *= 4; + break; + } + + par->crtc[VGA_CRTC_START_LO] = base & 0x000000FF; + par->crtc[VGA_CRTC_START_HI] = (base & 0x0000FF00) >> 8; + par->ext_start_addr_hi = (base & 0x3FC00000) >> 22; + par->ext_start_addr = + ((base & 0x003F0000) >> 16) | EXT_START_ADDR_ENABLE; + + i740outreg(par, VGA_CRT_IC, VGA_CRTC_START_LO, base & 0x000000FF); + i740outreg(par, VGA_CRT_IC, VGA_CRTC_START_HI, + (base & 0x0000FF00) >> 8); + i740outreg(par, VGA_CRT_IC, EXT_START_ADDR_HI, + (base & 0x3FC00000) >> 22); + i740outreg(par, VGA_CRT_IC, EXT_START_ADDR, + ((base & 0x003F0000) >> 16) | EXT_START_ADDR_ENABLE); + + return 0; +} + +static int i740fb_blank(int blank_mode, struct fb_info *info) +{ + struct i740fb_par *par = info->par; + + unsigned char SEQ01; + int DPMSSyncSelect; + + switch (blank_mode) { + case FB_BLANK_UNBLANK: + case FB_BLANK_NORMAL: + SEQ01 = 0x00; + DPMSSyncSelect = HSYNC_ON | VSYNC_ON; + break; + case FB_BLANK_VSYNC_SUSPEND: + SEQ01 = 0x20; + DPMSSyncSelect = HSYNC_ON | VSYNC_OFF; + break; + case FB_BLANK_HSYNC_SUSPEND: + SEQ01 = 0x20; + DPMSSyncSelect = HSYNC_OFF | VSYNC_ON; + break; + case FB_BLANK_POWERDOWN: + SEQ01 = 0x20; + DPMSSyncSelect = HSYNC_OFF | VSYNC_OFF; + break; + default: + return -EINVAL; + } + /* Turn the screen on/off */ + i740outb(par, SRX, 0x01); + SEQ01 |= i740inb(par, SRX + 1) & ~0x20; + i740outb(par, SRX, 0x01); + i740outb(par, SRX + 1, SEQ01); + + /* Set the DPMS mode */ + i740outreg(par, XRX, DPMS_SYNC_SELECT, DPMSSyncSelect); + + /* Let fbcon do a soft blank for us */ + return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0; +} + +static struct fb_ops i740fb_ops = { + .owner = THIS_MODULE, + .fb_open = i740fb_open, + .fb_release = i740fb_release, + .fb_check_var = i740fb_check_var, + .fb_set_par = i740fb_set_par, + .fb_setcolreg = i740fb_setcolreg, + .fb_blank = i740fb_blank, + .fb_pan_display = i740fb_pan_display, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + +/* ------------------------------------------------------------------------- */ + +static int __devinit i740fb_probe(struct pci_dev *dev, + const struct pci_device_id *ent) +{ + struct fb_info *info; + struct i740fb_par *par; + int ret, tmp; + bool found = false; + u8 *edid; + + info = framebuffer_alloc(sizeof(struct i740fb_par), &(dev->dev)); + if (!info) { + dev_err(&(dev->dev), "cannot allocate framebuffer\n"); + return -ENOMEM; + } + + par = info->par; + mutex_init(&par->open_lock); + + info->var.activate = FB_ACTIVATE_NOW; + info->var.bits_per_pixel = 8; + info->fbops = &i740fb_ops; + info->pseudo_palette = par->pseudo_palette; + + ret = pci_enable_device(dev); + if (ret) { + dev_err(info->device, "cannot enable PCI device\n"); + goto err_enable_device; + } + + ret = pci_request_regions(dev, info->fix.id); + if (ret) { + dev_err(info->device, "error requesting regions\n"); + goto err_request_regions; + } + + info->screen_base = pci_ioremap_bar(dev, 0); + if (!info->screen_base) { + dev_err(info->device, "error remapping base\n"); + ret = -ENOMEM; + goto err_ioremap_1; + } + + par->regs = pci_ioremap_bar(dev, 1); + if (!par->regs) { + dev_err(info->device, "error remapping MMIO\n"); + ret = -ENOMEM; + goto err_ioremap_2; + } + + /* detect memory size */ + if ((i740inreg(par, XRX, DRAM_ROW_TYPE) & DRAM_ROW_1) + == DRAM_ROW_1_SDRAM) + i740outb(par, XRX, DRAM_ROW_BNDRY_1); + else + i740outb(par, XRX, DRAM_ROW_BNDRY_0); + info->screen_size = i740inb(par, XRX + 1) * 1024 * 1024; + /* detect memory type */ + tmp = i740inreg(par, XRX, DRAM_ROW_CNTL_LO); + par->has_sgram = !((tmp & DRAM_RAS_TIMING) || + (tmp & DRAM_RAS_PRECHARGE)); + + printk(KERN_INFO "fb%d: Intel740 on %s, %ld KB %s\n", info->node, + pci_name(dev), info->screen_size >> 10, + par->has_sgram ? "SGRAM" : "SDRAM"); + + info->fix = i740fb_fix; + info->fix.mmio_start = pci_resource_start(dev, 1); + info->fix.mmio_len = pci_resource_len(dev, 1); + info->fix.smem_start = pci_resource_start(dev, 0); + info->fix.smem_len = info->screen_size; + info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN; + + if (i740fb_setup_ddc_bus(info) == 0) { + par->ddc_registered = true; + edid = fb_ddc_read(&par->ddc_adapter); + if (edid) { + fb_edid_to_monspecs(edid, &info->monspecs); + kfree(edid); + if (!info->monspecs.modedb) + dev_err(info->device, + "error getting mode database\n"); + else { + const struct fb_videomode *m; + + fb_videomode_to_modelist( + info->monspecs.modedb, + info->monspecs.modedb_len, + &info->modelist); + m = fb_find_best_display(&info->monspecs, + &info->modelist); + if (m) { + fb_videomode_to_var(&info->var, m); + /* fill all other info->var's fields */ + if (!i740fb_check_var(&info->var, info)) + found = true; + } + } + } + } + + if (!mode_option && !found) + mode_option = "640x480-8@60"; + + if (mode_option) { + ret = fb_find_mode(&info->var, info, mode_option, + info->monspecs.modedb, + info->monspecs.modedb_len, + NULL, info->var.bits_per_pixel); + if (!ret || ret == 4) { + dev_err(info->device, "mode %s not found\n", + mode_option); + ret = -EINVAL; + } + } + + fb_destroy_modedb(info->monspecs.modedb); + info->monspecs.modedb = NULL; + + /* maximize virtual vertical size for fast scrolling */ + info->var.yres_virtual = info->fix.smem_len * 8 / + (info->var.bits_per_pixel * info->var.xres_virtual); + + if (ret == -EINVAL) + goto err_find_mode; + + ret = fb_alloc_cmap(&info->cmap, 256, 0); + if (ret) { + dev_err(info->device, "cannot allocate colormap\n"); + goto err_alloc_cmap; + } + + ret = register_framebuffer(info); + if (ret) { + dev_err(info->device, "error registering framebuffer\n"); + goto err_reg_framebuffer; + } + + printk(KERN_INFO "fb%d: %s frame buffer device\n", + info->node, info->fix.id); + pci_set_drvdata(dev, info); +#ifdef CONFIG_MTRR + if (mtrr) { + par->mtrr_reg = -1; + par->mtrr_reg = mtrr_add(info->fix.smem_start, + info->fix.smem_len, MTRR_TYPE_WRCOMB, 1); + } +#endif + return 0; + +err_reg_framebuffer: + fb_dealloc_cmap(&info->cmap); +err_alloc_cmap: +err_find_mode: + if (par->ddc_registered) + i2c_del_adapter(&par->ddc_adapter); + pci_iounmap(dev, par->regs); +err_ioremap_2: + pci_iounmap(dev, info->screen_base); +err_ioremap_1: + pci_release_regions(dev); +err_request_regions: +/* pci_disable_device(dev); */ +err_enable_device: + framebuffer_release(info); + return ret; +} + +static void __devexit i740fb_remove(struct pci_dev *dev) +{ + struct fb_info *info = pci_get_drvdata(dev); + + if (info) { + struct i740fb_par *par = info->par; + +#ifdef CONFIG_MTRR + if (par->mtrr_reg >= 0) { + mtrr_del(par->mtrr_reg, 0, 0); + par->mtrr_reg = -1; + } +#endif + unregister_framebuffer(info); + fb_dealloc_cmap(&info->cmap); + if (par->ddc_registered) + i2c_del_adapter(&par->ddc_adapter); + pci_iounmap(dev, par->regs); + pci_iounmap(dev, info->screen_base); + pci_release_regions(dev); +/* pci_disable_device(dev); */ + pci_set_drvdata(dev, NULL); + framebuffer_release(info); + } +} + +#ifdef CONFIG_PM +static int i740fb_suspend(struct pci_dev *dev, pm_message_t state) +{ + struct fb_info *info = pci_get_drvdata(dev); + struct i740fb_par *par = info->par; + + /* don't disable console during hibernation and wakeup from it */ + if (state.event == PM_EVENT_FREEZE || state.event == PM_EVENT_PRETHAW) + return 0; + + console_lock(); + mutex_lock(&(par->open_lock)); + + /* do nothing if framebuffer is not active */ + if (par->ref_count == 0) { + mutex_unlock(&(par->open_lock)); + console_unlock(); + return 0; + } + + fb_set_suspend(info, 1); + + pci_save_state(dev); + pci_disable_device(dev); + pci_set_power_state(dev, pci_choose_state(dev, state)); + + mutex_unlock(&(par->open_lock)); + console_unlock(); + + return 0; +} + +static int i740fb_resume(struct pci_dev *dev) +{ + struct fb_info *info = pci_get_drvdata(dev); + struct i740fb_par *par = info->par; + + console_lock(); + mutex_lock(&(par->open_lock)); + + if (par->ref_count == 0) + goto fail; + + pci_set_power_state(dev, PCI_D0); + pci_restore_state(dev); + if (pci_enable_device(dev)) + goto fail; + + i740fb_set_par(info); + fb_set_suspend(info, 0); + +fail: + mutex_unlock(&(par->open_lock)); + console_unlock(); + return 0; +} +#else +#define i740fb_suspend NULL +#define i740fb_resume NULL +#endif /* CONFIG_PM */ + +#define I740_ID_PCI 0x00d1 +#define I740_ID_AGP 0x7800 + +static DEFINE_PCI_DEVICE_TABLE(i740fb_id_table) = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, I740_ID_PCI) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, I740_ID_AGP) }, + { 0 } +}; +MODULE_DEVICE_TABLE(pci, i740fb_id_table); + +static struct pci_driver i740fb_driver = { + .name = "i740fb", + .id_table = i740fb_id_table, + .probe = i740fb_probe, + .remove = __devexit_p(i740fb_remove), + .suspend = i740fb_suspend, + .resume = i740fb_resume, +}; + +#ifndef MODULE +static int __init i740fb_setup(char *options) +{ + char *opt; + + if (!options || !*options) + return 0; + + while ((opt = strsep(&options, ",")) != NULL) { + if (!*opt) + continue; +#ifdef CONFIG_MTRR + else if (!strncmp(opt, "mtrr:", 5)) + mtrr = simple_strtoul(opt + 5, NULL, 0); +#endif + else + mode_option = opt; + } + + return 0; +} +#endif + +int __init i740fb_init(void) +{ +#ifndef MODULE + char *option = NULL; + + if (fb_get_options("i740fb", &option)) + return -ENODEV; + i740fb_setup(option); +#endif + + return pci_register_driver(&i740fb_driver); +} + +static void __exit i740fb_exit(void) +{ + pci_unregister_driver(&i740fb_driver); +} + +module_init(i740fb_init); +module_exit(i740fb_exit); + +MODULE_AUTHOR("(c) 2011 Ondrej Zary <linux@rainbow-software.org>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("fbdev driver for Intel740"); + +module_param(mode_option, charp, 0444); +MODULE_PARM_DESC(mode_option, "Default video mode ('640x480-8@60', etc)"); + +#ifdef CONFIG_MTRR +module_param(mtrr, int, 0444); +MODULE_PARM_DESC(mtrr, "Enable write-combining with MTRR (1=enable, 0=disable, default=1)"); +#endif diff --git a/drivers/video/msm/mddi_client_nt35399.c b/drivers/video/msm/mddi_client_nt35399.c index f239f4a25e01..7fcd67e132bf 100644 --- a/drivers/video/msm/mddi_client_nt35399.c +++ b/drivers/video/msm/mddi_client_nt35399.c @@ -155,14 +155,10 @@ static int setup_vsync(struct panel_info *panel, int init) ret = 0; goto uninit; } - ret = gpio_request(gpio, "vsync"); + ret = gpio_request_one(gpio, GPIOF_IN, "vsync"); if (ret) goto err_request_gpio_failed; - ret = gpio_direction_input(gpio); - if (ret) - goto err_gpio_direction_input_failed; - ret = irq = gpio_to_irq(gpio); if (ret < 0) goto err_get_irq_num_failed; @@ -180,7 +176,6 @@ uninit: free_irq(gpio_to_irq(gpio), panel->client_data); err_request_irq_failed: err_get_irq_num_failed: -err_gpio_direction_input_failed: gpio_free(gpio); err_request_gpio_failed: return ret; diff --git a/drivers/video/msm/mddi_client_toshiba.c b/drivers/video/msm/mddi_client_toshiba.c index f9bc932ac46b..053eb6877330 100644 --- a/drivers/video/msm/mddi_client_toshiba.c +++ b/drivers/video/msm/mddi_client_toshiba.c @@ -186,14 +186,10 @@ static int setup_vsync(struct panel_info *panel, ret = 0; goto uninit; } - ret = gpio_request(gpio, "vsync"); + ret = gpio_request_one(gpio, GPIOF_IN, "vsync"); if (ret) goto err_request_gpio_failed; - ret = gpio_direction_input(gpio); - if (ret) - goto err_gpio_direction_input_failed; - ret = irq = gpio_to_irq(gpio); if (ret < 0) goto err_get_irq_num_failed; @@ -210,7 +206,6 @@ uninit: free_irq(gpio_to_irq(gpio), panel); err_request_irq_failed: err_get_irq_num_failed: -err_gpio_direction_input_failed: gpio_free(gpio); err_request_gpio_failed: return ret; diff --git a/drivers/video/mx3fb.c b/drivers/video/mx3fb.c index 727a5149d818..eec0d7b748eb 100644 --- a/drivers/video/mx3fb.c +++ b/drivers/video/mx3fb.c @@ -337,7 +337,7 @@ static void sdc_enable_channel(struct mx3fb_info *mx3_fbi) /* This enables the channel */ if (mx3_fbi->cookie < 0) { - mx3_fbi->txd = dma_chan->device->device_prep_slave_sg(dma_chan, + mx3_fbi->txd = dmaengine_prep_slave_sg(dma_chan, &mx3_fbi->sg[0], 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT); if (!mx3_fbi->txd) { dev_err(mx3fb->dev, "Cannot allocate descriptor on %d\n", @@ -1091,7 +1091,7 @@ static int mx3fb_pan_display(struct fb_var_screeninfo *var, if (mx3_fbi->txd) async_tx_ack(mx3_fbi->txd); - txd = dma_chan->device->device_prep_slave_sg(dma_chan, sg + + txd = dmaengine_prep_slave_sg(dma_chan, sg + mx3_fbi->cur_ipu_buf, 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT); if (!txd) { dev_err(fbi->device, diff --git a/drivers/video/neofb.c b/drivers/video/neofb.c index fb3f67391105..afc9521173ef 100644 --- a/drivers/video/neofb.c +++ b/drivers/video/neofb.c @@ -71,7 +71,6 @@ #include <asm/io.h> #include <asm/irq.h> #include <asm/pgtable.h> -#include <asm/system.h> #ifdef CONFIG_MTRR #include <asm/mtrr.h> diff --git a/drivers/video/omap/Kconfig b/drivers/video/omap/Kconfig index 84ff23208c25..1e7536d9a8fc 100644 --- a/drivers/video/omap/Kconfig +++ b/drivers/video/omap/Kconfig @@ -1,11 +1,10 @@ config FB_OMAP tristate "OMAP frame buffer support (EXPERIMENTAL)" - depends on FB && (OMAP2_DSS = "n") - depends on ARCH_OMAP1 || ARCH_OMAP2 || ARCH_OMAP3 + depends on FB + depends on ARCH_OMAP1 select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT - select TWL4030_CORE if MACH_OMAP_2430SDP help Frame buffer driver for OMAP based boards. @@ -23,13 +22,6 @@ config FB_OMAP_LCDC_HWA742 Say Y here if you want to have support for the external Epson HWA742 LCD controller. -config FB_OMAP_LCDC_BLIZZARD - bool "Epson Blizzard LCD controller support" - depends on FB_OMAP && FB_OMAP_LCDC_EXTERNAL - help - Say Y here if you want to have support for the external - Epson Blizzard LCD controller. - config FB_OMAP_MANUAL_UPDATE bool "Default to manual update mode" depends on FB_OMAP && FB_OMAP_LCDC_EXTERNAL @@ -49,7 +41,7 @@ config FB_OMAP_LCD_MIPID config FB_OMAP_BOOTLOADER_INIT bool "Check bootloader initialization" - depends on FB_OMAP || FB_OMAP2 + depends on FB_OMAP help Say Y here if you want to enable checking if the bootloader has already initialized the display controller. In this case the @@ -68,7 +60,7 @@ config FB_OMAP_CONSISTENT_DMA_SIZE config FB_OMAP_DMA_TUNE bool "Set DMA SDRAM access priority high" - depends on FB_OMAP && ARCH_OMAP1 + depends on FB_OMAP help On systems in which video memory is in system memory (SDRAM) this will speed up graphics DMA operations. diff --git a/drivers/video/omap/Makefile b/drivers/video/omap/Makefile index ef78550917ff..1927faffb5bc 100644 --- a/drivers/video/omap/Makefile +++ b/drivers/video/omap/Makefile @@ -1,20 +1,14 @@ # -# Makefile for the new OMAP framebuffer device driver +# Makefile for the OMAP1 framebuffer device driver # obj-$(CONFIG_FB_OMAP) += omapfb.o -objs-yy := omapfb_main.o +objs-yy := omapfb_main.o lcdc.o -objs-y$(CONFIG_ARCH_OMAP1) += lcdc.o -objs-y$(CONFIG_ARCH_OMAP2) += dispc.o -objs-y$(CONFIG_ARCH_OMAP3) += dispc.o - -objs-$(CONFIG_ARCH_OMAP1)$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += sossi.o -objs-$(CONFIG_ARCH_OMAP2)$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += rfbi.o +objs-y$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += sossi.o objs-y$(CONFIG_FB_OMAP_LCDC_HWA742) += hwa742.o -objs-y$(CONFIG_FB_OMAP_LCDC_BLIZZARD) += blizzard.o objs-y$(CONFIG_MACH_AMS_DELTA) += lcd_ams_delta.o objs-y$(CONFIG_MACH_OMAP_H3) += lcd_h3.o diff --git a/drivers/video/omap/blizzard.c b/drivers/video/omap/blizzard.c deleted file mode 100644 index c0504a8a5079..000000000000 --- a/drivers/video/omap/blizzard.c +++ /dev/null @@ -1,1648 +0,0 @@ -/* - * Epson Blizzard LCD controller driver - * - * Copyright (C) 2004-2005 Nokia Corporation - * Authors: Juha Yrjola <juha.yrjola@nokia.com> - * Imre Deak <imre.deak@nokia.com> - * YUV support: Jussi Laako <jussi.laako@nokia.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#include <linux/module.h> -#include <linux/mm.h> -#include <linux/fb.h> -#include <linux/delay.h> -#include <linux/clk.h> - -#include <plat/dma.h> -#include <plat/blizzard.h> - -#include "omapfb.h" -#include "dispc.h" - -#define MODULE_NAME "blizzard" - -#define BLIZZARD_REV_CODE 0x00 -#define BLIZZARD_CONFIG 0x02 -#define BLIZZARD_PLL_DIV 0x04 -#define BLIZZARD_PLL_LOCK_RANGE 0x06 -#define BLIZZARD_PLL_CLOCK_SYNTH_0 0x08 -#define BLIZZARD_PLL_CLOCK_SYNTH_1 0x0a -#define BLIZZARD_PLL_MODE 0x0c -#define BLIZZARD_CLK_SRC 0x0e -#define BLIZZARD_MEM_BANK0_ACTIVATE 0x10 -#define BLIZZARD_MEM_BANK0_STATUS 0x14 -#define BLIZZARD_PANEL_CONFIGURATION 0x28 -#define BLIZZARD_HDISP 0x2a -#define BLIZZARD_HNDP 0x2c -#define BLIZZARD_VDISP0 0x2e -#define BLIZZARD_VDISP1 0x30 -#define BLIZZARD_VNDP 0x32 -#define BLIZZARD_HSW 0x34 -#define BLIZZARD_VSW 0x38 -#define BLIZZARD_DISPLAY_MODE 0x68 -#define BLIZZARD_INPUT_WIN_X_START_0 0x6c -#define BLIZZARD_DATA_SOURCE_SELECT 0x8e -#define BLIZZARD_DISP_MEM_DATA_PORT 0x90 -#define BLIZZARD_DISP_MEM_READ_ADDR0 0x92 -#define BLIZZARD_POWER_SAVE 0xE6 -#define BLIZZARD_NDISP_CTRL_STATUS 0xE8 - -/* Data source select */ -/* For S1D13745 */ -#define BLIZZARD_SRC_WRITE_LCD_BACKGROUND 0x00 -#define BLIZZARD_SRC_WRITE_LCD_DESTRUCTIVE 0x01 -#define BLIZZARD_SRC_WRITE_OVERLAY_ENABLE 0x04 -#define BLIZZARD_SRC_DISABLE_OVERLAY 0x05 -/* For S1D13744 */ -#define BLIZZARD_SRC_WRITE_LCD 0x00 -#define BLIZZARD_SRC_BLT_LCD 0x06 - -#define BLIZZARD_COLOR_RGB565 0x01 -#define BLIZZARD_COLOR_YUV420 0x09 - -#define BLIZZARD_VERSION_S1D13745 0x01 /* Hailstorm */ -#define BLIZZARD_VERSION_S1D13744 0x02 /* Blizzard */ - -#define BLIZZARD_AUTO_UPDATE_TIME (HZ / 20) - -/* Reserve 4 request slots for requests in irq context */ -#define REQ_POOL_SIZE 24 -#define IRQ_REQ_POOL_SIZE 4 - -#define REQ_FROM_IRQ_POOL 0x01 - -#define REQ_COMPLETE 0 -#define REQ_PENDING 1 - -struct blizzard_reg_list { - int start; - int end; -}; - -/* These need to be saved / restored separately from the rest. */ -static const struct blizzard_reg_list blizzard_pll_regs[] = { - { - .start = 0x04, /* Don't save PLL ctrl (0x0C) */ - .end = 0x0a, - }, - { - .start = 0x0e, /* Clock configuration */ - .end = 0x0e, - }, -}; - -static const struct blizzard_reg_list blizzard_gen_regs[] = { - { - .start = 0x18, /* SDRAM control */ - .end = 0x20, - }, - { - .start = 0x28, /* LCD Panel configuration */ - .end = 0x5a, /* HSSI interface, TV configuration */ - }, -}; - -static u8 blizzard_reg_cache[0x5a / 2]; - -struct update_param { - int plane; - int x, y, width, height; - int out_x, out_y; - int out_width, out_height; - int color_mode; - int bpp; - int flags; -}; - -struct blizzard_request { - struct list_head entry; - unsigned int flags; - - int (*handler)(struct blizzard_request *req); - void (*complete)(void *data); - void *complete_data; - - union { - struct update_param update; - struct completion *sync; - } par; -}; - -struct plane_info { - unsigned long offset; - int pos_x, pos_y; - int width, height; - int out_width, out_height; - int scr_width; - int color_mode; - int bpp; -}; - -struct blizzard_struct { - enum omapfb_update_mode update_mode; - enum omapfb_update_mode update_mode_before_suspend; - - struct timer_list auto_update_timer; - int stop_auto_update; - struct omapfb_update_window auto_update_window; - int enabled_planes; - int vid_nonstd_color; - int vid_scaled; - int last_color_mode; - int zoom_on; - int zoom_area_gx1; - int zoom_area_gx2; - int zoom_area_gy1; - int zoom_area_gy2; - int screen_width; - int screen_height; - unsigned te_connected:1; - unsigned vsync_only:1; - - struct plane_info plane[OMAPFB_PLANE_NUM]; - - struct blizzard_request req_pool[REQ_POOL_SIZE]; - struct list_head pending_req_list; - struct list_head free_req_list; - struct semaphore req_sema; - spinlock_t req_lock; - - unsigned long sys_ck_rate; - struct extif_timings reg_timings, lut_timings; - - u32 max_transmit_size; - u32 extif_clk_period; - int extif_clk_div; - unsigned long pix_tx_time; - unsigned long line_upd_time; - - struct omapfb_device *fbdev; - struct lcd_ctrl_extif *extif; - const struct lcd_ctrl *int_ctrl; - - void (*power_up)(struct device *dev); - void (*power_down)(struct device *dev); - - int version; -} blizzard; - -struct lcd_ctrl blizzard_ctrl; - -static u8 blizzard_read_reg(u8 reg) -{ - u8 data; - - blizzard.extif->set_bits_per_cycle(8); - blizzard.extif->write_command(®, 1); - blizzard.extif->read_data(&data, 1); - - return data; -} - -static void blizzard_write_reg(u8 reg, u8 val) -{ - blizzard.extif->set_bits_per_cycle(8); - blizzard.extif->write_command(®, 1); - blizzard.extif->write_data(&val, 1); -} - -static void blizzard_restart_sdram(void) -{ - unsigned long tmo; - - blizzard_write_reg(BLIZZARD_MEM_BANK0_ACTIVATE, 0); - udelay(50); - blizzard_write_reg(BLIZZARD_MEM_BANK0_ACTIVATE, 1); - tmo = jiffies + msecs_to_jiffies(200); - while (!(blizzard_read_reg(BLIZZARD_MEM_BANK0_STATUS) & 0x01)) { - if (time_after(jiffies, tmo)) { - dev_err(blizzard.fbdev->dev, - "s1d1374x: SDRAM not ready\n"); - break; - } - msleep(1); - } -} - -static void blizzard_stop_sdram(void) -{ - blizzard_write_reg(BLIZZARD_MEM_BANK0_ACTIVATE, 0); -} - -/* Wait until the last window was completely written into the controllers - * SDRAM and we can start transferring the next window. - */ -static void blizzard_wait_line_buffer(void) -{ - unsigned long tmo = jiffies + msecs_to_jiffies(30); - - while (blizzard_read_reg(BLIZZARD_NDISP_CTRL_STATUS) & (1 << 7)) { - if (time_after(jiffies, tmo)) { - if (printk_ratelimit()) - dev_err(blizzard.fbdev->dev, - "s1d1374x: line buffer not ready\n"); - break; - } - } -} - -/* Wait until the YYC color space converter is idle. */ -static void blizzard_wait_yyc(void) -{ - unsigned long tmo = jiffies + msecs_to_jiffies(30); - - while (blizzard_read_reg(BLIZZARD_NDISP_CTRL_STATUS) & (1 << 4)) { - if (time_after(jiffies, tmo)) { - if (printk_ratelimit()) - dev_err(blizzard.fbdev->dev, - "s1d1374x: YYC not ready\n"); - break; - } - } -} - -static void disable_overlay(void) -{ - blizzard_write_reg(BLIZZARD_DATA_SOURCE_SELECT, - BLIZZARD_SRC_DISABLE_OVERLAY); -} - -static void set_window_regs(int x_start, int y_start, int x_end, int y_end, - int x_out_start, int y_out_start, - int x_out_end, int y_out_end, int color_mode, - int zoom_off, int flags) -{ - u8 tmp[18]; - u8 cmd; - - x_end--; - y_end--; - tmp[0] = x_start; - tmp[1] = x_start >> 8; - tmp[2] = y_start; - tmp[3] = y_start >> 8; - tmp[4] = x_end; - tmp[5] = x_end >> 8; - tmp[6] = y_end; - tmp[7] = y_end >> 8; - - x_out_end--; - y_out_end--; - tmp[8] = x_out_start; - tmp[9] = x_out_start >> 8; - tmp[10] = y_out_start; - tmp[11] = y_out_start >> 8; - tmp[12] = x_out_end; - tmp[13] = x_out_end >> 8; - tmp[14] = y_out_end; - tmp[15] = y_out_end >> 8; - - tmp[16] = color_mode; - if (zoom_off && blizzard.version == BLIZZARD_VERSION_S1D13745) - tmp[17] = BLIZZARD_SRC_WRITE_LCD_BACKGROUND; - else if (flags & OMAPFB_FORMAT_FLAG_ENABLE_OVERLAY) - tmp[17] = BLIZZARD_SRC_WRITE_OVERLAY_ENABLE; - else - tmp[17] = blizzard.version == BLIZZARD_VERSION_S1D13744 ? - BLIZZARD_SRC_WRITE_LCD : - BLIZZARD_SRC_WRITE_LCD_DESTRUCTIVE; - - blizzard.extif->set_bits_per_cycle(8); - cmd = BLIZZARD_INPUT_WIN_X_START_0; - blizzard.extif->write_command(&cmd, 1); - blizzard.extif->write_data(tmp, 18); -} - -static void enable_tearsync(int y, int width, int height, int screen_height, - int out_height, int force_vsync) -{ - u8 b; - - b = blizzard_read_reg(BLIZZARD_NDISP_CTRL_STATUS); - b |= 1 << 3; - blizzard_write_reg(BLIZZARD_NDISP_CTRL_STATUS, b); - - if (likely(blizzard.vsync_only || force_vsync)) { - blizzard.extif->enable_tearsync(1, 0); - return; - } - - if (width * blizzard.pix_tx_time < blizzard.line_upd_time) { - blizzard.extif->enable_tearsync(1, 0); - return; - } - - if ((width * blizzard.pix_tx_time / 1000) * height < - (y + out_height) * (blizzard.line_upd_time / 1000)) { - blizzard.extif->enable_tearsync(1, 0); - return; - } - - blizzard.extif->enable_tearsync(1, y + 1); -} - -static void disable_tearsync(void) -{ - u8 b; - - blizzard.extif->enable_tearsync(0, 0); - b = blizzard_read_reg(BLIZZARD_NDISP_CTRL_STATUS); - b &= ~(1 << 3); - blizzard_write_reg(BLIZZARD_NDISP_CTRL_STATUS, b); - b = blizzard_read_reg(BLIZZARD_NDISP_CTRL_STATUS); -} - -static inline void set_extif_timings(const struct extif_timings *t); - -static inline struct blizzard_request *alloc_req(void) -{ - unsigned long flags; - struct blizzard_request *req; - int req_flags = 0; - - if (!in_interrupt()) - down(&blizzard.req_sema); - else - req_flags = REQ_FROM_IRQ_POOL; - - spin_lock_irqsave(&blizzard.req_lock, flags); - BUG_ON(list_empty(&blizzard.free_req_list)); - req = list_entry(blizzard.free_req_list.next, - struct blizzard_request, entry); - list_del(&req->entry); - spin_unlock_irqrestore(&blizzard.req_lock, flags); - - INIT_LIST_HEAD(&req->entry); - req->flags = req_flags; - - return req; -} - -static inline void free_req(struct blizzard_request *req) -{ - unsigned long flags; - - spin_lock_irqsave(&blizzard.req_lock, flags); - - list_move(&req->entry, &blizzard.free_req_list); - if (!(req->flags & REQ_FROM_IRQ_POOL)) - up(&blizzard.req_sema); - - spin_unlock_irqrestore(&blizzard.req_lock, flags); -} - -static void process_pending_requests(void) -{ - unsigned long flags; - - spin_lock_irqsave(&blizzard.req_lock, flags); - - while (!list_empty(&blizzard.pending_req_list)) { - struct blizzard_request *req; - void (*complete)(void *); - void *complete_data; - - req = list_entry(blizzard.pending_req_list.next, - struct blizzard_request, entry); - spin_unlock_irqrestore(&blizzard.req_lock, flags); - - if (req->handler(req) == REQ_PENDING) - return; - - complete = req->complete; - complete_data = req->complete_data; - free_req(req); - - if (complete) - complete(complete_data); - - spin_lock_irqsave(&blizzard.req_lock, flags); - } - - spin_unlock_irqrestore(&blizzard.req_lock, flags); -} - -static void submit_req_list(struct list_head *head) -{ - unsigned long flags; - int process = 1; - - spin_lock_irqsave(&blizzard.req_lock, flags); - if (likely(!list_empty(&blizzard.pending_req_list))) - process = 0; - list_splice_init(head, blizzard.pending_req_list.prev); - spin_unlock_irqrestore(&blizzard.req_lock, flags); - - if (process) - process_pending_requests(); -} - -static void request_complete(void *data) -{ - struct blizzard_request *req = (struct blizzard_request *)data; - void (*complete)(void *); - void *complete_data; - - complete = req->complete; - complete_data = req->complete_data; - - free_req(req); - - if (complete) - complete(complete_data); - - process_pending_requests(); -} - - -static int do_full_screen_update(struct blizzard_request *req) -{ - int i; - int flags; - - for (i = 0; i < 3; i++) { - struct plane_info *p = &blizzard.plane[i]; - if (!(blizzard.enabled_planes & (1 << i))) { - blizzard.int_ctrl->enable_plane(i, 0); - continue; - } - dev_dbg(blizzard.fbdev->dev, "pw %d ph %d\n", - p->width, p->height); - blizzard.int_ctrl->setup_plane(i, - OMAPFB_CHANNEL_OUT_LCD, p->offset, - p->scr_width, p->pos_x, p->pos_y, - p->width, p->height, - p->color_mode); - blizzard.int_ctrl->enable_plane(i, 1); - } - - dev_dbg(blizzard.fbdev->dev, "sw %d sh %d\n", - blizzard.screen_width, blizzard.screen_height); - blizzard_wait_line_buffer(); - flags = req->par.update.flags; - if (flags & OMAPFB_FORMAT_FLAG_TEARSYNC) - enable_tearsync(0, blizzard.screen_width, - blizzard.screen_height, - blizzard.screen_height, - blizzard.screen_height, - flags & OMAPFB_FORMAT_FLAG_FORCE_VSYNC); - else - disable_tearsync(); - - set_window_regs(0, 0, blizzard.screen_width, blizzard.screen_height, - 0, 0, blizzard.screen_width, blizzard.screen_height, - BLIZZARD_COLOR_RGB565, blizzard.zoom_on, flags); - blizzard.zoom_on = 0; - - blizzard.extif->set_bits_per_cycle(16); - /* set_window_regs has left the register index at the right - * place, so no need to set it here. - */ - blizzard.extif->transfer_area(blizzard.screen_width, - blizzard.screen_height, - request_complete, req); - return REQ_PENDING; -} - -static int check_1d_intersect(int a1, int a2, int b1, int b2) -{ - if (a2 <= b1 || b2 <= a1) - return 0; - return 1; -} - -/* Setup all planes with an overlapping area with the update window. */ -static int do_partial_update(struct blizzard_request *req, int plane, - int x, int y, int w, int h, - int x_out, int y_out, int w_out, int h_out, - int wnd_color_mode, int bpp) -{ - int i; - int gx1, gy1, gx2, gy2; - int gx1_out, gy1_out, gx2_out, gy2_out; - int color_mode; - int flags; - int zoom_off; - int have_zoom_for_this_update = 0; - - /* Global coordinates, relative to pixel 0,0 of the LCD */ - gx1 = x + blizzard.plane[plane].pos_x; - gy1 = y + blizzard.plane[plane].pos_y; - gx2 = gx1 + w; - gy2 = gy1 + h; - - flags = req->par.update.flags; - if (flags & OMAPFB_FORMAT_FLAG_DOUBLE) { - gx1_out = gx1; - gy1_out = gy1; - gx2_out = gx1 + w * 2; - gy2_out = gy1 + h * 2; - } else { - gx1_out = x_out + blizzard.plane[plane].pos_x; - gy1_out = y_out + blizzard.plane[plane].pos_y; - gx2_out = gx1_out + w_out; - gy2_out = gy1_out + h_out; - } - - for (i = 0; i < OMAPFB_PLANE_NUM; i++) { - struct plane_info *p = &blizzard.plane[i]; - int px1, py1; - int px2, py2; - int pw, ph; - int pposx, pposy; - unsigned long offset; - - if (!(blizzard.enabled_planes & (1 << i)) || - (wnd_color_mode && i != plane)) { - blizzard.int_ctrl->enable_plane(i, 0); - continue; - } - /* Plane coordinates */ - if (i == plane) { - /* Plane in which we are doing the update. - * Local coordinates are the one in the update - * request. - */ - px1 = x; - py1 = y; - px2 = x + w; - py2 = y + h; - pposx = 0; - pposy = 0; - } else { - /* Check if this plane has an overlapping part */ - px1 = gx1 - p->pos_x; - py1 = gy1 - p->pos_y; - px2 = gx2 - p->pos_x; - py2 = gy2 - p->pos_y; - if (px1 >= p->width || py1 >= p->height || - px2 <= 0 || py2 <= 0) { - blizzard.int_ctrl->enable_plane(i, 0); - continue; - } - /* Calculate the coordinates for the overlapping - * part in the plane's local coordinates. - */ - pposx = -px1; - pposy = -py1; - if (px1 < 0) - px1 = 0; - if (py1 < 0) - py1 = 0; - if (px2 > p->width) - px2 = p->width; - if (py2 > p->height) - py2 = p->height; - if (pposx < 0) - pposx = 0; - if (pposy < 0) - pposy = 0; - } - pw = px2 - px1; - ph = py2 - py1; - offset = p->offset + (p->scr_width * py1 + px1) * p->bpp / 8; - if (wnd_color_mode) - /* Window embedded in the plane with a differing - * color mode / bpp. Calculate the number of DMA - * transfer elements in terms of the plane's bpp. - */ - pw = (pw + 1) * bpp / p->bpp; -#ifdef VERBOSE - dev_dbg(blizzard.fbdev->dev, - "plane %d offset %#08lx pposx %d pposy %d " - "px1 %d py1 %d pw %d ph %d\n", - i, offset, pposx, pposy, px1, py1, pw, ph); -#endif - blizzard.int_ctrl->setup_plane(i, - OMAPFB_CHANNEL_OUT_LCD, offset, - p->scr_width, - pposx, pposy, pw, ph, - p->color_mode); - - blizzard.int_ctrl->enable_plane(i, 1); - } - - switch (wnd_color_mode) { - case OMAPFB_COLOR_YUV420: - color_mode = BLIZZARD_COLOR_YUV420; - /* Currently only the 16 bits/pixel cycle format is - * supported on the external interface. Adjust the number - * of transfer elements per line for 12bpp format. - */ - w = (w + 1) * 3 / 4; - break; - default: - color_mode = BLIZZARD_COLOR_RGB565; - break; - } - - blizzard_wait_line_buffer(); - if (blizzard.last_color_mode == BLIZZARD_COLOR_YUV420) - blizzard_wait_yyc(); - blizzard.last_color_mode = color_mode; - if (flags & OMAPFB_FORMAT_FLAG_TEARSYNC) - enable_tearsync(gy1, w, h, - blizzard.screen_height, - h_out, - flags & OMAPFB_FORMAT_FLAG_FORCE_VSYNC); - else - disable_tearsync(); - - if ((gx2_out - gx1_out) != (gx2 - gx1) || - (gy2_out - gy1_out) != (gy2 - gy1)) - have_zoom_for_this_update = 1; - - /* 'background' type of screen update (as opposed to 'destructive') - can be used to disable scaling if scaling is active */ - zoom_off = blizzard.zoom_on && !have_zoom_for_this_update && - (gx1_out == 0) && (gx2_out == blizzard.screen_width) && - (gy1_out == 0) && (gy2_out == blizzard.screen_height) && - (gx1 == 0) && (gy1 == 0); - - if (blizzard.zoom_on && !have_zoom_for_this_update && !zoom_off && - check_1d_intersect(blizzard.zoom_area_gx1, blizzard.zoom_area_gx2, - gx1_out, gx2_out) && - check_1d_intersect(blizzard.zoom_area_gy1, blizzard.zoom_area_gy2, - gy1_out, gy2_out)) { - /* Previous screen update was using scaling, current update - * is not using it. Additionally, current screen update is - * going to overlap with the scaled area. Scaling needs to be - * disabled in order to avoid 'magnifying glass' effect. - * Dummy setup of background window can be used for this. - */ - set_window_regs(0, 0, blizzard.screen_width, - blizzard.screen_height, - 0, 0, blizzard.screen_width, - blizzard.screen_height, - BLIZZARD_COLOR_RGB565, 1, flags); - blizzard.zoom_on = 0; - } - - /* remember scaling settings if we have scaled update */ - if (have_zoom_for_this_update) { - blizzard.zoom_on = 1; - blizzard.zoom_area_gx1 = gx1_out; - blizzard.zoom_area_gx2 = gx2_out; - blizzard.zoom_area_gy1 = gy1_out; - blizzard.zoom_area_gy2 = gy2_out; - } - - set_window_regs(gx1, gy1, gx2, gy2, gx1_out, gy1_out, gx2_out, gy2_out, - color_mode, zoom_off, flags); - if (zoom_off) - blizzard.zoom_on = 0; - - blizzard.extif->set_bits_per_cycle(16); - /* set_window_regs has left the register index at the right - * place, so no need to set it here. - */ - blizzard.extif->transfer_area(w, h, request_complete, req); - - return REQ_PENDING; -} - -static int send_frame_handler(struct blizzard_request *req) -{ - struct update_param *par = &req->par.update; - int plane = par->plane; - -#ifdef VERBOSE - dev_dbg(blizzard.fbdev->dev, - "send_frame: x %d y %d w %d h %d " - "x_out %d y_out %d w_out %d h_out %d " - "color_mode %04x flags %04x planes %01x\n", - par->x, par->y, par->width, par->height, - par->out_x, par->out_y, par->out_width, par->out_height, - par->color_mode, par->flags, blizzard.enabled_planes); -#endif - if (par->flags & OMAPFB_FORMAT_FLAG_DISABLE_OVERLAY) - disable_overlay(); - - if ((blizzard.enabled_planes & blizzard.vid_nonstd_color) || - (blizzard.enabled_planes & blizzard.vid_scaled)) - return do_full_screen_update(req); - - return do_partial_update(req, plane, par->x, par->y, - par->width, par->height, - par->out_x, par->out_y, - par->out_width, par->out_height, - par->color_mode, par->bpp); -} - -static void send_frame_complete(void *data) -{ -} - -#define ADD_PREQ(_x, _y, _w, _h, _x_out, _y_out, _w_out, _h_out) do { \ - req = alloc_req(); \ - req->handler = send_frame_handler; \ - req->complete = send_frame_complete; \ - req->par.update.plane = plane_idx; \ - req->par.update.x = _x; \ - req->par.update.y = _y; \ - req->par.update.width = _w; \ - req->par.update.height = _h; \ - req->par.update.out_x = _x_out; \ - req->par.update.out_y = _y_out; \ - req->par.update.out_width = _w_out; \ - req->par.update.out_height = _h_out; \ - req->par.update.bpp = bpp; \ - req->par.update.color_mode = color_mode;\ - req->par.update.flags = flags; \ - list_add_tail(&req->entry, req_head); \ -} while(0) - -static void create_req_list(int plane_idx, - struct omapfb_update_window *win, - struct list_head *req_head) -{ - struct blizzard_request *req; - int x = win->x; - int y = win->y; - int width = win->width; - int height = win->height; - int x_out = win->out_x; - int y_out = win->out_y; - int width_out = win->out_width; - int height_out = win->out_height; - int color_mode; - int bpp; - int flags; - unsigned int ystart = y; - unsigned int yspan = height; - unsigned int ystart_out = y_out; - unsigned int yspan_out = height_out; - - flags = win->format & ~OMAPFB_FORMAT_MASK; - color_mode = win->format & OMAPFB_FORMAT_MASK; - switch (color_mode) { - case OMAPFB_COLOR_YUV420: - /* Embedded window with different color mode */ - bpp = 12; - /* X, Y, height must be aligned at 2, width at 4 pixels */ - x &= ~1; - y &= ~1; - height = yspan = height & ~1; - width = width & ~3; - break; - default: - /* Same as the plane color mode */ - bpp = blizzard.plane[plane_idx].bpp; - break; - } - if (width * height * bpp / 8 > blizzard.max_transmit_size) { - yspan = blizzard.max_transmit_size / (width * bpp / 8); - yspan_out = yspan * height_out / height; - ADD_PREQ(x, ystart, width, yspan, x_out, ystart_out, - width_out, yspan_out); - ystart += yspan; - ystart_out += yspan_out; - yspan = height - yspan; - yspan_out = height_out - yspan_out; - flags &= ~OMAPFB_FORMAT_FLAG_TEARSYNC; - } - - ADD_PREQ(x, ystart, width, yspan, x_out, ystart_out, - width_out, yspan_out); -} - -static void auto_update_complete(void *data) -{ - if (!blizzard.stop_auto_update) - mod_timer(&blizzard.auto_update_timer, - jiffies + BLIZZARD_AUTO_UPDATE_TIME); -} - -static void blizzard_update_window_auto(unsigned long arg) -{ - LIST_HEAD(req_list); - struct blizzard_request *last; - struct omapfb_plane_struct *plane; - - plane = blizzard.fbdev->fb_info[0]->par; - create_req_list(plane->idx, - &blizzard.auto_update_window, &req_list); - last = list_entry(req_list.prev, struct blizzard_request, entry); - - last->complete = auto_update_complete; - last->complete_data = NULL; - - submit_req_list(&req_list); -} - -int blizzard_update_window_async(struct fb_info *fbi, - struct omapfb_update_window *win, - void (*complete_callback)(void *arg), - void *complete_callback_data) -{ - LIST_HEAD(req_list); - struct blizzard_request *last; - struct omapfb_plane_struct *plane = fbi->par; - - if (unlikely(blizzard.update_mode != OMAPFB_MANUAL_UPDATE)) - return -EINVAL; - if (unlikely(!blizzard.te_connected && - (win->format & OMAPFB_FORMAT_FLAG_TEARSYNC))) - return -EINVAL; - - create_req_list(plane->idx, win, &req_list); - last = list_entry(req_list.prev, struct blizzard_request, entry); - - last->complete = complete_callback; - last->complete_data = (void *)complete_callback_data; - - submit_req_list(&req_list); - - return 0; -} -EXPORT_SYMBOL(blizzard_update_window_async); - -static int update_full_screen(void) -{ - return blizzard_update_window_async(blizzard.fbdev->fb_info[0], - &blizzard.auto_update_window, NULL, NULL); - -} - -static int blizzard_setup_plane(int plane, int channel_out, - unsigned long offset, int screen_width, - int pos_x, int pos_y, int width, int height, - int color_mode) -{ - struct plane_info *p; - -#ifdef VERBOSE - dev_dbg(blizzard.fbdev->dev, - "plane %d ch_out %d offset %#08lx scr_width %d " - "pos_x %d pos_y %d width %d height %d color_mode %d\n", - plane, channel_out, offset, screen_width, - pos_x, pos_y, width, height, color_mode); -#endif - if ((unsigned)plane > OMAPFB_PLANE_NUM) - return -EINVAL; - p = &blizzard.plane[plane]; - - switch (color_mode) { - case OMAPFB_COLOR_YUV422: - case OMAPFB_COLOR_YUY422: - p->bpp = 16; - blizzard.vid_nonstd_color &= ~(1 << plane); - break; - case OMAPFB_COLOR_YUV420: - p->bpp = 12; - blizzard.vid_nonstd_color |= 1 << plane; - break; - case OMAPFB_COLOR_RGB565: - p->bpp = 16; - blizzard.vid_nonstd_color &= ~(1 << plane); - break; - default: - return -EINVAL; - } - - p->offset = offset; - p->pos_x = pos_x; - p->pos_y = pos_y; - p->width = width; - p->height = height; - p->scr_width = screen_width; - if (!p->out_width) - p->out_width = width; - if (!p->out_height) - p->out_height = height; - - p->color_mode = color_mode; - - return 0; -} - -static int blizzard_set_scale(int plane, int orig_w, int orig_h, - int out_w, int out_h) -{ - struct plane_info *p = &blizzard.plane[plane]; - int r; - - dev_dbg(blizzard.fbdev->dev, - "plane %d orig_w %d orig_h %d out_w %d out_h %d\n", - plane, orig_w, orig_h, out_w, out_h); - if ((unsigned)plane > OMAPFB_PLANE_NUM) - return -ENODEV; - - r = blizzard.int_ctrl->set_scale(plane, orig_w, orig_h, out_w, out_h); - if (r < 0) - return r; - - p->width = orig_w; - p->height = orig_h; - p->out_width = out_w; - p->out_height = out_h; - if (orig_w == out_w && orig_h == out_h) - blizzard.vid_scaled &= ~(1 << plane); - else - blizzard.vid_scaled |= 1 << plane; - - return 0; -} - -static int blizzard_set_rotate(int angle) -{ - u32 l; - - l = blizzard_read_reg(BLIZZARD_PANEL_CONFIGURATION); - l &= ~0x03; - - switch (angle) { - case 0: - l = l | 0x00; - break; - case 90: - l = l | 0x03; - break; - case 180: - l = l | 0x02; - break; - case 270: - l = l | 0x01; - break; - default: - return -EINVAL; - } - - blizzard_write_reg(BLIZZARD_PANEL_CONFIGURATION, l); - - return 0; -} - -static int blizzard_enable_plane(int plane, int enable) -{ - if (enable) - blizzard.enabled_planes |= 1 << plane; - else - blizzard.enabled_planes &= ~(1 << plane); - - return 0; -} - -static int sync_handler(struct blizzard_request *req) -{ - complete(req->par.sync); - return REQ_COMPLETE; -} - -static void blizzard_sync(void) -{ - LIST_HEAD(req_list); - struct blizzard_request *req; - struct completion comp; - - req = alloc_req(); - - req->handler = sync_handler; - req->complete = NULL; - init_completion(&comp); - req->par.sync = ∁ - - list_add(&req->entry, &req_list); - submit_req_list(&req_list); - - wait_for_completion(&comp); -} - - -static void blizzard_bind_client(struct omapfb_notifier_block *nb) -{ - if (blizzard.update_mode == OMAPFB_MANUAL_UPDATE) { - omapfb_notify_clients(blizzard.fbdev, OMAPFB_EVENT_READY); - } -} - -static int blizzard_set_update_mode(enum omapfb_update_mode mode) -{ - if (unlikely(mode != OMAPFB_MANUAL_UPDATE && - mode != OMAPFB_AUTO_UPDATE && - mode != OMAPFB_UPDATE_DISABLED)) - return -EINVAL; - - if (mode == blizzard.update_mode) - return 0; - - dev_info(blizzard.fbdev->dev, "s1d1374x: setting update mode to %s\n", - mode == OMAPFB_UPDATE_DISABLED ? "disabled" : - (mode == OMAPFB_AUTO_UPDATE ? "auto" : "manual")); - - switch (blizzard.update_mode) { - case OMAPFB_MANUAL_UPDATE: - omapfb_notify_clients(blizzard.fbdev, OMAPFB_EVENT_DISABLED); - break; - case OMAPFB_AUTO_UPDATE: - blizzard.stop_auto_update = 1; - del_timer_sync(&blizzard.auto_update_timer); - break; - case OMAPFB_UPDATE_DISABLED: - break; - } - - blizzard.update_mode = mode; - blizzard_sync(); - blizzard.stop_auto_update = 0; - - switch (mode) { - case OMAPFB_MANUAL_UPDATE: - omapfb_notify_clients(blizzard.fbdev, OMAPFB_EVENT_READY); - break; - case OMAPFB_AUTO_UPDATE: - blizzard_update_window_auto(0); - break; - case OMAPFB_UPDATE_DISABLED: - break; - } - - return 0; -} - -static enum omapfb_update_mode blizzard_get_update_mode(void) -{ - return blizzard.update_mode; -} - -static inline void set_extif_timings(const struct extif_timings *t) -{ - blizzard.extif->set_timings(t); -} - -static inline unsigned long round_to_extif_ticks(unsigned long ps, int div) -{ - int bus_tick = blizzard.extif_clk_period * div; - return (ps + bus_tick - 1) / bus_tick * bus_tick; -} - -static int calc_reg_timing(unsigned long sysclk, int div) -{ - struct extif_timings *t; - unsigned long systim; - - /* CSOnTime 0, WEOnTime 2 ns, REOnTime 2 ns, - * AccessTime 2 ns + 12.2 ns (regs), - * WEOffTime = WEOnTime + 1 ns, - * REOffTime = REOnTime + 12 ns (regs), - * CSOffTime = REOffTime + 1 ns - * ReadCycle = 2ns + 2*SYSCLK (regs), - * WriteCycle = 2*SYSCLK + 2 ns, - * CSPulseWidth = 10 ns */ - - systim = 1000000000 / (sysclk / 1000); - dev_dbg(blizzard.fbdev->dev, - "Blizzard systim %lu ps extif_clk_period %u div %d\n", - systim, blizzard.extif_clk_period, div); - - t = &blizzard.reg_timings; - memset(t, 0, sizeof(*t)); - - t->clk_div = div; - - t->cs_on_time = 0; - t->we_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div); - t->re_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div); - t->access_time = round_to_extif_ticks(t->re_on_time + 12200, div); - t->we_off_time = round_to_extif_ticks(t->we_on_time + 1000, div); - t->re_off_time = round_to_extif_ticks(t->re_on_time + 13000, div); - t->cs_off_time = round_to_extif_ticks(t->re_off_time + 1000, div); - t->we_cycle_time = round_to_extif_ticks(2 * systim + 2000, div); - if (t->we_cycle_time < t->we_off_time) - t->we_cycle_time = t->we_off_time; - t->re_cycle_time = round_to_extif_ticks(2 * systim + 2000, div); - if (t->re_cycle_time < t->re_off_time) - t->re_cycle_time = t->re_off_time; - t->cs_pulse_width = 0; - - dev_dbg(blizzard.fbdev->dev, "[reg]cson %d csoff %d reon %d reoff %d\n", - t->cs_on_time, t->cs_off_time, t->re_on_time, t->re_off_time); - dev_dbg(blizzard.fbdev->dev, "[reg]weon %d weoff %d recyc %d wecyc %d\n", - t->we_on_time, t->we_off_time, t->re_cycle_time, - t->we_cycle_time); - dev_dbg(blizzard.fbdev->dev, "[reg]rdaccess %d cspulse %d\n", - t->access_time, t->cs_pulse_width); - - return blizzard.extif->convert_timings(t); -} - -static int calc_lut_timing(unsigned long sysclk, int div) -{ - struct extif_timings *t; - unsigned long systim; - - /* CSOnTime 0, WEOnTime 2 ns, REOnTime 2 ns, - * AccessTime 2 ns + 4 * SYSCLK + 26 (lut), - * WEOffTime = WEOnTime + 1 ns, - * REOffTime = REOnTime + 4*SYSCLK + 26 ns (lut), - * CSOffTime = REOffTime + 1 ns - * ReadCycle = 2ns + 4*SYSCLK + 26 ns (lut), - * WriteCycle = 2*SYSCLK + 2 ns, - * CSPulseWidth = 10 ns */ - - systim = 1000000000 / (sysclk / 1000); - dev_dbg(blizzard.fbdev->dev, - "Blizzard systim %lu ps extif_clk_period %u div %d\n", - systim, blizzard.extif_clk_period, div); - - t = &blizzard.lut_timings; - memset(t, 0, sizeof(*t)); - - t->clk_div = div; - - t->cs_on_time = 0; - t->we_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div); - t->re_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div); - t->access_time = round_to_extif_ticks(t->re_on_time + 4 * systim + - 26000, div); - t->we_off_time = round_to_extif_ticks(t->we_on_time + 1000, div); - t->re_off_time = round_to_extif_ticks(t->re_on_time + 4 * systim + - 26000, div); - t->cs_off_time = round_to_extif_ticks(t->re_off_time + 1000, div); - t->we_cycle_time = round_to_extif_ticks(2 * systim + 2000, div); - if (t->we_cycle_time < t->we_off_time) - t->we_cycle_time = t->we_off_time; - t->re_cycle_time = round_to_extif_ticks(2000 + 4 * systim + 26000, div); - if (t->re_cycle_time < t->re_off_time) - t->re_cycle_time = t->re_off_time; - t->cs_pulse_width = 0; - - dev_dbg(blizzard.fbdev->dev, - "[lut]cson %d csoff %d reon %d reoff %d\n", - t->cs_on_time, t->cs_off_time, t->re_on_time, t->re_off_time); - dev_dbg(blizzard.fbdev->dev, - "[lut]weon %d weoff %d recyc %d wecyc %d\n", - t->we_on_time, t->we_off_time, t->re_cycle_time, - t->we_cycle_time); - dev_dbg(blizzard.fbdev->dev, "[lut]rdaccess %d cspulse %d\n", - t->access_time, t->cs_pulse_width); - - return blizzard.extif->convert_timings(t); -} - -static int calc_extif_timings(unsigned long sysclk, int *extif_mem_div) -{ - int max_clk_div; - int div; - - blizzard.extif->get_clk_info(&blizzard.extif_clk_period, &max_clk_div); - for (div = 1; div <= max_clk_div; div++) { - if (calc_reg_timing(sysclk, div) == 0) - break; - } - if (div > max_clk_div) { - dev_dbg(blizzard.fbdev->dev, "reg timing failed\n"); - goto err; - } - *extif_mem_div = div; - - for (div = 1; div <= max_clk_div; div++) { - if (calc_lut_timing(sysclk, div) == 0) - break; - } - - if (div > max_clk_div) - goto err; - - blizzard.extif_clk_div = div; - - return 0; -err: - dev_err(blizzard.fbdev->dev, "can't setup timings\n"); - return -1; -} - -static void calc_blizzard_clk_rates(unsigned long ext_clk, - unsigned long *sys_clk, unsigned long *pix_clk) -{ - int pix_clk_src; - int sys_div = 0, sys_mul = 0; - int pix_div; - - pix_clk_src = blizzard_read_reg(BLIZZARD_CLK_SRC); - pix_div = ((pix_clk_src >> 3) & 0x1f) + 1; - if ((pix_clk_src & (0x3 << 1)) == 0) { - /* Source is the PLL */ - sys_div = (blizzard_read_reg(BLIZZARD_PLL_DIV) & 0x3f) + 1; - sys_mul = blizzard_read_reg(BLIZZARD_PLL_CLOCK_SYNTH_0); - sys_mul |= ((blizzard_read_reg(BLIZZARD_PLL_CLOCK_SYNTH_1) - & 0x0f) << 11); - *sys_clk = ext_clk * sys_mul / sys_div; - } else /* else source is ext clk, or oscillator */ - *sys_clk = ext_clk; - - *pix_clk = *sys_clk / pix_div; /* HZ */ - dev_dbg(blizzard.fbdev->dev, - "ext_clk %ld pix_src %d pix_div %d sys_div %d sys_mul %d\n", - ext_clk, pix_clk_src & (0x3 << 1), pix_div, sys_div, sys_mul); - dev_dbg(blizzard.fbdev->dev, "sys_clk %ld pix_clk %ld\n", - *sys_clk, *pix_clk); -} - -static int setup_tearsync(unsigned long pix_clk, int extif_div) -{ - int hdisp, vdisp; - int hndp, vndp; - int hsw, vsw; - int hs, vs; - int hs_pol_inv, vs_pol_inv; - int use_hsvs, use_ndp; - u8 b; - - hsw = blizzard_read_reg(BLIZZARD_HSW); - vsw = blizzard_read_reg(BLIZZARD_VSW); - hs_pol_inv = !(hsw & 0x80); - vs_pol_inv = !(vsw & 0x80); - hsw = hsw & 0x7f; - vsw = vsw & 0x3f; - - hdisp = blizzard_read_reg(BLIZZARD_HDISP) * 8; - vdisp = blizzard_read_reg(BLIZZARD_VDISP0) + - ((blizzard_read_reg(BLIZZARD_VDISP1) & 0x3) << 8); - - hndp = blizzard_read_reg(BLIZZARD_HNDP) & 0x3f; - vndp = blizzard_read_reg(BLIZZARD_VNDP); - - /* time to transfer one pixel (16bpp) in ps */ - blizzard.pix_tx_time = blizzard.reg_timings.we_cycle_time; - if (blizzard.extif->get_max_tx_rate != NULL) { - /* The external interface might have a rate limitation, - * if so, we have to maximize our transfer rate. - */ - unsigned long min_tx_time; - unsigned long max_tx_rate = blizzard.extif->get_max_tx_rate(); - - dev_dbg(blizzard.fbdev->dev, "max_tx_rate %ld HZ\n", - max_tx_rate); - min_tx_time = 1000000000 / (max_tx_rate / 1000); /* ps */ - if (blizzard.pix_tx_time < min_tx_time) - blizzard.pix_tx_time = min_tx_time; - } - - /* time to update one line in ps */ - blizzard.line_upd_time = (hdisp + hndp) * 1000000 / (pix_clk / 1000); - blizzard.line_upd_time *= 1000; - if (hdisp * blizzard.pix_tx_time > blizzard.line_upd_time) - /* transfer speed too low, we might have to use both - * HS and VS */ - use_hsvs = 1; - else - /* decent transfer speed, we'll always use only VS */ - use_hsvs = 0; - - if (use_hsvs && (hs_pol_inv || vs_pol_inv)) { - /* HS or'ed with VS doesn't work, use the active high - * TE signal based on HNDP / VNDP */ - use_ndp = 1; - hs_pol_inv = 0; - vs_pol_inv = 0; - hs = hndp; - vs = vndp; - } else { - /* Use HS or'ed with VS as a TE signal if both are needed - * or VNDP if only vsync is needed. */ - use_ndp = 0; - hs = hsw; - vs = vsw; - if (!use_hsvs) { - hs_pol_inv = 0; - vs_pol_inv = 0; - } - } - - hs = hs * 1000000 / (pix_clk / 1000); /* ps */ - hs *= 1000; - - vs = vs * (hdisp + hndp) * 1000000 / (pix_clk / 1000); /* ps */ - vs *= 1000; - - if (vs <= hs) - return -EDOM; - /* set VS to 120% of HS to minimize VS detection time */ - vs = hs * 12 / 10; - /* minimize HS too */ - if (hs > 10000) - hs = 10000; - - b = blizzard_read_reg(BLIZZARD_NDISP_CTRL_STATUS); - b &= ~0x3; - b |= use_hsvs ? 1 : 0; - b |= (use_ndp && use_hsvs) ? 0 : 2; - blizzard_write_reg(BLIZZARD_NDISP_CTRL_STATUS, b); - - blizzard.vsync_only = !use_hsvs; - - dev_dbg(blizzard.fbdev->dev, - "pix_clk %ld HZ pix_tx_time %ld ps line_upd_time %ld ps\n", - pix_clk, blizzard.pix_tx_time, blizzard.line_upd_time); - dev_dbg(blizzard.fbdev->dev, - "hs %d ps vs %d ps mode %d vsync_only %d\n", - hs, vs, b & 0x3, !use_hsvs); - - return blizzard.extif->setup_tearsync(1, hs, vs, - hs_pol_inv, vs_pol_inv, - extif_div); -} - -static void blizzard_get_caps(int plane, struct omapfb_caps *caps) -{ - blizzard.int_ctrl->get_caps(plane, caps); - caps->ctrl |= OMAPFB_CAPS_MANUAL_UPDATE | - OMAPFB_CAPS_WINDOW_PIXEL_DOUBLE | - OMAPFB_CAPS_WINDOW_SCALE | - OMAPFB_CAPS_WINDOW_OVERLAY | - OMAPFB_CAPS_WINDOW_ROTATE; - if (blizzard.te_connected) - caps->ctrl |= OMAPFB_CAPS_TEARSYNC; - caps->wnd_color |= (1 << OMAPFB_COLOR_RGB565) | - (1 << OMAPFB_COLOR_YUV420); -} - -static void _save_regs(const struct blizzard_reg_list *list, int cnt) -{ - int i; - - for (i = 0; i < cnt; i++, list++) { - int reg; - for (reg = list->start; reg <= list->end; reg += 2) - blizzard_reg_cache[reg / 2] = blizzard_read_reg(reg); - } -} - -static void _restore_regs(const struct blizzard_reg_list *list, int cnt) -{ - int i; - - for (i = 0; i < cnt; i++, list++) { - int reg; - for (reg = list->start; reg <= list->end; reg += 2) - blizzard_write_reg(reg, blizzard_reg_cache[reg / 2]); - } -} - -static void blizzard_save_all_regs(void) -{ - _save_regs(blizzard_pll_regs, ARRAY_SIZE(blizzard_pll_regs)); - _save_regs(blizzard_gen_regs, ARRAY_SIZE(blizzard_gen_regs)); -} - -static void blizzard_restore_pll_regs(void) -{ - _restore_regs(blizzard_pll_regs, ARRAY_SIZE(blizzard_pll_regs)); -} - -static void blizzard_restore_gen_regs(void) -{ - _restore_regs(blizzard_gen_regs, ARRAY_SIZE(blizzard_gen_regs)); -} - -static void blizzard_suspend(void) -{ - u32 l; - unsigned long tmo; - - if (blizzard.last_color_mode) { - update_full_screen(); - blizzard_sync(); - } - blizzard.update_mode_before_suspend = blizzard.update_mode; - /* the following will disable clocks as well */ - blizzard_set_update_mode(OMAPFB_UPDATE_DISABLED); - - blizzard_save_all_regs(); - - blizzard_stop_sdram(); - - l = blizzard_read_reg(BLIZZARD_POWER_SAVE); - /* Standby, Sleep. We assume we use an external clock. */ - l |= 0x03; - blizzard_write_reg(BLIZZARD_POWER_SAVE, l); - - tmo = jiffies + msecs_to_jiffies(100); - while (!(blizzard_read_reg(BLIZZARD_PLL_MODE) & (1 << 1))) { - if (time_after(jiffies, tmo)) { - dev_err(blizzard.fbdev->dev, - "s1d1374x: sleep timeout, stopping PLL manually\n"); - l = blizzard_read_reg(BLIZZARD_PLL_MODE); - l &= ~0x03; - /* Disable PLL, counter function */ - l |= 0x2; - blizzard_write_reg(BLIZZARD_PLL_MODE, l); - break; - } - msleep(1); - } - - if (blizzard.power_down != NULL) - blizzard.power_down(blizzard.fbdev->dev); -} - -static void blizzard_resume(void) -{ - u32 l; - - if (blizzard.power_up != NULL) - blizzard.power_up(blizzard.fbdev->dev); - - l = blizzard_read_reg(BLIZZARD_POWER_SAVE); - /* Standby, Sleep */ - l &= ~0x03; - blizzard_write_reg(BLIZZARD_POWER_SAVE, l); - - blizzard_restore_pll_regs(); - l = blizzard_read_reg(BLIZZARD_PLL_MODE); - l &= ~0x03; - /* Enable PLL, counter function */ - l |= 0x1; - blizzard_write_reg(BLIZZARD_PLL_MODE, l); - - while (!(blizzard_read_reg(BLIZZARD_PLL_DIV) & (1 << 7))) - msleep(1); - - blizzard_restart_sdram(); - - blizzard_restore_gen_regs(); - - /* Enable display */ - blizzard_write_reg(BLIZZARD_DISPLAY_MODE, 0x01); - - /* the following will enable clocks as necessary */ - blizzard_set_update_mode(blizzard.update_mode_before_suspend); - - /* Force a background update */ - blizzard.zoom_on = 1; - update_full_screen(); - blizzard_sync(); -} - -static int blizzard_init(struct omapfb_device *fbdev, int ext_mode, - struct omapfb_mem_desc *req_vram) -{ - int r = 0, i; - u8 rev, conf; - unsigned long ext_clk; - int extif_div; - unsigned long sys_clk, pix_clk; - struct omapfb_platform_data *omapfb_conf; - struct blizzard_platform_data *ctrl_conf; - - blizzard.fbdev = fbdev; - - BUG_ON(!fbdev->ext_if || !fbdev->int_ctrl); - - blizzard.fbdev = fbdev; - blizzard.extif = fbdev->ext_if; - blizzard.int_ctrl = fbdev->int_ctrl; - - omapfb_conf = fbdev->dev->platform_data; - ctrl_conf = omapfb_conf->ctrl_platform_data; - if (ctrl_conf == NULL || ctrl_conf->get_clock_rate == NULL) { - dev_err(fbdev->dev, "s1d1374x: missing platform data\n"); - r = -ENOENT; - goto err1; - } - - blizzard.power_down = ctrl_conf->power_down; - blizzard.power_up = ctrl_conf->power_up; - - spin_lock_init(&blizzard.req_lock); - - if ((r = blizzard.int_ctrl->init(fbdev, 1, req_vram)) < 0) - goto err1; - - if ((r = blizzard.extif->init(fbdev)) < 0) - goto err2; - - blizzard_ctrl.set_color_key = blizzard.int_ctrl->set_color_key; - blizzard_ctrl.get_color_key = blizzard.int_ctrl->get_color_key; - blizzard_ctrl.setup_mem = blizzard.int_ctrl->setup_mem; - blizzard_ctrl.mmap = blizzard.int_ctrl->mmap; - - ext_clk = ctrl_conf->get_clock_rate(fbdev->dev); - if ((r = calc_extif_timings(ext_clk, &extif_div)) < 0) - goto err3; - - set_extif_timings(&blizzard.reg_timings); - - if (blizzard.power_up != NULL) - blizzard.power_up(fbdev->dev); - - calc_blizzard_clk_rates(ext_clk, &sys_clk, &pix_clk); - - if ((r = calc_extif_timings(sys_clk, &extif_div)) < 0) - goto err3; - set_extif_timings(&blizzard.reg_timings); - - if (!(blizzard_read_reg(BLIZZARD_PLL_DIV) & 0x80)) { - dev_err(fbdev->dev, - "controller not initialized by the bootloader\n"); - r = -ENODEV; - goto err3; - } - - if (ctrl_conf->te_connected) { - if ((r = setup_tearsync(pix_clk, extif_div)) < 0) - goto err3; - blizzard.te_connected = 1; - } - - rev = blizzard_read_reg(BLIZZARD_REV_CODE); - conf = blizzard_read_reg(BLIZZARD_CONFIG); - - switch (rev & 0xfc) { - case 0x9c: - blizzard.version = BLIZZARD_VERSION_S1D13744; - pr_info("omapfb: s1d13744 LCD controller rev %d " - "initialized (CNF pins %x)\n", rev & 0x03, conf & 0x07); - break; - case 0xa4: - blizzard.version = BLIZZARD_VERSION_S1D13745; - pr_info("omapfb: s1d13745 LCD controller rev %d " - "initialized (CNF pins %x)\n", rev & 0x03, conf & 0x07); - break; - default: - dev_err(fbdev->dev, "invalid s1d1374x revision %02x\n", - rev); - r = -ENODEV; - goto err3; - } - - blizzard.max_transmit_size = blizzard.extif->max_transmit_size; - - blizzard.update_mode = OMAPFB_UPDATE_DISABLED; - - blizzard.auto_update_window.x = 0; - blizzard.auto_update_window.y = 0; - blizzard.auto_update_window.width = fbdev->panel->x_res; - blizzard.auto_update_window.height = fbdev->panel->y_res; - blizzard.auto_update_window.out_x = 0; - blizzard.auto_update_window.out_y = 0; - blizzard.auto_update_window.out_width = fbdev->panel->x_res; - blizzard.auto_update_window.out_height = fbdev->panel->y_res; - blizzard.auto_update_window.format = 0; - - blizzard.screen_width = fbdev->panel->x_res; - blizzard.screen_height = fbdev->panel->y_res; - - init_timer(&blizzard.auto_update_timer); - blizzard.auto_update_timer.function = blizzard_update_window_auto; - blizzard.auto_update_timer.data = 0; - - INIT_LIST_HEAD(&blizzard.free_req_list); - INIT_LIST_HEAD(&blizzard.pending_req_list); - for (i = 0; i < ARRAY_SIZE(blizzard.req_pool); i++) - list_add(&blizzard.req_pool[i].entry, &blizzard.free_req_list); - BUG_ON(i <= IRQ_REQ_POOL_SIZE); - sema_init(&blizzard.req_sema, i - IRQ_REQ_POOL_SIZE); - - return 0; -err3: - if (blizzard.power_down != NULL) - blizzard.power_down(fbdev->dev); - blizzard.extif->cleanup(); -err2: - blizzard.int_ctrl->cleanup(); -err1: - return r; -} - -static void blizzard_cleanup(void) -{ - blizzard_set_update_mode(OMAPFB_UPDATE_DISABLED); - blizzard.extif->cleanup(); - blizzard.int_ctrl->cleanup(); - if (blizzard.power_down != NULL) - blizzard.power_down(blizzard.fbdev->dev); -} - -struct lcd_ctrl blizzard_ctrl = { - .name = "blizzard", - .init = blizzard_init, - .cleanup = blizzard_cleanup, - .bind_client = blizzard_bind_client, - .get_caps = blizzard_get_caps, - .set_update_mode = blizzard_set_update_mode, - .get_update_mode = blizzard_get_update_mode, - .setup_plane = blizzard_setup_plane, - .set_scale = blizzard_set_scale, - .enable_plane = blizzard_enable_plane, - .set_rotate = blizzard_set_rotate, - .update_window = blizzard_update_window_async, - .sync = blizzard_sync, - .suspend = blizzard_suspend, - .resume = blizzard_resume, -}; - diff --git a/drivers/video/omap/dispc.c b/drivers/video/omap/dispc.c deleted file mode 100644 index 6f61e781f15a..000000000000 --- a/drivers/video/omap/dispc.c +++ /dev/null @@ -1,1547 +0,0 @@ -/* - * OMAP2 display controller support - * - * Copyright (C) 2005 Nokia Corporation - * Author: Imre Deak <imre.deak@nokia.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/dma-mapping.h> -#include <linux/mm.h> -#include <linux/vmalloc.h> -#include <linux/clk.h> -#include <linux/io.h> -#include <linux/platform_device.h> -#include <linux/slab.h> - -#include <plat/sram.h> -#include <plat/board.h> - -#include "omapfb.h" -#include "dispc.h" - -#define MODULE_NAME "dispc" - -#define DSS_BASE 0x48050000 -#define DSS_SYSCONFIG 0x0010 - -#define DISPC_BASE 0x48050400 - -/* DISPC common */ -#define DISPC_REVISION 0x0000 -#define DISPC_SYSCONFIG 0x0010 -#define DISPC_SYSSTATUS 0x0014 -#define DISPC_IRQSTATUS 0x0018 -#define DISPC_IRQENABLE 0x001C -#define DISPC_CONTROL 0x0040 -#define DISPC_CONFIG 0x0044 -#define DISPC_CAPABLE 0x0048 -#define DISPC_DEFAULT_COLOR0 0x004C -#define DISPC_DEFAULT_COLOR1 0x0050 -#define DISPC_TRANS_COLOR0 0x0054 -#define DISPC_TRANS_COLOR1 0x0058 -#define DISPC_LINE_STATUS 0x005C -#define DISPC_LINE_NUMBER 0x0060 -#define DISPC_TIMING_H 0x0064 -#define DISPC_TIMING_V 0x0068 -#define DISPC_POL_FREQ 0x006C -#define DISPC_DIVISOR 0x0070 -#define DISPC_SIZE_DIG 0x0078 -#define DISPC_SIZE_LCD 0x007C - -#define DISPC_DATA_CYCLE1 0x01D4 -#define DISPC_DATA_CYCLE2 0x01D8 -#define DISPC_DATA_CYCLE3 0x01DC - -/* DISPC GFX plane */ -#define DISPC_GFX_BA0 0x0080 -#define DISPC_GFX_BA1 0x0084 -#define DISPC_GFX_POSITION 0x0088 -#define DISPC_GFX_SIZE 0x008C -#define DISPC_GFX_ATTRIBUTES 0x00A0 -#define DISPC_GFX_FIFO_THRESHOLD 0x00A4 -#define DISPC_GFX_FIFO_SIZE_STATUS 0x00A8 -#define DISPC_GFX_ROW_INC 0x00AC -#define DISPC_GFX_PIXEL_INC 0x00B0 -#define DISPC_GFX_WINDOW_SKIP 0x00B4 -#define DISPC_GFX_TABLE_BA 0x00B8 - -/* DISPC Video plane 1/2 */ -#define DISPC_VID1_BASE 0x00BC -#define DISPC_VID2_BASE 0x014C - -/* Offsets into DISPC_VID1/2_BASE */ -#define DISPC_VID_BA0 0x0000 -#define DISPC_VID_BA1 0x0004 -#define DISPC_VID_POSITION 0x0008 -#define DISPC_VID_SIZE 0x000C -#define DISPC_VID_ATTRIBUTES 0x0010 -#define DISPC_VID_FIFO_THRESHOLD 0x0014 -#define DISPC_VID_FIFO_SIZE_STATUS 0x0018 -#define DISPC_VID_ROW_INC 0x001C -#define DISPC_VID_PIXEL_INC 0x0020 -#define DISPC_VID_FIR 0x0024 -#define DISPC_VID_PICTURE_SIZE 0x0028 -#define DISPC_VID_ACCU0 0x002C -#define DISPC_VID_ACCU1 0x0030 - -/* 8 elements in 8 byte increments */ -#define DISPC_VID_FIR_COEF_H0 0x0034 -/* 8 elements in 8 byte increments */ -#define DISPC_VID_FIR_COEF_HV0 0x0038 -/* 5 elements in 4 byte increments */ -#define DISPC_VID_CONV_COEF0 0x0074 - -#define DISPC_IRQ_FRAMEMASK 0x0001 -#define DISPC_IRQ_VSYNC 0x0002 -#define DISPC_IRQ_EVSYNC_EVEN 0x0004 -#define DISPC_IRQ_EVSYNC_ODD 0x0008 -#define DISPC_IRQ_ACBIAS_COUNT_STAT 0x0010 -#define DISPC_IRQ_PROG_LINE_NUM 0x0020 -#define DISPC_IRQ_GFX_FIFO_UNDERFLOW 0x0040 -#define DISPC_IRQ_GFX_END_WIN 0x0080 -#define DISPC_IRQ_PAL_GAMMA_MASK 0x0100 -#define DISPC_IRQ_OCP_ERR 0x0200 -#define DISPC_IRQ_VID1_FIFO_UNDERFLOW 0x0400 -#define DISPC_IRQ_VID1_END_WIN 0x0800 -#define DISPC_IRQ_VID2_FIFO_UNDERFLOW 0x1000 -#define DISPC_IRQ_VID2_END_WIN 0x2000 -#define DISPC_IRQ_SYNC_LOST 0x4000 - -#define DISPC_IRQ_MASK_ALL 0x7fff - -#define DISPC_IRQ_MASK_ERROR (DISPC_IRQ_GFX_FIFO_UNDERFLOW | \ - DISPC_IRQ_VID1_FIFO_UNDERFLOW | \ - DISPC_IRQ_VID2_FIFO_UNDERFLOW | \ - DISPC_IRQ_SYNC_LOST) - -#define RFBI_CONTROL 0x48050040 - -#define MAX_PALETTE_SIZE (256 * 16) - -#define FLD_MASK(pos, len) (((1 << len) - 1) << pos) - -#define MOD_REG_FLD(reg, mask, val) \ - dispc_write_reg((reg), (dispc_read_reg(reg) & ~(mask)) | (val)); - -#define OMAP2_SRAM_START 0x40200000 -/* Maximum size, in reality this is smaller if SRAM is partially locked. */ -#define OMAP2_SRAM_SIZE 0xa0000 /* 640k */ - -/* We support the SDRAM / SRAM types. See OMAPFB_PLANE_MEMTYPE_* in omapfb.h */ -#define DISPC_MEMTYPE_NUM 2 - -#define RESMAP_SIZE(_page_cnt) \ - ((_page_cnt + (sizeof(unsigned long) * 8) - 1) / 8) -#define RESMAP_PTR(_res_map, _page_nr) \ - (((_res_map)->map) + (_page_nr) / (sizeof(unsigned long) * 8)) -#define RESMAP_MASK(_page_nr) \ - (1 << ((_page_nr) & (sizeof(unsigned long) * 8 - 1))) - -struct resmap { - unsigned long start; - unsigned page_cnt; - unsigned long *map; -}; - -#define MAX_IRQ_HANDLERS 4 - -static struct { - void __iomem *base; - - struct omapfb_mem_desc mem_desc; - struct resmap *res_map[DISPC_MEMTYPE_NUM]; - atomic_t map_count[OMAPFB_PLANE_NUM]; - - dma_addr_t palette_paddr; - void *palette_vaddr; - - int ext_mode; - - struct { - u32 irq_mask; - void (*callback)(void *); - void *data; - } irq_handlers[MAX_IRQ_HANDLERS]; - struct completion frame_done; - - int fir_hinc[OMAPFB_PLANE_NUM]; - int fir_vinc[OMAPFB_PLANE_NUM]; - - struct clk *dss_ick, *dss1_fck; - struct clk *dss_54m_fck; - - enum omapfb_update_mode update_mode; - struct omapfb_device *fbdev; - - struct omapfb_color_key color_key; -} dispc; - -static void enable_lcd_clocks(int enable); - -static void inline dispc_write_reg(int idx, u32 val) -{ - __raw_writel(val, dispc.base + idx); -} - -static u32 inline dispc_read_reg(int idx) -{ - u32 l = __raw_readl(dispc.base + idx); - return l; -} - -/* Select RFBI or bypass mode */ -static void enable_rfbi_mode(int enable) -{ - void __iomem *rfbi_control; - u32 l; - - l = dispc_read_reg(DISPC_CONTROL); - /* Enable RFBI, GPIO0/1 */ - l &= ~((1 << 11) | (1 << 15) | (1 << 16)); - l |= enable ? (1 << 11) : 0; - /* RFBI En: GPIO0/1=10 RFBI Dis: GPIO0/1=11 */ - l |= 1 << 15; - l |= enable ? 0 : (1 << 16); - dispc_write_reg(DISPC_CONTROL, l); - - /* Set bypass mode in RFBI module */ - rfbi_control = ioremap(RFBI_CONTROL, SZ_1K); - if (!rfbi_control) { - pr_err("Unable to ioremap rfbi_control\n"); - return; - } - l = __raw_readl(rfbi_control); - l |= enable ? 0 : (1 << 1); - __raw_writel(l, rfbi_control); - iounmap(rfbi_control); -} - -static void set_lcd_data_lines(int data_lines) -{ - u32 l; - int code = 0; - - switch (data_lines) { - case 12: - code = 0; - break; - case 16: - code = 1; - break; - case 18: - code = 2; - break; - case 24: - code = 3; - break; - default: - BUG(); - } - - l = dispc_read_reg(DISPC_CONTROL); - l &= ~(0x03 << 8); - l |= code << 8; - dispc_write_reg(DISPC_CONTROL, l); -} - -static void set_load_mode(int mode) -{ - BUG_ON(mode & ~(DISPC_LOAD_CLUT_ONLY | DISPC_LOAD_FRAME_ONLY | - DISPC_LOAD_CLUT_ONCE_FRAME)); - MOD_REG_FLD(DISPC_CONFIG, 0x03 << 1, mode << 1); -} - -void omap_dispc_set_lcd_size(int x, int y) -{ - BUG_ON((x > (1 << 11)) || (y > (1 << 11))); - enable_lcd_clocks(1); - MOD_REG_FLD(DISPC_SIZE_LCD, FLD_MASK(16, 11) | FLD_MASK(0, 11), - ((y - 1) << 16) | (x - 1)); - enable_lcd_clocks(0); -} -EXPORT_SYMBOL(omap_dispc_set_lcd_size); - -void omap_dispc_set_digit_size(int x, int y) -{ - BUG_ON((x > (1 << 11)) || (y > (1 << 11))); - enable_lcd_clocks(1); - MOD_REG_FLD(DISPC_SIZE_DIG, FLD_MASK(16, 11) | FLD_MASK(0, 11), - ((y - 1) << 16) | (x - 1)); - enable_lcd_clocks(0); -} -EXPORT_SYMBOL(omap_dispc_set_digit_size); - -static void setup_plane_fifo(int plane, int ext_mode) -{ - const u32 ftrs_reg[] = { DISPC_GFX_FIFO_THRESHOLD, - DISPC_VID1_BASE + DISPC_VID_FIFO_THRESHOLD, - DISPC_VID2_BASE + DISPC_VID_FIFO_THRESHOLD }; - const u32 fsz_reg[] = { DISPC_GFX_FIFO_SIZE_STATUS, - DISPC_VID1_BASE + DISPC_VID_FIFO_SIZE_STATUS, - DISPC_VID2_BASE + DISPC_VID_FIFO_SIZE_STATUS }; - int low, high; - u32 l; - - BUG_ON(plane > 2); - - l = dispc_read_reg(fsz_reg[plane]); - l &= FLD_MASK(0, 11); - if (ext_mode) { - low = l * 3 / 4; - high = l; - } else { - low = l / 4; - high = l * 3 / 4; - } - MOD_REG_FLD(ftrs_reg[plane], FLD_MASK(16, 12) | FLD_MASK(0, 12), - (high << 16) | low); -} - -void omap_dispc_enable_lcd_out(int enable) -{ - enable_lcd_clocks(1); - MOD_REG_FLD(DISPC_CONTROL, 1, enable ? 1 : 0); - enable_lcd_clocks(0); -} -EXPORT_SYMBOL(omap_dispc_enable_lcd_out); - -void omap_dispc_enable_digit_out(int enable) -{ - enable_lcd_clocks(1); - MOD_REG_FLD(DISPC_CONTROL, 1 << 1, enable ? 1 << 1 : 0); - enable_lcd_clocks(0); -} -EXPORT_SYMBOL(omap_dispc_enable_digit_out); - -static inline int _setup_plane(int plane, int channel_out, - u32 paddr, int screen_width, - int pos_x, int pos_y, int width, int height, - int color_mode) -{ - const u32 at_reg[] = { DISPC_GFX_ATTRIBUTES, - DISPC_VID1_BASE + DISPC_VID_ATTRIBUTES, - DISPC_VID2_BASE + DISPC_VID_ATTRIBUTES }; - const u32 ba_reg[] = { DISPC_GFX_BA0, DISPC_VID1_BASE + DISPC_VID_BA0, - DISPC_VID2_BASE + DISPC_VID_BA0 }; - const u32 ps_reg[] = { DISPC_GFX_POSITION, - DISPC_VID1_BASE + DISPC_VID_POSITION, - DISPC_VID2_BASE + DISPC_VID_POSITION }; - const u32 sz_reg[] = { DISPC_GFX_SIZE, - DISPC_VID1_BASE + DISPC_VID_PICTURE_SIZE, - DISPC_VID2_BASE + DISPC_VID_PICTURE_SIZE }; - const u32 ri_reg[] = { DISPC_GFX_ROW_INC, - DISPC_VID1_BASE + DISPC_VID_ROW_INC, - DISPC_VID2_BASE + DISPC_VID_ROW_INC }; - const u32 vs_reg[] = { 0, DISPC_VID1_BASE + DISPC_VID_SIZE, - DISPC_VID2_BASE + DISPC_VID_SIZE }; - - int chout_shift, burst_shift; - int chout_val; - int color_code; - int bpp; - int cconv_en; - int set_vsize; - u32 l; - -#ifdef VERBOSE - dev_dbg(dispc.fbdev->dev, "plane %d channel %d paddr %#08x scr_width %d" - " pos_x %d pos_y %d width %d height %d color_mode %d\n", - plane, channel_out, paddr, screen_width, pos_x, pos_y, - width, height, color_mode); -#endif - - set_vsize = 0; - switch (plane) { - case OMAPFB_PLANE_GFX: - burst_shift = 6; - chout_shift = 8; - break; - case OMAPFB_PLANE_VID1: - case OMAPFB_PLANE_VID2: - burst_shift = 14; - chout_shift = 16; - set_vsize = 1; - break; - default: - return -EINVAL; - } - - switch (channel_out) { - case OMAPFB_CHANNEL_OUT_LCD: - chout_val = 0; - break; - case OMAPFB_CHANNEL_OUT_DIGIT: - chout_val = 1; - break; - default: - return -EINVAL; - } - - cconv_en = 0; - switch (color_mode) { - case OMAPFB_COLOR_RGB565: - color_code = DISPC_RGB_16_BPP; - bpp = 16; - break; - case OMAPFB_COLOR_YUV422: - if (plane == 0) - return -EINVAL; - color_code = DISPC_UYVY_422; - cconv_en = 1; - bpp = 16; - break; - case OMAPFB_COLOR_YUY422: - if (plane == 0) - return -EINVAL; - color_code = DISPC_YUV2_422; - cconv_en = 1; - bpp = 16; - break; - default: - return -EINVAL; - } - - l = dispc_read_reg(at_reg[plane]); - - l &= ~(0x0f << 1); - l |= color_code << 1; - l &= ~(1 << 9); - l |= cconv_en << 9; - - l &= ~(0x03 << burst_shift); - l |= DISPC_BURST_8x32 << burst_shift; - - l &= ~(1 << chout_shift); - l |= chout_val << chout_shift; - - dispc_write_reg(at_reg[plane], l); - - dispc_write_reg(ba_reg[plane], paddr); - MOD_REG_FLD(ps_reg[plane], - FLD_MASK(16, 11) | FLD_MASK(0, 11), (pos_y << 16) | pos_x); - - MOD_REG_FLD(sz_reg[plane], FLD_MASK(16, 11) | FLD_MASK(0, 11), - ((height - 1) << 16) | (width - 1)); - - if (set_vsize) { - /* Set video size if set_scale hasn't set it */ - if (!dispc.fir_vinc[plane]) - MOD_REG_FLD(vs_reg[plane], - FLD_MASK(16, 11), (height - 1) << 16); - if (!dispc.fir_hinc[plane]) - MOD_REG_FLD(vs_reg[plane], - FLD_MASK(0, 11), width - 1); - } - - dispc_write_reg(ri_reg[plane], (screen_width - width) * bpp / 8 + 1); - - return height * screen_width * bpp / 8; -} - -static int omap_dispc_setup_plane(int plane, int channel_out, - unsigned long offset, - int screen_width, - int pos_x, int pos_y, int width, int height, - int color_mode) -{ - u32 paddr; - int r; - - if ((unsigned)plane > dispc.mem_desc.region_cnt) - return -EINVAL; - paddr = dispc.mem_desc.region[plane].paddr + offset; - enable_lcd_clocks(1); - r = _setup_plane(plane, channel_out, paddr, - screen_width, - pos_x, pos_y, width, height, color_mode); - enable_lcd_clocks(0); - return r; -} - -static void write_firh_reg(int plane, int reg, u32 value) -{ - u32 base; - - if (plane == 1) - base = DISPC_VID1_BASE + DISPC_VID_FIR_COEF_H0; - else - base = DISPC_VID2_BASE + DISPC_VID_FIR_COEF_H0; - dispc_write_reg(base + reg * 8, value); -} - -static void write_firhv_reg(int plane, int reg, u32 value) -{ - u32 base; - - if (plane == 1) - base = DISPC_VID1_BASE + DISPC_VID_FIR_COEF_HV0; - else - base = DISPC_VID2_BASE + DISPC_VID_FIR_COEF_HV0; - dispc_write_reg(base + reg * 8, value); -} - -static void set_upsampling_coef_table(int plane) -{ - const u32 coef[][2] = { - { 0x00800000, 0x00800000 }, - { 0x0D7CF800, 0x037B02FF }, - { 0x1E70F5FF, 0x0C6F05FE }, - { 0x335FF5FE, 0x205907FB }, - { 0xF74949F7, 0x00404000 }, - { 0xF55F33FB, 0x075920FE }, - { 0xF5701EFE, 0x056F0CFF }, - { 0xF87C0DFF, 0x027B0300 }, - }; - int i; - - for (i = 0; i < 8; i++) { - write_firh_reg(plane, i, coef[i][0]); - write_firhv_reg(plane, i, coef[i][1]); - } -} - -static int omap_dispc_set_scale(int plane, - int orig_width, int orig_height, - int out_width, int out_height) -{ - const u32 at_reg[] = { 0, DISPC_VID1_BASE + DISPC_VID_ATTRIBUTES, - DISPC_VID2_BASE + DISPC_VID_ATTRIBUTES }; - const u32 vs_reg[] = { 0, DISPC_VID1_BASE + DISPC_VID_SIZE, - DISPC_VID2_BASE + DISPC_VID_SIZE }; - const u32 fir_reg[] = { 0, DISPC_VID1_BASE + DISPC_VID_FIR, - DISPC_VID2_BASE + DISPC_VID_FIR }; - - u32 l; - int fir_hinc; - int fir_vinc; - - if ((unsigned)plane > OMAPFB_PLANE_NUM) - return -ENODEV; - - if (plane == OMAPFB_PLANE_GFX && - (out_width != orig_width || out_height != orig_height)) - return -EINVAL; - - enable_lcd_clocks(1); - if (orig_width < out_width) { - /* - * Upsampling. - * Currently you can only scale both dimensions in one way. - */ - if (orig_height > out_height || - orig_width * 8 < out_width || - orig_height * 8 < out_height) { - enable_lcd_clocks(0); - return -EINVAL; - } - set_upsampling_coef_table(plane); - } else if (orig_width > out_width) { - /* Downsampling not yet supported - */ - - enable_lcd_clocks(0); - return -EINVAL; - } - if (!orig_width || orig_width == out_width) - fir_hinc = 0; - else - fir_hinc = 1024 * orig_width / out_width; - if (!orig_height || orig_height == out_height) - fir_vinc = 0; - else - fir_vinc = 1024 * orig_height / out_height; - dispc.fir_hinc[plane] = fir_hinc; - dispc.fir_vinc[plane] = fir_vinc; - - MOD_REG_FLD(fir_reg[plane], - FLD_MASK(16, 12) | FLD_MASK(0, 12), - ((fir_vinc & 4095) << 16) | - (fir_hinc & 4095)); - - dev_dbg(dispc.fbdev->dev, "out_width %d out_height %d orig_width %d " - "orig_height %d fir_hinc %d fir_vinc %d\n", - out_width, out_height, orig_width, orig_height, - fir_hinc, fir_vinc); - - MOD_REG_FLD(vs_reg[plane], - FLD_MASK(16, 11) | FLD_MASK(0, 11), - ((out_height - 1) << 16) | (out_width - 1)); - - l = dispc_read_reg(at_reg[plane]); - l &= ~(0x03 << 5); - l |= fir_hinc ? (1 << 5) : 0; - l |= fir_vinc ? (1 << 6) : 0; - dispc_write_reg(at_reg[plane], l); - - enable_lcd_clocks(0); - return 0; -} - -static int omap_dispc_enable_plane(int plane, int enable) -{ - const u32 at_reg[] = { DISPC_GFX_ATTRIBUTES, - DISPC_VID1_BASE + DISPC_VID_ATTRIBUTES, - DISPC_VID2_BASE + DISPC_VID_ATTRIBUTES }; - if ((unsigned int)plane > dispc.mem_desc.region_cnt) - return -EINVAL; - - enable_lcd_clocks(1); - MOD_REG_FLD(at_reg[plane], 1, enable ? 1 : 0); - enable_lcd_clocks(0); - - return 0; -} - -static int omap_dispc_set_color_key(struct omapfb_color_key *ck) -{ - u32 df_reg, tr_reg; - int shift, val; - - switch (ck->channel_out) { - case OMAPFB_CHANNEL_OUT_LCD: - df_reg = DISPC_DEFAULT_COLOR0; - tr_reg = DISPC_TRANS_COLOR0; - shift = 10; - break; - case OMAPFB_CHANNEL_OUT_DIGIT: - df_reg = DISPC_DEFAULT_COLOR1; - tr_reg = DISPC_TRANS_COLOR1; - shift = 12; - break; - default: - return -EINVAL; - } - switch (ck->key_type) { - case OMAPFB_COLOR_KEY_DISABLED: - val = 0; - break; - case OMAPFB_COLOR_KEY_GFX_DST: - val = 1; - break; - case OMAPFB_COLOR_KEY_VID_SRC: - val = 3; - break; - default: - return -EINVAL; - } - enable_lcd_clocks(1); - MOD_REG_FLD(DISPC_CONFIG, FLD_MASK(shift, 2), val << shift); - - if (val != 0) - dispc_write_reg(tr_reg, ck->trans_key); - dispc_write_reg(df_reg, ck->background); - enable_lcd_clocks(0); - - dispc.color_key = *ck; - - return 0; -} - -static int omap_dispc_get_color_key(struct omapfb_color_key *ck) -{ - *ck = dispc.color_key; - return 0; -} - -static void load_palette(void) -{ -} - -static int omap_dispc_set_update_mode(enum omapfb_update_mode mode) -{ - int r = 0; - - if (mode != dispc.update_mode) { - switch (mode) { - case OMAPFB_AUTO_UPDATE: - case OMAPFB_MANUAL_UPDATE: - enable_lcd_clocks(1); - omap_dispc_enable_lcd_out(1); - dispc.update_mode = mode; - break; - case OMAPFB_UPDATE_DISABLED: - init_completion(&dispc.frame_done); - omap_dispc_enable_lcd_out(0); - if (!wait_for_completion_timeout(&dispc.frame_done, - msecs_to_jiffies(500))) { - dev_err(dispc.fbdev->dev, - "timeout waiting for FRAME DONE\n"); - } - dispc.update_mode = mode; - enable_lcd_clocks(0); - break; - default: - r = -EINVAL; - } - } - - return r; -} - -static void omap_dispc_get_caps(int plane, struct omapfb_caps *caps) -{ - caps->ctrl |= OMAPFB_CAPS_PLANE_RELOCATE_MEM; - if (plane > 0) - caps->ctrl |= OMAPFB_CAPS_PLANE_SCALE; - caps->plane_color |= (1 << OMAPFB_COLOR_RGB565) | - (1 << OMAPFB_COLOR_YUV422) | - (1 << OMAPFB_COLOR_YUY422); - if (plane == 0) - caps->plane_color |= (1 << OMAPFB_COLOR_CLUT_8BPP) | - (1 << OMAPFB_COLOR_CLUT_4BPP) | - (1 << OMAPFB_COLOR_CLUT_2BPP) | - (1 << OMAPFB_COLOR_CLUT_1BPP) | - (1 << OMAPFB_COLOR_RGB444); -} - -static enum omapfb_update_mode omap_dispc_get_update_mode(void) -{ - return dispc.update_mode; -} - -static void setup_color_conv_coef(void) -{ - u32 mask = FLD_MASK(16, 11) | FLD_MASK(0, 11); - int cf1_reg = DISPC_VID1_BASE + DISPC_VID_CONV_COEF0; - int cf2_reg = DISPC_VID2_BASE + DISPC_VID_CONV_COEF0; - int at1_reg = DISPC_VID1_BASE + DISPC_VID_ATTRIBUTES; - int at2_reg = DISPC_VID2_BASE + DISPC_VID_ATTRIBUTES; - const struct color_conv_coef { - int ry, rcr, rcb, gy, gcr, gcb, by, bcr, bcb; - int full_range; - } ctbl_bt601_5 = { - 298, 409, 0, 298, -208, -100, 298, 0, 517, 0, - }; - const struct color_conv_coef *ct; -#define CVAL(x, y) (((x & 2047) << 16) | (y & 2047)) - - ct = &ctbl_bt601_5; - - MOD_REG_FLD(cf1_reg, mask, CVAL(ct->rcr, ct->ry)); - MOD_REG_FLD(cf1_reg + 4, mask, CVAL(ct->gy, ct->rcb)); - MOD_REG_FLD(cf1_reg + 8, mask, CVAL(ct->gcb, ct->gcr)); - MOD_REG_FLD(cf1_reg + 12, mask, CVAL(ct->bcr, ct->by)); - MOD_REG_FLD(cf1_reg + 16, mask, CVAL(0, ct->bcb)); - - MOD_REG_FLD(cf2_reg, mask, CVAL(ct->rcr, ct->ry)); - MOD_REG_FLD(cf2_reg + 4, mask, CVAL(ct->gy, ct->rcb)); - MOD_REG_FLD(cf2_reg + 8, mask, CVAL(ct->gcb, ct->gcr)); - MOD_REG_FLD(cf2_reg + 12, mask, CVAL(ct->bcr, ct->by)); - MOD_REG_FLD(cf2_reg + 16, mask, CVAL(0, ct->bcb)); -#undef CVAL - - MOD_REG_FLD(at1_reg, (1 << 11), ct->full_range); - MOD_REG_FLD(at2_reg, (1 << 11), ct->full_range); -} - -static void calc_ck_div(int is_tft, int pck, int *lck_div, int *pck_div) -{ - unsigned long fck, lck; - - *lck_div = 1; - pck = max(1, pck); - fck = clk_get_rate(dispc.dss1_fck); - lck = fck; - *pck_div = (lck + pck - 1) / pck; - if (is_tft) - *pck_div = max(2, *pck_div); - else - *pck_div = max(3, *pck_div); - if (*pck_div > 255) { - *pck_div = 255; - lck = pck * *pck_div; - *lck_div = fck / lck; - BUG_ON(*lck_div < 1); - if (*lck_div > 255) { - *lck_div = 255; - dev_warn(dispc.fbdev->dev, "pixclock %d kHz too low.\n", - pck / 1000); - } - } -} - -static void set_lcd_tft_mode(int enable) -{ - u32 mask; - - mask = 1 << 3; - MOD_REG_FLD(DISPC_CONTROL, mask, enable ? mask : 0); -} - -static void set_lcd_timings(void) -{ - u32 l; - int lck_div, pck_div; - struct lcd_panel *panel = dispc.fbdev->panel; - int is_tft = panel->config & OMAP_LCDC_PANEL_TFT; - unsigned long fck; - - l = dispc_read_reg(DISPC_TIMING_H); - l &= ~(FLD_MASK(0, 6) | FLD_MASK(8, 8) | FLD_MASK(20, 8)); - l |= ( max(1, (min(64, panel->hsw))) - 1 ) << 0; - l |= ( max(1, (min(256, panel->hfp))) - 1 ) << 8; - l |= ( max(1, (min(256, panel->hbp))) - 1 ) << 20; - dispc_write_reg(DISPC_TIMING_H, l); - - l = dispc_read_reg(DISPC_TIMING_V); - l &= ~(FLD_MASK(0, 6) | FLD_MASK(8, 8) | FLD_MASK(20, 8)); - l |= ( max(1, (min(64, panel->vsw))) - 1 ) << 0; - l |= ( max(0, (min(255, panel->vfp))) - 0 ) << 8; - l |= ( max(0, (min(255, panel->vbp))) - 0 ) << 20; - dispc_write_reg(DISPC_TIMING_V, l); - - l = dispc_read_reg(DISPC_POL_FREQ); - l &= ~FLD_MASK(12, 6); - l |= (panel->config & OMAP_LCDC_SIGNAL_MASK) << 12; - l |= panel->acb & 0xff; - dispc_write_reg(DISPC_POL_FREQ, l); - - calc_ck_div(is_tft, panel->pixel_clock * 1000, &lck_div, &pck_div); - - l = dispc_read_reg(DISPC_DIVISOR); - l &= ~(FLD_MASK(16, 8) | FLD_MASK(0, 8)); - l |= (lck_div << 16) | (pck_div << 0); - dispc_write_reg(DISPC_DIVISOR, l); - - /* update panel info with the exact clock */ - fck = clk_get_rate(dispc.dss1_fck); - panel->pixel_clock = fck / lck_div / pck_div / 1000; -} - -static void recalc_irq_mask(void) -{ - int i; - unsigned long irq_mask = DISPC_IRQ_MASK_ERROR; - - for (i = 0; i < MAX_IRQ_HANDLERS; i++) { - if (!dispc.irq_handlers[i].callback) - continue; - - irq_mask |= dispc.irq_handlers[i].irq_mask; - } - - enable_lcd_clocks(1); - MOD_REG_FLD(DISPC_IRQENABLE, 0x7fff, irq_mask); - enable_lcd_clocks(0); -} - -int omap_dispc_request_irq(unsigned long irq_mask, void (*callback)(void *data), - void *data) -{ - int i; - - BUG_ON(callback == NULL); - - for (i = 0; i < MAX_IRQ_HANDLERS; i++) { - if (dispc.irq_handlers[i].callback) - continue; - - dispc.irq_handlers[i].irq_mask = irq_mask; - dispc.irq_handlers[i].callback = callback; - dispc.irq_handlers[i].data = data; - recalc_irq_mask(); - - return 0; - } - - return -EBUSY; -} -EXPORT_SYMBOL(omap_dispc_request_irq); - -void omap_dispc_free_irq(unsigned long irq_mask, void (*callback)(void *data), - void *data) -{ - int i; - - for (i = 0; i < MAX_IRQ_HANDLERS; i++) { - if (dispc.irq_handlers[i].callback == callback && - dispc.irq_handlers[i].data == data) { - dispc.irq_handlers[i].irq_mask = 0; - dispc.irq_handlers[i].callback = NULL; - dispc.irq_handlers[i].data = NULL; - recalc_irq_mask(); - return; - } - } - - BUG(); -} -EXPORT_SYMBOL(omap_dispc_free_irq); - -static irqreturn_t omap_dispc_irq_handler(int irq, void *dev) -{ - u32 stat; - int i = 0; - - enable_lcd_clocks(1); - - stat = dispc_read_reg(DISPC_IRQSTATUS); - if (stat & DISPC_IRQ_FRAMEMASK) - complete(&dispc.frame_done); - - if (stat & DISPC_IRQ_MASK_ERROR) { - if (printk_ratelimit()) { - dev_err(dispc.fbdev->dev, "irq error status %04x\n", - stat & 0x7fff); - } - } - - for (i = 0; i < MAX_IRQ_HANDLERS; i++) { - if (unlikely(dispc.irq_handlers[i].callback && - (stat & dispc.irq_handlers[i].irq_mask))) - dispc.irq_handlers[i].callback( - dispc.irq_handlers[i].data); - } - - dispc_write_reg(DISPC_IRQSTATUS, stat); - - enable_lcd_clocks(0); - - return IRQ_HANDLED; -} - -static int get_dss_clocks(void) -{ - dispc.dss_ick = clk_get(&dispc.fbdev->dssdev->dev, "ick"); - if (IS_ERR(dispc.dss_ick)) { - dev_err(dispc.fbdev->dev, "can't get ick\n"); - return PTR_ERR(dispc.dss_ick); - } - - dispc.dss1_fck = clk_get(&dispc.fbdev->dssdev->dev, "fck"); - if (IS_ERR(dispc.dss1_fck)) { - dev_err(dispc.fbdev->dev, "can't get dss1_fck\n"); - clk_put(dispc.dss_ick); - return PTR_ERR(dispc.dss1_fck); - } - - dispc.dss_54m_fck = clk_get(&dispc.fbdev->dssdev->dev, "tv_clk"); - if (IS_ERR(dispc.dss_54m_fck)) { - dev_err(dispc.fbdev->dev, "can't get tv_fck\n"); - clk_put(dispc.dss_ick); - clk_put(dispc.dss1_fck); - return PTR_ERR(dispc.dss_54m_fck); - } - - return 0; -} - -static void put_dss_clocks(void) -{ - clk_put(dispc.dss_54m_fck); - clk_put(dispc.dss1_fck); - clk_put(dispc.dss_ick); -} - -static void enable_lcd_clocks(int enable) -{ - if (enable) { - clk_enable(dispc.dss_ick); - clk_enable(dispc.dss1_fck); - } else { - clk_disable(dispc.dss1_fck); - clk_disable(dispc.dss_ick); - } -} - -static void enable_digit_clocks(int enable) -{ - if (enable) - clk_enable(dispc.dss_54m_fck); - else - clk_disable(dispc.dss_54m_fck); -} - -static void omap_dispc_suspend(void) -{ - if (dispc.update_mode == OMAPFB_AUTO_UPDATE) { - init_completion(&dispc.frame_done); - omap_dispc_enable_lcd_out(0); - if (!wait_for_completion_timeout(&dispc.frame_done, - msecs_to_jiffies(500))) { - dev_err(dispc.fbdev->dev, - "timeout waiting for FRAME DONE\n"); - } - enable_lcd_clocks(0); - } -} - -static void omap_dispc_resume(void) -{ - if (dispc.update_mode == OMAPFB_AUTO_UPDATE) { - enable_lcd_clocks(1); - if (!dispc.ext_mode) { - set_lcd_timings(); - load_palette(); - } - omap_dispc_enable_lcd_out(1); - } -} - - -static int omap_dispc_update_window(struct fb_info *fbi, - struct omapfb_update_window *win, - void (*complete_callback)(void *arg), - void *complete_callback_data) -{ - return dispc.update_mode == OMAPFB_UPDATE_DISABLED ? -ENODEV : 0; -} - -static int mmap_kern(struct omapfb_mem_region *region) -{ - struct vm_struct *kvma; - struct vm_area_struct vma; - pgprot_t pgprot; - unsigned long vaddr; - - kvma = get_vm_area(region->size, VM_IOREMAP); - if (kvma == NULL) { - dev_err(dispc.fbdev->dev, "can't get kernel vm area\n"); - return -ENOMEM; - } - vma.vm_mm = &init_mm; - - vaddr = (unsigned long)kvma->addr; - - pgprot = pgprot_writecombine(pgprot_kernel); - vma.vm_start = vaddr; - vma.vm_end = vaddr + region->size; - if (io_remap_pfn_range(&vma, vaddr, region->paddr >> PAGE_SHIFT, - region->size, pgprot) < 0) { - dev_err(dispc.fbdev->dev, "kernel mmap for FBMEM failed\n"); - return -EAGAIN; - } - region->vaddr = (void *)vaddr; - - return 0; -} - -static void mmap_user_open(struct vm_area_struct *vma) -{ - int plane = (int)vma->vm_private_data; - - atomic_inc(&dispc.map_count[plane]); -} - -static void mmap_user_close(struct vm_area_struct *vma) -{ - int plane = (int)vma->vm_private_data; - - atomic_dec(&dispc.map_count[plane]); -} - -static const struct vm_operations_struct mmap_user_ops = { - .open = mmap_user_open, - .close = mmap_user_close, -}; - -static int omap_dispc_mmap_user(struct fb_info *info, - struct vm_area_struct *vma) -{ - struct omapfb_plane_struct *plane = info->par; - unsigned long off; - unsigned long start; - u32 len; - - if (vma->vm_end - vma->vm_start == 0) - return 0; - if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) - return -EINVAL; - off = vma->vm_pgoff << PAGE_SHIFT; - - start = info->fix.smem_start; - len = info->fix.smem_len; - if (off >= len) - return -EINVAL; - if ((vma->vm_end - vma->vm_start + off) > len) - return -EINVAL; - off += start; - vma->vm_pgoff = off >> PAGE_SHIFT; - vma->vm_flags |= VM_IO | VM_RESERVED; - vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); - vma->vm_ops = &mmap_user_ops; - vma->vm_private_data = (void *)plane->idx; - if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, - vma->vm_end - vma->vm_start, vma->vm_page_prot)) - return -EAGAIN; - /* vm_ops.open won't be called for mmap itself. */ - atomic_inc(&dispc.map_count[plane->idx]); - return 0; -} - -static void unmap_kern(struct omapfb_mem_region *region) -{ - vunmap(region->vaddr); -} - -static int alloc_palette_ram(void) -{ - dispc.palette_vaddr = dma_alloc_writecombine(dispc.fbdev->dev, - MAX_PALETTE_SIZE, &dispc.palette_paddr, GFP_KERNEL); - if (dispc.palette_vaddr == NULL) { - dev_err(dispc.fbdev->dev, "failed to alloc palette memory\n"); - return -ENOMEM; - } - - return 0; -} - -static void free_palette_ram(void) -{ - dma_free_writecombine(dispc.fbdev->dev, MAX_PALETTE_SIZE, - dispc.palette_vaddr, dispc.palette_paddr); -} - -static int alloc_fbmem(struct omapfb_mem_region *region) -{ - region->vaddr = dma_alloc_writecombine(dispc.fbdev->dev, - region->size, ®ion->paddr, GFP_KERNEL); - - if (region->vaddr == NULL) { - dev_err(dispc.fbdev->dev, "unable to allocate FB DMA memory\n"); - return -ENOMEM; - } - - return 0; -} - -static void free_fbmem(struct omapfb_mem_region *region) -{ - dma_free_writecombine(dispc.fbdev->dev, region->size, - region->vaddr, region->paddr); -} - -static struct resmap *init_resmap(unsigned long start, size_t size) -{ - unsigned page_cnt; - struct resmap *res_map; - - page_cnt = PAGE_ALIGN(size) / PAGE_SIZE; - res_map = - kzalloc(sizeof(struct resmap) + RESMAP_SIZE(page_cnt), GFP_KERNEL); - if (res_map == NULL) - return NULL; - res_map->start = start; - res_map->page_cnt = page_cnt; - res_map->map = (unsigned long *)(res_map + 1); - return res_map; -} - -static void cleanup_resmap(struct resmap *res_map) -{ - kfree(res_map); -} - -static inline int resmap_mem_type(unsigned long start) -{ - if (start >= OMAP2_SRAM_START && - start < OMAP2_SRAM_START + OMAP2_SRAM_SIZE) - return OMAPFB_MEMTYPE_SRAM; - else - return OMAPFB_MEMTYPE_SDRAM; -} - -static inline int resmap_page_reserved(struct resmap *res_map, unsigned page_nr) -{ - return *RESMAP_PTR(res_map, page_nr) & RESMAP_MASK(page_nr) ? 1 : 0; -} - -static inline void resmap_reserve_page(struct resmap *res_map, unsigned page_nr) -{ - BUG_ON(resmap_page_reserved(res_map, page_nr)); - *RESMAP_PTR(res_map, page_nr) |= RESMAP_MASK(page_nr); -} - -static inline void resmap_free_page(struct resmap *res_map, unsigned page_nr) -{ - BUG_ON(!resmap_page_reserved(res_map, page_nr)); - *RESMAP_PTR(res_map, page_nr) &= ~RESMAP_MASK(page_nr); -} - -static void resmap_reserve_region(unsigned long start, size_t size) -{ - - struct resmap *res_map; - unsigned start_page; - unsigned end_page; - int mtype; - unsigned i; - - mtype = resmap_mem_type(start); - res_map = dispc.res_map[mtype]; - dev_dbg(dispc.fbdev->dev, "reserve mem type %d start %08lx size %d\n", - mtype, start, size); - start_page = (start - res_map->start) / PAGE_SIZE; - end_page = start_page + PAGE_ALIGN(size) / PAGE_SIZE; - for (i = start_page; i < end_page; i++) - resmap_reserve_page(res_map, i); -} - -static void resmap_free_region(unsigned long start, size_t size) -{ - struct resmap *res_map; - unsigned start_page; - unsigned end_page; - unsigned i; - int mtype; - - mtype = resmap_mem_type(start); - res_map = dispc.res_map[mtype]; - dev_dbg(dispc.fbdev->dev, "free mem type %d start %08lx size %d\n", - mtype, start, size); - start_page = (start - res_map->start) / PAGE_SIZE; - end_page = start_page + PAGE_ALIGN(size) / PAGE_SIZE; - for (i = start_page; i < end_page; i++) - resmap_free_page(res_map, i); -} - -static unsigned long resmap_alloc_region(int mtype, size_t size) -{ - unsigned i; - unsigned total; - unsigned start_page; - unsigned long start; - struct resmap *res_map = dispc.res_map[mtype]; - - BUG_ON(mtype >= DISPC_MEMTYPE_NUM || res_map == NULL || !size); - - size = PAGE_ALIGN(size) / PAGE_SIZE; - start_page = 0; - total = 0; - for (i = 0; i < res_map->page_cnt; i++) { - if (resmap_page_reserved(res_map, i)) { - start_page = i + 1; - total = 0; - } else if (++total == size) - break; - } - if (total < size) - return 0; - - start = res_map->start + start_page * PAGE_SIZE; - resmap_reserve_region(start, size * PAGE_SIZE); - - return start; -} - -/* Note that this will only work for user mappings, we don't deal with - * kernel mappings here, so fbcon will keep using the old region. - */ -static int omap_dispc_setup_mem(int plane, size_t size, int mem_type, - unsigned long *paddr) -{ - struct omapfb_mem_region *rg; - unsigned long new_addr = 0; - - if ((unsigned)plane > dispc.mem_desc.region_cnt) - return -EINVAL; - if (mem_type >= DISPC_MEMTYPE_NUM) - return -EINVAL; - if (dispc.res_map[mem_type] == NULL) - return -ENOMEM; - rg = &dispc.mem_desc.region[plane]; - if (size == rg->size && mem_type == rg->type) - return 0; - if (atomic_read(&dispc.map_count[plane])) - return -EBUSY; - if (rg->size != 0) - resmap_free_region(rg->paddr, rg->size); - if (size != 0) { - new_addr = resmap_alloc_region(mem_type, size); - if (!new_addr) { - /* Reallocate old region. */ - resmap_reserve_region(rg->paddr, rg->size); - return -ENOMEM; - } - } - rg->paddr = new_addr; - rg->size = size; - rg->type = mem_type; - - *paddr = new_addr; - - return 0; -} - -static int setup_fbmem(struct omapfb_mem_desc *req_md) -{ - struct omapfb_mem_region *rg; - int i; - int r; - unsigned long mem_start[DISPC_MEMTYPE_NUM]; - unsigned long mem_end[DISPC_MEMTYPE_NUM]; - - if (!req_md->region_cnt) { - dev_err(dispc.fbdev->dev, "no memory regions defined\n"); - return -ENOENT; - } - - rg = &req_md->region[0]; - memset(mem_start, 0xff, sizeof(mem_start)); - memset(mem_end, 0, sizeof(mem_end)); - - for (i = 0; i < req_md->region_cnt; i++, rg++) { - int mtype; - if (rg->paddr) { - rg->alloc = 0; - if (rg->vaddr == NULL) { - rg->map = 1; - if ((r = mmap_kern(rg)) < 0) - return r; - } - } else { - if (rg->type != OMAPFB_MEMTYPE_SDRAM) { - dev_err(dispc.fbdev->dev, - "unsupported memory type\n"); - return -EINVAL; - } - rg->alloc = rg->map = 1; - if ((r = alloc_fbmem(rg)) < 0) - return r; - } - mtype = rg->type; - - if (rg->paddr < mem_start[mtype]) - mem_start[mtype] = rg->paddr; - if (rg->paddr + rg->size > mem_end[mtype]) - mem_end[mtype] = rg->paddr + rg->size; - } - - for (i = 0; i < DISPC_MEMTYPE_NUM; i++) { - unsigned long start; - size_t size; - if (mem_end[i] == 0) - continue; - start = mem_start[i]; - size = mem_end[i] - start; - dispc.res_map[i] = init_resmap(start, size); - r = -ENOMEM; - if (dispc.res_map[i] == NULL) - goto fail; - /* Initial state is that everything is reserved. This - * includes possible holes as well, which will never be - * freed. - */ - resmap_reserve_region(start, size); - } - - dispc.mem_desc = *req_md; - - return 0; -fail: - for (i = 0; i < DISPC_MEMTYPE_NUM; i++) { - if (dispc.res_map[i] != NULL) - cleanup_resmap(dispc.res_map[i]); - } - return r; -} - -static void cleanup_fbmem(void) -{ - struct omapfb_mem_region *rg; - int i; - - for (i = 0; i < DISPC_MEMTYPE_NUM; i++) { - if (dispc.res_map[i] != NULL) - cleanup_resmap(dispc.res_map[i]); - } - rg = &dispc.mem_desc.region[0]; - for (i = 0; i < dispc.mem_desc.region_cnt; i++, rg++) { - if (rg->alloc) - free_fbmem(rg); - else { - if (rg->map) - unmap_kern(rg); - } - } -} - -static int omap_dispc_init(struct omapfb_device *fbdev, int ext_mode, - struct omapfb_mem_desc *req_vram) -{ - int r; - u32 l; - struct lcd_panel *panel = fbdev->panel; - void __iomem *ram_fw_base; - int tmo = 10000; - int skip_init = 0; - int i; - - memset(&dispc, 0, sizeof(dispc)); - - dispc.base = ioremap(DISPC_BASE, SZ_1K); - if (!dispc.base) { - dev_err(fbdev->dev, "can't ioremap DISPC\n"); - return -ENOMEM; - } - - dispc.fbdev = fbdev; - dispc.ext_mode = ext_mode; - - init_completion(&dispc.frame_done); - - if ((r = get_dss_clocks()) < 0) - goto fail0; - - enable_lcd_clocks(1); - -#ifdef CONFIG_FB_OMAP_BOOTLOADER_INIT - l = dispc_read_reg(DISPC_CONTROL); - /* LCD enabled ? */ - if (l & 1) { - pr_info("omapfb: skipping hardware initialization\n"); - skip_init = 1; - } -#endif - - if (!skip_init) { - /* Reset monitoring works only w/ the 54M clk */ - enable_digit_clocks(1); - - /* Soft reset */ - MOD_REG_FLD(DISPC_SYSCONFIG, 1 << 1, 1 << 1); - - while (!(dispc_read_reg(DISPC_SYSSTATUS) & 1)) { - if (!--tmo) { - dev_err(dispc.fbdev->dev, "soft reset failed\n"); - r = -ENODEV; - enable_digit_clocks(0); - goto fail1; - } - } - - enable_digit_clocks(0); - } - - /* Enable smart standby/idle, autoidle and wakeup */ - l = dispc_read_reg(DISPC_SYSCONFIG); - l &= ~((3 << 12) | (3 << 3)); - l |= (2 << 12) | (2 << 3) | (1 << 2) | (1 << 0); - dispc_write_reg(DISPC_SYSCONFIG, l); - omap_writel(1 << 0, DSS_BASE + DSS_SYSCONFIG); - - /* Set functional clock autogating */ - l = dispc_read_reg(DISPC_CONFIG); - l |= 1 << 9; - dispc_write_reg(DISPC_CONFIG, l); - - l = dispc_read_reg(DISPC_IRQSTATUS); - dispc_write_reg(DISPC_IRQSTATUS, l); - - recalc_irq_mask(); - - if ((r = request_irq(INT_24XX_DSS_IRQ, omap_dispc_irq_handler, - 0, MODULE_NAME, fbdev)) < 0) { - dev_err(dispc.fbdev->dev, "can't get DSS IRQ\n"); - goto fail1; - } - - /* L3 firewall setting: enable access to OCM RAM */ - ram_fw_base = ioremap(0x68005000, SZ_1K); - if (!ram_fw_base) { - dev_err(dispc.fbdev->dev, "Cannot ioremap to enable OCM RAM\n"); - goto fail1; - } - __raw_writel(0x402000b0, ram_fw_base + 0xa0); - iounmap(ram_fw_base); - - if ((r = alloc_palette_ram()) < 0) - goto fail2; - - if ((r = setup_fbmem(req_vram)) < 0) - goto fail3; - - if (!skip_init) { - for (i = 0; i < dispc.mem_desc.region_cnt; i++) { - memset(dispc.mem_desc.region[i].vaddr, 0, - dispc.mem_desc.region[i].size); - } - - /* Set logic clock to fck, pixel clock to fck/2 for now */ - MOD_REG_FLD(DISPC_DIVISOR, FLD_MASK(16, 8), 1 << 16); - MOD_REG_FLD(DISPC_DIVISOR, FLD_MASK(0, 8), 2 << 0); - - setup_plane_fifo(0, ext_mode); - setup_plane_fifo(1, ext_mode); - setup_plane_fifo(2, ext_mode); - - setup_color_conv_coef(); - - set_lcd_tft_mode(panel->config & OMAP_LCDC_PANEL_TFT); - set_load_mode(DISPC_LOAD_FRAME_ONLY); - - if (!ext_mode) { - set_lcd_data_lines(panel->data_lines); - omap_dispc_set_lcd_size(panel->x_res, panel->y_res); - set_lcd_timings(); - } else - set_lcd_data_lines(panel->bpp); - enable_rfbi_mode(ext_mode); - } - - l = dispc_read_reg(DISPC_REVISION); - pr_info("omapfb: DISPC version %d.%d initialized\n", - l >> 4 & 0x0f, l & 0x0f); - enable_lcd_clocks(0); - - return 0; -fail3: - free_palette_ram(); -fail2: - free_irq(INT_24XX_DSS_IRQ, fbdev); -fail1: - enable_lcd_clocks(0); - put_dss_clocks(); -fail0: - iounmap(dispc.base); - return r; -} - -static void omap_dispc_cleanup(void) -{ - int i; - - omap_dispc_set_update_mode(OMAPFB_UPDATE_DISABLED); - /* This will also disable clocks that are on */ - for (i = 0; i < dispc.mem_desc.region_cnt; i++) - omap_dispc_enable_plane(i, 0); - cleanup_fbmem(); - free_palette_ram(); - free_irq(INT_24XX_DSS_IRQ, dispc.fbdev); - put_dss_clocks(); - iounmap(dispc.base); -} - -const struct lcd_ctrl omap2_int_ctrl = { - .name = "internal", - .init = omap_dispc_init, - .cleanup = omap_dispc_cleanup, - .get_caps = omap_dispc_get_caps, - .set_update_mode = omap_dispc_set_update_mode, - .get_update_mode = omap_dispc_get_update_mode, - .update_window = omap_dispc_update_window, - .suspend = omap_dispc_suspend, - .resume = omap_dispc_resume, - .setup_plane = omap_dispc_setup_plane, - .setup_mem = omap_dispc_setup_mem, - .set_scale = omap_dispc_set_scale, - .enable_plane = omap_dispc_enable_plane, - .set_color_key = omap_dispc_set_color_key, - .get_color_key = omap_dispc_get_color_key, - .mmap = omap_dispc_mmap_user, -}; diff --git a/drivers/video/omap/dispc.h b/drivers/video/omap/dispc.h deleted file mode 100644 index c15ea77f0604..000000000000 --- a/drivers/video/omap/dispc.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef _DISPC_H -#define _DISPC_H - -#include <linux/interrupt.h> - -#define DISPC_PLANE_GFX 0 -#define DISPC_PLANE_VID1 1 -#define DISPC_PLANE_VID2 2 - -#define DISPC_RGB_1_BPP 0x00 -#define DISPC_RGB_2_BPP 0x01 -#define DISPC_RGB_4_BPP 0x02 -#define DISPC_RGB_8_BPP 0x03 -#define DISPC_RGB_12_BPP 0x04 -#define DISPC_RGB_16_BPP 0x06 -#define DISPC_RGB_24_BPP 0x08 -#define DISPC_RGB_24_BPP_UNPACK_32 0x09 -#define DISPC_YUV2_422 0x0a -#define DISPC_UYVY_422 0x0b - -#define DISPC_BURST_4x32 0 -#define DISPC_BURST_8x32 1 -#define DISPC_BURST_16x32 2 - -#define DISPC_LOAD_CLUT_AND_FRAME 0x00 -#define DISPC_LOAD_CLUT_ONLY 0x01 -#define DISPC_LOAD_FRAME_ONLY 0x02 -#define DISPC_LOAD_CLUT_ONCE_FRAME 0x03 - -#define DISPC_TFT_DATA_LINES_12 0 -#define DISPC_TFT_DATA_LINES_16 1 -#define DISPC_TFT_DATA_LINES_18 2 -#define DISPC_TFT_DATA_LINES_24 3 - -extern void omap_dispc_set_lcd_size(int width, int height); - -extern void omap_dispc_enable_lcd_out(int enable); -extern void omap_dispc_enable_digit_out(int enable); - -extern int omap_dispc_request_irq(unsigned long irq_mask, - void (*callback)(void *data), void *data); -extern void omap_dispc_free_irq(unsigned long irq_mask, - void (*callback)(void *data), void *data); - -extern const struct lcd_ctrl omap2_int_ctrl; -#endif diff --git a/drivers/video/omap/hwa742.c b/drivers/video/omap/hwa742.c index 084aa0ac562b..9f1d23c319cb 100644 --- a/drivers/video/omap/hwa742.c +++ b/drivers/video/omap/hwa742.c @@ -28,7 +28,6 @@ #include <linux/interrupt.h> #include <plat/dma.h> -#include <plat/hwa742.h> #include "omapfb.h" #define HWA742_REV_CODE_REG 0x0 @@ -942,7 +941,6 @@ static int hwa742_init(struct omapfb_device *fbdev, int ext_mode, unsigned long sys_clk, pix_clk; int extif_mem_div; struct omapfb_platform_data *omapfb_conf; - struct hwa742_platform_data *ctrl_conf; BUG_ON(!fbdev->ext_if || !fbdev->int_ctrl); @@ -951,13 +949,6 @@ static int hwa742_init(struct omapfb_device *fbdev, int ext_mode, hwa742.int_ctrl = fbdev->int_ctrl; omapfb_conf = fbdev->dev->platform_data; - ctrl_conf = omapfb_conf->ctrl_platform_data; - - if (ctrl_conf == NULL) { - dev_err(fbdev->dev, "HWA742: missing platform data\n"); - r = -ENOENT; - goto err1; - } hwa742.sys_ck = clk_get(NULL, "hwa_sys_ck"); @@ -995,14 +986,12 @@ static int hwa742_init(struct omapfb_device *fbdev, int ext_mode, goto err4; } - if (ctrl_conf->te_connected) { - if ((r = setup_tearsync(pix_clk, extif_mem_div)) < 0) { - dev_err(hwa742.fbdev->dev, - "HWA742: can't setup tearing synchronization\n"); - goto err4; - } - hwa742.te_connected = 1; + if ((r = setup_tearsync(pix_clk, extif_mem_div)) < 0) { + dev_err(hwa742.fbdev->dev, + "HWA742: can't setup tearing synchronization\n"); + goto err4; } + hwa742.te_connected = 1; hwa742.max_transmit_size = hwa742.extif->max_transmit_size; diff --git a/drivers/video/omap/lcd_ams_delta.c b/drivers/video/omap/lcd_ams_delta.c index 0fdd6f6873bf..d3a311327227 100644 --- a/drivers/video/omap/lcd_ams_delta.c +++ b/drivers/video/omap/lcd_ams_delta.c @@ -25,6 +25,7 @@ #include <linux/io.h> #include <linux/delay.h> #include <linux/lcd.h> +#include <linux/gpio.h> #include <plat/board-ams-delta.h> #include <mach/hardware.h> @@ -98,29 +99,41 @@ static struct lcd_ops ams_delta_lcd_ops = { /* omapfb panel section */ +static const struct gpio _gpios[] = { + { + .gpio = AMS_DELTA_GPIO_PIN_LCD_VBLEN, + .flags = GPIOF_OUT_INIT_LOW, + .label = "lcd_vblen", + }, + { + .gpio = AMS_DELTA_GPIO_PIN_LCD_NDISP, + .flags = GPIOF_OUT_INIT_LOW, + .label = "lcd_ndisp", + }, +}; + static int ams_delta_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev) { - return 0; + return gpio_request_array(_gpios, ARRAY_SIZE(_gpios)); } static void ams_delta_panel_cleanup(struct lcd_panel *panel) { + gpio_free_array(_gpios, ARRAY_SIZE(_gpios)); } static int ams_delta_panel_enable(struct lcd_panel *panel) { - ams_delta_latch2_write(AMS_DELTA_LATCH2_LCD_NDISP, - AMS_DELTA_LATCH2_LCD_NDISP); - ams_delta_latch2_write(AMS_DELTA_LATCH2_LCD_VBLEN, - AMS_DELTA_LATCH2_LCD_VBLEN); + gpio_set_value(AMS_DELTA_GPIO_PIN_LCD_NDISP, 1); + gpio_set_value(AMS_DELTA_GPIO_PIN_LCD_VBLEN, 1); return 0; } static void ams_delta_panel_disable(struct lcd_panel *panel) { - ams_delta_latch2_write(AMS_DELTA_LATCH2_LCD_VBLEN, 0); - ams_delta_latch2_write(AMS_DELTA_LATCH2_LCD_NDISP, 0); + gpio_set_value(AMS_DELTA_GPIO_PIN_LCD_VBLEN, 0); + gpio_set_value(AMS_DELTA_GPIO_PIN_LCD_NDISP, 0); } static unsigned long ams_delta_panel_get_caps(struct lcd_panel *panel) diff --git a/drivers/video/omap/lcd_inn1610.c b/drivers/video/omap/lcd_inn1610.c index 7e8bd8e08a98..e3d3d135aa48 100644 --- a/drivers/video/omap/lcd_inn1610.c +++ b/drivers/video/omap/lcd_inn1610.c @@ -22,7 +22,7 @@ #include <linux/module.h> #include <linux/platform_device.h> -#include <asm/gpio.h> +#include <linux/gpio.h> #include "omapfb.h" #define MODULE_NAME "omapfb-lcd_h3" @@ -32,20 +32,18 @@ static int innovator1610_panel_init(struct lcd_panel *panel, { int r = 0; - if (gpio_request(14, "lcd_en0")) { + /* configure GPIO(14, 15) as outputs */ + if (gpio_request_one(14, GPIOF_OUT_INIT_LOW, "lcd_en0")) { pr_err(MODULE_NAME ": can't request GPIO 14\n"); r = -1; goto exit; } - if (gpio_request(15, "lcd_en1")) { + if (gpio_request_one(15, GPIOF_OUT_INIT_LOW, "lcd_en1")) { pr_err(MODULE_NAME ": can't request GPIO 15\n"); gpio_free(14); r = -1; goto exit; } - /* configure GPIO(14, 15) as outputs */ - gpio_direction_output(14, 0); - gpio_direction_output(15, 0); exit: return r; } diff --git a/drivers/video/omap/lcd_mipid.c b/drivers/video/omap/lcd_mipid.c index 8d546dd55e81..e3880c4a0bb1 100644 --- a/drivers/video/omap/lcd_mipid.c +++ b/drivers/video/omap/lcd_mipid.c @@ -609,19 +609,7 @@ static struct spi_driver mipid_spi_driver = { .remove = __devexit_p(mipid_spi_remove), }; -static int __init mipid_drv_init(void) -{ - spi_register_driver(&mipid_spi_driver); - - return 0; -} -module_init(mipid_drv_init); - -static void __exit mipid_drv_cleanup(void) -{ - spi_unregister_driver(&mipid_spi_driver); -} -module_exit(mipid_drv_cleanup); +module_spi_driver(mipid_spi_driver); MODULE_DESCRIPTION("MIPI display driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap/omapfb.h b/drivers/video/omap/omapfb.h index af3c9e571ec3..2921d20e4fba 100644 --- a/drivers/video/omap/omapfb.h +++ b/drivers/video/omap/omapfb.h @@ -47,6 +47,27 @@ struct omapfb_device; +#define OMAPFB_PLANE_NUM 1 + +struct omapfb_mem_region { + u32 paddr; + void __iomem *vaddr; + unsigned long size; + u8 type; /* OMAPFB_PLANE_MEM_* */ + enum omapfb_color_format format;/* OMAPFB_COLOR_* */ + unsigned format_used:1; /* Must be set when format is set. + * Needed b/c of the badly chosen 0 + * base for OMAPFB_COLOR_* values + */ + unsigned alloc:1; /* allocated by the driver */ + unsigned map:1; /* kernel mapped by the driver */ +}; + +struct omapfb_mem_desc { + int region_cnt; + struct omapfb_mem_region region[OMAPFB_PLANE_NUM]; +}; + struct lcd_panel { const char *name; int config; /* TFT/STN, signal inversion */ @@ -207,11 +228,7 @@ struct omapfb_device { struct platform_device *dssdev; /* dummy dev for clocks */ }; -#ifdef CONFIG_ARCH_OMAP1 extern struct lcd_ctrl omap1_lcd_ctrl; -#else -extern struct lcd_ctrl omap2_disp_ctrl; -#endif extern void omapfb_register_panel(struct lcd_panel *panel); extern void omapfb_write_first_pixel(struct omapfb_device *fbdev, u16 pixval); diff --git a/drivers/video/omap/omapfb_main.c b/drivers/video/omap/omapfb_main.c index b291bfaac80e..f54b463709e9 100644 --- a/drivers/video/omap/omapfb_main.c +++ b/drivers/video/omap/omapfb_main.c @@ -34,7 +34,6 @@ #include "omapfb.h" #include "lcdc.h" -#include "dispc.h" #define MODULE_NAME "omapfb" @@ -104,29 +103,17 @@ static struct platform_device omapdss_device = { * --------------------------------------------------------------------------- */ extern struct lcd_ctrl hwa742_ctrl; -extern struct lcd_ctrl blizzard_ctrl; static const struct lcd_ctrl *ctrls[] = { -#ifdef CONFIG_ARCH_OMAP1 &omap1_int_ctrl, -#else - &omap2_int_ctrl, -#endif #ifdef CONFIG_FB_OMAP_LCDC_HWA742 &hwa742_ctrl, #endif -#ifdef CONFIG_FB_OMAP_LCDC_BLIZZARD - &blizzard_ctrl, -#endif }; #ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL -#ifdef CONFIG_ARCH_OMAP1 extern struct lcd_ctrl_extif omap1_ext_if; -#else -extern struct lcd_ctrl_extif omap2_ext_if; -#endif #endif static void omapfb_rqueue_lock(struct omapfb_device *fbdev) @@ -170,11 +157,6 @@ static int ctrl_init(struct omapfb_device *fbdev) fbdev->mem_desc.region[i].size = PAGE_ALIGN(def_vram[i]); fbdev->mem_desc.region_cnt = i; - } else { - struct omapfb_platform_data *conf; - - conf = fbdev->dev->platform_data; - fbdev->mem_desc = conf->mem_desc; } if (!fbdev->mem_desc.region_cnt) { @@ -880,7 +862,7 @@ static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi) if (fbdev->ctrl->setup_mem == NULL) return -ENODEV; - if (mi->type > OMAPFB_MEMTYPE_MAX) + if (mi->type != OMAPFB_MEMTYPE_SDRAM) return -EINVAL; size = PAGE_ALIGN(mi->size); @@ -1721,17 +1703,10 @@ static int omapfb_do_probe(struct platform_device *pdev, mutex_init(&fbdev->rqueue_mutex); -#ifdef CONFIG_ARCH_OMAP1 fbdev->int_ctrl = &omap1_int_ctrl; #ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL fbdev->ext_if = &omap1_ext_if; #endif -#else /* OMAP2 */ - fbdev->int_ctrl = &omap2_int_ctrl; -#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL - fbdev->ext_if = &omap2_ext_if; -#endif -#endif if (omapfb_find_ctrl(fbdev) < 0) { dev_err(fbdev->dev, "LCD controller not found, board not supported\n"); @@ -1766,8 +1741,7 @@ static int omapfb_do_probe(struct platform_device *pdev, #ifdef CONFIG_FB_OMAP_DMA_TUNE /* Set DMA priority for EMIFF access to highest */ - if (cpu_class_is_omap1()) - omap_set_dma_priority(0, OMAP_DMA_PORT_EMIFF, 15); + omap_set_dma_priority(0, OMAP_DMA_PORT_EMIFF, 15); #endif r = ctrl_change_mode(fbdev->fb_info[0]); diff --git a/drivers/video/omap/rfbi.c b/drivers/video/omap/rfbi.c deleted file mode 100644 index 2c1a3402bef0..000000000000 --- a/drivers/video/omap/rfbi.c +++ /dev/null @@ -1,598 +0,0 @@ -/* - * OMAP2 Remote Frame Buffer Interface support - * - * Copyright (C) 2005 Nokia Corporation - * Author: Juha Yrjölä <juha.yrjola@nokia.com> - * Imre Deak <imre.deak@nokia.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/i2c.h> -#include <linux/err.h> -#include <linux/interrupt.h> -#include <linux/clk.h> -#include <linux/io.h> -#include <linux/platform_device.h> - -#include "omapfb.h" -#include "dispc.h" - -/* To work around an RFBI transfer rate limitation */ -#define OMAP_RFBI_RATE_LIMIT 1 - -#define RFBI_BASE 0x48050800 -#define RFBI_REVISION 0x0000 -#define RFBI_SYSCONFIG 0x0010 -#define RFBI_SYSSTATUS 0x0014 -#define RFBI_CONTROL 0x0040 -#define RFBI_PIXEL_CNT 0x0044 -#define RFBI_LINE_NUMBER 0x0048 -#define RFBI_CMD 0x004c -#define RFBI_PARAM 0x0050 -#define RFBI_DATA 0x0054 -#define RFBI_READ 0x0058 -#define RFBI_STATUS 0x005c -#define RFBI_CONFIG0 0x0060 -#define RFBI_ONOFF_TIME0 0x0064 -#define RFBI_CYCLE_TIME0 0x0068 -#define RFBI_DATA_CYCLE1_0 0x006c -#define RFBI_DATA_CYCLE2_0 0x0070 -#define RFBI_DATA_CYCLE3_0 0x0074 -#define RFBI_VSYNC_WIDTH 0x0090 -#define RFBI_HSYNC_WIDTH 0x0094 - -#define DISPC_BASE 0x48050400 -#define DISPC_CONTROL 0x0040 -#define DISPC_IRQ_FRAMEMASK 0x0001 - -static struct { - void __iomem *base; - void (*lcdc_callback)(void *data); - void *lcdc_callback_data; - unsigned long l4_khz; - int bits_per_cycle; - struct omapfb_device *fbdev; - struct clk *dss_ick; - struct clk *dss1_fck; - unsigned tearsync_pin_cnt; - unsigned tearsync_mode; -} rfbi; - -static inline void rfbi_write_reg(int idx, u32 val) -{ - __raw_writel(val, rfbi.base + idx); -} - -static inline u32 rfbi_read_reg(int idx) -{ - return __raw_readl(rfbi.base + idx); -} - -static int rfbi_get_clocks(void) -{ - rfbi.dss_ick = clk_get(&rfbi.fbdev->dssdev->dev, "ick"); - if (IS_ERR(rfbi.dss_ick)) { - dev_err(rfbi.fbdev->dev, "can't get ick\n"); - return PTR_ERR(rfbi.dss_ick); - } - - rfbi.dss1_fck = clk_get(&rfbi.fbdev->dssdev->dev, "fck"); - if (IS_ERR(rfbi.dss1_fck)) { - dev_err(rfbi.fbdev->dev, "can't get dss1_fck\n"); - clk_put(rfbi.dss_ick); - return PTR_ERR(rfbi.dss1_fck); - } - - return 0; -} - -static void rfbi_put_clocks(void) -{ - clk_put(rfbi.dss1_fck); - clk_put(rfbi.dss_ick); -} - -static void rfbi_enable_clocks(int enable) -{ - if (enable) { - clk_enable(rfbi.dss_ick); - clk_enable(rfbi.dss1_fck); - } else { - clk_disable(rfbi.dss1_fck); - clk_disable(rfbi.dss_ick); - } -} - - -#ifdef VERBOSE -static void rfbi_print_timings(void) -{ - u32 l; - u32 time; - - l = rfbi_read_reg(RFBI_CONFIG0); - time = 1000000000 / rfbi.l4_khz; - if (l & (1 << 4)) - time *= 2; - - dev_dbg(rfbi.fbdev->dev, "Tick time %u ps\n", time); - l = rfbi_read_reg(RFBI_ONOFF_TIME0); - dev_dbg(rfbi.fbdev->dev, - "CSONTIME %d, CSOFFTIME %d, WEONTIME %d, WEOFFTIME %d, " - "REONTIME %d, REOFFTIME %d\n", - l & 0x0f, (l >> 4) & 0x3f, (l >> 10) & 0x0f, (l >> 14) & 0x3f, - (l >> 20) & 0x0f, (l >> 24) & 0x3f); - - l = rfbi_read_reg(RFBI_CYCLE_TIME0); - dev_dbg(rfbi.fbdev->dev, - "WECYCLETIME %d, RECYCLETIME %d, CSPULSEWIDTH %d, " - "ACCESSTIME %d\n", - (l & 0x3f), (l >> 6) & 0x3f, (l >> 12) & 0x3f, - (l >> 22) & 0x3f); -} -#else -static void rfbi_print_timings(void) {} -#endif - -static void rfbi_set_timings(const struct extif_timings *t) -{ - u32 l; - - BUG_ON(!t->converted); - - rfbi_enable_clocks(1); - rfbi_write_reg(RFBI_ONOFF_TIME0, t->tim[0]); - rfbi_write_reg(RFBI_CYCLE_TIME0, t->tim[1]); - - l = rfbi_read_reg(RFBI_CONFIG0); - l &= ~(1 << 4); - l |= (t->tim[2] ? 1 : 0) << 4; - rfbi_write_reg(RFBI_CONFIG0, l); - - rfbi_print_timings(); - rfbi_enable_clocks(0); -} - -static void rfbi_get_clk_info(u32 *clk_period, u32 *max_clk_div) -{ - *clk_period = 1000000000 / rfbi.l4_khz; - *max_clk_div = 2; -} - -static int ps_to_rfbi_ticks(int time, int div) -{ - unsigned long tick_ps; - int ret; - - /* Calculate in picosecs to yield more exact results */ - tick_ps = 1000000000 / (rfbi.l4_khz) * div; - - ret = (time + tick_ps - 1) / tick_ps; - - return ret; -} - -#ifdef OMAP_RFBI_RATE_LIMIT -static unsigned long rfbi_get_max_tx_rate(void) -{ - unsigned long l4_rate, dss1_rate; - int min_l4_ticks = 0; - int i; - - /* According to TI this can't be calculated so make the - * adjustments for a couple of known frequencies and warn for - * others. - */ - static const struct { - unsigned long l4_clk; /* HZ */ - unsigned long dss1_clk; /* HZ */ - unsigned long min_l4_ticks; - } ftab[] = { - { 55, 132, 7, }, /* 7.86 MPix/s */ - { 110, 110, 12, }, /* 9.16 MPix/s */ - { 110, 132, 10, }, /* 11 Mpix/s */ - { 120, 120, 10, }, /* 12 Mpix/s */ - { 133, 133, 10, }, /* 13.3 Mpix/s */ - }; - - l4_rate = rfbi.l4_khz / 1000; - dss1_rate = clk_get_rate(rfbi.dss1_fck) / 1000000; - - for (i = 0; i < ARRAY_SIZE(ftab); i++) { - /* Use a window instead of an exact match, to account - * for different DPLL multiplier / divider pairs. - */ - if (abs(ftab[i].l4_clk - l4_rate) < 3 && - abs(ftab[i].dss1_clk - dss1_rate) < 3) { - min_l4_ticks = ftab[i].min_l4_ticks; - break; - } - } - if (i == ARRAY_SIZE(ftab)) { - /* Can't be sure, return anyway the maximum not - * rate-limited. This might cause a problem only for the - * tearing synchronisation. - */ - dev_err(rfbi.fbdev->dev, - "can't determine maximum RFBI transfer rate\n"); - return rfbi.l4_khz * 1000; - } - return rfbi.l4_khz * 1000 / min_l4_ticks; -} -#else -static int rfbi_get_max_tx_rate(void) -{ - return rfbi.l4_khz * 1000; -} -#endif - - -static int rfbi_convert_timings(struct extif_timings *t) -{ - u32 l; - int reon, reoff, weon, weoff, cson, csoff, cs_pulse; - int actim, recyc, wecyc; - int div = t->clk_div; - - if (div <= 0 || div > 2) - return -1; - - /* Make sure that after conversion it still holds that: - * weoff > weon, reoff > reon, recyc >= reoff, wecyc >= weoff, - * csoff > cson, csoff >= max(weoff, reoff), actim > reon - */ - weon = ps_to_rfbi_ticks(t->we_on_time, div); - weoff = ps_to_rfbi_ticks(t->we_off_time, div); - if (weoff <= weon) - weoff = weon + 1; - if (weon > 0x0f) - return -1; - if (weoff > 0x3f) - return -1; - - reon = ps_to_rfbi_ticks(t->re_on_time, div); - reoff = ps_to_rfbi_ticks(t->re_off_time, div); - if (reoff <= reon) - reoff = reon + 1; - if (reon > 0x0f) - return -1; - if (reoff > 0x3f) - return -1; - - cson = ps_to_rfbi_ticks(t->cs_on_time, div); - csoff = ps_to_rfbi_ticks(t->cs_off_time, div); - if (csoff <= cson) - csoff = cson + 1; - if (csoff < max(weoff, reoff)) - csoff = max(weoff, reoff); - if (cson > 0x0f) - return -1; - if (csoff > 0x3f) - return -1; - - l = cson; - l |= csoff << 4; - l |= weon << 10; - l |= weoff << 14; - l |= reon << 20; - l |= reoff << 24; - - t->tim[0] = l; - - actim = ps_to_rfbi_ticks(t->access_time, div); - if (actim <= reon) - actim = reon + 1; - if (actim > 0x3f) - return -1; - - wecyc = ps_to_rfbi_ticks(t->we_cycle_time, div); - if (wecyc < weoff) - wecyc = weoff; - if (wecyc > 0x3f) - return -1; - - recyc = ps_to_rfbi_ticks(t->re_cycle_time, div); - if (recyc < reoff) - recyc = reoff; - if (recyc > 0x3f) - return -1; - - cs_pulse = ps_to_rfbi_ticks(t->cs_pulse_width, div); - if (cs_pulse > 0x3f) - return -1; - - l = wecyc; - l |= recyc << 6; - l |= cs_pulse << 12; - l |= actim << 22; - - t->tim[1] = l; - - t->tim[2] = div - 1; - - t->converted = 1; - - return 0; -} - -static int rfbi_setup_tearsync(unsigned pin_cnt, - unsigned hs_pulse_time, unsigned vs_pulse_time, - int hs_pol_inv, int vs_pol_inv, int extif_div) -{ - int hs, vs; - int min; - u32 l; - - if (pin_cnt != 1 && pin_cnt != 2) - return -EINVAL; - - hs = ps_to_rfbi_ticks(hs_pulse_time, 1); - vs = ps_to_rfbi_ticks(vs_pulse_time, 1); - if (hs < 2) - return -EDOM; - if (pin_cnt == 2) - min = 2; - else - min = 4; - if (vs < min) - return -EDOM; - if (vs == hs) - return -EINVAL; - rfbi.tearsync_pin_cnt = pin_cnt; - dev_dbg(rfbi.fbdev->dev, - "setup_tearsync: pins %d hs %d vs %d hs_inv %d vs_inv %d\n", - pin_cnt, hs, vs, hs_pol_inv, vs_pol_inv); - - rfbi_enable_clocks(1); - rfbi_write_reg(RFBI_HSYNC_WIDTH, hs); - rfbi_write_reg(RFBI_VSYNC_WIDTH, vs); - - l = rfbi_read_reg(RFBI_CONFIG0); - if (hs_pol_inv) - l &= ~(1 << 21); - else - l |= 1 << 21; - if (vs_pol_inv) - l &= ~(1 << 20); - else - l |= 1 << 20; - rfbi_enable_clocks(0); - - return 0; -} - -static int rfbi_enable_tearsync(int enable, unsigned line) -{ - u32 l; - - dev_dbg(rfbi.fbdev->dev, "tearsync %d line %d mode %d\n", - enable, line, rfbi.tearsync_mode); - if (line > (1 << 11) - 1) - return -EINVAL; - - rfbi_enable_clocks(1); - l = rfbi_read_reg(RFBI_CONFIG0); - l &= ~(0x3 << 2); - if (enable) { - rfbi.tearsync_mode = rfbi.tearsync_pin_cnt; - l |= rfbi.tearsync_mode << 2; - } else - rfbi.tearsync_mode = 0; - rfbi_write_reg(RFBI_CONFIG0, l); - rfbi_write_reg(RFBI_LINE_NUMBER, line); - rfbi_enable_clocks(0); - - return 0; -} - -static void rfbi_write_command(const void *buf, unsigned int len) -{ - rfbi_enable_clocks(1); - if (rfbi.bits_per_cycle == 16) { - const u16 *w = buf; - BUG_ON(len & 1); - for (; len; len -= 2) - rfbi_write_reg(RFBI_CMD, *w++); - } else { - const u8 *b = buf; - BUG_ON(rfbi.bits_per_cycle != 8); - for (; len; len--) - rfbi_write_reg(RFBI_CMD, *b++); - } - rfbi_enable_clocks(0); -} - -static void rfbi_read_data(void *buf, unsigned int len) -{ - rfbi_enable_clocks(1); - if (rfbi.bits_per_cycle == 16) { - u16 *w = buf; - BUG_ON(len & ~1); - for (; len; len -= 2) { - rfbi_write_reg(RFBI_READ, 0); - *w++ = rfbi_read_reg(RFBI_READ); - } - } else { - u8 *b = buf; - BUG_ON(rfbi.bits_per_cycle != 8); - for (; len; len--) { - rfbi_write_reg(RFBI_READ, 0); - *b++ = rfbi_read_reg(RFBI_READ); - } - } - rfbi_enable_clocks(0); -} - -static void rfbi_write_data(const void *buf, unsigned int len) -{ - rfbi_enable_clocks(1); - if (rfbi.bits_per_cycle == 16) { - const u16 *w = buf; - BUG_ON(len & 1); - for (; len; len -= 2) - rfbi_write_reg(RFBI_PARAM, *w++); - } else { - const u8 *b = buf; - BUG_ON(rfbi.bits_per_cycle != 8); - for (; len; len--) - rfbi_write_reg(RFBI_PARAM, *b++); - } - rfbi_enable_clocks(0); -} - -static void rfbi_transfer_area(int width, int height, - void (callback)(void * data), void *data) -{ - u32 w; - - BUG_ON(callback == NULL); - - rfbi_enable_clocks(1); - omap_dispc_set_lcd_size(width, height); - - rfbi.lcdc_callback = callback; - rfbi.lcdc_callback_data = data; - - rfbi_write_reg(RFBI_PIXEL_CNT, width * height); - - w = rfbi_read_reg(RFBI_CONTROL); - w |= 1; /* enable */ - if (!rfbi.tearsync_mode) - w |= 1 << 4; /* internal trigger, reset by HW */ - rfbi_write_reg(RFBI_CONTROL, w); - - omap_dispc_enable_lcd_out(1); -} - -static inline void _stop_transfer(void) -{ - u32 w; - - w = rfbi_read_reg(RFBI_CONTROL); - rfbi_write_reg(RFBI_CONTROL, w & ~(1 << 0)); - rfbi_enable_clocks(0); -} - -static void rfbi_dma_callback(void *data) -{ - _stop_transfer(); - rfbi.lcdc_callback(rfbi.lcdc_callback_data); -} - -static void rfbi_set_bits_per_cycle(int bpc) -{ - u32 l; - - rfbi_enable_clocks(1); - l = rfbi_read_reg(RFBI_CONFIG0); - l &= ~(0x03 << 0); - - switch (bpc) { - case 8: - break; - case 16: - l |= 3; - break; - default: - BUG(); - } - rfbi_write_reg(RFBI_CONFIG0, l); - rfbi.bits_per_cycle = bpc; - rfbi_enable_clocks(0); -} - -static int rfbi_init(struct omapfb_device *fbdev) -{ - u32 l; - int r; - - rfbi.fbdev = fbdev; - rfbi.base = ioremap(RFBI_BASE, SZ_1K); - if (!rfbi.base) { - dev_err(fbdev->dev, "can't ioremap RFBI\n"); - return -ENOMEM; - } - - if ((r = rfbi_get_clocks()) < 0) - return r; - rfbi_enable_clocks(1); - - rfbi.l4_khz = clk_get_rate(rfbi.dss_ick) / 1000; - - /* Reset */ - rfbi_write_reg(RFBI_SYSCONFIG, 1 << 1); - while (!(rfbi_read_reg(RFBI_SYSSTATUS) & (1 << 0))); - - l = rfbi_read_reg(RFBI_SYSCONFIG); - /* Enable autoidle and smart-idle */ - l |= (1 << 0) | (2 << 3); - rfbi_write_reg(RFBI_SYSCONFIG, l); - - /* 16-bit interface, ITE trigger mode, 16-bit data */ - l = (0x03 << 0) | (0x00 << 2) | (0x01 << 5) | (0x02 << 7); - l |= (0 << 9) | (1 << 20) | (1 << 21); - rfbi_write_reg(RFBI_CONFIG0, l); - - rfbi_write_reg(RFBI_DATA_CYCLE1_0, 0x00000010); - - l = rfbi_read_reg(RFBI_CONTROL); - /* Select CS0, clear bypass mode */ - l = (0x01 << 2); - rfbi_write_reg(RFBI_CONTROL, l); - - r = omap_dispc_request_irq(DISPC_IRQ_FRAMEMASK, rfbi_dma_callback, - NULL); - if (r < 0) { - dev_err(fbdev->dev, "can't get DISPC irq\n"); - rfbi_enable_clocks(0); - return r; - } - - l = rfbi_read_reg(RFBI_REVISION); - pr_info("omapfb: RFBI version %d.%d initialized\n", - (l >> 4) & 0x0f, l & 0x0f); - - rfbi_enable_clocks(0); - - return 0; -} - -static void rfbi_cleanup(void) -{ - omap_dispc_free_irq(DISPC_IRQ_FRAMEMASK, rfbi_dma_callback, NULL); - rfbi_put_clocks(); - iounmap(rfbi.base); -} - -const struct lcd_ctrl_extif omap2_ext_if = { - .init = rfbi_init, - .cleanup = rfbi_cleanup, - .get_clk_info = rfbi_get_clk_info, - .get_max_tx_rate = rfbi_get_max_tx_rate, - .set_bits_per_cycle = rfbi_set_bits_per_cycle, - .convert_timings = rfbi_convert_timings, - .set_timings = rfbi_set_timings, - .write_command = rfbi_write_command, - .read_data = rfbi_read_data, - .write_data = rfbi_write_data, - .transfer_area = rfbi_transfer_area, - .setup_tearsync = rfbi_setup_tearsync, - .enable_tearsync = rfbi_enable_tearsync, - - .max_transmit_size = (u32) ~0, -}; - diff --git a/drivers/video/omap2/displays/panel-acx565akm.c b/drivers/video/omap2/displays/panel-acx565akm.c index 51a87e149e24..d26f37ac69d8 100644 --- a/drivers/video/omap2/displays/panel-acx565akm.c +++ b/drivers/video/omap2/displays/panel-acx565akm.c @@ -809,18 +809,7 @@ static struct spi_driver acx565akm_spi_driver = { .remove = __devexit_p(acx565akm_spi_remove), }; -static int __init acx565akm_init(void) -{ - return spi_register_driver(&acx565akm_spi_driver); -} - -static void __exit acx565akm_exit(void) -{ - spi_unregister_driver(&acx565akm_spi_driver); -} - -module_init(acx565akm_init); -module_exit(acx565akm_exit); +module_spi_driver(acx565akm_spi_driver); MODULE_AUTHOR("Nokia Corporation"); MODULE_DESCRIPTION("acx565akm LCD Driver"); diff --git a/drivers/video/omap2/displays/panel-generic-dpi.c b/drivers/video/omap2/displays/panel-generic-dpi.c index 28b9a6d61b0f..30fe4dfeb227 100644 --- a/drivers/video/omap2/displays/panel-generic-dpi.c +++ b/drivers/video/omap2/displays/panel-generic-dpi.c @@ -363,6 +363,29 @@ static struct panel_config generic_dpi_panels[] = { .name = "ortustech_com43h4m10xtc", }, + + /* Innolux AT080TN52 */ + { + { + .x_res = 800, + .y_res = 600, + + .pixel_clock = 41142, + + .hsw = 20, + .hfp = 210, + .hbp = 46, + + .vsw = 10, + .vfp = 12, + .vbp = 23, + }, + .acb = 0x0, + .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | + OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IEO, + + .name = "innolux_at080tn52", + }, }; struct panel_drv_data { diff --git a/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c b/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c index e0eb35be303e..0841cc2b3f77 100644 --- a/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c +++ b/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c @@ -264,16 +264,6 @@ static struct spi_driver lb035q02_spi_driver = { .remove = __devexit_p(lb035q02_panel_spi_remove), }; -static int __init lb035q02_panel_drv_init(void) -{ - return spi_register_driver(&lb035q02_spi_driver); -} - -static void __exit lb035q02_panel_drv_exit(void) -{ - spi_unregister_driver(&lb035q02_spi_driver); -} +module_spi_driver(lb035q02_spi_driver); -module_init(lb035q02_panel_drv_init); -module_exit(lb035q02_panel_drv_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c b/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c index 0eb31caddca8..8b38b39213f4 100644 --- a/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c +++ b/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c @@ -350,18 +350,8 @@ static struct spi_driver nec_8048_spi_driver = { }, }; -static int __init nec_8048_lcd_init(void) -{ - return spi_register_driver(&nec_8048_spi_driver); -} - -static void __exit nec_8048_lcd_exit(void) -{ - return spi_unregister_driver(&nec_8048_spi_driver); -} +module_spi_driver(nec_8048_spi_driver); -module_init(nec_8048_lcd_init); -module_exit(nec_8048_lcd_exit); MODULE_AUTHOR("Erik Gilling <konkers@android.com>"); MODULE_DESCRIPTION("NEC-nl8048hl11-01b Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/displays/panel-taal.c b/drivers/video/omap2/displays/panel-taal.c index 00c5c615585f..0f21fa5a16ae 100644 --- a/drivers/video/omap2/displays/panel-taal.c +++ b/drivers/video/omap2/displays/panel-taal.c @@ -1019,14 +1019,12 @@ static int taal_probe(struct omap_dss_device *dssdev) if (panel_data->use_ext_te) { int gpio = panel_data->ext_te_gpio; - r = gpio_request(gpio, "taal irq"); + r = gpio_request_one(gpio, GPIOF_IN, "taal irq"); if (r) { dev_err(&dssdev->dev, "GPIO request failed\n"); goto err_gpio; } - gpio_direction_input(gpio); - r = request_irq(gpio_to_irq(gpio), taal_te_isr, IRQF_TRIGGER_RISING, "taal vsync", dssdev); diff --git a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c index e6649aa89591..32f3fcd7f0f0 100644 --- a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c +++ b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c @@ -47,16 +47,20 @@ TPO_R03_EN_PRE_CHARGE | TPO_R03_SOFTWARE_CTL) static const u16 tpo_td043_def_gamma[12] = { - 106, 200, 289, 375, 460, 543, 625, 705, 785, 864, 942, 1020 + 105, 315, 381, 431, 490, 537, 579, 686, 780, 837, 880, 1023 }; struct tpo_td043_device { struct spi_device *spi; struct regulator *vcc_reg; + int nreset_gpio; u16 gamma[12]; u32 mode; u32 hmirror:1; u32 vmirror:1; + u32 powered_on:1; + u32 spi_suspended:1; + u32 power_on_resume:1; }; static int tpo_td043_write(struct spi_device *spi, u8 addr, u8 data) @@ -265,28 +269,16 @@ static const struct omap_video_timings tpo_td043_timings = { .vbp = 34, }; -static int tpo_td043_power_on(struct omap_dss_device *dssdev) +static int tpo_td043_power_on(struct tpo_td043_device *tpo_td043) { - struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev); - int nreset_gpio = dssdev->reset_gpio; - int r; + int nreset_gpio = tpo_td043->nreset_gpio; - if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) + if (tpo_td043->powered_on) return 0; - r = omapdss_dpi_display_enable(dssdev); - if (r) - goto err0; - - if (dssdev->platform_enable) { - r = dssdev->platform_enable(dssdev); - if (r) - goto err1; - } - regulator_enable(tpo_td043->vcc_reg); - /* wait for power up */ + /* wait for regulator to stabilize */ msleep(160); if (gpio_is_valid(nreset_gpio)) @@ -301,19 +293,15 @@ static int tpo_td043_power_on(struct omap_dss_device *dssdev) tpo_td043->vmirror); tpo_td043_write_gamma(tpo_td043->spi, tpo_td043->gamma); + tpo_td043->powered_on = 1; return 0; -err1: - omapdss_dpi_display_disable(dssdev); -err0: - return r; } -static void tpo_td043_power_off(struct omap_dss_device *dssdev) +static void tpo_td043_power_off(struct tpo_td043_device *tpo_td043) { - struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev); - int nreset_gpio = dssdev->reset_gpio; + int nreset_gpio = tpo_td043->nreset_gpio; - if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) + if (!tpo_td043->powered_on) return; tpo_td043_write(tpo_td043->spi, 3, @@ -329,54 +317,94 @@ static void tpo_td043_power_off(struct omap_dss_device *dssdev) regulator_disable(tpo_td043->vcc_reg); + tpo_td043->powered_on = 0; +} + +static int tpo_td043_enable_dss(struct omap_dss_device *dssdev) +{ + struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev); + int r; + + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) + return 0; + + r = omapdss_dpi_display_enable(dssdev); + if (r) + goto err0; + + if (dssdev->platform_enable) { + r = dssdev->platform_enable(dssdev); + if (r) + goto err1; + } + + /* + * If we are resuming from system suspend, SPI clocks might not be + * enabled yet, so we'll program the LCD from SPI PM resume callback. + */ + if (!tpo_td043->spi_suspended) { + r = tpo_td043_power_on(tpo_td043); + if (r) + goto err1; + } + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + return 0; +err1: + omapdss_dpi_display_disable(dssdev); +err0: + return r; +} + +static void tpo_td043_disable_dss(struct omap_dss_device *dssdev) +{ + struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev); + + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) + return; + if (dssdev->platform_disable) dssdev->platform_disable(dssdev); omapdss_dpi_display_disable(dssdev); + + if (!tpo_td043->spi_suspended) + tpo_td043_power_off(tpo_td043); } static int tpo_td043_enable(struct omap_dss_device *dssdev) { - int ret; - dev_dbg(&dssdev->dev, "enable\n"); - ret = tpo_td043_power_on(dssdev); - if (ret) - return ret; - - dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; - - return 0; + return tpo_td043_enable_dss(dssdev); } static void tpo_td043_disable(struct omap_dss_device *dssdev) { dev_dbg(&dssdev->dev, "disable\n"); - tpo_td043_power_off(dssdev); + tpo_td043_disable_dss(dssdev); dssdev->state = OMAP_DSS_DISPLAY_DISABLED; } static int tpo_td043_suspend(struct omap_dss_device *dssdev) { - tpo_td043_power_off(dssdev); + dev_dbg(&dssdev->dev, "suspend\n"); + + tpo_td043_disable_dss(dssdev); + dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; + return 0; } static int tpo_td043_resume(struct omap_dss_device *dssdev) { - int r = 0; - - r = tpo_td043_power_on(dssdev); - if (r) - return r; + dev_dbg(&dssdev->dev, "resume\n"); - dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; - - return 0; + return tpo_td043_enable_dss(dssdev); } static int tpo_td043_probe(struct omap_dss_device *dssdev) @@ -408,17 +436,12 @@ static int tpo_td043_probe(struct omap_dss_device *dssdev) } if (gpio_is_valid(nreset_gpio)) { - ret = gpio_request(nreset_gpio, "lcd reset"); + ret = gpio_request_one(nreset_gpio, GPIOF_OUT_INIT_LOW, + "lcd reset"); if (ret < 0) { dev_err(&dssdev->dev, "couldn't request reset GPIO\n"); goto fail_gpio_req; } - - ret = gpio_direction_output(nreset_gpio, 0); - if (ret < 0) { - dev_err(&dssdev->dev, "couldn't set GPIO direction\n"); - goto fail_gpio_direction; - } } ret = sysfs_create_group(&dssdev->dev.kobj, &tpo_td043_attr_group); @@ -427,8 +450,6 @@ static int tpo_td043_probe(struct omap_dss_device *dssdev) return 0; -fail_gpio_direction: - gpio_free(nreset_gpio); fail_gpio_req: regulator_put(tpo_td043->vcc_reg); fail_regulator: @@ -491,6 +512,7 @@ static int tpo_td043_spi_probe(struct spi_device *spi) return -ENOMEM; tpo_td043->spi = spi; + tpo_td043->nreset_gpio = dssdev->reset_gpio; dev_set_drvdata(&spi->dev, tpo_td043); dev_set_drvdata(&dssdev->dev, tpo_td043); @@ -509,27 +531,52 @@ static int __devexit tpo_td043_spi_remove(struct spi_device *spi) return 0; } +#ifdef CONFIG_PM_SLEEP +static int tpo_td043_spi_suspend(struct device *dev) +{ + struct tpo_td043_device *tpo_td043 = dev_get_drvdata(dev); + + dev_dbg(dev, "tpo_td043_spi_suspend, tpo %p\n", tpo_td043); + + tpo_td043->power_on_resume = tpo_td043->powered_on; + tpo_td043_power_off(tpo_td043); + tpo_td043->spi_suspended = 1; + + return 0; +} + +static int tpo_td043_spi_resume(struct device *dev) +{ + struct tpo_td043_device *tpo_td043 = dev_get_drvdata(dev); + int ret; + + dev_dbg(dev, "tpo_td043_spi_resume\n"); + + if (tpo_td043->power_on_resume) { + ret = tpo_td043_power_on(tpo_td043); + if (ret) + return ret; + } + tpo_td043->spi_suspended = 0; + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(tpo_td043_spi_pm, + tpo_td043_spi_suspend, tpo_td043_spi_resume); + static struct spi_driver tpo_td043_spi_driver = { .driver = { .name = "tpo_td043mtea1_panel_spi", .owner = THIS_MODULE, + .pm = &tpo_td043_spi_pm, }, .probe = tpo_td043_spi_probe, .remove = __devexit_p(tpo_td043_spi_remove), }; -static int __init tpo_td043_init(void) -{ - return spi_register_driver(&tpo_td043_spi_driver); -} - -static void __exit tpo_td043_exit(void) -{ - spi_unregister_driver(&tpo_td043_spi_driver); -} - -module_init(tpo_td043_init); -module_exit(tpo_td043_exit); +module_spi_driver(tpo_td043_spi_driver); MODULE_AUTHOR("Gražvydas Ignotas <notasas@gmail.com>"); MODULE_DESCRIPTION("TPO TD043MTEA1 LCD Driver"); diff --git a/drivers/video/omap2/dss/apply.c b/drivers/video/omap2/dss/apply.c index 87b3e25294cf..b10b3bc1931e 100644 --- a/drivers/video/omap2/dss/apply.c +++ b/drivers/video/omap2/dss/apply.c @@ -105,6 +105,9 @@ static struct { struct ovl_priv_data ovl_priv_data_array[MAX_DSS_OVERLAYS]; struct mgr_priv_data mgr_priv_data_array[MAX_DSS_MANAGERS]; + bool fifo_merge_dirty; + bool fifo_merge; + bool irq_enabled; } dss_data; @@ -351,6 +354,7 @@ static void wait_pending_extra_info_updates(void) bool updating; unsigned long flags; unsigned long t; + int r; spin_lock_irqsave(&data_lock, flags); @@ -366,11 +370,11 @@ static void wait_pending_extra_info_updates(void) spin_unlock_irqrestore(&data_lock, flags); t = msecs_to_jiffies(500); - wait_for_completion_timeout(&extra_updated_completion, t); - - updating = extra_info_update_ongoing(); - - WARN_ON(updating); + r = wait_for_completion_timeout(&extra_updated_completion, t); + if (r == 0) + DSSWARN("timeout in wait_pending_extra_info_updates\n"); + else if (r < 0) + DSSERR("wait_pending_extra_info_updates failed: %d\n", r); } int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr) @@ -388,6 +392,10 @@ int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr) if (mgr_manual_update(mgr)) return 0; + r = dispc_runtime_get(); + if (r) + return r; + irq = dispc_mgr_get_vsync_irq(mgr->id); mp = get_mgr_priv(mgr); @@ -428,6 +436,8 @@ int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr) } } + dispc_runtime_put(); + return r; } @@ -451,6 +461,10 @@ int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl) if (ovl_manual_update(ovl)) return 0; + r = dispc_runtime_get(); + if (r) + return r; + irq = dispc_mgr_get_vsync_irq(ovl->manager->id); op = get_ovl_priv(ovl); @@ -491,6 +505,8 @@ int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl) } } + dispc_runtime_put(); + return r; } @@ -585,11 +601,40 @@ static void dss_mgr_write_regs(struct omap_overlay_manager *mgr) } } +static void dss_write_regs_common(void) +{ + const int num_mgrs = omap_dss_get_num_overlay_managers(); + int i; + + if (!dss_data.fifo_merge_dirty) + return; + + for (i = 0; i < num_mgrs; ++i) { + struct omap_overlay_manager *mgr; + struct mgr_priv_data *mp; + + mgr = omap_dss_get_overlay_manager(i); + mp = get_mgr_priv(mgr); + + if (mp->enabled) { + if (dss_data.fifo_merge_dirty) { + dispc_enable_fifomerge(dss_data.fifo_merge); + dss_data.fifo_merge_dirty = false; + } + + if (mp->updating) + mp->shadow_info_dirty = true; + } + } +} + static void dss_write_regs(void) { const int num_mgrs = omap_dss_get_num_overlay_managers(); int i; + dss_write_regs_common(); + for (i = 0; i < num_mgrs; ++i) { struct omap_overlay_manager *mgr; struct mgr_priv_data *mp; @@ -640,6 +685,22 @@ static void dss_set_go_bits(void) } +static void mgr_clear_shadow_dirty(struct omap_overlay_manager *mgr) +{ + struct omap_overlay *ovl; + struct mgr_priv_data *mp; + struct ovl_priv_data *op; + + mp = get_mgr_priv(mgr); + mp->shadow_info_dirty = false; + + list_for_each_entry(ovl, &mgr->overlays, list) { + op = get_ovl_priv(ovl); + op->shadow_info_dirty = false; + op->shadow_extra_info_dirty = false; + } +} + void dss_mgr_start_update(struct omap_overlay_manager *mgr) { struct mgr_priv_data *mp = get_mgr_priv(mgr); @@ -659,6 +720,8 @@ void dss_mgr_start_update(struct omap_overlay_manager *mgr) dss_mgr_write_regs(mgr); + dss_write_regs_common(); + mp->updating = true; if (!dss_data.irq_enabled && need_isr()) @@ -666,6 +729,8 @@ void dss_mgr_start_update(struct omap_overlay_manager *mgr) dispc_mgr_enable(mgr->id, true); + mgr_clear_shadow_dirty(mgr); + spin_unlock_irqrestore(&data_lock, flags); } @@ -709,22 +774,6 @@ static void dss_unregister_vsync_isr(void) dss_data.irq_enabled = false; } -static void mgr_clear_shadow_dirty(struct omap_overlay_manager *mgr) -{ - struct omap_overlay *ovl; - struct mgr_priv_data *mp; - struct ovl_priv_data *op; - - mp = get_mgr_priv(mgr); - mp->shadow_info_dirty = false; - - list_for_each_entry(ovl, &mgr->overlays, list) { - op = get_ovl_priv(ovl); - op->shadow_info_dirty = false; - op->shadow_extra_info_dirty = false; - } -} - static void dss_apply_irq_handler(void *data, u32 mask) { const int num_mgrs = dss_feat_get_num_mgrs(); @@ -754,9 +803,6 @@ static void dss_apply_irq_handler(void *data, u32 mask) if (was_busy && !mp->busy) mgr_clear_shadow_dirty(mgr); - } else { - if (was_updating && !mp->updating) - mgr_clear_shadow_dirty(mgr); } } @@ -859,11 +905,20 @@ static void dss_apply_ovl_fifo_thresholds(struct omap_overlay *ovl, op->extra_info_dirty = true; } -static void dss_ovl_setup_fifo(struct omap_overlay *ovl) +static void dss_apply_fifo_merge(bool use_fifo_merge) +{ + if (dss_data.fifo_merge == use_fifo_merge) + return; + + dss_data.fifo_merge = use_fifo_merge; + dss_data.fifo_merge_dirty = true; +} + +static void dss_ovl_setup_fifo(struct omap_overlay *ovl, + bool use_fifo_merge) { struct ovl_priv_data *op = get_ovl_priv(ovl); struct omap_dss_device *dssdev; - u32 size, burst_size; u32 fifo_low, fifo_high; if (!op->enabled && !op->enabling) @@ -871,33 +926,14 @@ static void dss_ovl_setup_fifo(struct omap_overlay *ovl) dssdev = ovl->manager->device; - size = dispc_ovl_get_fifo_size(ovl->id); - - burst_size = dispc_ovl_get_burst_size(ovl->id); - - switch (dssdev->type) { - case OMAP_DISPLAY_TYPE_DPI: - case OMAP_DISPLAY_TYPE_DBI: - case OMAP_DISPLAY_TYPE_SDI: - case OMAP_DISPLAY_TYPE_VENC: - case OMAP_DISPLAY_TYPE_HDMI: - default_get_overlay_fifo_thresholds(ovl->id, size, - burst_size, &fifo_low, &fifo_high); - break; -#ifdef CONFIG_OMAP2_DSS_DSI - case OMAP_DISPLAY_TYPE_DSI: - dsi_get_overlay_fifo_thresholds(ovl->id, size, - burst_size, &fifo_low, &fifo_high); - break; -#endif - default: - BUG(); - } + dispc_ovl_compute_fifo_thresholds(ovl->id, &fifo_low, &fifo_high, + use_fifo_merge); dss_apply_ovl_fifo_thresholds(ovl, fifo_low, fifo_high); } -static void dss_mgr_setup_fifos(struct omap_overlay_manager *mgr) +static void dss_mgr_setup_fifos(struct omap_overlay_manager *mgr, + bool use_fifo_merge) { struct omap_overlay *ovl; struct mgr_priv_data *mp; @@ -908,19 +944,94 @@ static void dss_mgr_setup_fifos(struct omap_overlay_manager *mgr) return; list_for_each_entry(ovl, &mgr->overlays, list) - dss_ovl_setup_fifo(ovl); + dss_ovl_setup_fifo(ovl, use_fifo_merge); +} + +static void dss_setup_fifos(bool use_fifo_merge) +{ + const int num_mgrs = omap_dss_get_num_overlay_managers(); + struct omap_overlay_manager *mgr; + int i; + + for (i = 0; i < num_mgrs; ++i) { + mgr = omap_dss_get_overlay_manager(i); + dss_mgr_setup_fifos(mgr, use_fifo_merge); + } } -static void dss_setup_fifos(void) +static int get_num_used_managers(void) { const int num_mgrs = omap_dss_get_num_overlay_managers(); struct omap_overlay_manager *mgr; + struct mgr_priv_data *mp; int i; + int enabled_mgrs; + + enabled_mgrs = 0; for (i = 0; i < num_mgrs; ++i) { mgr = omap_dss_get_overlay_manager(i); - dss_mgr_setup_fifos(mgr); + mp = get_mgr_priv(mgr); + + if (!mp->enabled) + continue; + + enabled_mgrs++; + } + + return enabled_mgrs; +} + +static int get_num_used_overlays(void) +{ + const int num_ovls = omap_dss_get_num_overlays(); + struct omap_overlay *ovl; + struct ovl_priv_data *op; + struct mgr_priv_data *mp; + int i; + int enabled_ovls; + + enabled_ovls = 0; + + for (i = 0; i < num_ovls; ++i) { + ovl = omap_dss_get_overlay(i); + op = get_ovl_priv(ovl); + + if (!op->enabled && !op->enabling) + continue; + + mp = get_mgr_priv(ovl->manager); + + if (!mp->enabled) + continue; + + enabled_ovls++; } + + return enabled_ovls; +} + +static bool get_use_fifo_merge(void) +{ + int enabled_mgrs = get_num_used_managers(); + int enabled_ovls = get_num_used_overlays(); + + if (!dss_has_feature(FEAT_FIFO_MERGE)) + return false; + + /* + * In theory the only requirement for fifomerge is enabled_ovls <= 1. + * However, if we have two managers enabled and set/unset the fifomerge, + * we need to set the GO bits in particular sequence for the managers, + * and wait in between. + * + * This is rather difficult as new apply calls can happen at any time, + * so we simplify the problem by requiring also that enabled_mgrs <= 1. + * In practice this shouldn't matter, because when only one overlay is + * enabled, most likely only one output is enabled. + */ + + return enabled_mgrs <= 1 && enabled_ovls <= 1; } int dss_mgr_enable(struct omap_overlay_manager *mgr) @@ -928,6 +1039,7 @@ int dss_mgr_enable(struct omap_overlay_manager *mgr) struct mgr_priv_data *mp = get_mgr_priv(mgr); unsigned long flags; int r; + bool fifo_merge; mutex_lock(&apply_lock); @@ -945,11 +1057,23 @@ int dss_mgr_enable(struct omap_overlay_manager *mgr) goto err; } - dss_setup_fifos(); + /* step 1: setup fifos/fifomerge before enabling the manager */ + + fifo_merge = get_use_fifo_merge(); + dss_setup_fifos(fifo_merge); + dss_apply_fifo_merge(fifo_merge); dss_write_regs(); dss_set_go_bits(); + spin_unlock_irqrestore(&data_lock, flags); + + /* wait until fifo config is in */ + wait_pending_extra_info_updates(); + + /* step 2: enable the manager */ + spin_lock_irqsave(&data_lock, flags); + if (!mgr_manual_update(mgr)) mp->updating = true; @@ -974,6 +1098,7 @@ void dss_mgr_disable(struct omap_overlay_manager *mgr) { struct mgr_priv_data *mp = get_mgr_priv(mgr); unsigned long flags; + bool fifo_merge; mutex_lock(&apply_lock); @@ -988,8 +1113,16 @@ void dss_mgr_disable(struct omap_overlay_manager *mgr) mp->updating = false; mp->enabled = false; + fifo_merge = get_use_fifo_merge(); + dss_setup_fifos(fifo_merge); + dss_apply_fifo_merge(fifo_merge); + + dss_write_regs(); + dss_set_go_bits(); + spin_unlock_irqrestore(&data_lock, flags); + wait_pending_extra_info_updates(); out: mutex_unlock(&apply_lock); } @@ -1241,6 +1374,7 @@ int dss_ovl_enable(struct omap_overlay *ovl) { struct ovl_priv_data *op = get_ovl_priv(ovl); unsigned long flags; + bool fifo_merge; int r; mutex_lock(&apply_lock); @@ -1266,7 +1400,22 @@ int dss_ovl_enable(struct omap_overlay *ovl) goto err2; } - dss_setup_fifos(); + /* step 1: configure fifos/fifomerge for currently enabled ovls */ + + fifo_merge = get_use_fifo_merge(); + dss_setup_fifos(fifo_merge); + dss_apply_fifo_merge(fifo_merge); + + dss_write_regs(); + dss_set_go_bits(); + + spin_unlock_irqrestore(&data_lock, flags); + + /* wait for fifo configs to go in */ + wait_pending_extra_info_updates(); + + /* step 2: enable the overlay */ + spin_lock_irqsave(&data_lock, flags); op->enabling = false; dss_apply_ovl_enable(ovl, true); @@ -1294,6 +1443,7 @@ int dss_ovl_disable(struct omap_overlay *ovl) { struct ovl_priv_data *op = get_ovl_priv(ovl); unsigned long flags; + bool fifo_merge; int r; mutex_lock(&apply_lock); @@ -1308,9 +1458,11 @@ int dss_ovl_disable(struct omap_overlay *ovl) goto err; } + /* step 1: disable the overlay */ spin_lock_irqsave(&data_lock, flags); dss_apply_ovl_enable(ovl, false); + dss_write_regs(); dss_set_go_bits(); @@ -1319,6 +1471,21 @@ int dss_ovl_disable(struct omap_overlay *ovl) /* wait for the overlay to be disabled */ wait_pending_extra_info_updates(); + /* step 2: configure fifos/fifomerge */ + spin_lock_irqsave(&data_lock, flags); + + fifo_merge = get_use_fifo_merge(); + dss_setup_fifos(fifo_merge); + dss_apply_fifo_merge(fifo_merge); + + dss_write_regs(); + dss_set_go_bits(); + + spin_unlock_irqrestore(&data_lock, flags); + + /* wait for fifo config to go in */ + wait_pending_extra_info_updates(); + mutex_unlock(&apply_lock); return 0; diff --git a/drivers/video/omap2/dss/core.c b/drivers/video/omap2/dss/core.c index 8613f86fb56d..e8a120771ac6 100644 --- a/drivers/video/omap2/dss/core.c +++ b/drivers/video/omap2/dss/core.c @@ -183,42 +183,6 @@ static int omap_dss_probe(struct platform_device *pdev) dss_init_overlay_managers(pdev); dss_init_overlays(pdev); - r = dss_init_platform_driver(); - if (r) { - DSSERR("Failed to initialize DSS platform driver\n"); - goto err_dss; - } - - r = dispc_init_platform_driver(); - if (r) { - DSSERR("Failed to initialize dispc platform driver\n"); - goto err_dispc; - } - - r = rfbi_init_platform_driver(); - if (r) { - DSSERR("Failed to initialize rfbi platform driver\n"); - goto err_rfbi; - } - - r = venc_init_platform_driver(); - if (r) { - DSSERR("Failed to initialize venc platform driver\n"); - goto err_venc; - } - - r = dsi_init_platform_driver(); - if (r) { - DSSERR("Failed to initialize DSI platform driver\n"); - goto err_dsi; - } - - r = hdmi_init_platform_driver(); - if (r) { - DSSERR("Failed to initialize hdmi\n"); - goto err_hdmi; - } - r = dss_initialize_debugfs(); if (r) goto err_debugfs; @@ -246,18 +210,6 @@ static int omap_dss_probe(struct platform_device *pdev) err_register: dss_uninitialize_debugfs(); err_debugfs: - hdmi_uninit_platform_driver(); -err_hdmi: - dsi_uninit_platform_driver(); -err_dsi: - venc_uninit_platform_driver(); -err_venc: - dispc_uninit_platform_driver(); -err_dispc: - rfbi_uninit_platform_driver(); -err_rfbi: - dss_uninit_platform_driver(); -err_dss: return r; } @@ -269,13 +221,6 @@ static int omap_dss_remove(struct platform_device *pdev) dss_uninitialize_debugfs(); - hdmi_uninit_platform_driver(); - dsi_uninit_platform_driver(); - venc_uninit_platform_driver(); - rfbi_uninit_platform_driver(); - dispc_uninit_platform_driver(); - dss_uninit_platform_driver(); - dss_uninit_overlays(pdev); dss_uninit_overlay_managers(pdev); @@ -525,6 +470,80 @@ static int omap_dss_bus_register(void) /* INIT */ +static int __init omap_dss_register_drivers(void) +{ + int r; + + r = platform_driver_register(&omap_dss_driver); + if (r) + return r; + + r = dss_init_platform_driver(); + if (r) { + DSSERR("Failed to initialize DSS platform driver\n"); + goto err_dss; + } + + r = dispc_init_platform_driver(); + if (r) { + DSSERR("Failed to initialize dispc platform driver\n"); + goto err_dispc; + } + + r = rfbi_init_platform_driver(); + if (r) { + DSSERR("Failed to initialize rfbi platform driver\n"); + goto err_rfbi; + } + + r = venc_init_platform_driver(); + if (r) { + DSSERR("Failed to initialize venc platform driver\n"); + goto err_venc; + } + + r = dsi_init_platform_driver(); + if (r) { + DSSERR("Failed to initialize DSI platform driver\n"); + goto err_dsi; + } + + r = hdmi_init_platform_driver(); + if (r) { + DSSERR("Failed to initialize hdmi\n"); + goto err_hdmi; + } + + return 0; + +err_hdmi: + dsi_uninit_platform_driver(); +err_dsi: + venc_uninit_platform_driver(); +err_venc: + rfbi_uninit_platform_driver(); +err_rfbi: + dispc_uninit_platform_driver(); +err_dispc: + dss_uninit_platform_driver(); +err_dss: + platform_driver_unregister(&omap_dss_driver); + + return r; +} + +static void __exit omap_dss_unregister_drivers(void) +{ + hdmi_uninit_platform_driver(); + dsi_uninit_platform_driver(); + venc_uninit_platform_driver(); + rfbi_uninit_platform_driver(); + dispc_uninit_platform_driver(); + dss_uninit_platform_driver(); + + platform_driver_unregister(&omap_dss_driver); +} + #ifdef CONFIG_OMAP2_DSS_MODULE static void omap_dss_bus_unregister(void) { @@ -541,7 +560,7 @@ static int __init omap_dss_init(void) if (r) return r; - r = platform_driver_register(&omap_dss_driver); + r = omap_dss_register_drivers(); if (r) { omap_dss_bus_unregister(); return r; @@ -562,7 +581,7 @@ static void __exit omap_dss_exit(void) core.vdds_sdi_reg = NULL; } - platform_driver_unregister(&omap_dss_driver); + omap_dss_unregister_drivers(); omap_dss_bus_unregister(); } @@ -577,7 +596,7 @@ static int __init omap_dss_init(void) static int __init omap_dss_init2(void) { - return platform_driver_register(&omap_dss_driver); + return omap_dss_register_drivers(); } core_initcall(omap_dss_init); diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c index e1626a1d5c45..ee30937482e1 100644 --- a/drivers/video/omap2/dss/dispc.c +++ b/drivers/video/omap2/dss/dispc.c @@ -37,7 +37,6 @@ #include <linux/platform_device.h> #include <linux/pm_runtime.h> -#include <plat/sram.h> #include <plat/clock.h> #include <video/omapdss.h> @@ -736,11 +735,11 @@ static void dispc_ovl_set_color_mode(enum omap_plane plane, switch (color_mode) { case OMAP_DSS_COLOR_NV12: m = 0x0; break; - case OMAP_DSS_COLOR_RGB12U: + case OMAP_DSS_COLOR_RGBX16: m = 0x1; break; case OMAP_DSS_COLOR_RGBA16: m = 0x2; break; - case OMAP_DSS_COLOR_RGBX16: + case OMAP_DSS_COLOR_RGB12U: m = 0x4; break; case OMAP_DSS_COLOR_ARGB16: m = 0x5; break; @@ -789,9 +788,9 @@ static void dispc_ovl_set_color_mode(enum omap_plane plane, m = 0x8; break; case OMAP_DSS_COLOR_RGB24P: m = 0x9; break; - case OMAP_DSS_COLOR_YUV2: + case OMAP_DSS_COLOR_RGBX16: m = 0xa; break; - case OMAP_DSS_COLOR_UYVY: + case OMAP_DSS_COLOR_RGBA16: m = 0xb; break; case OMAP_DSS_COLOR_ARGB32: m = 0xc; break; @@ -909,7 +908,7 @@ static void dispc_configure_burst_sizes(void) dispc_ovl_set_burst_size(i, burst_size); } -u32 dispc_ovl_get_burst_size(enum omap_plane plane) +static u32 dispc_ovl_get_burst_size(enum omap_plane plane) { unsigned unit = dss_feat_get_burst_size_unit(); /* burst multiplier is always x8 (see dispc_configure_burst_sizes()) */ @@ -1018,7 +1017,7 @@ static void dispc_read_plane_fifo_sizes(void) } } -u32 dispc_ovl_get_fifo_size(enum omap_plane plane) +static u32 dispc_ovl_get_fifo_size(enum omap_plane plane) { return dispc.fifo_size[plane]; } @@ -1039,13 +1038,13 @@ void dispc_ovl_set_fifo_threshold(enum omap_plane plane, u32 low, u32 high) dss_feat_get_reg_field(FEAT_REG_FIFOHIGHTHRESHOLD, &hi_start, &hi_end); dss_feat_get_reg_field(FEAT_REG_FIFOLOWTHRESHOLD, &lo_start, &lo_end); - DSSDBG("fifo(%d) low/high old %u/%u, new %u/%u\n", + DSSDBG("fifo(%d) threshold (bytes), old %u/%u, new %u/%u\n", plane, REG_GET(DISPC_OVL_FIFO_THRESHOLD(plane), - lo_start, lo_end), + lo_start, lo_end) * unit, REG_GET(DISPC_OVL_FIFO_THRESHOLD(plane), - hi_start, hi_end), - low, high); + hi_start, hi_end) * unit, + low * unit, high * unit); dispc_write_reg(DISPC_OVL_FIFO_THRESHOLD(plane), FLD_VAL(high, hi_start, hi_end) | @@ -1054,10 +1053,53 @@ void dispc_ovl_set_fifo_threshold(enum omap_plane plane, u32 low, u32 high) void dispc_enable_fifomerge(bool enable) { + if (!dss_has_feature(FEAT_FIFO_MERGE)) { + WARN_ON(enable); + return; + } + DSSDBG("FIFO merge %s\n", enable ? "enabled" : "disabled"); REG_FLD_MOD(DISPC_CONFIG, enable ? 1 : 0, 14, 14); } +void dispc_ovl_compute_fifo_thresholds(enum omap_plane plane, + u32 *fifo_low, u32 *fifo_high, bool use_fifomerge) +{ + /* + * All sizes are in bytes. Both the buffer and burst are made of + * buffer_units, and the fifo thresholds must be buffer_unit aligned. + */ + + unsigned buf_unit = dss_feat_get_buffer_size_unit(); + unsigned ovl_fifo_size, total_fifo_size, burst_size; + int i; + + burst_size = dispc_ovl_get_burst_size(plane); + ovl_fifo_size = dispc_ovl_get_fifo_size(plane); + + if (use_fifomerge) { + total_fifo_size = 0; + for (i = 0; i < omap_dss_get_num_overlays(); ++i) + total_fifo_size += dispc_ovl_get_fifo_size(i); + } else { + total_fifo_size = ovl_fifo_size; + } + + /* + * We use the same low threshold for both fifomerge and non-fifomerge + * cases, but for fifomerge we calculate the high threshold using the + * combined fifo size + */ + + if (dss_has_feature(FEAT_OMAP3_DSI_FIFO_BUG)) { + *fifo_low = ovl_fifo_size - burst_size * 2; + *fifo_high = total_fifo_size - burst_size; + } else { + *fifo_low = ovl_fifo_size - burst_size; + *fifo_high = total_fifo_size - buf_unit; + } +} + static void dispc_ovl_set_fir(enum omap_plane plane, int hinc, int vinc, enum omap_color_component color_comp) @@ -1651,6 +1693,7 @@ static unsigned long calc_fclk(enum omap_channel channel, u16 width, u16 height, u16 out_width, u16 out_height) { unsigned int hf, vf; + unsigned long pclk = dispc_mgr_pclk_rate(channel); /* * FIXME how to determine the 'A' factor @@ -1673,13 +1716,16 @@ static unsigned long calc_fclk(enum omap_channel channel, u16 width, if (cpu_is_omap24xx()) { if (vf > 1 && hf > 1) - return dispc_mgr_pclk_rate(channel) * 4; + return pclk * 4; else - return dispc_mgr_pclk_rate(channel) * 2; + return pclk * 2; } else if (cpu_is_omap34xx()) { - return dispc_mgr_pclk_rate(channel) * vf * hf; + return pclk * vf * hf; } else { - return dispc_mgr_pclk_rate(channel) * hf; + if (hf > 1) + return DIV_ROUND_UP(pclk, out_width) * width; + else + return pclk; } } @@ -3272,11 +3318,6 @@ static void _omap_dispc_initial_config(void) if (dss_has_feature(FEAT_FUNCGATED)) REG_FLD_MOD(DISPC_CONFIG, 1, 9, 9); - /* L3 firewall setting: enable access to OCM RAM */ - /* XXX this should be somewhere in plat-omap */ - if (cpu_is_omap24xx()) - __raw_writel(0x402000b0, OMAP2_L3_IO_ADDRESS(0x680050a0)); - _dispc_setup_color_conv_coef(); dispc_set_loadmode(OMAP_DSS_LOAD_FRAME_ONLY); @@ -3298,15 +3339,6 @@ static int omap_dispchw_probe(struct platform_device *pdev) dispc.pdev = pdev; - clk = clk_get(&pdev->dev, "fck"); - if (IS_ERR(clk)) { - DSSERR("can't get fck\n"); - r = PTR_ERR(clk); - goto err_get_clk; - } - - dispc.dss_clk = clk; - spin_lock_init(&dispc.irq_lock); #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS @@ -3319,29 +3351,38 @@ static int omap_dispchw_probe(struct platform_device *pdev) dispc_mem = platform_get_resource(dispc.pdev, IORESOURCE_MEM, 0); if (!dispc_mem) { DSSERR("can't get IORESOURCE_MEM DISPC\n"); - r = -EINVAL; - goto err_ioremap; + return -EINVAL; } - dispc.base = ioremap(dispc_mem->start, resource_size(dispc_mem)); + + dispc.base = devm_ioremap(&pdev->dev, dispc_mem->start, + resource_size(dispc_mem)); if (!dispc.base) { DSSERR("can't ioremap DISPC\n"); - r = -ENOMEM; - goto err_ioremap; + return -ENOMEM; } + dispc.irq = platform_get_irq(dispc.pdev, 0); if (dispc.irq < 0) { DSSERR("platform_get_irq failed\n"); - r = -ENODEV; - goto err_irq; + return -ENODEV; } - r = request_irq(dispc.irq, omap_dispc_irq_handler, IRQF_SHARED, - "OMAP DISPC", dispc.pdev); + r = devm_request_irq(&pdev->dev, dispc.irq, omap_dispc_irq_handler, + IRQF_SHARED, "OMAP DISPC", dispc.pdev); if (r < 0) { DSSERR("request_irq failed\n"); - goto err_irq; + return r; + } + + clk = clk_get(&pdev->dev, "fck"); + if (IS_ERR(clk)) { + DSSERR("can't get fck\n"); + r = PTR_ERR(clk); + return r; } + dispc.dss_clk = clk; + pm_runtime_enable(&pdev->dev); r = dispc_runtime_get(); @@ -3362,12 +3403,7 @@ static int omap_dispchw_probe(struct platform_device *pdev) err_runtime_get: pm_runtime_disable(&pdev->dev); - free_irq(dispc.irq, dispc.pdev); -err_irq: - iounmap(dispc.base); -err_ioremap: clk_put(dispc.dss_clk); -err_get_clk: return r; } @@ -3377,8 +3413,6 @@ static int omap_dispchw_remove(struct platform_device *pdev) clk_put(dispc.dss_clk); - free_irq(dispc.irq, dispc.pdev); - iounmap(dispc.base); return 0; } diff --git a/drivers/video/omap2/dss/dispc_coefs.c b/drivers/video/omap2/dss/dispc_coefs.c index 069bccbb3f12..038c15b04215 100644 --- a/drivers/video/omap2/dss/dispc_coefs.c +++ b/drivers/video/omap2/dss/dispc_coefs.c @@ -19,14 +19,13 @@ #include <linux/kernel.h> #include <video/omapdss.h> -#include "dispc.h" -#define ARRAY_LEN(array) (sizeof(array) / sizeof(array[0])) +#include "dispc.h" static const struct dispc_coef coef3_M8[8] = { { 0, 0, 128, 0, 0 }, { 0, -4, 123, 9, 0 }, - { 0, -4, 108, 87, 0 }, + { 0, -4, 108, 24, 0 }, { 0, -2, 87, 43, 0 }, { 0, 64, 64, 0, 0 }, { 0, 43, 87, -2, 0 }, @@ -168,7 +167,7 @@ static const struct dispc_coef coef5_M8[8] = { static const struct dispc_coef coef5_M9[8] = { { -3, 10, 114, 10, -3 }, - { -6, 24, 110, 0, -1 }, + { -6, 24, 111, 0, -1 }, { -8, 40, 103, -7, 0 }, { -11, 58, 91, -11, 1 }, { 0, -12, 76, 76, -12 }, @@ -319,7 +318,7 @@ const struct dispc_coef *dispc_ovl_get_scale_coef(int inc, int five_taps) }; inc /= 128; - for (i = 0; i < ARRAY_LEN(coefs); ++i) + for (i = 0; i < ARRAY_SIZE(coefs); ++i) if (inc >= coefs[i].Mmin && inc <= coefs[i].Mmax) return five_taps ? coefs[i].coef_5 : coefs[i].coef_3; return NULL; diff --git a/drivers/video/omap2/dss/display.c b/drivers/video/omap2/dss/display.c index be331dc5a61b..4424c198dbcd 100644 --- a/drivers/video/omap2/dss/display.c +++ b/drivers/video/omap2/dss/display.c @@ -279,16 +279,6 @@ void omapdss_default_get_resolution(struct omap_dss_device *dssdev, } EXPORT_SYMBOL(omapdss_default_get_resolution); -void default_get_overlay_fifo_thresholds(enum omap_plane plane, - u32 fifo_size, u32 burst_size, - u32 *fifo_low, u32 *fifo_high) -{ - unsigned buf_unit = dss_feat_get_buffer_size_unit(); - - *fifo_high = fifo_size - buf_unit; - *fifo_low = fifo_size - burst_size; -} - int omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev) { switch (dssdev->type) { diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c index 52f36ec1c8bb..662d14f8c2c3 100644 --- a/drivers/video/omap2/dss/dsi.c +++ b/drivers/video/omap2/dss/dsi.c @@ -4524,14 +4524,6 @@ int omapdss_dsi_enable_te(struct omap_dss_device *dssdev, bool enable) } EXPORT_SYMBOL(omapdss_dsi_enable_te); -void dsi_get_overlay_fifo_thresholds(enum omap_plane plane, - u32 fifo_size, u32 burst_size, - u32 *fifo_low, u32 *fifo_high) -{ - *fifo_high = fifo_size - burst_size; - *fifo_low = fifo_size - burst_size * 2; -} - int dsi_init_display(struct omap_dss_device *dssdev) { struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); @@ -4695,11 +4687,9 @@ static int omap_dsihw_probe(struct platform_device *dsidev) struct resource *dsi_mem; struct dsi_data *dsi; - dsi = kzalloc(sizeof(*dsi), GFP_KERNEL); - if (!dsi) { - r = -ENOMEM; - goto err_alloc; - } + dsi = devm_kzalloc(&dsidev->dev, sizeof(*dsi), GFP_KERNEL); + if (!dsi) + return -ENOMEM; dsi->pdev = dsidev; dsi_pdev_map[dsi_module] = dsidev; @@ -4722,12 +4712,6 @@ static int omap_dsihw_probe(struct platform_device *dsidev) mutex_init(&dsi->lock); sema_init(&dsi->bus_lock, 1); - r = dsi_get_clocks(dsidev); - if (r) - goto err_get_clk; - - pm_runtime_enable(&dsidev->dev); - INIT_DELAYED_WORK_DEFERRABLE(&dsi->framedone_timeout_work, dsi_framedone_timeout_work_callback); @@ -4739,27 +4723,27 @@ static int omap_dsihw_probe(struct platform_device *dsidev) dsi_mem = platform_get_resource(dsi->pdev, IORESOURCE_MEM, 0); if (!dsi_mem) { DSSERR("can't get IORESOURCE_MEM DSI\n"); - r = -EINVAL; - goto err_ioremap; + return -EINVAL; } - dsi->base = ioremap(dsi_mem->start, resource_size(dsi_mem)); + + dsi->base = devm_ioremap(&dsidev->dev, dsi_mem->start, + resource_size(dsi_mem)); if (!dsi->base) { DSSERR("can't ioremap DSI\n"); - r = -ENOMEM; - goto err_ioremap; + return -ENOMEM; } + dsi->irq = platform_get_irq(dsi->pdev, 0); if (dsi->irq < 0) { DSSERR("platform_get_irq failed\n"); - r = -ENODEV; - goto err_get_irq; + return -ENODEV; } - r = request_irq(dsi->irq, omap_dsi_irq_handler, IRQF_SHARED, - dev_name(&dsidev->dev), dsi->pdev); + r = devm_request_irq(&dsidev->dev, dsi->irq, omap_dsi_irq_handler, + IRQF_SHARED, dev_name(&dsidev->dev), dsi->pdev); if (r < 0) { DSSERR("request_irq failed\n"); - goto err_get_irq; + return r; } /* DSI VCs initialization */ @@ -4771,9 +4755,15 @@ static int omap_dsihw_probe(struct platform_device *dsidev) dsi_calc_clock_param_ranges(dsidev); + r = dsi_get_clocks(dsidev); + if (r) + return r; + + pm_runtime_enable(&dsidev->dev); + r = dsi_runtime_get(dsidev); if (r) - goto err_get_dsi; + goto err_runtime_get; rev = dsi_read_reg(dsidev, DSI_REVISION); dev_dbg(&dsidev->dev, "OMAP DSI rev %d.%d\n", @@ -4791,15 +4781,9 @@ static int omap_dsihw_probe(struct platform_device *dsidev) return 0; -err_get_dsi: - free_irq(dsi->irq, dsi->pdev); -err_get_irq: - iounmap(dsi->base); -err_ioremap: +err_runtime_get: pm_runtime_disable(&dsidev->dev); -err_get_clk: - kfree(dsi); -err_alloc: + dsi_put_clocks(dsidev); return r; } @@ -4823,11 +4807,6 @@ static int omap_dsihw_remove(struct platform_device *dsidev) dsi->vdds_dsi_reg = NULL; } - free_irq(dsi->irq, dsi->pdev); - iounmap(dsi->base); - - kfree(dsi); - return 0; } diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c index 77c2b5a32b5d..bd2d5e159463 100644 --- a/drivers/video/omap2/dss/dss.c +++ b/drivers/video/omap2/dss/dss.c @@ -33,7 +33,10 @@ #include <linux/pm_runtime.h> #include <video/omapdss.h> + +#include <plat/cpu.h> #include <plat/clock.h> + #include "dss.h" #include "dss_features.h" @@ -748,19 +751,19 @@ static int omap_dsshw_probe(struct platform_device *pdev) dss_mem = platform_get_resource(dss.pdev, IORESOURCE_MEM, 0); if (!dss_mem) { DSSERR("can't get IORESOURCE_MEM DSS\n"); - r = -EINVAL; - goto err_ioremap; + return -EINVAL; } - dss.base = ioremap(dss_mem->start, resource_size(dss_mem)); + + dss.base = devm_ioremap(&pdev->dev, dss_mem->start, + resource_size(dss_mem)); if (!dss.base) { DSSERR("can't ioremap DSS\n"); - r = -ENOMEM; - goto err_ioremap; + return -ENOMEM; } r = dss_get_clocks(); if (r) - goto err_clocks; + return r; pm_runtime_enable(&pdev->dev); @@ -808,9 +811,6 @@ err_dpi: err_runtime_get: pm_runtime_disable(&pdev->dev); dss_put_clocks(); -err_clocks: - iounmap(dss.base); -err_ioremap: return r; } @@ -819,8 +819,6 @@ static int omap_dsshw_remove(struct platform_device *pdev) dpi_exit(); sdi_exit(); - iounmap(dss.base); - pm_runtime_disable(&pdev->dev); dss_put_clocks(); diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h index 32ff69fb3333..d4b3dff2ead3 100644 --- a/drivers/video/omap2/dss/dss.h +++ b/drivers/video/omap2/dss/dss.h @@ -202,9 +202,6 @@ void dss_uninit_device(struct platform_device *pdev, struct omap_dss_device *dssdev); bool dss_use_replication(struct omap_dss_device *dssdev, enum omap_color_mode mode); -void default_get_overlay_fifo_thresholds(enum omap_plane plane, - u32 fifo_size, u32 burst_size, - u32 *fifo_low, u32 *fifo_high); /* manager */ int dss_init_overlay_managers(struct platform_device *pdev); @@ -313,9 +310,6 @@ int dsi_pll_calc_clock_div_pck(struct platform_device *dsidev, bool is_tft, int dsi_pll_init(struct platform_device *dsidev, bool enable_hsclk, bool enable_hsdiv); void dsi_pll_uninit(struct platform_device *dsidev, bool disconnect_lanes); -void dsi_get_overlay_fifo_thresholds(enum omap_plane plane, - u32 fifo_size, u32 burst_size, - u32 *fifo_low, u32 *fifo_high); void dsi_wait_pll_hsdiv_dispc_active(struct platform_device *dsidev); void dsi_wait_pll_hsdiv_dsi_active(struct platform_device *dsidev); struct platform_device *dsi_get_dsidev_from_id(int module); @@ -429,8 +423,8 @@ int dispc_calc_clock_rates(unsigned long dispc_fclk_rate, void dispc_ovl_set_fifo_threshold(enum omap_plane plane, u32 low, u32 high); -u32 dispc_ovl_get_fifo_size(enum omap_plane plane); -u32 dispc_ovl_get_burst_size(enum omap_plane plane); +void dispc_ovl_compute_fifo_thresholds(enum omap_plane plane, + u32 *fifo_low, u32 *fifo_high, bool use_fifomerge); int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi, bool ilace, bool replication); int dispc_ovl_enable(enum omap_plane plane, bool enable); diff --git a/drivers/video/omap2/dss/dss_features.c b/drivers/video/omap2/dss/dss_features.c index afcb59301c37..ce14aa6dd672 100644 --- a/drivers/video/omap2/dss/dss_features.c +++ b/drivers/video/omap2/dss/dss_features.c @@ -41,7 +41,8 @@ struct omap_dss_features { const struct dss_reg_field *reg_fields; const int num_reg_fields; - const u32 has_feature; + const enum dss_feat_id *features; + const int num_features; const int num_mgrs; const int num_ovls; @@ -189,7 +190,8 @@ static const enum omap_color_mode omap4_dss_supported_color_modes[] = { OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U | OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_ARGB32 | OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_RGBX32 | - OMAP_DSS_COLOR_ARGB16_1555, + OMAP_DSS_COLOR_ARGB16_1555 | OMAP_DSS_COLOR_RGBX16 | + OMAP_DSS_COLOR_RGBA16 | OMAP_DSS_COLOR_XRGB16_1555, /* OMAP_DSS_VIDEO1 */ OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB12U | @@ -337,15 +339,110 @@ static const struct dss_param_range omap4_dss_param_range[] = { [FEAT_PARAM_LINEWIDTH] = { 1, 2048 }, }; +static const enum dss_feat_id omap2_dss_feat_list[] = { + FEAT_LCDENABLEPOL, + FEAT_LCDENABLESIGNAL, + FEAT_PCKFREEENABLE, + FEAT_FUNCGATED, + FEAT_ROWREPEATENABLE, + FEAT_RESIZECONF, +}; + +static const enum dss_feat_id omap3430_dss_feat_list[] = { + FEAT_LCDENABLEPOL, + FEAT_LCDENABLESIGNAL, + FEAT_PCKFREEENABLE, + FEAT_FUNCGATED, + FEAT_LINEBUFFERSPLIT, + FEAT_ROWREPEATENABLE, + FEAT_RESIZECONF, + FEAT_DSI_PLL_FREQSEL, + FEAT_DSI_REVERSE_TXCLKESC, + FEAT_VENC_REQUIRES_TV_DAC_CLK, + FEAT_CPR, + FEAT_PRELOAD, + FEAT_FIR_COEF_V, + FEAT_ALPHA_FIXED_ZORDER, + FEAT_FIFO_MERGE, + FEAT_OMAP3_DSI_FIFO_BUG, +}; + +static const enum dss_feat_id omap3630_dss_feat_list[] = { + FEAT_LCDENABLEPOL, + FEAT_LCDENABLESIGNAL, + FEAT_PCKFREEENABLE, + FEAT_FUNCGATED, + FEAT_LINEBUFFERSPLIT, + FEAT_ROWREPEATENABLE, + FEAT_RESIZECONF, + FEAT_DSI_PLL_PWR_BUG, + FEAT_DSI_PLL_FREQSEL, + FEAT_CPR, + FEAT_PRELOAD, + FEAT_FIR_COEF_V, + FEAT_ALPHA_FIXED_ZORDER, + FEAT_FIFO_MERGE, + FEAT_OMAP3_DSI_FIFO_BUG, +}; + +static const enum dss_feat_id omap4430_es1_0_dss_feat_list[] = { + FEAT_MGR_LCD2, + FEAT_CORE_CLK_DIV, + FEAT_LCD_CLK_SRC, + FEAT_DSI_DCS_CMD_CONFIG_VC, + FEAT_DSI_VC_OCP_WIDTH, + FEAT_DSI_GNQ, + FEAT_HANDLE_UV_SEPARATE, + FEAT_ATTR2, + FEAT_CPR, + FEAT_PRELOAD, + FEAT_FIR_COEF_V, + FEAT_ALPHA_FREE_ZORDER, + FEAT_FIFO_MERGE, +}; + +static const enum dss_feat_id omap4430_es2_0_1_2_dss_feat_list[] = { + FEAT_MGR_LCD2, + FEAT_CORE_CLK_DIV, + FEAT_LCD_CLK_SRC, + FEAT_DSI_DCS_CMD_CONFIG_VC, + FEAT_DSI_VC_OCP_WIDTH, + FEAT_DSI_GNQ, + FEAT_HDMI_CTS_SWMODE, + FEAT_HANDLE_UV_SEPARATE, + FEAT_ATTR2, + FEAT_CPR, + FEAT_PRELOAD, + FEAT_FIR_COEF_V, + FEAT_ALPHA_FREE_ZORDER, + FEAT_FIFO_MERGE, +}; + +static const enum dss_feat_id omap4_dss_feat_list[] = { + FEAT_MGR_LCD2, + FEAT_CORE_CLK_DIV, + FEAT_LCD_CLK_SRC, + FEAT_DSI_DCS_CMD_CONFIG_VC, + FEAT_DSI_VC_OCP_WIDTH, + FEAT_DSI_GNQ, + FEAT_HDMI_CTS_SWMODE, + FEAT_HDMI_AUDIO_USE_MCLK, + FEAT_HANDLE_UV_SEPARATE, + FEAT_ATTR2, + FEAT_CPR, + FEAT_PRELOAD, + FEAT_FIR_COEF_V, + FEAT_ALPHA_FREE_ZORDER, + FEAT_FIFO_MERGE, +}; + /* OMAP2 DSS Features */ static const struct omap_dss_features omap2_dss_features = { .reg_fields = omap2_dss_reg_fields, .num_reg_fields = ARRAY_SIZE(omap2_dss_reg_fields), - .has_feature = - FEAT_LCDENABLEPOL | FEAT_LCDENABLESIGNAL | - FEAT_PCKFREEENABLE | FEAT_FUNCGATED | - FEAT_ROWREPEATENABLE | FEAT_RESIZECONF, + .features = omap2_dss_feat_list, + .num_features = ARRAY_SIZE(omap2_dss_feat_list), .num_mgrs = 2, .num_ovls = 3, @@ -363,14 +460,8 @@ static const struct omap_dss_features omap3430_dss_features = { .reg_fields = omap3_dss_reg_fields, .num_reg_fields = ARRAY_SIZE(omap3_dss_reg_fields), - .has_feature = - FEAT_LCDENABLEPOL | - FEAT_LCDENABLESIGNAL | FEAT_PCKFREEENABLE | - FEAT_FUNCGATED | FEAT_ROWREPEATENABLE | - FEAT_LINEBUFFERSPLIT | FEAT_RESIZECONF | - FEAT_DSI_PLL_FREQSEL | FEAT_DSI_REVERSE_TXCLKESC | - FEAT_VENC_REQUIRES_TV_DAC_CLK | FEAT_CPR | FEAT_PRELOAD | - FEAT_FIR_COEF_V | FEAT_ALPHA_FIXED_ZORDER, + .features = omap3430_dss_feat_list, + .num_features = ARRAY_SIZE(omap3430_dss_feat_list), .num_mgrs = 2, .num_ovls = 3, @@ -387,14 +478,8 @@ static const struct omap_dss_features omap3630_dss_features = { .reg_fields = omap3_dss_reg_fields, .num_reg_fields = ARRAY_SIZE(omap3_dss_reg_fields), - .has_feature = - FEAT_LCDENABLEPOL | - FEAT_LCDENABLESIGNAL | FEAT_PCKFREEENABLE | - FEAT_FUNCGATED | - FEAT_ROWREPEATENABLE | FEAT_LINEBUFFERSPLIT | - FEAT_RESIZECONF | FEAT_DSI_PLL_PWR_BUG | - FEAT_DSI_PLL_FREQSEL | FEAT_CPR | FEAT_PRELOAD | - FEAT_FIR_COEF_V | FEAT_ALPHA_FIXED_ZORDER, + .features = omap3630_dss_feat_list, + .num_features = ARRAY_SIZE(omap3630_dss_feat_list), .num_mgrs = 2, .num_ovls = 3, @@ -413,13 +498,27 @@ static const struct omap_dss_features omap4430_es1_0_dss_features = { .reg_fields = omap4_dss_reg_fields, .num_reg_fields = ARRAY_SIZE(omap4_dss_reg_fields), - .has_feature = - FEAT_MGR_LCD2 | - FEAT_CORE_CLK_DIV | FEAT_LCD_CLK_SRC | - FEAT_DSI_DCS_CMD_CONFIG_VC | FEAT_DSI_VC_OCP_WIDTH | - FEAT_DSI_GNQ | FEAT_HANDLE_UV_SEPARATE | FEAT_ATTR2 | - FEAT_CPR | FEAT_PRELOAD | FEAT_FIR_COEF_V | - FEAT_ALPHA_FREE_ZORDER, + .features = omap4430_es1_0_dss_feat_list, + .num_features = ARRAY_SIZE(omap4430_es1_0_dss_feat_list), + + .num_mgrs = 3, + .num_ovls = 4, + .supported_displays = omap4_dss_supported_displays, + .supported_color_modes = omap4_dss_supported_color_modes, + .overlay_caps = omap4_dss_overlay_caps, + .clksrc_names = omap4_dss_clk_source_names, + .dss_params = omap4_dss_param_range, + .buffer_size_unit = 16, + .burst_size_unit = 16, +}; + +/* For OMAP4430 ES 2.0, 2.1 and 2.2 revisions */ +static const struct omap_dss_features omap4430_es2_0_1_2_dss_features = { + .reg_fields = omap4_dss_reg_fields, + .num_reg_fields = ARRAY_SIZE(omap4_dss_reg_fields), + + .features = omap4430_es2_0_1_2_dss_feat_list, + .num_features = ARRAY_SIZE(omap4430_es2_0_1_2_dss_feat_list), .num_mgrs = 3, .num_ovls = 4, @@ -437,13 +536,8 @@ static const struct omap_dss_features omap4_dss_features = { .reg_fields = omap4_dss_reg_fields, .num_reg_fields = ARRAY_SIZE(omap4_dss_reg_fields), - .has_feature = - FEAT_MGR_LCD2 | - FEAT_CORE_CLK_DIV | FEAT_LCD_CLK_SRC | - FEAT_DSI_DCS_CMD_CONFIG_VC | FEAT_DSI_VC_OCP_WIDTH | - FEAT_DSI_GNQ | FEAT_HDMI_CTS_SWMODE | - FEAT_HANDLE_UV_SEPARATE | FEAT_ATTR2 | FEAT_CPR | - FEAT_PRELOAD | FEAT_FIR_COEF_V | FEAT_ALPHA_FREE_ZORDER, + .features = omap4_dss_feat_list, + .num_features = ARRAY_SIZE(omap4_dss_feat_list), .num_mgrs = 3, .num_ovls = 4, @@ -547,7 +641,16 @@ u32 dss_feat_get_burst_size_unit(void) /* DSS has_feature check */ bool dss_has_feature(enum dss_feat_id id) { - return omap_current_dss_features->has_feature & id; + int i; + const enum dss_feat_id *features = omap_current_dss_features->features; + const int num_features = omap_current_dss_features->num_features; + + for (i = 0; i < num_features; i++) { + if (features[i] == id) + return true; + } + + return false; } void dss_feat_get_reg_field(enum dss_feat_reg_field id, u8 *start, u8 *end) @@ -569,6 +672,10 @@ void dss_features_init(void) omap_current_dss_features = &omap3430_dss_features; else if (omap_rev() == OMAP4430_REV_ES1_0) omap_current_dss_features = &omap4430_es1_0_dss_features; + else if (omap_rev() == OMAP4430_REV_ES2_0 || + omap_rev() == OMAP4430_REV_ES2_1 || + omap_rev() == OMAP4430_REV_ES2_2) + omap_current_dss_features = &omap4430_es2_0_1_2_dss_features; else if (cpu_is_omap44xx()) omap_current_dss_features = &omap4_dss_features; else diff --git a/drivers/video/omap2/dss/dss_features.h b/drivers/video/omap2/dss/dss_features.h index cd833bbaac3d..c332e7ddfce1 100644 --- a/drivers/video/omap2/dss/dss_features.h +++ b/drivers/video/omap2/dss/dss_features.h @@ -31,33 +31,37 @@ /* DSS has feature id */ enum dss_feat_id { - FEAT_LCDENABLEPOL = 1 << 3, - FEAT_LCDENABLESIGNAL = 1 << 4, - FEAT_PCKFREEENABLE = 1 << 5, - FEAT_FUNCGATED = 1 << 6, - FEAT_MGR_LCD2 = 1 << 7, - FEAT_LINEBUFFERSPLIT = 1 << 8, - FEAT_ROWREPEATENABLE = 1 << 9, - FEAT_RESIZECONF = 1 << 10, + FEAT_LCDENABLEPOL, + FEAT_LCDENABLESIGNAL, + FEAT_PCKFREEENABLE, + FEAT_FUNCGATED, + FEAT_MGR_LCD2, + FEAT_LINEBUFFERSPLIT, + FEAT_ROWREPEATENABLE, + FEAT_RESIZECONF, /* Independent core clk divider */ - FEAT_CORE_CLK_DIV = 1 << 11, - FEAT_LCD_CLK_SRC = 1 << 12, + FEAT_CORE_CLK_DIV, + FEAT_LCD_CLK_SRC, /* DSI-PLL power command 0x3 is not working */ - FEAT_DSI_PLL_PWR_BUG = 1 << 13, - FEAT_DSI_PLL_FREQSEL = 1 << 14, - FEAT_DSI_DCS_CMD_CONFIG_VC = 1 << 15, - FEAT_DSI_VC_OCP_WIDTH = 1 << 16, - FEAT_DSI_REVERSE_TXCLKESC = 1 << 17, - FEAT_DSI_GNQ = 1 << 18, - FEAT_HDMI_CTS_SWMODE = 1 << 19, - FEAT_HANDLE_UV_SEPARATE = 1 << 20, - FEAT_ATTR2 = 1 << 21, - FEAT_VENC_REQUIRES_TV_DAC_CLK = 1 << 22, - FEAT_CPR = 1 << 23, - FEAT_PRELOAD = 1 << 24, - FEAT_FIR_COEF_V = 1 << 25, - FEAT_ALPHA_FIXED_ZORDER = 1 << 26, - FEAT_ALPHA_FREE_ZORDER = 1 << 27, + FEAT_DSI_PLL_PWR_BUG, + FEAT_DSI_PLL_FREQSEL, + FEAT_DSI_DCS_CMD_CONFIG_VC, + FEAT_DSI_VC_OCP_WIDTH, + FEAT_DSI_REVERSE_TXCLKESC, + FEAT_DSI_GNQ, + FEAT_HDMI_CTS_SWMODE, + FEAT_HDMI_AUDIO_USE_MCLK, + FEAT_HANDLE_UV_SEPARATE, + FEAT_ATTR2, + FEAT_VENC_REQUIRES_TV_DAC_CLK, + FEAT_CPR, + FEAT_PRELOAD, + FEAT_FIR_COEF_V, + FEAT_ALPHA_FIXED_ZORDER, + FEAT_ALPHA_FREE_ZORDER, + FEAT_FIFO_MERGE, + /* An unknown HW bug causing the normal FIFO thresholds not to work */ + FEAT_OMAP3_DSI_FIFO_BUG, }; /* DSS register field id */ diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c index a36b934b2db4..c4b4f6950a92 100644 --- a/drivers/video/omap2/dss/hdmi.c +++ b/drivers/video/omap2/dss/hdmi.c @@ -58,8 +58,6 @@ #define EDID_SIZE_BLOCK0_TIMING_DESCRIPTOR 4 #define EDID_SIZE_BLOCK1_TIMING_DESCRIPTOR 4 -#define OMAP_HDMI_TIMINGS_NB 34 - #define HDMI_DEFAULT_REGN 16 #define HDMI_DEFAULT_REGM2 1 @@ -68,8 +66,6 @@ static struct { struct omap_display_platform_data *pdata; struct platform_device *pdev; struct hdmi_ip_data ip_data; - int code; - int mode; struct clk *sys_clk; } hdmi; @@ -88,77 +84,46 @@ static struct { * map it to corresponding CEA or VESA index. */ -static const struct hdmi_timings cea_vesa_timings[OMAP_HDMI_TIMINGS_NB] = { - { {640, 480, 25200, 96, 16, 48, 2, 10, 33} , 0 , 0}, - { {1280, 720, 74250, 40, 440, 220, 5, 5, 20}, 1, 1}, - { {1280, 720, 74250, 40, 110, 220, 5, 5, 20}, 1, 1}, - { {720, 480, 27027, 62, 16, 60, 6, 9, 30}, 0, 0}, - { {2880, 576, 108000, 256, 48, 272, 5, 5, 39}, 0, 0}, - { {1440, 240, 27027, 124, 38, 114, 3, 4, 15}, 0, 0}, - { {1440, 288, 27000, 126, 24, 138, 3, 2, 19}, 0, 0}, - { {1920, 540, 74250, 44, 528, 148, 5, 2, 15}, 1, 1}, - { {1920, 540, 74250, 44, 88, 148, 5, 2, 15}, 1, 1}, - { {1920, 1080, 148500, 44, 88, 148, 5, 4, 36}, 1, 1}, - { {720, 576, 27000, 64, 12, 68, 5, 5, 39}, 0, 0}, - { {1440, 576, 54000, 128, 24, 136, 5, 5, 39}, 0, 0}, - { {1920, 1080, 148500, 44, 528, 148, 5, 4, 36}, 1, 1}, - { {2880, 480, 108108, 248, 64, 240, 6, 9, 30}, 0, 0}, - { {1920, 1080, 74250, 44, 638, 148, 5, 4, 36}, 1, 1}, - /* VESA From Here */ - { {640, 480, 25175, 96, 16, 48, 2 , 11, 31}, 0, 0}, - { {800, 600, 40000, 128, 40, 88, 4 , 1, 23}, 1, 1}, - { {848, 480, 33750, 112, 16, 112, 8 , 6, 23}, 1, 1}, - { {1280, 768, 79500, 128, 64, 192, 7 , 3, 20}, 1, 0}, - { {1280, 800, 83500, 128, 72, 200, 6 , 3, 22}, 1, 0}, - { {1360, 768, 85500, 112, 64, 256, 6 , 3, 18}, 1, 1}, - { {1280, 960, 108000, 112, 96, 312, 3 , 1, 36}, 1, 1}, - { {1280, 1024, 108000, 112, 48, 248, 3 , 1, 38}, 1, 1}, - { {1024, 768, 65000, 136, 24, 160, 6, 3, 29}, 0, 0}, - { {1400, 1050, 121750, 144, 88, 232, 4, 3, 32}, 1, 0}, - { {1440, 900, 106500, 152, 80, 232, 6, 3, 25}, 1, 0}, - { {1680, 1050, 146250, 176 , 104, 280, 6, 3, 30}, 1, 0}, - { {1366, 768, 85500, 143, 70, 213, 3, 3, 24}, 1, 1}, - { {1920, 1080, 148500, 44, 148, 80, 5, 4, 36}, 1, 1}, - { {1280, 768, 68250, 32, 48, 80, 7, 3, 12}, 0, 1}, - { {1400, 1050, 101000, 32, 48, 80, 4, 3, 23}, 0, 1}, - { {1680, 1050, 119000, 32, 48, 80, 6, 3, 21}, 0, 1}, - { {1280, 800, 79500, 32, 48, 80, 6, 3, 14}, 0, 1}, - { {1280, 720, 74250, 40, 110, 220, 5, 5, 20}, 1, 1} -}; - -/* - * This is a static mapping array which maps the timing values - * with corresponding CEA / VESA code - */ -static const int code_index[OMAP_HDMI_TIMINGS_NB] = { - 1, 19, 4, 2, 37, 6, 21, 20, 5, 16, 17, 29, 31, 35, 32, - /* <--15 CEA 17--> vesa*/ - 4, 9, 0xE, 0x17, 0x1C, 0x27, 0x20, 0x23, 0x10, 0x2A, - 0X2F, 0x3A, 0X51, 0X52, 0x16, 0x29, 0x39, 0x1B +static const struct hdmi_config cea_timings[] = { +{ {640, 480, 25200, 96, 16, 48, 2, 10, 33, 0, 0, 0}, {1, HDMI_HDMI} }, +{ {720, 480, 27027, 62, 16, 60, 6, 9, 30, 0, 0, 0}, {2, HDMI_HDMI} }, +{ {1280, 720, 74250, 40, 110, 220, 5, 5, 20, 1, 1, 0}, {4, HDMI_HDMI} }, +{ {1920, 540, 74250, 44, 88, 148, 5, 2, 15, 1, 1, 1}, {5, HDMI_HDMI} }, +{ {1440, 240, 27027, 124, 38, 114, 3, 4, 15, 0, 0, 1}, {6, HDMI_HDMI} }, +{ {1920, 1080, 148500, 44, 88, 148, 5, 4, 36, 1, 1, 0}, {16, HDMI_HDMI} }, +{ {720, 576, 27000, 64, 12, 68, 5, 5, 39, 0, 0, 0}, {17, HDMI_HDMI} }, +{ {1280, 720, 74250, 40, 440, 220, 5, 5, 20, 1, 1, 0}, {19, HDMI_HDMI} }, +{ {1920, 540, 74250, 44, 528, 148, 5, 2, 15, 1, 1, 1}, {20, HDMI_HDMI} }, +{ {1440, 288, 27000, 126, 24, 138, 3, 2, 19, 0, 0, 1}, {21, HDMI_HDMI} }, +{ {1440, 576, 54000, 128, 24, 136, 5, 5, 39, 0, 0, 0}, {29, HDMI_HDMI} }, +{ {1920, 1080, 148500, 44, 528, 148, 5, 4, 36, 1, 1, 0}, {31, HDMI_HDMI} }, +{ {1920, 1080, 74250, 44, 638, 148, 5, 4, 36, 1, 1, 0}, {32, HDMI_HDMI} }, +{ {2880, 480, 108108, 248, 64, 240, 6, 9, 30, 0, 0, 0}, {35, HDMI_HDMI} }, +{ {2880, 576, 108000, 256, 48, 272, 5, 5, 39, 0, 0, 0}, {37, HDMI_HDMI} }, }; - -/* - * This is reverse static mapping which maps the CEA / VESA code - * to the corresponding timing values - */ -static const int code_cea[39] = { - -1, 0, 3, 3, 2, 8, 5, 5, -1, -1, - -1, -1, -1, -1, -1, -1, 9, 10, 10, 1, - 7, 6, 6, -1, -1, -1, -1, -1, -1, 11, - 11, 12, 14, -1, -1, 13, 13, 4, 4 +static const struct hdmi_config vesa_timings[] = { +/* VESA From Here */ +{ {640, 480, 25175, 96, 16, 48, 2 , 11, 31, 0, 0, 0}, {4, HDMI_DVI} }, +{ {800, 600, 40000, 128, 40, 88, 4 , 1, 23, 1, 1, 0}, {9, HDMI_DVI} }, +{ {848, 480, 33750, 112, 16, 112, 8 , 6, 23, 1, 1, 0}, {0xE, HDMI_DVI} }, +{ {1280, 768, 79500, 128, 64, 192, 7 , 3, 20, 1, 0, 0}, {0x17, HDMI_DVI} }, +{ {1280, 800, 83500, 128, 72, 200, 6 , 3, 22, 1, 0, 0}, {0x1C, HDMI_DVI} }, +{ {1360, 768, 85500, 112, 64, 256, 6 , 3, 18, 1, 1, 0}, {0x27, HDMI_DVI} }, +{ {1280, 960, 108000, 112, 96, 312, 3 , 1, 36, 1, 1, 0}, {0x20, HDMI_DVI} }, +{ {1280, 1024, 108000, 112, 48, 248, 3 , 1, 38, 1, 1, 0}, {0x23, HDMI_DVI} }, +{ {1024, 768, 65000, 136, 24, 160, 6, 3, 29, 0, 0, 0}, {0x10, HDMI_DVI} }, +{ {1400, 1050, 121750, 144, 88, 232, 4, 3, 32, 1, 0, 0}, {0x2A, HDMI_DVI} }, +{ {1440, 900, 106500, 152, 80, 232, 6, 3, 25, 1, 0, 0}, {0x2F, HDMI_DVI} }, +{ {1680, 1050, 146250, 176 , 104, 280, 6, 3, 30, 1, 0, 0}, {0x3A, HDMI_DVI} }, +{ {1366, 768, 85500, 143, 70, 213, 3, 3, 24, 1, 1, 0}, {0x51, HDMI_DVI} }, +{ {1920, 1080, 148500, 44, 148, 80, 5, 4, 36, 1, 1, 0}, {0x52, HDMI_DVI} }, +{ {1280, 768, 68250, 32, 48, 80, 7, 3, 12, 0, 1, 0}, {0x16, HDMI_DVI} }, +{ {1400, 1050, 101000, 32, 48, 80, 4, 3, 23, 0, 1, 0}, {0x29, HDMI_DVI} }, +{ {1680, 1050, 119000, 32, 48, 80, 6, 3, 21, 0, 1, 0}, {0x39, HDMI_DVI} }, +{ {1280, 800, 79500, 32, 48, 80, 6, 3, 14, 0, 1, 0}, {0x1B, HDMI_DVI} }, +{ {1280, 720, 74250, 40, 110, 220, 5, 5, 20, 1, 1, 0}, {0x55, HDMI_DVI} } }; -static const int code_vesa[85] = { - -1, -1, -1, -1, 15, -1, -1, -1, -1, 16, - -1, -1, -1, -1, 17, -1, 23, -1, -1, -1, - -1, -1, 29, 18, -1, -1, -1, 32, 19, -1, - -1, -1, 21, -1, -1, 22, -1, -1, -1, 20, - -1, 30, 24, -1, -1, -1, -1, 25, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 31, 26, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 27, 28, -1, 33}; - static int hdmi_runtime_get(void) { int r; @@ -210,88 +175,89 @@ int hdmi_init_display(struct omap_dss_device *dssdev) return 0; } -static int get_timings_index(void) +static const struct hdmi_config *hdmi_find_timing( + const struct hdmi_config *timings_arr, + int len) { - int code; + int i; - if (hdmi.mode == 0) - code = code_vesa[hdmi.code]; - else - code = code_cea[hdmi.code]; + for (i = 0; i < len; i++) { + if (timings_arr[i].cm.code == hdmi.ip_data.cfg.cm.code) + return &timings_arr[i]; + } + return NULL; +} - if (code == -1) { - /* HDMI code 4 corresponds to 640 * 480 VGA */ - hdmi.code = 4; - /* DVI mode 1 corresponds to HDMI 0 to DVI */ - hdmi.mode = HDMI_DVI; +static const struct hdmi_config *hdmi_get_timings(void) +{ + const struct hdmi_config *arr; + int len; + + if (hdmi.ip_data.cfg.cm.mode == HDMI_DVI) { + arr = vesa_timings; + len = ARRAY_SIZE(vesa_timings); + } else { + arr = cea_timings; + len = ARRAY_SIZE(cea_timings); + } + + return hdmi_find_timing(arr, len); +} + +static bool hdmi_timings_compare(struct omap_video_timings *timing1, + const struct hdmi_video_timings *timing2) +{ + int timing1_vsync, timing1_hsync, timing2_vsync, timing2_hsync; - code = code_vesa[hdmi.code]; + if ((timing2->pixel_clock == timing1->pixel_clock) && + (timing2->x_res == timing1->x_res) && + (timing2->y_res == timing1->y_res)) { + + timing2_hsync = timing2->hfp + timing2->hsw + timing2->hbp; + timing1_hsync = timing1->hfp + timing1->hsw + timing1->hbp; + timing2_vsync = timing2->vfp + timing2->vsw + timing2->vbp; + timing1_vsync = timing2->vfp + timing2->vsw + timing2->vbp; + + DSSDBG("timing1_hsync = %d timing1_vsync = %d"\ + "timing2_hsync = %d timing2_vsync = %d\n", + timing1_hsync, timing1_vsync, + timing2_hsync, timing2_vsync); + + if ((timing1_hsync == timing2_hsync) && + (timing1_vsync == timing2_vsync)) { + return true; + } } - return code; + return false; } static struct hdmi_cm hdmi_get_code(struct omap_video_timings *timing) { - int i = 0, code = -1, temp_vsync = 0, temp_hsync = 0; - int timing_vsync = 0, timing_hsync = 0; - struct hdmi_video_timings temp; + int i; struct hdmi_cm cm = {-1}; DSSDBG("hdmi_get_code\n"); - for (i = 0; i < OMAP_HDMI_TIMINGS_NB; i++) { - temp = cea_vesa_timings[i].timings; - if ((temp.pixel_clock == timing->pixel_clock) && - (temp.x_res == timing->x_res) && - (temp.y_res == timing->y_res)) { - - temp_hsync = temp.hfp + temp.hsw + temp.hbp; - timing_hsync = timing->hfp + timing->hsw + timing->hbp; - temp_vsync = temp.vfp + temp.vsw + temp.vbp; - timing_vsync = timing->vfp + timing->vsw + timing->vbp; - - DSSDBG("temp_hsync = %d , temp_vsync = %d" - "timing_hsync = %d, timing_vsync = %d\n", - temp_hsync, temp_hsync, - timing_hsync, timing_vsync); - - if ((temp_hsync == timing_hsync) && - (temp_vsync == timing_vsync)) { - code = i; - cm.code = code_index[i]; - if (code < 14) - cm.mode = HDMI_HDMI; - else - cm.mode = HDMI_DVI; - DSSDBG("Hdmi_code = %d mode = %d\n", - cm.code, cm.mode); - break; - } + for (i = 0; i < ARRAY_SIZE(cea_timings); i++) { + if (hdmi_timings_compare(timing, &cea_timings[i].timings)) { + cm = cea_timings[i].cm; + goto end; + } + } + for (i = 0; i < ARRAY_SIZE(vesa_timings); i++) { + if (hdmi_timings_compare(timing, &vesa_timings[i].timings)) { + cm = vesa_timings[i].cm; + goto end; } } - return cm; -} +end: return cm; -static void update_hdmi_timings(struct hdmi_config *cfg, - struct omap_video_timings *timings, int code) -{ - cfg->timings.timings.x_res = timings->x_res; - cfg->timings.timings.y_res = timings->y_res; - cfg->timings.timings.hbp = timings->hbp; - cfg->timings.timings.hfp = timings->hfp; - cfg->timings.timings.hsw = timings->hsw; - cfg->timings.timings.vbp = timings->vbp; - cfg->timings.timings.vfp = timings->vfp; - cfg->timings.timings.vsw = timings->vsw; - cfg->timings.timings.pixel_clock = timings->pixel_clock; - cfg->timings.vsync_pol = cea_vesa_timings[code].vsync_pol; - cfg->timings.hsync_pol = cea_vesa_timings[code].hsync_pol; } unsigned long hdmi_get_pixel_clock(void) { /* HDMI Pixel Clock in Mhz */ - return hdmi.ip_data.cfg.timings.timings.pixel_clock * 1000; + return hdmi.ip_data.cfg.timings.pixel_clock * 1000; } static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy, @@ -312,24 +278,24 @@ static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy, refclk = clkin / pi->regn; - /* - * multiplier is pixel_clk/ref_clk - * Multiplying by 100 to avoid fractional part removal - */ - pi->regm = (phy * 100 / (refclk)) / 100; - if (dssdev->clocks.hdmi.regm2 == 0) pi->regm2 = HDMI_DEFAULT_REGM2; else pi->regm2 = dssdev->clocks.hdmi.regm2; /* + * multiplier is pixel_clk/ref_clk + * Multiplying by 100 to avoid fractional part removal + */ + pi->regm = phy * pi->regm2 / refclk; + + /* * fractional multiplier is remainder of the difference between * multiplier and actual phy(required pixel clock thus should be * multiplied by 2^18(262144) divided by the reference clock */ - mf = (phy - pi->regm * refclk) * 262144; - pi->regmf = mf / (refclk); + mf = (phy - pi->regm / pi->regm2 * refclk) * 262144; + pi->regmf = pi->regm2 * mf / refclk; /* * Dcofreq should be set to 1 if required pixel clock @@ -347,7 +313,8 @@ static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy, static int hdmi_power_on(struct omap_dss_device *dssdev) { - int r, code = 0; + int r; + const struct hdmi_config *timing; struct omap_video_timings *p; unsigned long phy; @@ -363,9 +330,16 @@ static int hdmi_power_on(struct omap_dss_device *dssdev) dssdev->panel.timings.x_res, dssdev->panel.timings.y_res); - code = get_timings_index(); - update_hdmi_timings(&hdmi.ip_data.cfg, p, code); - + timing = hdmi_get_timings(); + if (timing == NULL) { + /* HDMI code 4 corresponds to 640 * 480 VGA */ + hdmi.ip_data.cfg.cm.code = 4; + /* DVI mode 1 corresponds to HDMI 0 to DVI */ + hdmi.ip_data.cfg.cm.mode = HDMI_DVI; + hdmi.ip_data.cfg = vesa_timings[0]; + } else { + hdmi.ip_data.cfg = *timing; + } phy = p->pixel_clock; hdmi_compute_pll(dssdev, phy, &hdmi.ip_data.pll_data); @@ -385,8 +359,6 @@ static int hdmi_power_on(struct omap_dss_device *dssdev) goto err; } - hdmi.ip_data.cfg.cm.mode = hdmi.mode; - hdmi.ip_data.cfg.cm.code = hdmi.code; hdmi.ip_data.ops->video_configure(&hdmi.ip_data); /* Make selection of HDMI in DSS */ @@ -453,8 +425,8 @@ void omapdss_hdmi_display_set_timing(struct omap_dss_device *dssdev) struct hdmi_cm cm; cm = hdmi_get_code(&dssdev->panel.timings); - hdmi.code = cm.code; - hdmi.mode = cm.mode; + hdmi.ip_data.cfg.cm.code = cm.code; + hdmi.ip_data.cfg.cm.mode = cm.mode; if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { int r; @@ -717,13 +689,15 @@ static int hdmi_audio_hw_params(struct snd_pcm_substream *substream, if (dss_has_feature(FEAT_HDMI_CTS_SWMODE)) { core_cfg.aud_par_busclk = 0; core_cfg.cts_mode = HDMI_AUDIO_CTS_MODE_SW; - core_cfg.use_mclk = false; + core_cfg.use_mclk = dss_has_feature(FEAT_HDMI_AUDIO_USE_MCLK); } else { core_cfg.aud_par_busclk = (((128 * 31) - 1) << 8); core_cfg.cts_mode = HDMI_AUDIO_CTS_MODE_HW; core_cfg.use_mclk = true; - core_cfg.mclk_mode = HDMI_AUDIO_MCLK_128FS; } + + if (core_cfg.use_mclk) + core_cfg.mclk_mode = HDMI_AUDIO_MCLK_128FS; core_cfg.layout = HDMI_AUDIO_LAYOUT_2CH; core_cfg.en_spdif = false; /* Use sample frequency from channel status word */ @@ -756,7 +730,7 @@ static int hdmi_audio_hw_params(struct snd_pcm_substream *substream, static int hdmi_audio_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - if (!hdmi.mode) { + if (!hdmi.ip_data.cfg.cm.mode) { pr_err("Current video settings do not support audio.\n"); return -EIO; } diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c index d1858e71c64e..e7364603f6a1 100644 --- a/drivers/video/omap2/dss/manager.c +++ b/drivers/video/omap2/dss/manager.c @@ -494,6 +494,11 @@ static int dss_mgr_wait_for_vsync(struct omap_overlay_manager *mgr) { unsigned long timeout = msecs_to_jiffies(500); u32 irq; + int r; + + r = dispc_runtime_get(); + if (r) + return r; if (mgr->device->type == OMAP_DISPLAY_TYPE_VENC) { irq = DISPC_IRQ_EVSYNC_ODD; @@ -505,7 +510,12 @@ static int dss_mgr_wait_for_vsync(struct omap_overlay_manager *mgr) else irq = DISPC_IRQ_VSYNC2; } - return omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout); + + r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout); + + dispc_runtime_put(); + + return r; } int dss_init_overlay_managers(struct platform_device *pdev) diff --git a/drivers/video/omap2/dss/rfbi.c b/drivers/video/omap2/dss/rfbi.c index 55f398014f33..788a0ef6323a 100644 --- a/drivers/video/omap2/dss/rfbi.c +++ b/drivers/video/omap2/dss/rfbi.c @@ -922,35 +922,34 @@ static int omap_rfbihw_probe(struct platform_device *pdev) rfbi_mem = platform_get_resource(rfbi.pdev, IORESOURCE_MEM, 0); if (!rfbi_mem) { DSSERR("can't get IORESOURCE_MEM RFBI\n"); - r = -EINVAL; - goto err_ioremap; + return -EINVAL; } - rfbi.base = ioremap(rfbi_mem->start, resource_size(rfbi_mem)); + + rfbi.base = devm_ioremap(&pdev->dev, rfbi_mem->start, + resource_size(rfbi_mem)); if (!rfbi.base) { DSSERR("can't ioremap RFBI\n"); - r = -ENOMEM; - goto err_ioremap; + return -ENOMEM; } - pm_runtime_enable(&pdev->dev); - - r = rfbi_runtime_get(); - if (r) - goto err_get_rfbi; - - msleep(10); - clk = clk_get(&pdev->dev, "ick"); if (IS_ERR(clk)) { DSSERR("can't get ick\n"); - r = PTR_ERR(clk); - goto err_get_ick; + return PTR_ERR(clk); } rfbi.l4_khz = clk_get_rate(clk) / 1000; clk_put(clk); + pm_runtime_enable(&pdev->dev); + + r = rfbi_runtime_get(); + if (r) + goto err_runtime_get; + + msleep(10); + rev = rfbi_read_reg(RFBI_REVISION); dev_dbg(&pdev->dev, "OMAP RFBI rev %d.%d\n", FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); @@ -959,19 +958,14 @@ static int omap_rfbihw_probe(struct platform_device *pdev) return 0; -err_get_ick: - rfbi_runtime_put(); -err_get_rfbi: +err_runtime_get: pm_runtime_disable(&pdev->dev); - iounmap(rfbi.base); -err_ioremap: return r; } static int omap_rfbihw_remove(struct platform_device *pdev) { pm_runtime_disable(&pdev->dev); - iounmap(rfbi.base); return 0; } diff --git a/drivers/video/omap2/dss/ti_hdmi.h b/drivers/video/omap2/dss/ti_hdmi.h index 50dadba5070a..1f58b84d6901 100644 --- a/drivers/video/omap2/dss/ti_hdmi.h +++ b/drivers/video/omap2/dss/ti_hdmi.h @@ -42,6 +42,7 @@ enum hdmi_clk_refsel { HDMI_REFSEL_SYSCLK = 3 }; +/* HDMI timing structure */ struct hdmi_video_timings { u16 x_res; u16 y_res; @@ -53,13 +54,9 @@ struct hdmi_video_timings { u16 vsw; u16 vfp; u16 vbp; -}; - -/* HDMI timing structure */ -struct hdmi_timings { - struct hdmi_video_timings timings; - int vsync_pol; - int hsync_pol; + bool vsync_pol; + bool hsync_pol; + bool interlace; }; struct hdmi_cm { @@ -68,8 +65,7 @@ struct hdmi_cm { }; struct hdmi_config { - struct hdmi_timings timings; - u16 interlace; + struct hdmi_video_timings timings; struct hdmi_cm cm; }; @@ -117,6 +113,47 @@ struct ti_hdmi_ip_ops { }; +/* + * Refer to section 8.2 in HDMI 1.3 specification for + * details about infoframe databytes + */ +struct hdmi_core_infoframe_avi { + /* Y0, Y1 rgb,yCbCr */ + u8 db1_format; + /* A0 Active information Present */ + u8 db1_active_info; + /* B0, B1 Bar info data valid */ + u8 db1_bar_info_dv; + /* S0, S1 scan information */ + u8 db1_scan_info; + /* C0, C1 colorimetry */ + u8 db2_colorimetry; + /* M0, M1 Aspect ratio (4:3, 16:9) */ + u8 db2_aspect_ratio; + /* R0...R3 Active format aspect ratio */ + u8 db2_active_fmt_ar; + /* ITC IT content. */ + u8 db3_itc; + /* EC0, EC1, EC2 Extended colorimetry */ + u8 db3_ec; + /* Q1, Q0 Quantization range */ + u8 db3_q_range; + /* SC1, SC0 Non-uniform picture scaling */ + u8 db3_nup_scaling; + /* VIC0..6 Video format identification */ + u8 db4_videocode; + /* PR0..PR3 Pixel repetition factor */ + u8 db5_pixel_repeat; + /* Line number end of top bar */ + u16 db6_7_line_eoftop; + /* Line number start of bottom bar */ + u16 db8_9_line_sofbottom; + /* Pixel number end of left bar */ + u16 db10_11_pixel_eofleft; + /* Pixel number start of right bar */ + u16 db12_13_pixel_sofright; +}; + struct hdmi_ip_data { void __iomem *base_wp; /* HDMI wrapper */ unsigned long core_sys_offset; @@ -126,6 +163,7 @@ struct hdmi_ip_data { const struct ti_hdmi_ip_ops *ops; struct hdmi_config cfg; struct hdmi_pll_info pll_data; + struct hdmi_core_infoframe_avi avi_cfg; /* ti_hdmi_4xxx_ip private data. These should be in a separate struct */ int hpd_gpio; diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c index 6847a478b459..bfe6fe65c8be 100644 --- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c @@ -587,12 +587,12 @@ static void hdmi_core_video_config(struct hdmi_ip_data *ip_data, HDMI_CORE_SYS_TMDS_CTRL, cfg->tclk_sel_clkmult, 6, 5); } -static void hdmi_core_aux_infoframe_avi_config(struct hdmi_ip_data *ip_data, - struct hdmi_core_infoframe_avi info_avi) +static void hdmi_core_aux_infoframe_avi_config(struct hdmi_ip_data *ip_data) { u32 val; char sum = 0, checksum = 0; void __iomem *av_base = hdmi_av_base(ip_data); + struct hdmi_core_infoframe_avi info_avi = ip_data->avi_cfg; sum += 0x82 + 0x002 + 0x00D; hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_TYPE, 0x082); @@ -682,8 +682,7 @@ static void hdmi_core_av_packet_config(struct hdmi_ip_data *ip_data, } static void hdmi_wp_init(struct omap_video_timings *timings, - struct hdmi_video_format *video_fmt, - struct hdmi_video_interface *video_int) + struct hdmi_video_format *video_fmt) { pr_debug("Enter hdmi_wp_init\n"); @@ -698,12 +697,6 @@ static void hdmi_wp_init(struct omap_video_timings *timings, video_fmt->y_res = 0; video_fmt->x_res = 0; - video_int->vsp = 0; - video_int->hsp = 0; - - video_int->interlacing = 0; - video_int->tm = 0; /* HDMI_TIMING_SLAVE */ - } void ti_hdmi_4xxx_wp_video_start(struct hdmi_ip_data *ip_data, bool start) @@ -716,15 +709,15 @@ static void hdmi_wp_video_init_format(struct hdmi_video_format *video_fmt, { pr_debug("Enter hdmi_wp_video_init_format\n"); - video_fmt->y_res = param->timings.timings.y_res; - video_fmt->x_res = param->timings.timings.x_res; + video_fmt->y_res = param->timings.y_res; + video_fmt->x_res = param->timings.x_res; - timings->hbp = param->timings.timings.hbp; - timings->hfp = param->timings.timings.hfp; - timings->hsw = param->timings.timings.hsw; - timings->vbp = param->timings.timings.vbp; - timings->vfp = param->timings.timings.vfp; - timings->vsw = param->timings.timings.vsw; + timings->hbp = param->timings.hbp; + timings->hfp = param->timings.hfp; + timings->hsw = param->timings.hsw; + timings->vbp = param->timings.vbp; + timings->vfp = param->timings.vfp; + timings->vsw = param->timings.vsw; } static void hdmi_wp_video_config_format(struct hdmi_ip_data *ip_data, @@ -740,17 +733,16 @@ static void hdmi_wp_video_config_format(struct hdmi_ip_data *ip_data, hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_SIZE, l); } -static void hdmi_wp_video_config_interface(struct hdmi_ip_data *ip_data, - struct hdmi_video_interface *video_int) +static void hdmi_wp_video_config_interface(struct hdmi_ip_data *ip_data) { u32 r; pr_debug("Enter hdmi_wp_video_config_interface\n"); r = hdmi_read_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG); - r = FLD_MOD(r, video_int->vsp, 7, 7); - r = FLD_MOD(r, video_int->hsp, 6, 6); - r = FLD_MOD(r, video_int->interlacing, 3, 3); - r = FLD_MOD(r, video_int->tm, 1, 0); + r = FLD_MOD(r, ip_data->cfg.timings.vsync_pol, 7, 7); + r = FLD_MOD(r, ip_data->cfg.timings.hsync_pol, 6, 6); + r = FLD_MOD(r, ip_data->cfg.timings.interlace, 3, 3); + r = FLD_MOD(r, 1, 1, 0); /* HDMI_TIMING_MASTER_24BIT */ hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG, r); } @@ -778,15 +770,13 @@ void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data) /* HDMI */ struct omap_video_timings video_timing; struct hdmi_video_format video_format; - struct hdmi_video_interface video_interface; /* HDMI core */ - struct hdmi_core_infoframe_avi avi_cfg; + struct hdmi_core_infoframe_avi avi_cfg = ip_data->avi_cfg; struct hdmi_core_video_config v_core_cfg; struct hdmi_core_packet_enable_repeat repeat_cfg; struct hdmi_config *cfg = &ip_data->cfg; - hdmi_wp_init(&video_timing, &video_format, - &video_interface); + hdmi_wp_init(&video_timing, &video_format); hdmi_core_init(&v_core_cfg, &avi_cfg, @@ -801,12 +791,7 @@ void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data) hdmi_wp_video_config_format(ip_data, &video_format); - video_interface.vsp = cfg->timings.vsync_pol; - video_interface.hsp = cfg->timings.hsync_pol; - video_interface.interlacing = cfg->interlace; - video_interface.tm = 1 ; /* HDMI_TIMING_MASTER_24BIT */ - - hdmi_wp_video_config_interface(ip_data, &video_interface); + hdmi_wp_video_config_interface(ip_data); /* * configure core video part @@ -848,7 +833,7 @@ void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data) avi_cfg.db10_11_pixel_eofleft = 0; avi_cfg.db12_13_pixel_sofright = 0; - hdmi_core_aux_infoframe_avi_config(ip_data, avi_cfg); + hdmi_core_aux_infoframe_avi_config(ip_data); /* enable/repeat the infoframe */ repeat_cfg.avi_infoframe = HDMI_PACKETENABLE; @@ -1076,13 +1061,9 @@ void hdmi_core_audio_config(struct hdmi_ip_data *ip_data, u32 r; void __iomem *av_base = hdmi_av_base(ip_data); - /* audio clock recovery parameters */ - r = hdmi_read_reg(av_base, HDMI_CORE_AV_ACR_CTRL); - r = FLD_MOD(r, cfg->use_mclk, 2, 2); - r = FLD_MOD(r, cfg->en_acr_pkt, 1, 1); - r = FLD_MOD(r, cfg->cts_mode, 0, 0); - hdmi_write_reg(av_base, HDMI_CORE_AV_ACR_CTRL, r); - + /* + * Parameters for generation of Audio Clock Recovery packets + */ REG_FLD_MOD(av_base, HDMI_CORE_AV_N_SVAL1, cfg->n, 7, 0); REG_FLD_MOD(av_base, HDMI_CORE_AV_N_SVAL2, cfg->n >> 8, 7, 0); REG_FLD_MOD(av_base, HDMI_CORE_AV_N_SVAL3, cfg->n >> 16, 7, 0); @@ -1094,14 +1075,6 @@ void hdmi_core_audio_config(struct hdmi_ip_data *ip_data, REG_FLD_MOD(av_base, HDMI_CORE_AV_CTS_SVAL3, cfg->cts >> 16, 7, 0); } else { - /* - * HDMI IP uses this configuration to divide the MCLK to - * update CTS value. - */ - REG_FLD_MOD(av_base, - HDMI_CORE_AV_FREQ_SVAL, cfg->mclk_mode, 2, 0); - - /* Configure clock for audio packets */ REG_FLD_MOD(av_base, HDMI_CORE_AV_AUD_PAR_BUSCLK_1, cfg->aud_par_busclk, 7, 0); REG_FLD_MOD(av_base, HDMI_CORE_AV_AUD_PAR_BUSCLK_2, @@ -1110,6 +1083,25 @@ void hdmi_core_audio_config(struct hdmi_ip_data *ip_data, (cfg->aud_par_busclk >> 16), 7, 0); } + /* Set ACR clock divisor */ + REG_FLD_MOD(av_base, + HDMI_CORE_AV_FREQ_SVAL, cfg->mclk_mode, 2, 0); + + r = hdmi_read_reg(av_base, HDMI_CORE_AV_ACR_CTRL); + /* + * Use TMDS clock for ACR packets. For devices that use + * the MCLK, this is the first part of the MCLK initialization. + */ + r = FLD_MOD(r, 0, 2, 2); + + r = FLD_MOD(r, cfg->en_acr_pkt, 1, 1); + r = FLD_MOD(r, cfg->cts_mode, 0, 0); + hdmi_write_reg(av_base, HDMI_CORE_AV_ACR_CTRL, r); + + /* For devices using MCLK, this completes its initialization. */ + if (cfg->use_mclk) + REG_FLD_MOD(av_base, HDMI_CORE_AV_ACR_CTRL, 1, 2, 2); + /* Override of SPDIF sample frequency with value in I2S_CHST4 */ REG_FLD_MOD(av_base, HDMI_CORE_AV_SPDIF_CTRL, cfg->fs_override, 1, 1); @@ -1205,7 +1197,7 @@ int hdmi_config_audio_acr(struct hdmi_ip_data *ip_data, { u32 r; u32 deep_color = 0; - u32 pclk = ip_data->cfg.timings.timings.pixel_clock; + u32 pclk = ip_data->cfg.timings.pixel_clock; if (n == NULL || cts == NULL) return -EINVAL; diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h index a442998980f1..a14d1a0e6e41 100644 --- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h @@ -450,46 +450,6 @@ struct hdmi_core_video_config { * Refer to section 8.2 in HDMI 1.3 specification for * details about infoframe databytes */ -struct hdmi_core_infoframe_avi { - /* Y0, Y1 rgb,yCbCr */ - u8 db1_format; - /* A0 Active information Present */ - u8 db1_active_info; - /* B0, B1 Bar info data valid */ - u8 db1_bar_info_dv; - /* S0, S1 scan information */ - u8 db1_scan_info; - /* C0, C1 colorimetry */ - u8 db2_colorimetry; - /* M0, M1 Aspect ratio (4:3, 16:9) */ - u8 db2_aspect_ratio; - /* R0...R3 Active format aspect ratio */ - u8 db2_active_fmt_ar; - /* ITC IT content. */ - u8 db3_itc; - /* EC0, EC1, EC2 Extended colorimetry */ - u8 db3_ec; - /* Q1, Q0 Quantization range */ - u8 db3_q_range; - /* SC1, SC0 Non-uniform picture scaling */ - u8 db3_nup_scaling; - /* VIC0..6 Video format identification */ - u8 db4_videocode; - /* PR0..PR3 Pixel repetition factor */ - u8 db5_pixel_repeat; - /* Line number end of top bar */ - u16 db6_7_line_eoftop; - /* Line number start of bottom bar */ - u16 db8_9_line_sofbottom; - /* Pixel number end of left bar */ - u16 db10_11_pixel_eofleft; - /* Pixel number start of right bar */ - u16 db12_13_pixel_sofright; -}; -/* - * Refer to section 8.2 in HDMI 1.3 specification for - * details about infoframe databytes - */ struct hdmi_core_infoframe_audio { u8 db1_coding_type; u8 db1_channel_count; @@ -517,13 +477,6 @@ struct hdmi_video_format { u32 x_res; /* pixel per line */ }; -struct hdmi_video_interface { - int vsp; /* Vsync polarity */ - int hsp; /* Hsync polarity */ - int interlacing; - int tm; /* Timing mode */ -}; - struct hdmi_audio_format { enum hdmi_stereo_channels stereo_channels; u8 active_chnnls_msk; diff --git a/drivers/video/omap2/dss/venc.c b/drivers/video/omap2/dss/venc.c index 5c3d0f901510..9c3daf71750c 100644 --- a/drivers/video/omap2/dss/venc.c +++ b/drivers/video/omap2/dss/venc.c @@ -699,6 +699,11 @@ void venc_dump_regs(struct seq_file *s) { #define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, venc_read_reg(r)) + if (cpu_is_omap44xx()) { + seq_printf(s, "VENC currently disabled on OMAP44xx\n"); + return; + } + if (venc_runtime_get()) return; @@ -790,39 +795,41 @@ static int omap_venchw_probe(struct platform_device *pdev) venc_mem = platform_get_resource(venc.pdev, IORESOURCE_MEM, 0); if (!venc_mem) { DSSERR("can't get IORESOURCE_MEM VENC\n"); - r = -EINVAL; - goto err_ioremap; + return -EINVAL; } - venc.base = ioremap(venc_mem->start, resource_size(venc_mem)); + + venc.base = devm_ioremap(&pdev->dev, venc_mem->start, + resource_size(venc_mem)); if (!venc.base) { DSSERR("can't ioremap VENC\n"); - r = -ENOMEM; - goto err_ioremap; + return -ENOMEM; } r = venc_get_clocks(pdev); if (r) - goto err_get_clk; + return r; pm_runtime_enable(&pdev->dev); r = venc_runtime_get(); if (r) - goto err_get_venc; + goto err_runtime_get; rev_id = (u8)(venc_read_reg(VENC_REV_ID) & 0xff); dev_dbg(&pdev->dev, "OMAP VENC rev %d\n", rev_id); venc_runtime_put(); - return omap_dss_register_driver(&venc_driver); + r = omap_dss_register_driver(&venc_driver); + if (r) + goto err_reg_panel_driver; + + return 0; -err_get_venc: +err_reg_panel_driver: +err_runtime_get: pm_runtime_disable(&pdev->dev); venc_put_clocks(); -err_get_clk: - iounmap(venc.base); -err_ioremap: return r; } @@ -837,7 +844,6 @@ static int omap_venchw_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); venc_put_clocks(); - iounmap(venc.base); return 0; } diff --git a/drivers/video/omap2/omapfb/omapfb-ioctl.c b/drivers/video/omap2/omapfb/omapfb-ioctl.c index 16ba6196f330..6a09ef87e14f 100644 --- a/drivers/video/omap2/omapfb/omapfb-ioctl.c +++ b/drivers/video/omap2/omapfb/omapfb-ioctl.c @@ -215,7 +215,7 @@ static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi) int r = 0, i; size_t size; - if (mi->type > OMAPFB_MEMTYPE_MAX) + if (mi->type != OMAPFB_MEMTYPE_SDRAM) return -EINVAL; size = PAGE_ALIGN(mi->size); diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c index ce158311ff59..b00db4068d21 100644 --- a/drivers/video/omap2/omapfb/omapfb-main.c +++ b/drivers/video/omap2/omapfb/omapfb-main.c @@ -1399,7 +1399,7 @@ static int omapfb_alloc_fbmem(struct fb_info *fbi, unsigned long size, if (!paddr) { DBG("allocating %lu bytes for fb %d\n", size, ofbi->id); - r = omap_vram_alloc(OMAP_VRAM_MEMTYPE_SDRAM, size, &paddr); + r = omap_vram_alloc(size, &paddr); } else { DBG("reserving %lu bytes at %lx for fb %d\n", size, paddr, ofbi->id); @@ -1487,60 +1487,6 @@ static int omapfb_alloc_fbmem_display(struct fb_info *fbi, unsigned long size, return omapfb_alloc_fbmem(fbi, size, paddr); } -static enum omap_color_mode fb_format_to_dss_mode(enum omapfb_color_format fmt) -{ - enum omap_color_mode mode; - - switch (fmt) { - case OMAPFB_COLOR_RGB565: - mode = OMAP_DSS_COLOR_RGB16; - break; - case OMAPFB_COLOR_YUV422: - mode = OMAP_DSS_COLOR_YUV2; - break; - case OMAPFB_COLOR_CLUT_8BPP: - mode = OMAP_DSS_COLOR_CLUT8; - break; - case OMAPFB_COLOR_CLUT_4BPP: - mode = OMAP_DSS_COLOR_CLUT4; - break; - case OMAPFB_COLOR_CLUT_2BPP: - mode = OMAP_DSS_COLOR_CLUT2; - break; - case OMAPFB_COLOR_CLUT_1BPP: - mode = OMAP_DSS_COLOR_CLUT1; - break; - case OMAPFB_COLOR_RGB444: - mode = OMAP_DSS_COLOR_RGB12U; - break; - case OMAPFB_COLOR_YUY422: - mode = OMAP_DSS_COLOR_UYVY; - break; - case OMAPFB_COLOR_ARGB16: - mode = OMAP_DSS_COLOR_ARGB16; - break; - case OMAPFB_COLOR_RGB24U: - mode = OMAP_DSS_COLOR_RGB24U; - break; - case OMAPFB_COLOR_RGB24P: - mode = OMAP_DSS_COLOR_RGB24P; - break; - case OMAPFB_COLOR_ARGB32: - mode = OMAP_DSS_COLOR_ARGB32; - break; - case OMAPFB_COLOR_RGBA32: - mode = OMAP_DSS_COLOR_RGBA32; - break; - case OMAPFB_COLOR_RGBX32: - mode = OMAP_DSS_COLOR_RGBX32; - break; - default: - mode = -EINVAL; - } - - return mode; -} - static int omapfb_parse_vram_param(const char *param, int max_entries, unsigned long *sizes, unsigned long *paddrs) { @@ -1614,23 +1560,6 @@ static int omapfb_allocate_all_fbs(struct omapfb2_device *fbdev) memset(&vram_paddrs, 0, sizeof(vram_paddrs)); } - if (fbdev->dev->platform_data) { - struct omapfb_platform_data *opd; - opd = fbdev->dev->platform_data; - for (i = 0; i < opd->mem_desc.region_cnt; ++i) { - if (!vram_sizes[i]) { - unsigned long size; - unsigned long paddr; - - size = opd->mem_desc.region[i].size; - paddr = opd->mem_desc.region[i].paddr; - - vram_sizes[i] = size; - vram_paddrs[i] = paddr; - } - } - } - for (i = 0; i < fbdev->num_fbs; i++) { /* allocate memory automatically only for fb0, or if * excplicitly defined with vram or plat data option */ @@ -1669,7 +1598,7 @@ int omapfb_realloc_fbmem(struct fb_info *fbi, unsigned long size, int type) int old_type = rg->type; int r; - if (type > OMAPFB_MEMTYPE_MAX) + if (type != OMAPFB_MEMTYPE_SDRAM) return -EINVAL; size = PAGE_ALIGN(size); @@ -1828,32 +1757,6 @@ static int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi) var->rotate = def_rotate; - /* - * Check if there is a default color format set in the board file, - * and use this format instead the default deducted from the - * display bpp. - */ - if (fbdev->dev->platform_data) { - struct omapfb_platform_data *opd; - int id = ofbi->id; - - opd = fbdev->dev->platform_data; - if (opd->mem_desc.region[id].format_used) { - enum omap_color_mode mode; - enum omapfb_color_format format; - - format = opd->mem_desc.region[id].format; - mode = fb_format_to_dss_mode(format); - if (mode < 0) { - r = mode; - goto err; - } - r = dss_mode_to_fb_mode(mode, var); - if (r < 0) - goto err; - } - } - if (display) { u16 w, h; int rotation = (var->rotate + ofbi->rotation[0]) % 4; diff --git a/drivers/video/omap2/vram.c b/drivers/video/omap2/vram.c index 9441e2eb3dee..87e421e25afe 100644 --- a/drivers/video/omap2/vram.c +++ b/drivers/video/omap2/vram.c @@ -33,7 +33,6 @@ #include <asm/setup.h> -#include <plat/sram.h> #include <plat/vram.h> #include <plat/dma.h> @@ -43,10 +42,6 @@ #define DBG(format, ...) #endif -#define OMAP2_SRAM_START 0x40200000 -/* Maximum size, in reality this is smaller if SRAM is partially locked. */ -#define OMAP2_SRAM_SIZE 0xa0000 /* 640k */ - /* postponed regions are used to temporarily store region information at boot * time when we cannot yet allocate the region list */ #define MAX_POSTPONED_REGIONS 10 @@ -74,15 +69,6 @@ struct vram_region { static DEFINE_MUTEX(region_mutex); static LIST_HEAD(region_list); -static inline int region_mem_type(unsigned long paddr) -{ - if (paddr >= OMAP2_SRAM_START && - paddr < OMAP2_SRAM_START + OMAP2_SRAM_SIZE) - return OMAP_VRAM_MEMTYPE_SRAM; - else - return OMAP_VRAM_MEMTYPE_SDRAM; -} - static struct vram_region *omap_vram_create_region(unsigned long paddr, unsigned pages) { @@ -212,9 +198,6 @@ static int _omap_vram_reserve(unsigned long paddr, unsigned pages) DBG("checking region %lx %d\n", rm->paddr, rm->pages); - if (region_mem_type(rm->paddr) != region_mem_type(paddr)) - continue; - start = rm->paddr; end = start + (rm->pages << PAGE_SHIFT) - 1; if (start > paddr || end < paddr + size - 1) @@ -320,7 +303,7 @@ err: return r; } -static int _omap_vram_alloc(int mtype, unsigned pages, unsigned long *paddr) +static int _omap_vram_alloc(unsigned pages, unsigned long *paddr) { struct vram_region *rm; struct vram_alloc *alloc; @@ -330,9 +313,6 @@ static int _omap_vram_alloc(int mtype, unsigned pages, unsigned long *paddr) DBG("checking region %lx %d\n", rm->paddr, rm->pages); - if (region_mem_type(rm->paddr) != mtype) - continue; - start = rm->paddr; list_for_each_entry(alloc, &rm->alloc_list, list) { @@ -365,21 +345,21 @@ found: return -ENOMEM; } -int omap_vram_alloc(int mtype, size_t size, unsigned long *paddr) +int omap_vram_alloc(size_t size, unsigned long *paddr) { unsigned pages; int r; - BUG_ON(mtype > OMAP_VRAM_MEMTYPE_MAX || !size); + BUG_ON(!size); - DBG("alloc mem type %d size %d\n", mtype, size); + DBG("alloc mem size %d\n", size); size = PAGE_ALIGN(size); pages = size >> PAGE_SHIFT; mutex_lock(®ion_mutex); - r = _omap_vram_alloc(mtype, pages, paddr); + r = _omap_vram_alloc(pages, paddr); mutex_unlock(®ion_mutex); @@ -501,10 +481,6 @@ arch_initcall(omap_vram_init); /* boottime vram alloc stuff */ /* set from board file */ -static u32 omap_vram_sram_start __initdata; -static u32 omap_vram_sram_size __initdata; - -/* set from board file */ static u32 omap_vram_sdram_start __initdata; static u32 omap_vram_sdram_size __initdata; @@ -587,73 +563,8 @@ void __init omap_vram_reserve_sdram_memblock(void) pr_info("Reserving %u bytes SDRAM for VRAM\n", size); } -/* - * Called at sram init time, before anything is pushed to the SRAM stack. - * Because of the stack scheme, we will allocate everything from the - * start of the lowest address region to the end of SRAM. This will also - * include padding for page alignment and possible holes between regions. - * - * As opposed to the SDRAM case, we'll also do any dynamic allocations at - * this point, since the driver built as a module would have problem with - * freeing / reallocating the regions. - */ -unsigned long __init omap_vram_reserve_sram(unsigned long sram_pstart, - unsigned long sram_vstart, - unsigned long sram_size, - unsigned long pstart_avail, - unsigned long size_avail) -{ - unsigned long pend_avail; - unsigned long reserved; - u32 paddr; - u32 size; - - paddr = omap_vram_sram_start; - size = omap_vram_sram_size; - - if (!size) - return 0; - - reserved = 0; - pend_avail = pstart_avail + size_avail; - - if (!paddr) { - /* Dynamic allocation */ - if ((size_avail & PAGE_MASK) < size) { - pr_err("Not enough SRAM for VRAM\n"); - return 0; - } - size_avail = (size_avail - size) & PAGE_MASK; - paddr = pstart_avail + size_avail; - } - - if (paddr < sram_pstart || - paddr + size > sram_pstart + sram_size) { - pr_err("Illegal SRAM region for VRAM\n"); - return 0; - } - - /* Reserve everything above the start of the region. */ - if (pend_avail - paddr > reserved) - reserved = pend_avail - paddr; - size_avail = pend_avail - reserved - pstart_avail; - - omap_vram_add_region(paddr, size); - - if (reserved) - pr_info("Reserving %lu bytes SRAM for VRAM\n", reserved); - - return reserved; -} - void __init omap_vram_set_sdram_vram(u32 size, u32 start) { omap_vram_sdram_start = start; omap_vram_sdram_size = size; } - -void __init omap_vram_set_sram_vram(u32 size, u32 start) -{ - omap_vram_sram_start = start; - omap_vram_sram_size = size; -} diff --git a/drivers/video/omap2/vrfb.c b/drivers/video/omap2/vrfb.c index fd2271600370..4e5b960c32c8 100644 --- a/drivers/video/omap2/vrfb.c +++ b/drivers/video/omap2/vrfb.c @@ -27,7 +27,6 @@ #include <linux/bitops.h> #include <linux/mutex.h> -#include <mach/io.h> #include <plat/vrfb.h> #include <plat/sdrc.h> diff --git a/drivers/video/pmag-ba-fb.c b/drivers/video/pmag-ba-fb.c index 0c69fa20251b..9b4a60b52a4c 100644 --- a/drivers/video/pmag-ba-fb.c +++ b/drivers/video/pmag-ba-fb.c @@ -33,7 +33,6 @@ #include <linux/types.h> #include <asm/io.h> -#include <asm/system.h> #include <video/pmag-ba-fb.h> diff --git a/drivers/video/pmagb-b-fb.c b/drivers/video/pmagb-b-fb.c index 22fcb9a3d5c0..4e7a9c46e112 100644 --- a/drivers/video/pmagb-b-fb.c +++ b/drivers/video/pmagb-b-fb.c @@ -29,7 +29,6 @@ #include <linux/types.h> #include <asm/io.h> -#include <asm/system.h> #include <video/pmagb-b-fb.h> diff --git a/drivers/video/pvr2fb.c b/drivers/video/pvr2fb.c index 3a3fdc62c75b..bcd44c32a2ed 100644 --- a/drivers/video/pvr2fb.c +++ b/drivers/video/pvr2fb.c @@ -895,7 +895,7 @@ static int __init pvr2fb_dc_init(void) #ifdef CONFIG_PVR2_DMA if (request_dma(pvr2dma, "pvr2") != 0) { - free_irq(HW_EVENT_VSYNC, 0); + free_irq(HW_EVENT_VSYNC, fb_info); return -EBUSY; } #endif @@ -914,7 +914,7 @@ static void __exit pvr2fb_dc_exit(void) currentpar->mmio_base = 0; } - free_irq(HW_EVENT_VSYNC, 0); + free_irq(HW_EVENT_VSYNC, fb_info); #ifdef CONFIG_PVR2_DMA free_dma(pvr2dma); #endif diff --git a/drivers/video/pxa168fb.c b/drivers/video/pxa168fb.c index 8384b941f6ba..f146089261f4 100644 --- a/drivers/video/pxa168fb.c +++ b/drivers/video/pxa168fb.c @@ -21,6 +21,7 @@ #include <linux/fb.h> #include <linux/delay.h> #include <linux/init.h> +#include <linux/io.h> #include <linux/ioport.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> @@ -670,7 +671,8 @@ static int __devinit pxa168fb_probe(struct platform_device *pdev) /* * Map LCD controller registers. */ - fbi->reg_base = ioremap_nocache(res->start, resource_size(res)); + fbi->reg_base = devm_ioremap_nocache(&pdev->dev, res->start, + resource_size(res)); if (fbi->reg_base == NULL) { ret = -ENOMEM; goto failed_free_info; @@ -739,8 +741,8 @@ static int __devinit pxa168fb_probe(struct platform_device *pdev) /* * Register irq handler. */ - ret = request_irq(irq, pxa168fb_handle_irq, IRQF_SHARED, - info->fix.id, fbi); + ret = devm_request_irq(&pdev->dev, irq, pxa168fb_handle_irq, + IRQF_SHARED, info->fix.id, fbi); if (ret < 0) { dev_err(&pdev->dev, "unable to request IRQ\n"); ret = -ENXIO; @@ -759,14 +761,12 @@ static int __devinit pxa168fb_probe(struct platform_device *pdev) if (ret < 0) { dev_err(&pdev->dev, "Failed to register pxa168-fb: %d\n", ret); ret = -ENXIO; - goto failed_free_irq; + goto failed_free_cmap; } platform_set_drvdata(pdev, fbi); return 0; -failed_free_irq: - free_irq(irq, fbi); failed_free_cmap: fb_dealloc_cmap(&info->cmap); failed_free_clk: @@ -808,13 +808,10 @@ static int __devexit pxa168fb_remove(struct platform_device *pdev) fb_dealloc_cmap(&info->cmap); irq = platform_get_irq(pdev, 0); - free_irq(irq, fbi); dma_free_writecombine(fbi->dev, PAGE_ALIGN(info->fix.smem_len), info->screen_base, info->fix.smem_start); - iounmap(fbi->reg_base); - clk_disable(fbi->clk); clk_put(fbi->clk); diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c index 1d1e4f175e78..3f902557690e 100644 --- a/drivers/video/pxafb.c +++ b/drivers/video/pxafb.c @@ -54,6 +54,7 @@ #include <linux/mutex.h> #include <linux/kthread.h> #include <linux/freezer.h> +#include <linux/console.h> #include <mach/hardware.h> #include <asm/io.h> @@ -730,9 +731,12 @@ static int overlayfb_open(struct fb_info *info, int user) if (user == 0) return -ENODEV; - if (ofb->usage++ == 0) + if (ofb->usage++ == 0) { /* unblank the base framebuffer */ + console_lock(); fb_blank(&ofb->fbi->fb, FB_BLANK_UNBLANK); + console_unlock(); + } return 0; } @@ -1431,7 +1435,7 @@ static void pxafb_enable_controller(struct pxafb_info *fbi) pr_debug("reg_lccr3 0x%08x\n", (unsigned int) fbi->reg_lccr3); /* enable LCD controller clock */ - clk_enable(fbi->clk); + clk_prepare_enable(fbi->clk); if (fbi->lccr0 & LCCR0_LCDT) return; @@ -1471,7 +1475,7 @@ static void pxafb_disable_controller(struct pxafb_info *fbi) wait_for_completion_timeout(&fbi->disable_done, 200 * HZ / 1000); /* disable LCD controller clock */ - clk_disable(fbi->clk); + clk_disable_unprepare(fbi->clk); } /* diff --git a/drivers/video/q40fb.c b/drivers/video/q40fb.c index f5a39f5aa900..a104e8cd2f54 100644 --- a/drivers/video/q40fb.c +++ b/drivers/video/q40fb.c @@ -20,7 +20,6 @@ #include <asm/uaccess.h> #include <asm/setup.h> -#include <asm/system.h> #include <asm/q40_master.h> #include <linux/fb.h> #include <linux/module.h> diff --git a/drivers/video/riva/fbdev.c b/drivers/video/riva/fbdev.c index 2f58cf9c813b..90df1a60bd16 100644 --- a/drivers/video/riva/fbdev.c +++ b/drivers/video/riva/fbdev.c @@ -1816,6 +1816,8 @@ static void __devinit riva_update_default_var(struct fb_var_screeninfo *var, specs->modedb, specs->modedb_len, NULL, 8); } else if (specs->modedb != NULL) { + /* get first mode in database as fallback */ + modedb = specs->modedb[0]; /* get preferred timing */ if (info->monspecs.misc & FB_MISC_1ST_DETAIL) { int i; @@ -1826,9 +1828,6 @@ static void __devinit riva_update_default_var(struct fb_var_screeninfo *var, break; } } - } else { - /* otherwise, get first mode in database */ - modedb = specs->modedb[0]; } var->bits_per_pixel = 8; riva_update_var(var, &modedb); diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c index 0c63b69b6340..f3105160bf98 100644 --- a/drivers/video/s3c-fb.c +++ b/drivers/video/s3c-fb.c @@ -48,7 +48,8 @@ #undef writel #define writel(v, r) do { \ printk(KERN_DEBUG "%s: %08x => %p\n", __func__, (unsigned int)v, r); \ - __raw_writel(v, r); } while (0) + __raw_writel(v, r); \ +} while (0) #endif /* FB_S3C_DEBUG_REGWRITE */ /* irq_flags bits */ @@ -81,12 +82,14 @@ struct s3c_fb; * @palette: Address of palette memory, or 0 if none. * @has_prtcon: Set if has PRTCON register. * @has_shadowcon: Set if has SHADOWCON register. + * @has_blendcon: Set if has BLENDCON register. * @has_clksel: Set if VIDCON0 register has CLKSEL bit. + * @has_fixvclk: Set if VIDCON1 register has FIXVCLK bits. */ struct s3c_fb_variant { unsigned int is_2443:1; unsigned short nr_windows; - unsigned short vidtcon; + unsigned int vidtcon; unsigned short wincon; unsigned short winmap; unsigned short keycon; @@ -99,7 +102,9 @@ struct s3c_fb_variant { unsigned int has_prtcon:1; unsigned int has_shadowcon:1; + unsigned int has_blendcon:1; unsigned int has_clksel:1; + unsigned int has_fixvclk:1; }; /** @@ -186,7 +191,6 @@ struct s3c_fb_vsync { * struct s3c_fb - overall hardware state of the hardware * @slock: The spinlock protection for this data sturcture. * @dev: The device that we bound to, for printing, etc. - * @regs_res: The resource we claimed for the IO registers. * @bus_clk: The clk (hclk) feeding our interface and possibly pixclk. * @lcd_clk: The clk (sclk) feeding pixclk. * @regs: The mapped hardware registers. @@ -202,7 +206,6 @@ struct s3c_fb_vsync { struct s3c_fb { spinlock_t slock; struct device *dev; - struct resource *regs_res; struct clk *bus_clk; struct clk *lcd_clk; void __iomem *regs; @@ -565,7 +568,9 @@ static int s3c_fb_set_par(struct fb_info *info) writel(data, regs + sfb->variant.vidtcon + 4); data = VIDTCON2_LINEVAL(var->yres - 1) | - VIDTCON2_HOZVAL(var->xres - 1); + VIDTCON2_HOZVAL(var->xres - 1) | + VIDTCON2_LINEVAL_E(var->yres - 1) | + VIDTCON2_HOZVAL_E(var->xres - 1); writel(data, regs + sfb->variant.vidtcon + 8); } @@ -581,17 +586,23 @@ static int s3c_fb_set_par(struct fb_info *info) pagewidth = (var->xres * var->bits_per_pixel) >> 3; data = VIDW_BUF_SIZE_OFFSET(info->fix.line_length - pagewidth) | - VIDW_BUF_SIZE_PAGEWIDTH(pagewidth); + VIDW_BUF_SIZE_PAGEWIDTH(pagewidth) | + VIDW_BUF_SIZE_OFFSET_E(info->fix.line_length - pagewidth) | + VIDW_BUF_SIZE_PAGEWIDTH_E(pagewidth); writel(data, regs + sfb->variant.buf_size + (win_no * 4)); /* write 'OSD' registers to control position of framebuffer */ - data = VIDOSDxA_TOPLEFT_X(0) | VIDOSDxA_TOPLEFT_Y(0); + data = VIDOSDxA_TOPLEFT_X(0) | VIDOSDxA_TOPLEFT_Y(0) | + VIDOSDxA_TOPLEFT_X_E(0) | VIDOSDxA_TOPLEFT_Y_E(0); writel(data, regs + VIDOSD_A(win_no, sfb->variant)); data = VIDOSDxB_BOTRIGHT_X(s3c_fb_align_word(var->bits_per_pixel, var->xres - 1)) | - VIDOSDxB_BOTRIGHT_Y(var->yres - 1); + VIDOSDxB_BOTRIGHT_Y(var->yres - 1) | + VIDOSDxB_BOTRIGHT_X_E(s3c_fb_align_word(var->bits_per_pixel, + var->xres - 1)) | + VIDOSDxB_BOTRIGHT_Y_E(var->yres - 1); writel(data, regs + VIDOSD_B(win_no, sfb->variant)); @@ -692,6 +703,17 @@ static int s3c_fb_set_par(struct fb_info *info) writel(data, regs + sfb->variant.wincon + (win_no * 4)); writel(0x0, regs + sfb->variant.winmap + (win_no * 4)); + /* Set alpha value width */ + if (sfb->variant.has_blendcon) { + data = readl(sfb->regs + BLENDCON); + data &= ~BLENDCON_NEW_MASK; + if (var->transp.length > 4) + data |= BLENDCON_NEW_8BIT_ALPHA_VALUE; + else + data |= BLENDCON_NEW_4BIT_ALPHA_VALUE; + writel(data, sfb->regs + BLENDCON); + } + shadow_protect_win(win, 0); pm_runtime_put_sync(sfb->dev); @@ -1346,6 +1368,7 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) struct resource *res; int win; int ret = 0; + u32 reg; platid = platform_get_device_id(pdev); fbdrv = (struct s3c_fb_driverdata *)platid->driver_data; @@ -1361,7 +1384,7 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) return -EINVAL; } - sfb = kzalloc(sizeof(struct s3c_fb), GFP_KERNEL); + sfb = devm_kzalloc(dev, sizeof(struct s3c_fb), GFP_KERNEL); if (!sfb) { dev_err(dev, "no memory for framebuffers\n"); return -ENOMEM; @@ -1404,33 +1427,25 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) goto err_lcd_clk; } - sfb->regs_res = request_mem_region(res->start, resource_size(res), - dev_name(dev)); - if (!sfb->regs_res) { - dev_err(dev, "failed to claim register region\n"); - ret = -ENOENT; - goto err_lcd_clk; - } - - sfb->regs = ioremap(res->start, resource_size(res)); + sfb->regs = devm_request_and_ioremap(dev, res); if (!sfb->regs) { dev_err(dev, "failed to map registers\n"); ret = -ENXIO; - goto err_req_region; + goto err_lcd_clk; } res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res) { dev_err(dev, "failed to acquire irq resource\n"); ret = -ENOENT; - goto err_ioremap; + goto err_lcd_clk; } sfb->irq_no = res->start; - ret = request_irq(sfb->irq_no, s3c_fb_irq, + ret = devm_request_irq(dev, sfb->irq_no, s3c_fb_irq, 0, "s3c_fb", sfb); if (ret) { dev_err(dev, "irq request failed\n"); - goto err_ioremap; + goto err_lcd_clk; } dev_dbg(dev, "got resources (regs %p), probing windows\n", sfb->regs); @@ -1444,6 +1459,14 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) writel(pd->vidcon1, sfb->regs + VIDCON1); + /* set video clock running at under-run */ + if (sfb->variant.has_fixvclk) { + reg = readl(sfb->regs + VIDCON1); + reg &= ~VIDCON1_VCLK_MASK; + reg |= VIDCON1_VCLK_RUN; + writel(reg, sfb->regs + VIDCON1); + } + /* zero all windows before we do anything */ for (win = 0; win < fbdrv->variant.nr_windows; win++) @@ -1484,13 +1507,6 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) err_pm_runtime: pm_runtime_put_sync(sfb->dev); - free_irq(sfb->irq_no, sfb); - -err_ioremap: - iounmap(sfb->regs); - -err_req_region: - release_mem_region(sfb->regs_res->start, resource_size(sfb->regs_res)); err_lcd_clk: pm_runtime_disable(sfb->dev); @@ -1505,7 +1521,6 @@ err_bus_clk: clk_put(sfb->bus_clk); err_sfb: - kfree(sfb); return ret; } @@ -1527,10 +1542,6 @@ static int __devexit s3c_fb_remove(struct platform_device *pdev) if (sfb->windows[win]) s3c_fb_release_win(sfb, sfb->windows[win]); - free_irq(sfb->irq_no, sfb); - - iounmap(sfb->regs); - if (!sfb->variant.has_clksel) { clk_disable(sfb->lcd_clk); clk_put(sfb->lcd_clk); @@ -1539,12 +1550,9 @@ static int __devexit s3c_fb_remove(struct platform_device *pdev) clk_disable(sfb->bus_clk); clk_put(sfb->bus_clk); - release_mem_region(sfb->regs_res->start, resource_size(sfb->regs_res)); - pm_runtime_put_sync(sfb->dev); pm_runtime_disable(sfb->dev); - kfree(sfb); return 0; } @@ -1579,6 +1587,7 @@ static int s3c_fb_resume(struct device *dev) struct s3c_fb_platdata *pd = sfb->pdata; struct s3c_fb_win *win; int win_no; + u32 reg; clk_enable(sfb->bus_clk); @@ -1589,6 +1598,14 @@ static int s3c_fb_resume(struct device *dev) pd->setup_gpio(); writel(pd->vidcon1, sfb->regs + VIDCON1); + /* set video clock running at under-run */ + if (sfb->variant.has_fixvclk) { + reg = readl(sfb->regs + VIDCON1); + reg &= ~VIDCON1_VCLK_MASK; + reg |= VIDCON1_VCLK_RUN; + writel(reg, sfb->regs + VIDCON1); + } + /* zero all windows before we do anything */ for (win_no = 0; win_no < sfb->variant.nr_windows; win_no++) s3c_fb_clear_win(sfb, win_no); @@ -1819,6 +1836,7 @@ static struct s3c_fb_driverdata s3c_fb_data_s5pc100 = { }, .has_prtcon = 1, + .has_blendcon = 1, .has_clksel = 1, }, .win[0] = &s3c_fb_data_s5p_wins[0], @@ -1850,7 +1868,9 @@ static struct s3c_fb_driverdata s3c_fb_data_s5pv210 = { }, .has_shadowcon = 1, + .has_blendcon = 1, .has_clksel = 1, + .has_fixvclk = 1, }, .win[0] = &s3c_fb_data_s5p_wins[0], .win[1] = &s3c_fb_data_s5p_wins[1], @@ -1881,6 +1901,39 @@ static struct s3c_fb_driverdata s3c_fb_data_exynos4 = { }, .has_shadowcon = 1, + .has_blendcon = 1, + .has_fixvclk = 1, + }, + .win[0] = &s3c_fb_data_s5p_wins[0], + .win[1] = &s3c_fb_data_s5p_wins[1], + .win[2] = &s3c_fb_data_s5p_wins[2], + .win[3] = &s3c_fb_data_s5p_wins[3], + .win[4] = &s3c_fb_data_s5p_wins[4], +}; + +static struct s3c_fb_driverdata s3c_fb_data_exynos5 = { + .variant = { + .nr_windows = 5, + .vidtcon = VIDTCON0, + .wincon = WINCON(0), + .winmap = WINxMAP(0), + .keycon = WKEYCON, + .osd = VIDOSD_BASE, + .osd_stride = 16, + .buf_start = VIDW_BUF_START(0), + .buf_size = VIDW_BUF_SIZE(0), + .buf_end = VIDW_BUF_END(0), + + .palette = { + [0] = 0x2400, + [1] = 0x2800, + [2] = 0x2c00, + [3] = 0x3000, + [4] = 0x3400, + }, + .has_shadowcon = 1, + .has_blendcon = 1, + .has_fixvclk = 1, }, .win[0] = &s3c_fb_data_s5p_wins[0], .win[1] = &s3c_fb_data_s5p_wins[1], @@ -1944,6 +1997,9 @@ static struct s3c_fb_driverdata s3c_fb_data_s5p64x0 = { [1] = 0x2800, [2] = 0x2c00, }, + + .has_blendcon = 1, + .has_fixvclk = 1, }, .win[0] = &s3c_fb_data_s5p_wins[0], .win[1] = &s3c_fb_data_s5p_wins[1], @@ -1964,6 +2020,9 @@ static struct platform_device_id s3c_fb_driver_ids[] = { .name = "exynos4-fb", .driver_data = (unsigned long)&s3c_fb_data_exynos4, }, { + .name = "exynos5-fb", + .driver_data = (unsigned long)&s3c_fb_data_exynos5, + }, { .name = "s3c2443-fb", .driver_data = (unsigned long)&s3c_fb_data_s3c2443, }, { diff --git a/drivers/video/sa1100fb.c b/drivers/video/sa1100fb.c index 98d55d0e2da5..b6325848ad61 100644 --- a/drivers/video/sa1100fb.c +++ b/drivers/video/sa1100fb.c @@ -173,282 +173,48 @@ #include <linux/init.h> #include <linux/ioport.h> #include <linux/cpufreq.h> +#include <linux/gpio.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> #include <linux/mutex.h> #include <linux/io.h> +#include <video/sa1100fb.h> + #include <mach/hardware.h> #include <asm/mach-types.h> -#include <mach/assabet.h> #include <mach/shannon.h> /* - * debugging? - */ -#define DEBUG 0 -/* * Complain if VAR is out of range. */ #define DEBUG_VAR 1 -#undef ASSABET_PAL_VIDEO - #include "sa1100fb.h" -extern void (*sa1100fb_backlight_power)(int on); -extern void (*sa1100fb_lcd_power)(int on); - -static struct sa1100fb_rgb rgb_4 = { +static const struct sa1100fb_rgb rgb_4 = { .red = { .offset = 0, .length = 4, }, .green = { .offset = 0, .length = 4, }, .blue = { .offset = 0, .length = 4, }, .transp = { .offset = 0, .length = 0, }, }; -static struct sa1100fb_rgb rgb_8 = { +static const struct sa1100fb_rgb rgb_8 = { .red = { .offset = 0, .length = 8, }, .green = { .offset = 0, .length = 8, }, .blue = { .offset = 0, .length = 8, }, .transp = { .offset = 0, .length = 0, }, }; -static struct sa1100fb_rgb def_rgb_16 = { +static const struct sa1100fb_rgb def_rgb_16 = { .red = { .offset = 11, .length = 5, }, .green = { .offset = 5, .length = 6, }, .blue = { .offset = 0, .length = 5, }, .transp = { .offset = 0, .length = 0, }, }; -#ifdef CONFIG_SA1100_ASSABET -#ifndef ASSABET_PAL_VIDEO -/* - * The assabet uses a sharp LQ039Q2DS54 LCD module. It is actually - * takes an RGB666 signal, but we provide it with an RGB565 signal - * instead (def_rgb_16). - */ -static struct sa1100fb_mach_info lq039q2ds54_info __initdata = { - .pixclock = 171521, .bpp = 16, - .xres = 320, .yres = 240, - - .hsync_len = 5, .vsync_len = 1, - .left_margin = 61, .upper_margin = 3, - .right_margin = 9, .lower_margin = 0, - - .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, - - .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act, - .lccr3 = LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2), -}; -#else -static struct sa1100fb_mach_info pal_info __initdata = { - .pixclock = 67797, .bpp = 16, - .xres = 640, .yres = 512, - - .hsync_len = 64, .vsync_len = 6, - .left_margin = 125, .upper_margin = 70, - .right_margin = 115, .lower_margin = 36, - - .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act, - .lccr3 = LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(512), -}; -#endif -#endif - -#ifdef CONFIG_SA1100_H3600 -static struct sa1100fb_mach_info h3600_info __initdata = { - .pixclock = 174757, .bpp = 16, - .xres = 320, .yres = 240, - - .hsync_len = 3, .vsync_len = 3, - .left_margin = 12, .upper_margin = 10, - .right_margin = 17, .lower_margin = 1, - - .cmap_static = 1, - - .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act, - .lccr3 = LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2), -}; - -static struct sa1100fb_rgb h3600_rgb_16 = { - .red = { .offset = 12, .length = 4, }, - .green = { .offset = 7, .length = 4, }, - .blue = { .offset = 1, .length = 4, }, - .transp = { .offset = 0, .length = 0, }, -}; -#endif - -#ifdef CONFIG_SA1100_H3100 -static struct sa1100fb_mach_info h3100_info __initdata = { - .pixclock = 406977, .bpp = 4, - .xres = 320, .yres = 240, - - .hsync_len = 26, .vsync_len = 41, - .left_margin = 4, .upper_margin = 0, - .right_margin = 4, .lower_margin = 0, - - .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, - .cmap_greyscale = 1, - .cmap_inverse = 1, - - .lccr0 = LCCR0_Mono | LCCR0_4PixMono | LCCR0_Sngl | LCCR0_Pas, - .lccr3 = LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2), -}; -#endif - -#ifdef CONFIG_SA1100_COLLIE -static struct sa1100fb_mach_info collie_info __initdata = { - .pixclock = 171521, .bpp = 16, - .xres = 320, .yres = 240, - - .hsync_len = 5, .vsync_len = 1, - .left_margin = 11, .upper_margin = 2, - .right_margin = 30, .lower_margin = 0, - - .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, - - .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act, - .lccr3 = LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2), -}; -#endif - -#ifdef LART_GREY_LCD -static struct sa1100fb_mach_info lart_grey_info __initdata = { - .pixclock = 150000, .bpp = 4, - .xres = 320, .yres = 240, - - .hsync_len = 1, .vsync_len = 1, - .left_margin = 4, .upper_margin = 0, - .right_margin = 2, .lower_margin = 0, - - .cmap_greyscale = 1, - .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, - - .lccr0 = LCCR0_Mono | LCCR0_Sngl | LCCR0_Pas | LCCR0_4PixMono, - .lccr3 = LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(512), -}; -#endif -#ifdef LART_COLOR_LCD -static struct sa1100fb_mach_info lart_color_info __initdata = { - .pixclock = 150000, .bpp = 16, - .xres = 320, .yres = 240, - - .hsync_len = 2, .vsync_len = 3, - .left_margin = 69, .upper_margin = 14, - .right_margin = 8, .lower_margin = 4, - - .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act, - .lccr3 = LCCR3_OutEnH | LCCR3_PixFlEdg | LCCR3_ACBsDiv(512), -}; -#endif -#ifdef LART_VIDEO_OUT -static struct sa1100fb_mach_info lart_video_info __initdata = { - .pixclock = 39721, .bpp = 16, - .xres = 640, .yres = 480, - - .hsync_len = 95, .vsync_len = 2, - .left_margin = 40, .upper_margin = 32, - .right_margin = 24, .lower_margin = 11, - - .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, - - .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act, - .lccr3 = LCCR3_OutEnL | LCCR3_PixFlEdg | LCCR3_ACBsDiv(512), -}; -#endif - -#ifdef LART_KIT01_LCD -static struct sa1100fb_mach_info lart_kit01_info __initdata = { - .pixclock = 63291, .bpp = 16, - .xres = 640, .yres = 480, - - .hsync_len = 64, .vsync_len = 3, - .left_margin = 122, .upper_margin = 45, - .right_margin = 10, .lower_margin = 10, - - .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act, - .lccr3 = LCCR3_OutEnH | LCCR3_PixFlEdg -}; -#endif - -#ifdef CONFIG_SA1100_SHANNON -static struct sa1100fb_mach_info shannon_info __initdata = { - .pixclock = 152500, .bpp = 8, - .xres = 640, .yres = 480, - - .hsync_len = 4, .vsync_len = 3, - .left_margin = 2, .upper_margin = 0, - .right_margin = 1, .lower_margin = 0, - - .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, - - .lccr0 = LCCR0_Color | LCCR0_Dual | LCCR0_Pas, - .lccr3 = LCCR3_ACBsDiv(512), -}; -#endif - -static struct sa1100fb_mach_info * __init -sa1100fb_get_machine_info(struct sa1100fb_info *fbi) -{ - struct sa1100fb_mach_info *inf = NULL; - - /* - * R G B T - * default {11,5}, { 5,6}, { 0,5}, { 0,0} - * h3600 {12,4}, { 7,4}, { 1,4}, { 0,0} - * freebird { 8,4}, { 4,4}, { 0,4}, {12,4} - */ -#ifdef CONFIG_SA1100_ASSABET - if (machine_is_assabet()) { -#ifndef ASSABET_PAL_VIDEO - inf = &lq039q2ds54_info; -#else - inf = &pal_info; -#endif - } -#endif -#ifdef CONFIG_SA1100_H3100 - if (machine_is_h3100()) { - inf = &h3100_info; - } -#endif -#ifdef CONFIG_SA1100_H3600 - if (machine_is_h3600()) { - inf = &h3600_info; - fbi->rgb[RGB_16] = &h3600_rgb_16; - } -#endif -#ifdef CONFIG_SA1100_COLLIE - if (machine_is_collie()) { - inf = &collie_info; - } -#endif -#ifdef CONFIG_SA1100_LART - if (machine_is_lart()) { -#ifdef LART_GREY_LCD - inf = &lart_grey_info; -#endif -#ifdef LART_COLOR_LCD - inf = &lart_color_info; -#endif -#ifdef LART_VIDEO_OUT - inf = &lart_video_info; -#endif -#ifdef LART_KIT01_LCD - inf = &lart_kit01_info; -#endif - } -#endif -#ifdef CONFIG_SA1100_SHANNON - if (machine_is_shannon()) { - inf = &shannon_info; - } -#endif - return inf; -} - static int sa1100fb_activate_var(struct fb_var_screeninfo *var, struct sa1100fb_info *); static void set_ctrlr_state(struct sa1100fb_info *fbi, u_int state); @@ -533,7 +299,7 @@ sa1100fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, * is what you poke into the framebuffer to produce the * colour you requested. */ - if (fbi->cmap_inverse) { + if (fbi->inf->cmap_inverse) { red = 0xffff - red; green = 0xffff - green; blue = 0xffff - blue; @@ -607,14 +373,14 @@ sa1100fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) var->xres = MIN_XRES; if (var->yres < MIN_YRES) var->yres = MIN_YRES; - if (var->xres > fbi->max_xres) - var->xres = fbi->max_xres; - if (var->yres > fbi->max_yres) - var->yres = fbi->max_yres; + if (var->xres > fbi->inf->xres) + var->xres = fbi->inf->xres; + if (var->yres > fbi->inf->yres) + var->yres = fbi->inf->yres; var->xres_virtual = max(var->xres_virtual, var->xres); var->yres_virtual = max(var->yres_virtual, var->yres); - DPRINTK("var->bits_per_pixel=%d\n", var->bits_per_pixel); + dev_dbg(fbi->dev, "var->bits_per_pixel=%d\n", var->bits_per_pixel); switch (var->bits_per_pixel) { case 4: rgbidx = RGB_4; @@ -638,16 +404,16 @@ sa1100fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) var->blue = fbi->rgb[rgbidx]->blue; var->transp = fbi->rgb[rgbidx]->transp; - DPRINTK("RGBT length = %d:%d:%d:%d\n", + dev_dbg(fbi->dev, "RGBT length = %d:%d:%d:%d\n", var->red.length, var->green.length, var->blue.length, var->transp.length); - DPRINTK("RGBT offset = %d:%d:%d:%d\n", + dev_dbg(fbi->dev, "RGBT offset = %d:%d:%d:%d\n", var->red.offset, var->green.offset, var->blue.offset, var->transp.offset); #ifdef CONFIG_CPU_FREQ - printk(KERN_DEBUG "dma period = %d ps, clock = %d kHz\n", + dev_dbg(fbi->dev, "dma period = %d ps, clock = %d kHz\n", sa1100fb_display_dma_period(var), cpufreq_get(smp_processor_id())); #endif @@ -655,22 +421,10 @@ sa1100fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) return 0; } -static inline void sa1100fb_set_truecolor(u_int is_true_color) +static void sa1100fb_set_visual(struct sa1100fb_info *fbi, u32 visual) { - if (machine_is_assabet()) { -#if 1 // phase 4 or newer Assabet's - if (is_true_color) - ASSABET_BCR_set(ASSABET_BCR_LCD_12RGB); - else - ASSABET_BCR_clear(ASSABET_BCR_LCD_12RGB); -#else - // older Assabet's - if (is_true_color) - ASSABET_BCR_clear(ASSABET_BCR_LCD_12RGB); - else - ASSABET_BCR_set(ASSABET_BCR_LCD_12RGB); -#endif - } + if (fbi->inf->set_visual) + fbi->inf->set_visual(visual); } /* @@ -683,11 +437,11 @@ static int sa1100fb_set_par(struct fb_info *info) struct fb_var_screeninfo *var = &info->var; unsigned long palette_mem_size; - DPRINTK("set_par\n"); + dev_dbg(fbi->dev, "set_par\n"); if (var->bits_per_pixel == 16) fbi->fb.fix.visual = FB_VISUAL_TRUECOLOR; - else if (!fbi->cmap_static) + else if (!fbi->inf->cmap_static) fbi->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; else { /* @@ -704,7 +458,7 @@ static int sa1100fb_set_par(struct fb_info *info) palette_mem_size = fbi->palette_size * sizeof(u16); - DPRINTK("palette_mem_size = 0x%08lx\n", (u_long) palette_mem_size); + dev_dbg(fbi->dev, "palette_mem_size = 0x%08lx\n", palette_mem_size); fbi->palette_cpu = (u16 *)(fbi->map_cpu + PAGE_SIZE - palette_mem_size); fbi->palette_dma = fbi->map_dma + PAGE_SIZE - palette_mem_size; @@ -712,7 +466,7 @@ static int sa1100fb_set_par(struct fb_info *info) /* * Set (any) board control register to handle new color depth */ - sa1100fb_set_truecolor(fbi->fb.fix.visual == FB_VISUAL_TRUECOLOR); + sa1100fb_set_visual(fbi, fbi->fb.fix.visual); sa1100fb_activate_var(var, fbi); return 0; @@ -728,7 +482,7 @@ sa1100fb_set_cmap(struct fb_cmap *cmap, int kspc, int con, /* * Make sure the user isn't doing something stupid. */ - if (!kspc && (fbi->fb.var.bits_per_pixel == 16 || fbi->cmap_static)) + if (!kspc && (fbi->fb.var.bits_per_pixel == 16 || fbi->inf->cmap_static)) return -EINVAL; return gen_set_cmap(cmap, kspc, con, info); @@ -775,7 +529,7 @@ static int sa1100fb_blank(int blank, struct fb_info *info) struct sa1100fb_info *fbi = (struct sa1100fb_info *)info; int i; - DPRINTK("sa1100fb_blank: blank=%d\n", blank); + dev_dbg(fbi->dev, "sa1100fb_blank: blank=%d\n", blank); switch (blank) { case FB_BLANK_POWERDOWN: @@ -863,43 +617,43 @@ static int sa1100fb_activate_var(struct fb_var_screeninfo *var, struct sa1100fb_ u_int half_screen_size, yres, pcd; u_long flags; - DPRINTK("Configuring SA1100 LCD\n"); + dev_dbg(fbi->dev, "Configuring SA1100 LCD\n"); - DPRINTK("var: xres=%d hslen=%d lm=%d rm=%d\n", + dev_dbg(fbi->dev, "var: xres=%d hslen=%d lm=%d rm=%d\n", var->xres, var->hsync_len, var->left_margin, var->right_margin); - DPRINTK("var: yres=%d vslen=%d um=%d bm=%d\n", + dev_dbg(fbi->dev, "var: yres=%d vslen=%d um=%d bm=%d\n", var->yres, var->vsync_len, var->upper_margin, var->lower_margin); #if DEBUG_VAR if (var->xres < 16 || var->xres > 1024) - printk(KERN_ERR "%s: invalid xres %d\n", + dev_err(fbi->dev, "%s: invalid xres %d\n", fbi->fb.fix.id, var->xres); if (var->hsync_len < 1 || var->hsync_len > 64) - printk(KERN_ERR "%s: invalid hsync_len %d\n", + dev_err(fbi->dev, "%s: invalid hsync_len %d\n", fbi->fb.fix.id, var->hsync_len); if (var->left_margin < 1 || var->left_margin > 255) - printk(KERN_ERR "%s: invalid left_margin %d\n", + dev_err(fbi->dev, "%s: invalid left_margin %d\n", fbi->fb.fix.id, var->left_margin); if (var->right_margin < 1 || var->right_margin > 255) - printk(KERN_ERR "%s: invalid right_margin %d\n", + dev_err(fbi->dev, "%s: invalid right_margin %d\n", fbi->fb.fix.id, var->right_margin); if (var->yres < 1 || var->yres > 1024) - printk(KERN_ERR "%s: invalid yres %d\n", + dev_err(fbi->dev, "%s: invalid yres %d\n", fbi->fb.fix.id, var->yres); if (var->vsync_len < 1 || var->vsync_len > 64) - printk(KERN_ERR "%s: invalid vsync_len %d\n", + dev_err(fbi->dev, "%s: invalid vsync_len %d\n", fbi->fb.fix.id, var->vsync_len); if (var->upper_margin < 0 || var->upper_margin > 255) - printk(KERN_ERR "%s: invalid upper_margin %d\n", + dev_err(fbi->dev, "%s: invalid upper_margin %d\n", fbi->fb.fix.id, var->upper_margin); if (var->lower_margin < 0 || var->lower_margin > 255) - printk(KERN_ERR "%s: invalid lower_margin %d\n", + dev_err(fbi->dev, "%s: invalid lower_margin %d\n", fbi->fb.fix.id, var->lower_margin); #endif - new_regs.lccr0 = fbi->lccr0 | + new_regs.lccr0 = fbi->inf->lccr0 | LCCR0_LEN | LCCR0_LDM | LCCR0_BAM | LCCR0_ERM | LCCR0_LtlEnd | LCCR0_DMADel(0); @@ -914,7 +668,7 @@ static int sa1100fb_activate_var(struct fb_var_screeninfo *var, struct sa1100fb_ * the YRES parameter. */ yres = var->yres; - if (fbi->lccr0 & LCCR0_Dual) + if (fbi->inf->lccr0 & LCCR0_Dual) yres /= 2; new_regs.lccr2 = @@ -924,14 +678,14 @@ static int sa1100fb_activate_var(struct fb_var_screeninfo *var, struct sa1100fb_ LCCR2_EndFrmDel(var->lower_margin); pcd = get_pcd(var->pixclock, cpufreq_get(0)); - new_regs.lccr3 = LCCR3_PixClkDiv(pcd) | fbi->lccr3 | + new_regs.lccr3 = LCCR3_PixClkDiv(pcd) | fbi->inf->lccr3 | (var->sync & FB_SYNC_HOR_HIGH_ACT ? LCCR3_HorSnchH : LCCR3_HorSnchL) | (var->sync & FB_SYNC_VERT_HIGH_ACT ? LCCR3_VrtSnchH : LCCR3_VrtSnchL); - DPRINTK("nlccr0 = 0x%08lx\n", new_regs.lccr0); - DPRINTK("nlccr1 = 0x%08lx\n", new_regs.lccr1); - DPRINTK("nlccr2 = 0x%08lx\n", new_regs.lccr2); - DPRINTK("nlccr3 = 0x%08lx\n", new_regs.lccr3); + dev_dbg(fbi->dev, "nlccr0 = 0x%08lx\n", new_regs.lccr0); + dev_dbg(fbi->dev, "nlccr1 = 0x%08lx\n", new_regs.lccr1); + dev_dbg(fbi->dev, "nlccr2 = 0x%08lx\n", new_regs.lccr2); + dev_dbg(fbi->dev, "nlccr3 = 0x%08lx\n", new_regs.lccr3); half_screen_size = var->bits_per_pixel; half_screen_size = half_screen_size * var->xres * var->yres / 16; @@ -951,9 +705,12 @@ static int sa1100fb_activate_var(struct fb_var_screeninfo *var, struct sa1100fb_ * Only update the registers if the controller is enabled * and something has changed. */ - if ((LCCR0 != fbi->reg_lccr0) || (LCCR1 != fbi->reg_lccr1) || - (LCCR2 != fbi->reg_lccr2) || (LCCR3 != fbi->reg_lccr3) || - (DBAR1 != fbi->dbar1) || (DBAR2 != fbi->dbar2)) + if (readl_relaxed(fbi->base + LCCR0) != fbi->reg_lccr0 || + readl_relaxed(fbi->base + LCCR1) != fbi->reg_lccr1 || + readl_relaxed(fbi->base + LCCR2) != fbi->reg_lccr2 || + readl_relaxed(fbi->base + LCCR3) != fbi->reg_lccr3 || + readl_relaxed(fbi->base + DBAR1) != fbi->dbar1 || + readl_relaxed(fbi->base + DBAR2) != fbi->dbar2) sa1100fb_schedule_work(fbi, C_REENABLE); return 0; @@ -967,18 +724,18 @@ static int sa1100fb_activate_var(struct fb_var_screeninfo *var, struct sa1100fb_ */ static inline void __sa1100fb_backlight_power(struct sa1100fb_info *fbi, int on) { - DPRINTK("backlight o%s\n", on ? "n" : "ff"); + dev_dbg(fbi->dev, "backlight o%s\n", on ? "n" : "ff"); - if (sa1100fb_backlight_power) - sa1100fb_backlight_power(on); + if (fbi->inf->backlight_power) + fbi->inf->backlight_power(on); } static inline void __sa1100fb_lcd_power(struct sa1100fb_info *fbi, int on) { - DPRINTK("LCD power o%s\n", on ? "n" : "ff"); + dev_dbg(fbi->dev, "LCD power o%s\n", on ? "n" : "ff"); - if (sa1100fb_lcd_power) - sa1100fb_lcd_power(on); + if (fbi->inf->lcd_power) + fbi->inf->lcd_power(on); } static void sa1100fb_setup_gpio(struct sa1100fb_info *fbi) @@ -1008,14 +765,25 @@ static void sa1100fb_setup_gpio(struct sa1100fb_info *fbi) } if (mask) { + unsigned long flags; + + /* + * SA-1100 requires the GPIO direction register set + * appropriately for the alternate function. Hence + * we set it here via bitmask rather than excessive + * fiddling via the GPIO subsystem - and even then + * we'll still have to deal with GAFR. + */ + local_irq_save(flags); GPDR |= mask; GAFR |= mask; + local_irq_restore(flags); } } static void sa1100fb_enable_controller(struct sa1100fb_info *fbi) { - DPRINTK("Enabling LCD controller\n"); + dev_dbg(fbi->dev, "Enabling LCD controller\n"); /* * Make sure the mode bits are present in the first palette entry @@ -1024,43 +792,46 @@ static void sa1100fb_enable_controller(struct sa1100fb_info *fbi) fbi->palette_cpu[0] |= palette_pbs(&fbi->fb.var); /* Sequence from 11.7.10 */ - LCCR3 = fbi->reg_lccr3; - LCCR2 = fbi->reg_lccr2; - LCCR1 = fbi->reg_lccr1; - LCCR0 = fbi->reg_lccr0 & ~LCCR0_LEN; - DBAR1 = fbi->dbar1; - DBAR2 = fbi->dbar2; - LCCR0 |= LCCR0_LEN; - - if (machine_is_shannon()) { - GPDR |= SHANNON_GPIO_DISP_EN; - GPSR |= SHANNON_GPIO_DISP_EN; - } - - DPRINTK("DBAR1 = 0x%08x\n", DBAR1); - DPRINTK("DBAR2 = 0x%08x\n", DBAR2); - DPRINTK("LCCR0 = 0x%08x\n", LCCR0); - DPRINTK("LCCR1 = 0x%08x\n", LCCR1); - DPRINTK("LCCR2 = 0x%08x\n", LCCR2); - DPRINTK("LCCR3 = 0x%08x\n", LCCR3); + writel_relaxed(fbi->reg_lccr3, fbi->base + LCCR3); + writel_relaxed(fbi->reg_lccr2, fbi->base + LCCR2); + writel_relaxed(fbi->reg_lccr1, fbi->base + LCCR1); + writel_relaxed(fbi->reg_lccr0 & ~LCCR0_LEN, fbi->base + LCCR0); + writel_relaxed(fbi->dbar1, fbi->base + DBAR1); + writel_relaxed(fbi->dbar2, fbi->base + DBAR2); + writel_relaxed(fbi->reg_lccr0 | LCCR0_LEN, fbi->base + LCCR0); + + if (machine_is_shannon()) + gpio_set_value(SHANNON_GPIO_DISP_EN, 1); + + dev_dbg(fbi->dev, "DBAR1: 0x%08x\n", readl_relaxed(fbi->base + DBAR1)); + dev_dbg(fbi->dev, "DBAR2: 0x%08x\n", readl_relaxed(fbi->base + DBAR2)); + dev_dbg(fbi->dev, "LCCR0: 0x%08x\n", readl_relaxed(fbi->base + LCCR0)); + dev_dbg(fbi->dev, "LCCR1: 0x%08x\n", readl_relaxed(fbi->base + LCCR1)); + dev_dbg(fbi->dev, "LCCR2: 0x%08x\n", readl_relaxed(fbi->base + LCCR2)); + dev_dbg(fbi->dev, "LCCR3: 0x%08x\n", readl_relaxed(fbi->base + LCCR3)); } static void sa1100fb_disable_controller(struct sa1100fb_info *fbi) { DECLARE_WAITQUEUE(wait, current); + u32 lccr0; - DPRINTK("Disabling LCD controller\n"); + dev_dbg(fbi->dev, "Disabling LCD controller\n"); - if (machine_is_shannon()) { - GPCR |= SHANNON_GPIO_DISP_EN; - } + if (machine_is_shannon()) + gpio_set_value(SHANNON_GPIO_DISP_EN, 0); set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&fbi->ctrlr_wait, &wait); - LCSR = 0xffffffff; /* Clear LCD Status Register */ - LCCR0 &= ~LCCR0_LDM; /* Enable LCD Disable Done Interrupt */ - LCCR0 &= ~LCCR0_LEN; /* Disable LCD Controller */ + /* Clear LCD Status Register */ + writel_relaxed(~0, fbi->base + LCSR); + + lccr0 = readl_relaxed(fbi->base + LCCR0); + lccr0 &= ~LCCR0_LDM; /* Enable LCD Disable Done Interrupt */ + writel_relaxed(lccr0, fbi->base + LCCR0); + lccr0 &= ~LCCR0_LEN; /* Disable LCD Controller */ + writel_relaxed(lccr0, fbi->base + LCCR0); schedule_timeout(20 * HZ / 1000); remove_wait_queue(&fbi->ctrlr_wait, &wait); @@ -1072,14 +843,15 @@ static void sa1100fb_disable_controller(struct sa1100fb_info *fbi) static irqreturn_t sa1100fb_handle_irq(int irq, void *dev_id) { struct sa1100fb_info *fbi = dev_id; - unsigned int lcsr = LCSR; + unsigned int lcsr = readl_relaxed(fbi->base + LCSR); if (lcsr & LCSR_LDD) { - LCCR0 |= LCCR0_LDM; + u32 lccr0 = readl_relaxed(fbi->base + LCCR0) | LCCR0_LDM; + writel_relaxed(lccr0, fbi->base + LCCR0); wake_up(&fbi->ctrlr_wait); } - LCSR = lcsr; + writel_relaxed(lcsr, fbi->base + LCSR); return IRQ_HANDLED; } @@ -1268,7 +1040,7 @@ sa1100fb_freq_policy(struct notifier_block *nb, unsigned long val, switch (val) { case CPUFREQ_ADJUST: case CPUFREQ_INCOMPATIBLE: - printk(KERN_DEBUG "min dma period: %d ps, " + dev_dbg(fbi->dev, "min dma period: %d ps, " "new clock %d kHz\n", sa1100fb_min_dma_period(fbi), policy->max); /* todo: fill in min/max values */ @@ -1318,7 +1090,7 @@ static int sa1100fb_resume(struct platform_device *dev) * cache. Once this area is remapped, all virtual memory * access to the video memory should occur at the new region. */ -static int __init sa1100fb_map_video_memory(struct sa1100fb_info *fbi) +static int __devinit sa1100fb_map_video_memory(struct sa1100fb_info *fbi) { /* * We reserve one page for the palette, plus the size @@ -1344,7 +1116,7 @@ static int __init sa1100fb_map_video_memory(struct sa1100fb_info *fbi) } /* Fake monspecs to fill in fbinfo structure */ -static struct fb_monspecs monspecs __initdata = { +static struct fb_monspecs monspecs __devinitdata = { .hfmin = 30000, .hfmax = 70000, .vfmin = 50, @@ -1352,10 +1124,11 @@ static struct fb_monspecs monspecs __initdata = { }; -static struct sa1100fb_info * __init sa1100fb_init_fbinfo(struct device *dev) +static struct sa1100fb_info * __devinit sa1100fb_init_fbinfo(struct device *dev) { - struct sa1100fb_mach_info *inf; + struct sa1100fb_mach_info *inf = dev->platform_data; struct sa1100fb_info *fbi; + unsigned i; fbi = kmalloc(sizeof(struct sa1100fb_info) + sizeof(u32) * 16, GFP_KERNEL); @@ -1390,8 +1163,6 @@ static struct sa1100fb_info * __init sa1100fb_init_fbinfo(struct device *dev) fbi->rgb[RGB_8] = &rgb_8; fbi->rgb[RGB_16] = &def_rgb_16; - inf = sa1100fb_get_machine_info(fbi); - /* * People just don't seem to get this. We don't support * anything but correct entries now, so panic if someone @@ -1402,13 +1173,10 @@ static struct sa1100fb_info * __init sa1100fb_init_fbinfo(struct device *dev) panic("sa1100fb error: invalid LCCR3 fields set or zero " "pixclock."); - fbi->max_xres = inf->xres; fbi->fb.var.xres = inf->xres; fbi->fb.var.xres_virtual = inf->xres; - fbi->max_yres = inf->yres; fbi->fb.var.yres = inf->yres; fbi->fb.var.yres_virtual = inf->yres; - fbi->max_bpp = inf->bpp; fbi->fb.var.bits_per_pixel = inf->bpp; fbi->fb.var.pixclock = inf->pixclock; fbi->fb.var.hsync_len = inf->hsync_len; @@ -1419,14 +1187,16 @@ static struct sa1100fb_info * __init sa1100fb_init_fbinfo(struct device *dev) fbi->fb.var.lower_margin = inf->lower_margin; fbi->fb.var.sync = inf->sync; fbi->fb.var.grayscale = inf->cmap_greyscale; - fbi->cmap_inverse = inf->cmap_inverse; - fbi->cmap_static = inf->cmap_static; - fbi->lccr0 = inf->lccr0; - fbi->lccr3 = inf->lccr3; fbi->state = C_STARTUP; fbi->task_state = (u_char)-1; - fbi->fb.fix.smem_len = fbi->max_xres * fbi->max_yres * - fbi->max_bpp / 8; + fbi->fb.fix.smem_len = inf->xres * inf->yres * + inf->bpp / 8; + fbi->inf = inf; + + /* Copy the RGB bitfield overrides */ + for (i = 0; i < NR_RGB; i++) + if (inf->rgb[i]) + fbi->rgb[i] = inf->rgb[i]; init_waitqueue_head(&fbi->ctrlr_wait); INIT_WORK(&fbi->task, sa1100fb_task); @@ -1438,13 +1208,20 @@ static struct sa1100fb_info * __init sa1100fb_init_fbinfo(struct device *dev) static int __devinit sa1100fb_probe(struct platform_device *pdev) { struct sa1100fb_info *fbi; + struct resource *res; int ret, irq; + if (!pdev->dev.platform_data) { + dev_err(&pdev->dev, "no platform LCD data\n"); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); - if (irq < 0) + if (irq < 0 || !res) return -EINVAL; - if (!request_mem_region(0xb0100000, 0x10000, "LCD")) + if (!request_mem_region(res->start, resource_size(res), "LCD")) return -EBUSY; fbi = sa1100fb_init_fbinfo(&pdev->dev); @@ -1452,6 +1229,10 @@ static int __devinit sa1100fb_probe(struct platform_device *pdev) if (!fbi) goto failed; + fbi->base = ioremap(res->start, resource_size(res)); + if (!fbi->base) + goto failed; + /* Initialize video memory */ ret = sa1100fb_map_video_memory(fbi); if (ret) @@ -1459,14 +1240,16 @@ static int __devinit sa1100fb_probe(struct platform_device *pdev) ret = request_irq(irq, sa1100fb_handle_irq, 0, "LCD", fbi); if (ret) { - printk(KERN_ERR "sa1100fb: request_irq failed: %d\n", ret); + dev_err(&pdev->dev, "request_irq failed: %d\n", ret); goto failed; } -#ifdef ASSABET_PAL_VIDEO - if (machine_is_assabet()) - ASSABET_BCR_clear(ASSABET_BCR_LCD_ON); -#endif + if (machine_is_shannon()) { + ret = gpio_request_one(SHANNON_GPIO_DISP_EN, + GPIOF_OUT_INIT_LOW, "display enable"); + if (ret) + goto err_free_irq; + } /* * This makes sure that our colour bitfield @@ -1478,7 +1261,7 @@ static int __devinit sa1100fb_probe(struct platform_device *pdev) ret = register_framebuffer(&fbi->fb); if (ret < 0) - goto err_free_irq; + goto err_reg_fb; #ifdef CONFIG_CPU_FREQ fbi->freq_transition.notifier_call = sa1100fb_freq_transition; @@ -1490,12 +1273,17 @@ static int __devinit sa1100fb_probe(struct platform_device *pdev) /* This driver cannot be unloaded at the moment */ return 0; + err_reg_fb: + if (machine_is_shannon()) + gpio_free(SHANNON_GPIO_DISP_EN); err_free_irq: free_irq(irq, fbi); failed: + if (fbi) + iounmap(fbi->base); platform_set_drvdata(pdev, NULL); kfree(fbi); - release_mem_region(0xb0100000, 0x10000); + release_mem_region(res->start, resource_size(res)); return ret; } @@ -1505,6 +1293,7 @@ static struct platform_driver sa1100fb_driver = { .resume = sa1100fb_resume, .driver = { .name = "sa11x0-fb", + .owner = THIS_MODULE, }, }; diff --git a/drivers/video/sa1100fb.h b/drivers/video/sa1100fb.h index 1c3b459865d8..fc5d4292fad6 100644 --- a/drivers/video/sa1100fb.h +++ b/drivers/video/sa1100fb.h @@ -10,44 +10,15 @@ * for more details. */ -/* - * These are the bitfields for each - * display depth that we support. - */ -struct sa1100fb_rgb { - struct fb_bitfield red; - struct fb_bitfield green; - struct fb_bitfield blue; - struct fb_bitfield transp; -}; - -/* - * This structure describes the machine which we are running on. - */ -struct sa1100fb_mach_info { - u_long pixclock; - - u_short xres; - u_short yres; - - u_char bpp; - u_char hsync_len; - u_char left_margin; - u_char right_margin; - - u_char vsync_len; - u_char upper_margin; - u_char lower_margin; - u_char sync; - - u_int cmap_greyscale:1, - cmap_inverse:1, - cmap_static:1, - unused:29; - - u_int lccr0; - u_int lccr3; -}; +#define LCCR0 0x0000 /* LCD Control Reg. 0 */ +#define LCSR 0x0004 /* LCD Status Reg. */ +#define DBAR1 0x0010 /* LCD DMA Base Address Reg. channel 1 */ +#define DCAR1 0x0014 /* LCD DMA Current Address Reg. channel 1 */ +#define DBAR2 0x0018 /* LCD DMA Base Address Reg. channel 2 */ +#define DCAR2 0x001C /* LCD DMA Current Address Reg. channel 2 */ +#define LCCR1 0x0020 /* LCD Control Reg. 1 */ +#define LCCR2 0x0024 /* LCD Control Reg. 2 */ +#define LCCR3 0x0028 /* LCD Control Reg. 3 */ /* Shadows for LCD controller registers */ struct sa1100fb_lcd_reg { @@ -57,19 +28,11 @@ struct sa1100fb_lcd_reg { unsigned long lccr3; }; -#define RGB_4 (0) -#define RGB_8 (1) -#define RGB_16 (2) -#define NR_RGB 3 - struct sa1100fb_info { struct fb_info fb; struct device *dev; - struct sa1100fb_rgb *rgb[NR_RGB]; - - u_int max_bpp; - u_int max_xres; - u_int max_yres; + const struct sa1100fb_rgb *rgb[NR_RGB]; + void __iomem *base; /* * These are the addresses we mapped @@ -88,12 +51,6 @@ struct sa1100fb_info { dma_addr_t dbar1; dma_addr_t dbar2; - u_int lccr0; - u_int lccr3; - u_int cmap_inverse:1, - cmap_static:1, - unused:30; - u_int reg_lccr0; u_int reg_lccr1; u_int reg_lccr2; @@ -109,6 +66,8 @@ struct sa1100fb_info { struct notifier_block freq_transition; struct notifier_block freq_policy; #endif + + const struct sa1100fb_mach_info *inf; }; #define TO_INF(ptr,member) container_of(ptr,struct sa1100fb_info,member) @@ -130,15 +89,6 @@ struct sa1100fb_info { #define SA1100_NAME "SA1100" /* - * Debug macros - */ -#if DEBUG -# define DPRINTK(fmt, args...) printk("%s: " fmt, __func__ , ## args) -#else -# define DPRINTK(fmt, args...) -#endif - -/* * Minimum X and Y resolutions */ #define MIN_XRES 64 diff --git a/drivers/video/savage/savagefb_driver.c b/drivers/video/savage/savagefb_driver.c index beb495044b24..cee7803a0a1c 100644 --- a/drivers/video/savage/savagefb_driver.c +++ b/drivers/video/savage/savagefb_driver.c @@ -56,7 +56,6 @@ #include <asm/io.h> #include <asm/irq.h> #include <asm/pgtable.h> -#include <asm/system.h> #ifdef CONFIG_MTRR #include <asm/mtrr.h> diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c index 05151b82f40f..4c6b84488561 100644 --- a/drivers/video/sh_mipi_dsi.c +++ b/drivers/video/sh_mipi_dsi.c @@ -24,6 +24,8 @@ #include <video/sh_mipi_dsi.h> #include <video/sh_mobile_lcdc.h> +#include "sh_mobile_lcdcfb.h" + #define SYSCTRL 0x0000 #define SYSCONF 0x0004 #define TIMSET 0x0008 @@ -50,16 +52,16 @@ #define MAX_SH_MIPI_DSI 2 struct sh_mipi { + struct sh_mobile_lcdc_entity entity; + void __iomem *base; void __iomem *linkbase; struct clk *dsit_clk; struct platform_device *pdev; - - void *next_board_data; - void (*next_display_on)(void *board_data, struct fb_info *info); - void (*next_display_off)(void *board_data); }; +#define to_sh_mipi(e) container_of(e, struct sh_mipi, entity) + static struct sh_mipi *mipi_dsi[MAX_SH_MIPI_DSI]; /* Protect the above array */ @@ -120,7 +122,7 @@ static void sh_mipi_dsi_enable(struct sh_mipi *mipi, bool enable) static void sh_mipi_shutdown(struct platform_device *pdev) { - struct sh_mipi *mipi = platform_get_drvdata(pdev); + struct sh_mipi *mipi = to_sh_mipi(platform_get_drvdata(pdev)); sh_mipi_dsi_enable(mipi, false); } @@ -145,77 +147,77 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, pctype = 0; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24; pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; - linelength = ch->lcd_cfg[0].xres * 3; + linelength = ch->lcd_modes[0].xres * 3; yuv = false; break; case MIPI_RGB565: pctype = 1; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16; pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; - linelength = ch->lcd_cfg[0].xres * 2; + linelength = ch->lcd_modes[0].xres * 2; yuv = false; break; case MIPI_RGB666_LP: pctype = 2; datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18; pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; - linelength = ch->lcd_cfg[0].xres * 3; + linelength = ch->lcd_modes[0].xres * 3; yuv = false; break; case MIPI_RGB666: pctype = 3; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18; pixfmt = MIPI_DCS_PIXEL_FMT_18BIT; - linelength = (ch->lcd_cfg[0].xres * 18 + 7) / 8; + linelength = (ch->lcd_modes[0].xres * 18 + 7) / 8; yuv = false; break; case MIPI_BGR888: pctype = 8; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24; pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; - linelength = ch->lcd_cfg[0].xres * 3; + linelength = ch->lcd_modes[0].xres * 3; yuv = false; break; case MIPI_BGR565: pctype = 9; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16; pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; - linelength = ch->lcd_cfg[0].xres * 2; + linelength = ch->lcd_modes[0].xres * 2; yuv = false; break; case MIPI_BGR666_LP: pctype = 0xa; datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18; pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; - linelength = ch->lcd_cfg[0].xres * 3; + linelength = ch->lcd_modes[0].xres * 3; yuv = false; break; case MIPI_BGR666: pctype = 0xb; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18; pixfmt = MIPI_DCS_PIXEL_FMT_18BIT; - linelength = (ch->lcd_cfg[0].xres * 18 + 7) / 8; + linelength = (ch->lcd_modes[0].xres * 18 + 7) / 8; yuv = false; break; case MIPI_YUYV: pctype = 4; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16; pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; - linelength = ch->lcd_cfg[0].xres * 2; + linelength = ch->lcd_modes[0].xres * 2; yuv = true; break; case MIPI_UYVY: pctype = 5; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16; pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; - linelength = ch->lcd_cfg[0].xres * 2; + linelength = ch->lcd_modes[0].xres * 2; yuv = true; break; case MIPI_YUV420_L: pctype = 6; datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12; pixfmt = MIPI_DCS_PIXEL_FMT_12BIT; - linelength = (ch->lcd_cfg[0].xres * 12 + 7) / 8; + linelength = (ch->lcd_modes[0].xres * 12 + 7) / 8; yuv = true; break; case MIPI_YUV420: @@ -223,7 +225,7 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12; pixfmt = MIPI_DCS_PIXEL_FMT_12BIT; /* Length of U/V line */ - linelength = (ch->lcd_cfg[0].xres + 1) / 2; + linelength = (ch->lcd_modes[0].xres + 1) / 2; yuv = true; break; default: @@ -271,7 +273,7 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, iowrite32(0x00000001, base + PHYCTRL); udelay(200); /* Deassert resets, power on */ - iowrite32(0x03070001, base + PHYCTRL); + iowrite32(0x03070001 | pdata->phyctrl, base + PHYCTRL); /* * Default = ULPS enable | @@ -292,7 +294,7 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, */ iowrite32(0x00000006, mipi->linkbase + DTCTR); /* VSYNC width = 2 (<< 17) */ - iowrite32((ch->lcd_cfg[0].vsync_len << pdata->vsynw_offset) | + iowrite32((ch->lcd_modes[0].vsync_len << pdata->vsynw_offset) | (pdata->clksrc << 16) | (pctype << 12) | datatype, mipi->linkbase + VMCTR1); @@ -326,7 +328,7 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, top = linelength << 16; /* RGBLEN */ bottom = 0x00000001; if (pdata->flags & SH_MIPI_DSI_HSABM) /* HSALEN */ - bottom = (pdata->lane * ch->lcd_cfg[0].hsync_len) - 10; + bottom = (pdata->lane * ch->lcd_modes[0].hsync_len) - 10; iowrite32(top | bottom , mipi->linkbase + VMLEN1); /* @@ -346,18 +348,18 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, div = 2; if (pdata->flags & SH_MIPI_DSI_HFPBM) { /* HBPLEN */ - top = ch->lcd_cfg[0].hsync_len + ch->lcd_cfg[0].left_margin; + top = ch->lcd_modes[0].hsync_len + ch->lcd_modes[0].left_margin; top = ((pdata->lane * top / div) - 10) << 16; } if (pdata->flags & SH_MIPI_DSI_HBPBM) { /* HFPLEN */ - bottom = ch->lcd_cfg[0].right_margin; + bottom = ch->lcd_modes[0].right_margin; bottom = (pdata->lane * bottom / div) - 12; } - bpp = linelength / ch->lcd_cfg[0].xres; /* byte / pixel */ + bpp = linelength / ch->lcd_modes[0].xres; /* byte / pixel */ if ((pdata->lane / div) > bpp) { - tmp = ch->lcd_cfg[0].xres / bpp; /* output cycle */ - tmp = ch->lcd_cfg[0].xres - tmp; /* (input - output) cycle */ + tmp = ch->lcd_modes[0].xres / bpp; /* output cycle */ + tmp = ch->lcd_modes[0].xres - tmp; /* (input - output) cycle */ delay = (pdata->lane * tmp); } @@ -392,9 +394,9 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, return 0; } -static void mipi_display_on(void *arg, struct fb_info *info) +static int mipi_display_on(struct sh_mobile_lcdc_entity *entity) { - struct sh_mipi *mipi = arg; + struct sh_mipi *mipi = to_sh_mipi(entity); struct sh_mipi_dsi_info *pdata = mipi->pdev->dev.platform_data; int ret; @@ -410,25 +412,21 @@ static void mipi_display_on(void *arg, struct fb_info *info) sh_mipi_dsi_enable(mipi, true); - if (mipi->next_display_on) - mipi->next_display_on(mipi->next_board_data, info); - - return; + return SH_MOBILE_LCDC_DISPLAY_CONNECTED; mipi_display_on_fail1: pm_runtime_put_sync(&mipi->pdev->dev); mipi_display_on_fail2: pdata->set_dot_clock(mipi->pdev, mipi->base, 0); + + return ret; } -static void mipi_display_off(void *arg) +static void mipi_display_off(struct sh_mobile_lcdc_entity *entity) { - struct sh_mipi *mipi = arg; + struct sh_mipi *mipi = to_sh_mipi(entity); struct sh_mipi_dsi_info *pdata = mipi->pdev->dev.platform_data; - if (mipi->next_display_off) - mipi->next_display_off(mipi->next_board_data); - sh_mipi_dsi_enable(mipi, false); pdata->set_dot_clock(mipi->pdev, mipi->base, 0); @@ -436,6 +434,11 @@ static void mipi_display_off(void *arg) pm_runtime_put_sync(&mipi->pdev->dev); } +static const struct sh_mobile_lcdc_entity_ops mipi_ops = { + .display_on = mipi_display_on, + .display_off = mipi_display_off, +}; + static int __init sh_mipi_probe(struct platform_device *pdev) { struct sh_mipi *mipi; @@ -467,6 +470,9 @@ static int __init sh_mipi_probe(struct platform_device *pdev) goto ealloc; } + mipi->entity.owner = THIS_MODULE; + mipi->entity.ops = &mipi_ops; + if (!request_mem_region(res->start, resource_size(res), pdev->name)) { dev_err(&pdev->dev, "MIPI register region already claimed\n"); ret = -EBUSY; @@ -521,18 +527,7 @@ static int __init sh_mipi_probe(struct platform_device *pdev) pm_runtime_resume(&pdev->dev); mutex_unlock(&array_lock); - platform_set_drvdata(pdev, mipi); - - /* Save original LCDC callbacks */ - mipi->next_board_data = pdata->lcd_chan->board_cfg.board_data; - mipi->next_display_on = pdata->lcd_chan->board_cfg.display_on; - mipi->next_display_off = pdata->lcd_chan->board_cfg.display_off; - - /* Set up LCDC callbacks */ - pdata->lcd_chan->board_cfg.board_data = mipi; - pdata->lcd_chan->board_cfg.display_on = mipi_display_on; - pdata->lcd_chan->board_cfg.display_off = mipi_display_off; - pdata->lcd_chan->board_cfg.owner = THIS_MODULE; + platform_set_drvdata(pdev, &mipi->entity); return 0; @@ -558,10 +553,9 @@ efindslot: static int __exit sh_mipi_remove(struct platform_device *pdev) { - struct sh_mipi_dsi_info *pdata = pdev->dev.platform_data; struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); struct resource *res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); - struct sh_mipi *mipi = platform_get_drvdata(pdev); + struct sh_mipi *mipi = to_sh_mipi(platform_get_drvdata(pdev)); int i, ret; mutex_lock(&array_lock); @@ -581,11 +575,6 @@ static int __exit sh_mipi_remove(struct platform_device *pdev) if (ret < 0) return ret; - pdata->lcd_chan->board_cfg.owner = NULL; - pdata->lcd_chan->board_cfg.display_on = NULL; - pdata->lcd_chan->board_cfg.display_off = NULL; - pdata->lcd_chan->board_cfg.board_data = NULL; - pm_runtime_disable(&pdev->dev); clk_disable(mipi->dsit_clk); clk_put(mipi->dsit_clk); diff --git a/drivers/video/sh_mobile_hdmi.c b/drivers/video/sh_mobile_hdmi.c index 647ba984f00f..eafb19da2c07 100644 --- a/drivers/video/sh_mobile_hdmi.c +++ b/drivers/video/sh_mobile_hdmi.c @@ -208,6 +208,8 @@ enum hotplug_state { }; struct sh_hdmi { + struct sh_mobile_lcdc_entity entity; + void __iomem *base; enum hotplug_state hp_state; /* hot-plug status */ u8 preprogrammed_vic; /* use a pre-programmed VIC or @@ -217,14 +219,13 @@ struct sh_hdmi { u8 edid_blocks; struct clk *hdmi_clk; struct device *dev; - struct fb_info *info; - struct mutex mutex; /* Protect the info pointer */ struct delayed_work edid_work; - struct fb_var_screeninfo var; + struct fb_videomode mode; struct fb_monspecs monspec; - struct notifier_block notifier; }; +#define entity_to_sh_hdmi(e) container_of(e, struct sh_hdmi, entity) + static void hdmi_write(struct sh_hdmi *hdmi, u8 data, u8 reg) { iowrite8(data, hdmi->base + reg); @@ -290,24 +291,24 @@ static struct snd_soc_codec_driver soc_codec_dev_sh_hdmi = { /* External video parameter settings */ static void sh_hdmi_external_video_param(struct sh_hdmi *hdmi) { - struct fb_var_screeninfo *var = &hdmi->var; + struct fb_videomode *mode = &hdmi->mode; u16 htotal, hblank, hdelay, vtotal, vblank, vdelay, voffset; u8 sync = 0; - htotal = var->xres + var->right_margin + var->left_margin + var->hsync_len; - - hdelay = var->hsync_len + var->left_margin; - hblank = var->right_margin + hdelay; + htotal = mode->xres + mode->right_margin + mode->left_margin + + mode->hsync_len; + hdelay = mode->hsync_len + mode->left_margin; + hblank = mode->right_margin + hdelay; /* * Vertical timing looks a bit different in Figure 18, * but let's try the same first by setting offset = 0 */ - vtotal = var->yres + var->upper_margin + var->lower_margin + var->vsync_len; - - vdelay = var->vsync_len + var->upper_margin; - vblank = var->lower_margin + vdelay; - voffset = min(var->upper_margin / 2, 6U); + vtotal = mode->yres + mode->upper_margin + mode->lower_margin + + mode->vsync_len; + vdelay = mode->vsync_len + mode->upper_margin; + vblank = mode->lower_margin + vdelay; + voffset = min(mode->upper_margin / 2, 6U); /* * [3]: VSYNC polarity: Positive @@ -315,14 +316,14 @@ static void sh_hdmi_external_video_param(struct sh_hdmi *hdmi) * [1]: Interlace/Progressive: Progressive * [0]: External video settings enable: used. */ - if (var->sync & FB_SYNC_HOR_HIGH_ACT) + if (mode->sync & FB_SYNC_HOR_HIGH_ACT) sync |= 4; - if (var->sync & FB_SYNC_VERT_HIGH_ACT) + if (mode->sync & FB_SYNC_VERT_HIGH_ACT) sync |= 8; dev_dbg(hdmi->dev, "H: %u, %u, %u, %u; V: %u, %u, %u, %u; sync 0x%x\n", - htotal, hblank, hdelay, var->hsync_len, - vtotal, vblank, vdelay, var->vsync_len, sync); + htotal, hblank, hdelay, mode->hsync_len, + vtotal, vblank, vdelay, mode->vsync_len, sync); hdmi_write(hdmi, sync | (voffset << 4), HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS); @@ -335,8 +336,8 @@ static void sh_hdmi_external_video_param(struct sh_hdmi *hdmi) hdmi_write(hdmi, hdelay, HDMI_EXTERNAL_H_DELAY_7_0); hdmi_write(hdmi, hdelay >> 8, HDMI_EXTERNAL_H_DELAY_9_8); - hdmi_write(hdmi, var->hsync_len, HDMI_EXTERNAL_H_DURATION_7_0); - hdmi_write(hdmi, var->hsync_len >> 8, HDMI_EXTERNAL_H_DURATION_9_8); + hdmi_write(hdmi, mode->hsync_len, HDMI_EXTERNAL_H_DURATION_7_0); + hdmi_write(hdmi, mode->hsync_len >> 8, HDMI_EXTERNAL_H_DURATION_9_8); hdmi_write(hdmi, vtotal, HDMI_EXTERNAL_V_TOTAL_7_0); hdmi_write(hdmi, vtotal >> 8, HDMI_EXTERNAL_V_TOTAL_9_8); @@ -345,7 +346,7 @@ static void sh_hdmi_external_video_param(struct sh_hdmi *hdmi) hdmi_write(hdmi, vdelay, HDMI_EXTERNAL_V_DELAY); - hdmi_write(hdmi, var->vsync_len, HDMI_EXTERNAL_V_DURATION); + hdmi_write(hdmi, mode->vsync_len, HDMI_EXTERNAL_V_DURATION); /* Set bit 0 of HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS here for external mode */ if (!hdmi->preprogrammed_vic) @@ -472,7 +473,7 @@ static void sh_hdmi_audio_config(struct sh_hdmi *hdmi) */ static void sh_hdmi_phy_config(struct sh_hdmi *hdmi) { - if (hdmi->var.pixclock < 10000) { + if (hdmi->mode.pixclock < 10000) { /* for 1080p8bit 148MHz */ hdmi_write(hdmi, 0x1d, HDMI_SLIPHDMIT_PARAM_SETTINGS_1); hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_2); @@ -483,7 +484,7 @@ static void sh_hdmi_phy_config(struct sh_hdmi *hdmi) hdmi_write(hdmi, 0x0e, HDMI_SLIPHDMIT_PARAM_SETTINGS_8); hdmi_write(hdmi, 0x25, HDMI_SLIPHDMIT_PARAM_SETTINGS_9); hdmi_write(hdmi, 0x04, HDMI_SLIPHDMIT_PARAM_SETTINGS_10); - } else if (hdmi->var.pixclock < 30000) { + } else if (hdmi->mode.pixclock < 30000) { /* 720p, 8bit, 74.25MHz. Might need to be adjusted for other formats */ /* * [1:0] Speed_A @@ -732,14 +733,12 @@ static unsigned long sh_hdmi_rate_error(struct sh_hdmi *hdmi, static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate, unsigned long *parent_rate) { - struct fb_var_screeninfo tmpvar; - struct fb_var_screeninfo *var = &tmpvar; + struct sh_mobile_lcdc_chan *ch = hdmi->entity.lcdc; const struct fb_videomode *mode, *found = NULL; - struct fb_info *info = hdmi->info; - struct fb_modelist *modelist = NULL; unsigned int f_width = 0, f_height = 0, f_refresh = 0; unsigned long found_rate_error = ULONG_MAX; /* silly compiler... */ bool scanning = false, preferred_bad = false; + bool use_edid_mode = false; u8 edid[128]; char *forced; int i; @@ -854,12 +853,9 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate, } /* Check if supported: sufficient fb memory, supported clock-rate */ - fb_videomode_to_var(var, mode); - - var->bits_per_pixel = info->var.bits_per_pixel; - - if (info && info->fbops->fb_check_var && - info->fbops->fb_check_var(var, info)) { + if (ch && ch->notify && + ch->notify(ch, SH_MOBILE_LCDC_EVENT_DISPLAY_MODE, mode, + NULL)) { scanning = true; preferred_bad = true; continue; @@ -867,28 +863,19 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate, found = mode; found_rate_error = rate_error; + use_edid_mode = true; } - hdmi->var.width = hdmi->monspec.max_x * 10; - hdmi->var.height = hdmi->monspec.max_y * 10; - /* - * TODO 1: if no ->info is present, postpone running the config until - * after ->info first gets registered. + * TODO 1: if no default mode is present, postpone running the config + * until after the LCDC channel is initialized. * TODO 2: consider registering the HDMI platform device from the LCDC - * driver, and passing ->info with HDMI platform data. + * driver. */ - if (info && !found) { - modelist = info->modelist.next && - !list_empty(&info->modelist) ? - list_entry(info->modelist.next, - struct fb_modelist, list) : - NULL; - - if (modelist) { - found = &modelist->mode; - found_rate_error = sh_hdmi_rate_error(hdmi, found, hdmi_rate, parent_rate); - } + if (!found && hdmi->entity.def_mode.xres != 0) { + found = &hdmi->entity.def_mode; + found_rate_error = sh_hdmi_rate_error(hdmi, found, hdmi_rate, + parent_rate); } /* No cookie today */ @@ -912,12 +899,13 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate, else hdmi->preprogrammed_vic = 0; - dev_dbg(hdmi->dev, "Using %s %s mode %ux%u@%uHz (%luHz), clock error %luHz\n", - modelist ? "default" : "EDID", hdmi->preprogrammed_vic ? "VIC" : "external", - found->xres, found->yres, found->refresh, - PICOS2KHZ(found->pixclock) * 1000, found_rate_error); + dev_dbg(hdmi->dev, "Using %s %s mode %ux%u@%uHz (%luHz), " + "clock error %luHz\n", use_edid_mode ? "EDID" : "default", + hdmi->preprogrammed_vic ? "VIC" : "external", found->xres, + found->yres, found->refresh, PICOS2KHZ(found->pixclock) * 1000, + found_rate_error); - fb_videomode_to_var(&hdmi->var, found); + hdmi->mode = *found; sh_hdmi_external_video_param(hdmi); return 0; @@ -998,22 +986,12 @@ static irqreturn_t sh_hdmi_hotplug(int irq, void *dev_id) return IRQ_HANDLED; } -/* locking: called with info->lock held, or before register_framebuffer() */ -static void sh_hdmi_display_on(void *arg, struct fb_info *info) +static int sh_hdmi_display_on(struct sh_mobile_lcdc_entity *entity) { - /* - * info is guaranteed to be valid, when we are called, because our - * FB_EVENT_FB_UNBIND notify is also called with info->lock held - */ - struct sh_hdmi *hdmi = arg; - struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; - struct sh_mobile_lcdc_chan *ch = info->par; + struct sh_hdmi *hdmi = entity_to_sh_hdmi(entity); - dev_dbg(hdmi->dev, "%s(%p): state %x\n", __func__, - pdata->lcd_dev, info->state); - - /* No need to lock */ - hdmi->info = info; + dev_dbg(hdmi->dev, "%s(%p): state %x\n", __func__, hdmi, + hdmi->hp_state); /* * hp_state can be set to @@ -1021,56 +999,30 @@ static void sh_hdmi_display_on(void *arg, struct fb_info *info) * HDMI_HOTPLUG_CONNECTED: on monitor plug-in * HDMI_HOTPLUG_EDID_DONE: on EDID read completion */ - switch (hdmi->hp_state) { - case HDMI_HOTPLUG_EDID_DONE: + if (hdmi->hp_state == HDMI_HOTPLUG_EDID_DONE) { /* PS mode d->e. All functions are active */ hdmi_write(hdmi, 0x80, HDMI_SYSTEM_CTRL); dev_dbg(hdmi->dev, "HDMI running\n"); - break; - case HDMI_HOTPLUG_DISCONNECTED: - info->state = FBINFO_STATE_SUSPENDED; - default: - hdmi->var = ch->display_var; } + + return hdmi->hp_state == HDMI_HOTPLUG_DISCONNECTED + ? SH_MOBILE_LCDC_DISPLAY_DISCONNECTED + : SH_MOBILE_LCDC_DISPLAY_CONNECTED; } -/* locking: called with info->lock held */ -static void sh_hdmi_display_off(void *arg) +static void sh_hdmi_display_off(struct sh_mobile_lcdc_entity *entity) { - struct sh_hdmi *hdmi = arg; - struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; + struct sh_hdmi *hdmi = entity_to_sh_hdmi(entity); - dev_dbg(hdmi->dev, "%s(%p)\n", __func__, pdata->lcd_dev); + dev_dbg(hdmi->dev, "%s(%p)\n", __func__, hdmi); /* PS mode e->a */ hdmi_write(hdmi, 0x10, HDMI_SYSTEM_CTRL); } -static bool sh_hdmi_must_reconfigure(struct sh_hdmi *hdmi) -{ - struct fb_info *info = hdmi->info; - struct sh_mobile_lcdc_chan *ch = info->par; - struct fb_var_screeninfo *new_var = &hdmi->var, *old_var = &ch->display_var; - struct fb_videomode mode1, mode2; - - fb_var_to_videomode(&mode1, old_var); - fb_var_to_videomode(&mode2, new_var); - - dev_dbg(info->dev, "Old %ux%u, new %ux%u\n", - mode1.xres, mode1.yres, mode2.xres, mode2.yres); - - if (fb_mode_is_equal(&mode1, &mode2)) { - /* It can be a different monitor with an equal video-mode */ - old_var->width = new_var->width; - old_var->height = new_var->height; - return false; - } - - dev_dbg(info->dev, "Switching %u -> %u lines\n", - mode1.yres, mode2.yres); - *old_var = *new_var; - - return true; -} +static const struct sh_mobile_lcdc_entity_ops sh_hdmi_ops = { + .display_on = sh_hdmi_display_on, + .display_off = sh_hdmi_display_off, +}; /** * sh_hdmi_clk_configure() - set HDMI clock frequency and enable the clock @@ -1111,20 +1063,11 @@ static long sh_hdmi_clk_configure(struct sh_hdmi *hdmi, unsigned long hdmi_rate, static void sh_hdmi_edid_work_fn(struct work_struct *work) { struct sh_hdmi *hdmi = container_of(work, struct sh_hdmi, edid_work.work); - struct fb_info *info; - struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; - struct sh_mobile_lcdc_chan *ch; + struct sh_mobile_lcdc_chan *ch = hdmi->entity.lcdc; int ret; - dev_dbg(hdmi->dev, "%s(%p): begin, hotplug status %d\n", __func__, - pdata->lcd_dev, hdmi->hp_state); - - if (!pdata->lcd_dev) - return; - - mutex_lock(&hdmi->mutex); - - info = hdmi->info; + dev_dbg(hdmi->dev, "%s(%p): begin, hotplug status %d\n", __func__, hdmi, + hdmi->hp_state); if (hdmi->hp_state == HDMI_HOTPLUG_CONNECTED) { unsigned long parent_rate = 0, hdmi_rate; @@ -1145,103 +1088,32 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work) /* Switched to another (d) power-save mode */ msleep(10); - if (!info) - goto out; - - ch = info->par; - - if (lock_fb_info(info)) { - console_lock(); - - /* HDMI plug in */ - if (!sh_hdmi_must_reconfigure(hdmi) && - info->state == FBINFO_STATE_RUNNING) { - /* - * First activation with the default monitor - just turn - * on, if we run a resume here, the logo disappears - */ - info->var.width = hdmi->var.width; - info->var.height = hdmi->var.height; - sh_hdmi_display_on(hdmi, info); - } else { - /* New monitor or have to wake up */ - fb_set_suspend(info, 0); - } - - console_unlock(); - unlock_fb_info(info); - } + if (ch && ch->notify) + ch->notify(ch, SH_MOBILE_LCDC_EVENT_DISPLAY_CONNECT, + &hdmi->mode, &hdmi->monspec); } else { - ret = 0; - if (!info) - goto out; - hdmi->monspec.modedb_len = 0; fb_destroy_modedb(hdmi->monspec.modedb); hdmi->monspec.modedb = NULL; - if (lock_fb_info(info)) { - console_lock(); + if (ch && ch->notify) + ch->notify(ch, SH_MOBILE_LCDC_EVENT_DISPLAY_DISCONNECT, + NULL, NULL); - /* HDMI disconnect */ - fb_set_suspend(info, 1); - - console_unlock(); - unlock_fb_info(info); - } + ret = 0; } out: if (ret < 0 && ret != -EAGAIN) hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED; - mutex_unlock(&hdmi->mutex); - dev_dbg(hdmi->dev, "%s(%p): end\n", __func__, pdata->lcd_dev); -} - -static int sh_hdmi_notify(struct notifier_block *nb, - unsigned long action, void *data) -{ - struct fb_event *event = data; - struct fb_info *info = event->info; - struct sh_mobile_lcdc_chan *ch = info->par; - struct sh_mobile_lcdc_board_cfg *board_cfg = &ch->cfg.board_cfg; - struct sh_hdmi *hdmi = board_cfg->board_data; - - if (!hdmi || nb != &hdmi->notifier || hdmi->info != info) - return NOTIFY_DONE; - - switch(action) { - case FB_EVENT_FB_REGISTERED: - /* Unneeded, activation taken care by sh_hdmi_display_on() */ - break; - case FB_EVENT_FB_UNREGISTERED: - /* - * We are called from unregister_framebuffer() with the - * info->lock held. This is bad for us, because we can race with - * the scheduled work, which has to call fb_set_suspend(), which - * takes info->lock internally, so, sh_hdmi_edid_work_fn() - * cannot take and hold info->lock for the whole function - * duration. Using an additional lock creates a classical AB-BA - * lock up. Therefore, we have to release the info->lock - * temporarily, synchronise with the work queue and re-acquire - * the info->lock. - */ - unlock_fb_info(info); - mutex_lock(&hdmi->mutex); - hdmi->info = NULL; - mutex_unlock(&hdmi->mutex); - lock_fb_info(info); - return NOTIFY_OK; - } - return NOTIFY_DONE; + dev_dbg(hdmi->dev, "%s(%p): end\n", __func__, hdmi); } static int __init sh_hdmi_probe(struct platform_device *pdev) { struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - struct sh_mobile_lcdc_board_cfg *board_cfg; int irq = platform_get_irq(pdev, 0), ret; struct sh_hdmi *hdmi; long rate; @@ -1255,9 +1127,9 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) return -ENOMEM; } - mutex_init(&hdmi->mutex); - hdmi->dev = &pdev->dev; + hdmi->entity.owner = THIS_MODULE; + hdmi->entity.ops = &sh_hdmi_ops; hdmi->hdmi_clk = clk_get(&pdev->dev, "ick"); if (IS_ERR(hdmi->hdmi_clk)) { @@ -1297,14 +1169,7 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) goto emap; } - platform_set_drvdata(pdev, hdmi); - - /* Set up LCDC callbacks */ - board_cfg = &pdata->lcd_chan->board_cfg; - board_cfg->owner = THIS_MODULE; - board_cfg->board_data = hdmi; - board_cfg->display_on = sh_hdmi_display_on; - board_cfg->display_off = sh_hdmi_display_off; + platform_set_drvdata(pdev, &hdmi->entity); INIT_DELAYED_WORK(&hdmi->edid_work, sh_hdmi_edid_work_fn); @@ -1329,9 +1194,6 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) goto ecodec; } - hdmi->notifier.notifier_call = sh_hdmi_notify; - fb_register_client(&hdmi->notifier); - return 0; ecodec: @@ -1347,7 +1209,6 @@ ereqreg: erate: clk_put(hdmi->hdmi_clk); egetclk: - mutex_destroy(&hdmi->mutex); kfree(hdmi); return ret; @@ -1355,21 +1216,12 @@ egetclk: static int __exit sh_hdmi_remove(struct platform_device *pdev) { - struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; - struct sh_hdmi *hdmi = platform_get_drvdata(pdev); + struct sh_hdmi *hdmi = entity_to_sh_hdmi(platform_get_drvdata(pdev)); struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - struct sh_mobile_lcdc_board_cfg *board_cfg = &pdata->lcd_chan->board_cfg; int irq = platform_get_irq(pdev, 0); snd_soc_unregister_codec(&pdev->dev); - fb_unregister_client(&hdmi->notifier); - - board_cfg->display_on = NULL; - board_cfg->display_off = NULL; - board_cfg->board_data = NULL; - board_cfg->owner = NULL; - /* No new work will be scheduled, wait for running ISR */ free_irq(irq, hdmi); /* Wait for already scheduled work */ @@ -1380,7 +1232,6 @@ static int __exit sh_hdmi_remove(struct platform_device *pdev) clk_put(hdmi->hdmi_clk); iounmap(hdmi->base); release_mem_region(res->start, resource_size(res)); - mutex_destroy(&hdmi->mutex); kfree(hdmi); return 0; diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index aac5b369d73c..7a0b301587f6 100644 --- a/drivers/video/sh_mobile_lcdcfb.c +++ b/drivers/video/sh_mobile_lcdcfb.c @@ -8,26 +8,27 @@ * for more details. */ -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/mm.h> +#include <linux/atomic.h> +#include <linux/backlight.h> #include <linux/clk.h> -#include <linux/pm_runtime.h> -#include <linux/platform_device.h> +#include <linux/console.h> #include <linux/dma-mapping.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/init.h> #include <linux/interrupt.h> -#include <linux/videodev2.h> -#include <linux/vmalloc.h> #include <linux/ioctl.h> -#include <linux/slab.h> -#include <linux/console.h> -#include <linux/backlight.h> -#include <linux/gpio.h> +#include <linux/kernel.h> +#include <linux/mm.h> #include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/videodev2.h> +#include <linux/vmalloc.h> + #include <video/sh_mobile_lcdc.h> #include <video/sh_mobile_meram.h> -#include <linux/atomic.h> #include "sh_mobile_lcdcfb.h" @@ -37,6 +38,24 @@ #define MAX_XRES 1920 #define MAX_YRES 1080 +struct sh_mobile_lcdc_priv { + void __iomem *base; + int irq; + atomic_t hw_usecnt; + struct device *dev; + struct clk *dot_clk; + unsigned long lddckr; + struct sh_mobile_lcdc_chan ch[2]; + struct notifier_block notifier; + int started; + int forced_fourcc; /* 2 channel LCDC must share fourcc setting */ + struct sh_mobile_meram_info *meram_dev; +}; + +/* ----------------------------------------------------------------------------- + * Registers access + */ + static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = { [LDDCKPAT1R] = 0x400, [LDDCKPAT2R] = 0x404, @@ -75,38 +94,6 @@ static unsigned long lcdc_offs_sublcd[NR_CH_REGS] = { [LDPMR] = 0x63c, }; -static const struct fb_videomode default_720p = { - .name = "HDMI 720p", - .xres = 1280, - .yres = 720, - - .left_margin = 220, - .right_margin = 110, - .hsync_len = 40, - - .upper_margin = 20, - .lower_margin = 5, - .vsync_len = 5, - - .pixclock = 13468, - .refresh = 60, - .sync = FB_SYNC_VERT_HIGH_ACT | FB_SYNC_HOR_HIGH_ACT, -}; - -struct sh_mobile_lcdc_priv { - void __iomem *base; - int irq; - atomic_t hw_usecnt; - struct device *dev; - struct clk *dot_clk; - unsigned long lddckr; - struct sh_mobile_lcdc_chan ch[2]; - struct notifier_block notifier; - int started; - int forced_fourcc; /* 2 channel LCDC must share fourcc setting */ - struct sh_mobile_meram_info *meram_dev; -}; - static bool banked(int reg_nr) { switch (reg_nr) { @@ -127,6 +114,11 @@ static bool banked(int reg_nr) return false; } +static int lcdc_chan_is_sublcd(struct sh_mobile_lcdc_chan *chan) +{ + return chan->cfg->chan == LCDC_CHAN_SUBLCD; +} + static void lcdc_write_chan(struct sh_mobile_lcdc_chan *chan, int reg_nr, unsigned long data) { @@ -169,11 +161,72 @@ static void lcdc_wait_bit(struct sh_mobile_lcdc_priv *priv, cpu_relax(); } -static int lcdc_chan_is_sublcd(struct sh_mobile_lcdc_chan *chan) +/* ----------------------------------------------------------------------------- + * Clock management + */ + +static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv) +{ + if (atomic_inc_and_test(&priv->hw_usecnt)) { + if (priv->dot_clk) + clk_enable(priv->dot_clk); + pm_runtime_get_sync(priv->dev); + if (priv->meram_dev && priv->meram_dev->pdev) + pm_runtime_get_sync(&priv->meram_dev->pdev->dev); + } +} + +static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv) { - return chan->cfg.chan == LCDC_CHAN_SUBLCD; + if (atomic_sub_return(1, &priv->hw_usecnt) == -1) { + if (priv->meram_dev && priv->meram_dev->pdev) + pm_runtime_put_sync(&priv->meram_dev->pdev->dev); + pm_runtime_put(priv->dev); + if (priv->dot_clk) + clk_disable(priv->dot_clk); + } } +static int sh_mobile_lcdc_setup_clocks(struct sh_mobile_lcdc_priv *priv, + int clock_source) +{ + struct clk *clk; + char *str; + + switch (clock_source) { + case LCDC_CLK_BUS: + str = "bus_clk"; + priv->lddckr = LDDCKR_ICKSEL_BUS; + break; + case LCDC_CLK_PERIPHERAL: + str = "peripheral_clk"; + priv->lddckr = LDDCKR_ICKSEL_MIPI; + break; + case LCDC_CLK_EXTERNAL: + str = NULL; + priv->lddckr = LDDCKR_ICKSEL_HDMI; + break; + default: + return -EINVAL; + } + + if (str == NULL) + return 0; + + clk = clk_get(priv->dev, str); + if (IS_ERR(clk)) { + dev_err(priv->dev, "cannot get dot clock %s\n", str); + return PTR_ERR(clk); + } + + priv->dot_clk = clk; + return 0; +} + +/* ----------------------------------------------------------------------------- + * Display, panel and deferred I/O + */ + static void lcdc_sys_write_index(void *handle, unsigned long data) { struct sh_mobile_lcdc_chan *ch = handle; @@ -216,74 +269,11 @@ struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = { lcdc_sys_read_data, }; -static int sh_mobile_format_fourcc(const struct fb_var_screeninfo *var) -{ - if (var->grayscale > 1) - return var->grayscale; - - switch (var->bits_per_pixel) { - case 16: - return V4L2_PIX_FMT_RGB565; - case 24: - return V4L2_PIX_FMT_BGR24; - case 32: - return V4L2_PIX_FMT_BGR32; - default: - return 0; - } -} - -static int sh_mobile_format_is_fourcc(const struct fb_var_screeninfo *var) -{ - return var->grayscale > 1; -} - -static bool sh_mobile_format_is_yuv(const struct fb_var_screeninfo *var) -{ - if (var->grayscale <= 1) - return false; - - switch (var->grayscale) { - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV21: - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV61: - case V4L2_PIX_FMT_NV24: - case V4L2_PIX_FMT_NV42: - return true; - - default: - return false; - } -} - -static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv) -{ - if (atomic_inc_and_test(&priv->hw_usecnt)) { - if (priv->dot_clk) - clk_enable(priv->dot_clk); - pm_runtime_get_sync(priv->dev); - if (priv->meram_dev && priv->meram_dev->pdev) - pm_runtime_get_sync(&priv->meram_dev->pdev->dev); - } -} - -static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv) -{ - if (atomic_sub_return(1, &priv->hw_usecnt) == -1) { - if (priv->meram_dev && priv->meram_dev->pdev) - pm_runtime_put_sync(&priv->meram_dev->pdev->dev); - pm_runtime_put(priv->dev); - if (priv->dot_clk) - clk_disable(priv->dot_clk); - } -} - static int sh_mobile_lcdc_sginit(struct fb_info *info, struct list_head *pagelist) { struct sh_mobile_lcdc_chan *ch = info->par; - unsigned int nr_pages_max = info->fix.smem_len >> PAGE_SHIFT; + unsigned int nr_pages_max = ch->fb_size >> PAGE_SHIFT; struct page *page; int nr_pages = 0; @@ -299,7 +289,7 @@ static void sh_mobile_lcdc_deferred_io(struct fb_info *info, struct list_head *pagelist) { struct sh_mobile_lcdc_chan *ch = info->par; - struct sh_mobile_lcdc_board_cfg *bcfg = &ch->cfg.board_cfg; + const struct sh_mobile_lcdc_panel_cfg *panel = &ch->cfg->panel_cfg; /* enable clocks before accessing hardware */ sh_mobile_lcdc_clk_on(ch->lcdc); @@ -323,16 +313,15 @@ static void sh_mobile_lcdc_deferred_io(struct fb_info *info, unsigned int nr_pages = sh_mobile_lcdc_sginit(info, pagelist); /* trigger panel update */ - dma_map_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE); - if (bcfg->start_transfer) - bcfg->start_transfer(bcfg->board_data, ch, - &sh_mobile_lcdc_sys_bus_ops); + dma_map_sg(ch->lcdc->dev, ch->sglist, nr_pages, DMA_TO_DEVICE); + if (panel->start_transfer) + panel->start_transfer(ch, &sh_mobile_lcdc_sys_bus_ops); lcdc_write_chan(ch, LDSM2R, LDSM2R_OSTRG); - dma_unmap_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE); + dma_unmap_sg(ch->lcdc->dev, ch->sglist, nr_pages, + DMA_TO_DEVICE); } else { - if (bcfg->start_transfer) - bcfg->start_transfer(bcfg->board_data, ch, - &sh_mobile_lcdc_sys_bus_ops); + if (panel->start_transfer) + panel->start_transfer(ch, &sh_mobile_lcdc_sys_bus_ops); lcdc_write_chan(ch, LDSM2R, LDSM2R_OSTRG); } } @@ -345,6 +334,217 @@ static void sh_mobile_lcdc_deferred_io_touch(struct fb_info *info) schedule_delayed_work(&info->deferred_work, fbdefio->delay); } +static void sh_mobile_lcdc_display_on(struct sh_mobile_lcdc_chan *ch) +{ + const struct sh_mobile_lcdc_panel_cfg *panel = &ch->cfg->panel_cfg; + + if (ch->tx_dev) { + int ret; + + ret = ch->tx_dev->ops->display_on(ch->tx_dev); + if (ret < 0) + return; + + if (ret == SH_MOBILE_LCDC_DISPLAY_DISCONNECTED) + ch->info->state = FBINFO_STATE_SUSPENDED; + } + + /* HDMI must be enabled before LCDC configuration */ + if (panel->display_on) + panel->display_on(); +} + +static void sh_mobile_lcdc_display_off(struct sh_mobile_lcdc_chan *ch) +{ + const struct sh_mobile_lcdc_panel_cfg *panel = &ch->cfg->panel_cfg; + + if (panel->display_off) + panel->display_off(); + + if (ch->tx_dev) + ch->tx_dev->ops->display_off(ch->tx_dev); +} + +static bool +sh_mobile_lcdc_must_reconfigure(struct sh_mobile_lcdc_chan *ch, + const struct fb_videomode *new_mode) +{ + dev_dbg(ch->info->dev, "Old %ux%u, new %ux%u\n", + ch->display.mode.xres, ch->display.mode.yres, + new_mode->xres, new_mode->yres); + + /* It can be a different monitor with an equal video-mode */ + if (fb_mode_is_equal(&ch->display.mode, new_mode)) + return false; + + dev_dbg(ch->info->dev, "Switching %u -> %u lines\n", + ch->display.mode.yres, new_mode->yres); + ch->display.mode = *new_mode; + + return true; +} + +static int sh_mobile_check_var(struct fb_var_screeninfo *var, + struct fb_info *info); + +static int sh_mobile_lcdc_display_notify(struct sh_mobile_lcdc_chan *ch, + enum sh_mobile_lcdc_entity_event event, + const struct fb_videomode *mode, + const struct fb_monspecs *monspec) +{ + struct fb_info *info = ch->info; + struct fb_var_screeninfo var; + int ret = 0; + + switch (event) { + case SH_MOBILE_LCDC_EVENT_DISPLAY_CONNECT: + /* HDMI plug in */ + if (lock_fb_info(info)) { + console_lock(); + + ch->display.width = monspec->max_x * 10; + ch->display.height = monspec->max_y * 10; + + if (!sh_mobile_lcdc_must_reconfigure(ch, mode) && + info->state == FBINFO_STATE_RUNNING) { + /* First activation with the default monitor. + * Just turn on, if we run a resume here, the + * logo disappears. + */ + info->var.width = monspec->max_x * 10; + info->var.height = monspec->max_y * 10; + sh_mobile_lcdc_display_on(ch); + } else { + /* New monitor or have to wake up */ + fb_set_suspend(info, 0); + } + + console_unlock(); + unlock_fb_info(info); + } + break; + + case SH_MOBILE_LCDC_EVENT_DISPLAY_DISCONNECT: + /* HDMI disconnect */ + if (lock_fb_info(info)) { + console_lock(); + fb_set_suspend(info, 1); + console_unlock(); + unlock_fb_info(info); + } + break; + + case SH_MOBILE_LCDC_EVENT_DISPLAY_MODE: + /* Validate a proposed new mode */ + fb_videomode_to_var(&var, mode); + var.bits_per_pixel = info->var.bits_per_pixel; + var.grayscale = info->var.grayscale; + ret = sh_mobile_check_var(&var, info); + break; + } + + return ret; +} + +/* ----------------------------------------------------------------------------- + * Format helpers + */ + +struct sh_mobile_lcdc_format_info { + u32 fourcc; + unsigned int bpp; + bool yuv; + u32 lddfr; +}; + +static const struct sh_mobile_lcdc_format_info sh_mobile_format_infos[] = { + { + .fourcc = V4L2_PIX_FMT_RGB565, + .bpp = 16, + .yuv = false, + .lddfr = LDDFR_PKF_RGB16, + }, { + .fourcc = V4L2_PIX_FMT_BGR24, + .bpp = 24, + .yuv = false, + .lddfr = LDDFR_PKF_RGB24, + }, { + .fourcc = V4L2_PIX_FMT_BGR32, + .bpp = 32, + .yuv = false, + .lddfr = LDDFR_PKF_ARGB32, + }, { + .fourcc = V4L2_PIX_FMT_NV12, + .bpp = 12, + .yuv = true, + .lddfr = LDDFR_CC | LDDFR_YF_420, + }, { + .fourcc = V4L2_PIX_FMT_NV21, + .bpp = 12, + .yuv = true, + .lddfr = LDDFR_CC | LDDFR_YF_420, + }, { + .fourcc = V4L2_PIX_FMT_NV16, + .bpp = 16, + .yuv = true, + .lddfr = LDDFR_CC | LDDFR_YF_422, + }, { + .fourcc = V4L2_PIX_FMT_NV61, + .bpp = 16, + .yuv = true, + .lddfr = LDDFR_CC | LDDFR_YF_422, + }, { + .fourcc = V4L2_PIX_FMT_NV24, + .bpp = 24, + .yuv = true, + .lddfr = LDDFR_CC | LDDFR_YF_444, + }, { + .fourcc = V4L2_PIX_FMT_NV42, + .bpp = 24, + .yuv = true, + .lddfr = LDDFR_CC | LDDFR_YF_444, + }, +}; + +static const struct sh_mobile_lcdc_format_info * +sh_mobile_format_info(u32 fourcc) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(sh_mobile_format_infos); ++i) { + if (sh_mobile_format_infos[i].fourcc == fourcc) + return &sh_mobile_format_infos[i]; + } + + return NULL; +} + +static int sh_mobile_format_fourcc(const struct fb_var_screeninfo *var) +{ + if (var->grayscale > 1) + return var->grayscale; + + switch (var->bits_per_pixel) { + case 16: + return V4L2_PIX_FMT_RGB565; + case 24: + return V4L2_PIX_FMT_BGR24; + case 32: + return V4L2_PIX_FMT_BGR32; + default: + return 0; + } +} + +static int sh_mobile_format_is_fourcc(const struct fb_var_screeninfo *var) +{ + return var->grayscale > 1; +} + +/* ----------------------------------------------------------------------------- + * Start, stop and IRQ + */ + static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data) { struct sh_mobile_lcdc_priv *priv = data; @@ -385,6 +585,26 @@ static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data) return IRQ_HANDLED; } +static int sh_mobile_wait_for_vsync(struct sh_mobile_lcdc_chan *ch) +{ + unsigned long ldintr; + int ret; + + /* Enable VSync End interrupt and be careful not to acknowledge any + * pending interrupt. + */ + ldintr = lcdc_read(ch->lcdc, _LDINTR); + ldintr |= LDINTR_VEE | LDINTR_STATUS_MASK; + lcdc_write(ch->lcdc, _LDINTR, ldintr); + + ret = wait_for_completion_interruptible_timeout(&ch->vsync_completion, + msecs_to_jiffies(100)); + if (!ret) + return -ETIMEDOUT; + + return 0; +} + static void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv, int start) { @@ -416,53 +636,52 @@ static void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv, static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch) { - struct fb_var_screeninfo *var = &ch->info->var, *display_var = &ch->display_var; + const struct fb_var_screeninfo *var = &ch->info->var; + const struct fb_videomode *mode = &ch->display.mode; unsigned long h_total, hsync_pos, display_h_total; u32 tmp; tmp = ch->ldmt1r_value; tmp |= (var->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : LDMT1R_VPOL; tmp |= (var->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : LDMT1R_HPOL; - tmp |= (ch->cfg.flags & LCDC_FLAGS_DWPOL) ? LDMT1R_DWPOL : 0; - tmp |= (ch->cfg.flags & LCDC_FLAGS_DIPOL) ? LDMT1R_DIPOL : 0; - tmp |= (ch->cfg.flags & LCDC_FLAGS_DAPOL) ? LDMT1R_DAPOL : 0; - tmp |= (ch->cfg.flags & LCDC_FLAGS_HSCNT) ? LDMT1R_HSCNT : 0; - tmp |= (ch->cfg.flags & LCDC_FLAGS_DWCNT) ? LDMT1R_DWCNT : 0; + tmp |= (ch->cfg->flags & LCDC_FLAGS_DWPOL) ? LDMT1R_DWPOL : 0; + tmp |= (ch->cfg->flags & LCDC_FLAGS_DIPOL) ? LDMT1R_DIPOL : 0; + tmp |= (ch->cfg->flags & LCDC_FLAGS_DAPOL) ? LDMT1R_DAPOL : 0; + tmp |= (ch->cfg->flags & LCDC_FLAGS_HSCNT) ? LDMT1R_HSCNT : 0; + tmp |= (ch->cfg->flags & LCDC_FLAGS_DWCNT) ? LDMT1R_DWCNT : 0; lcdc_write_chan(ch, LDMT1R, tmp); /* setup SYS bus */ - lcdc_write_chan(ch, LDMT2R, ch->cfg.sys_bus_cfg.ldmt2r); - lcdc_write_chan(ch, LDMT3R, ch->cfg.sys_bus_cfg.ldmt3r); + lcdc_write_chan(ch, LDMT2R, ch->cfg->sys_bus_cfg.ldmt2r); + lcdc_write_chan(ch, LDMT3R, ch->cfg->sys_bus_cfg.ldmt3r); /* horizontal configuration */ - h_total = display_var->xres + display_var->hsync_len + - display_var->left_margin + display_var->right_margin; + h_total = mode->xres + mode->hsync_len + mode->left_margin + + mode->right_margin; tmp = h_total / 8; /* HTCN */ - tmp |= (min(display_var->xres, var->xres) / 8) << 16; /* HDCN */ + tmp |= (min(mode->xres, ch->xres) / 8) << 16; /* HDCN */ lcdc_write_chan(ch, LDHCNR, tmp); - hsync_pos = display_var->xres + display_var->right_margin; + hsync_pos = mode->xres + mode->right_margin; tmp = hsync_pos / 8; /* HSYNP */ - tmp |= (display_var->hsync_len / 8) << 16; /* HSYNW */ + tmp |= (mode->hsync_len / 8) << 16; /* HSYNW */ lcdc_write_chan(ch, LDHSYNR, tmp); /* vertical configuration */ - tmp = display_var->yres + display_var->vsync_len + - display_var->upper_margin + display_var->lower_margin; /* VTLN */ - tmp |= min(display_var->yres, var->yres) << 16; /* VDLN */ + tmp = mode->yres + mode->vsync_len + mode->upper_margin + + mode->lower_margin; /* VTLN */ + tmp |= min(mode->yres, ch->yres) << 16; /* VDLN */ lcdc_write_chan(ch, LDVLNR, tmp); - tmp = display_var->yres + display_var->lower_margin; /* VSYNP */ - tmp |= display_var->vsync_len << 16; /* VSYNW */ + tmp = mode->yres + mode->lower_margin; /* VSYNP */ + tmp |= mode->vsync_len << 16; /* VSYNW */ lcdc_write_chan(ch, LDVSYNR, tmp); /* Adjust horizontal synchronisation for HDMI */ - display_h_total = display_var->xres + display_var->hsync_len + - display_var->left_margin + display_var->right_margin; - tmp = ((display_var->xres & 7) << 24) | - ((display_h_total & 7) << 16) | - ((display_var->hsync_len & 7) << 8) | - (hsync_pos & 7); + display_h_total = mode->xres + mode->hsync_len + mode->left_margin + + mode->right_margin; + tmp = ((mode->xres & 7) << 24) | ((display_h_total & 7) << 16) + | ((mode->hsync_len & 7) << 8) | (hsync_pos & 7); lcdc_write_chan(ch, LDHAJR, tmp); } @@ -498,7 +717,7 @@ static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) /* Power supply */ lcdc_write_chan(ch, LDPMR, 0); - m = ch->cfg.clock_divider; + m = ch->cfg->clock_divider; if (!m) continue; @@ -525,32 +744,10 @@ static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) sh_mobile_lcdc_geometry(ch); - switch (sh_mobile_format_fourcc(&ch->info->var)) { - case V4L2_PIX_FMT_RGB565: - tmp = LDDFR_PKF_RGB16; - break; - case V4L2_PIX_FMT_BGR24: - tmp = LDDFR_PKF_RGB24; - break; - case V4L2_PIX_FMT_BGR32: - tmp = LDDFR_PKF_ARGB32; - break; - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV21: - tmp = LDDFR_CC | LDDFR_YF_420; - break; - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV61: - tmp = LDDFR_CC | LDDFR_YF_422; - break; - case V4L2_PIX_FMT_NV24: - case V4L2_PIX_FMT_NV42: - tmp = LDDFR_CC | LDDFR_YF_444; - break; - } + tmp = ch->format->lddfr; - if (sh_mobile_format_is_yuv(&ch->info->var)) { - switch (ch->info->var.colorspace) { + if (ch->format->yuv) { + switch (ch->colorspace) { case V4L2_COLORSPACE_REC709: tmp |= LDDFR_CF1; break; @@ -563,7 +760,7 @@ static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) lcdc_write_chan(ch, LDDFR, tmp); lcdc_write_chan(ch, LDMLSR, ch->pitch); lcdc_write_chan(ch, LDSA1R, ch->base_addr_y); - if (sh_mobile_format_is_yuv(&ch->info->var)) + if (ch->format->yuv) lcdc_write_chan(ch, LDSA2R, ch->base_addr_c); /* When using deferred I/O mode, configure the LCDC for one-shot @@ -571,7 +768,7 @@ static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) * continuous read mode. */ if (ch->ldmt1r_value & LDMT1R_IFM && - ch->cfg.sys_bus_cfg.deferred_io_msec) { + ch->cfg->sys_bus_cfg.deferred_io_msec) { lcdc_write_chan(ch, LDSM1R, LDSM1R_OS); lcdc_write(priv, _LDINTR, LDINTR_FE); } else { @@ -580,7 +777,7 @@ static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) } /* Word and long word swap. */ - switch (sh_mobile_format_fourcc(&priv->ch[0].info->var)) { + switch (priv->ch[0].format->fourcc) { case V4L2_PIX_FMT_RGB565: case V4L2_PIX_FMT_NV21: case V4L2_PIX_FMT_NV61: @@ -609,7 +806,6 @@ static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) { struct sh_mobile_meram_info *mdev = priv->meram_dev; - struct sh_mobile_lcdc_board_cfg *board_cfg; struct sh_mobile_lcdc_chan *ch; unsigned long tmp; int ret; @@ -626,15 +822,15 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) lcdc_wait_bit(priv, _LDCNT2R, LDCNT2R_BR, 0); for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { - ch = &priv->ch[k]; + const struct sh_mobile_lcdc_panel_cfg *panel; + ch = &priv->ch[k]; if (!ch->enabled) continue; - board_cfg = &ch->cfg.board_cfg; - if (board_cfg->setup_sys) { - ret = board_cfg->setup_sys(board_cfg->board_data, ch, - &sh_mobile_lcdc_sys_bus_ops); + panel = &ch->cfg->panel_cfg; + if (panel->setup_sys) { + ret = panel->setup_sys(ch, &sh_mobile_lcdc_sys_bus_ops); if (ret) return ret; } @@ -642,33 +838,30 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) /* Compute frame buffer base address and pitch for each channel. */ for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { - struct sh_mobile_meram_cfg *cfg; int pixelformat; + void *meram; ch = &priv->ch[k]; if (!ch->enabled) continue; - ch->base_addr_y = ch->info->fix.smem_start; - ch->base_addr_c = ch->base_addr_y - + ch->info->var.xres - * ch->info->var.yres_virtual; - ch->pitch = ch->info->fix.line_length; + ch->base_addr_y = ch->dma_handle; + ch->base_addr_c = ch->base_addr_y + ch->xres * ch->yres_virtual; /* Enable MERAM if possible. */ - cfg = ch->cfg.meram_cfg; - if (mdev == NULL || mdev->ops == NULL || cfg == NULL) + if (mdev == NULL || mdev->ops == NULL || + ch->cfg->meram_cfg == NULL) continue; /* we need to de-init configured ICBs before we can * re-initialize them. */ - if (ch->meram_enabled) { - mdev->ops->meram_unregister(mdev, cfg); - ch->meram_enabled = 0; + if (ch->meram) { + mdev->ops->meram_unregister(mdev, ch->meram); + ch->meram = NULL; } - switch (sh_mobile_format_fourcc(&ch->info->var)) { + switch (ch->format->fourcc) { case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV21: case V4L2_PIX_FMT_NV16: @@ -687,13 +880,15 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) break; } - ret = mdev->ops->meram_register(mdev, cfg, ch->pitch, - ch->info->var.yres, pixelformat, - ch->base_addr_y, ch->base_addr_c, - &ch->base_addr_y, &ch->base_addr_c, + meram = mdev->ops->meram_register(mdev, ch->cfg->meram_cfg, + ch->pitch, ch->yres, pixelformat, &ch->pitch); - if (!ret) - ch->meram_enabled = 1; + if (!IS_ERR(meram)) { + mdev->ops->meram_update(mdev, meram, + ch->base_addr_y, ch->base_addr_c, + &ch->base_addr_y, &ch->base_addr_c); + ch->meram = meram; + } } /* Start the LCDC. */ @@ -707,7 +902,7 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) if (!ch->enabled) continue; - tmp = ch->cfg.sys_bus_cfg.deferred_io_msec; + tmp = ch->cfg->sys_bus_cfg.deferred_io_msec; if (ch->ldmt1r_value & LDMT1R_IFM && tmp) { ch->defio.deferred_io = sh_mobile_lcdc_deferred_io; ch->defio.delay = msecs_to_jiffies(tmp); @@ -715,11 +910,7 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) fb_deferred_io_init(ch->info); } - board_cfg = &ch->cfg.board_cfg; - if (board_cfg->display_on && try_module_get(board_cfg->owner)) { - board_cfg->display_on(board_cfg->board_data, ch->info); - module_put(board_cfg->owner); - } + sh_mobile_lcdc_display_on(ch); if (ch->bl) { ch->bl->props.power = FB_BLANK_UNBLANK; @@ -733,7 +924,6 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv) { struct sh_mobile_lcdc_chan *ch; - struct sh_mobile_lcdc_board_cfg *board_cfg; int k; /* clean up deferred io and ask board code to disable panel */ @@ -760,20 +950,14 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv) backlight_update_status(ch->bl); } - board_cfg = &ch->cfg.board_cfg; - if (board_cfg->display_off && try_module_get(board_cfg->owner)) { - board_cfg->display_off(board_cfg->board_data); - module_put(board_cfg->owner); - } + sh_mobile_lcdc_display_off(ch); /* disable the meram */ - if (ch->meram_enabled) { - struct sh_mobile_meram_cfg *cfg; + if (ch->meram) { struct sh_mobile_meram_info *mdev; - cfg = ch->cfg.meram_cfg; mdev = priv->meram_dev; - mdev->ops->meram_unregister(mdev, cfg); - ch->meram_enabled = 0; + mdev->ops->meram_unregister(mdev, ch->meram); + ch->meram = 0; } } @@ -790,86 +974,9 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv) sh_mobile_lcdc_clk_off(priv); } -static int sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *ch) -{ - int interface_type = ch->cfg.interface_type; - - switch (interface_type) { - case RGB8: - case RGB9: - case RGB12A: - case RGB12B: - case RGB16: - case RGB18: - case RGB24: - case SYS8A: - case SYS8B: - case SYS8C: - case SYS8D: - case SYS9: - case SYS12: - case SYS16A: - case SYS16B: - case SYS16C: - case SYS18: - case SYS24: - break; - default: - return -EINVAL; - } - - /* SUBLCD only supports SYS interface */ - if (lcdc_chan_is_sublcd(ch)) { - if (!(interface_type & LDMT1R_IFM)) - return -EINVAL; - - interface_type &= ~LDMT1R_IFM; - } - - ch->ldmt1r_value = interface_type; - return 0; -} - -static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev, - int clock_source, - struct sh_mobile_lcdc_priv *priv) -{ - char *str; - - switch (clock_source) { - case LCDC_CLK_BUS: - str = "bus_clk"; - priv->lddckr = LDDCKR_ICKSEL_BUS; - break; - case LCDC_CLK_PERIPHERAL: - str = "peripheral_clk"; - priv->lddckr = LDDCKR_ICKSEL_MIPI; - break; - case LCDC_CLK_EXTERNAL: - str = NULL; - priv->lddckr = LDDCKR_ICKSEL_HDMI; - break; - default: - return -EINVAL; - } - - if (str) { - priv->dot_clk = clk_get(&pdev->dev, str); - if (IS_ERR(priv->dot_clk)) { - dev_err(&pdev->dev, "cannot get dot clock %s\n", str); - return PTR_ERR(priv->dot_clk); - } - } - - /* Runtime PM support involves two step for this driver: - * 1) Enable Runtime PM - * 2) Force Runtime PM Resume since hardware is accessed from probe() - */ - priv->dev = &pdev->dev; - pm_runtime_enable(priv->dev); - pm_runtime_resume(priv->dev); - return 0; -} +/* ----------------------------------------------------------------------------- + * Frame buffer operations + */ static int sh_mobile_lcdc_setcolreg(u_int regno, u_int red, u_int green, u_int blue, @@ -936,14 +1043,12 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var, unsigned long new_pan_offset; unsigned long base_addr_y, base_addr_c; unsigned long c_offset; - bool yuv = sh_mobile_format_is_yuv(&info->var); - if (!yuv) - new_pan_offset = var->yoffset * info->fix.line_length - + var->xoffset * (info->var.bits_per_pixel / 8); + if (!ch->format->yuv) + new_pan_offset = var->yoffset * ch->pitch + + var->xoffset * (ch->format->bpp / 8); else - new_pan_offset = var->yoffset * info->fix.line_length - + var->xoffset; + new_pan_offset = var->yoffset * ch->pitch + var->xoffset; if (new_pan_offset == ch->pan_offset) return 0; /* No change, do nothing */ @@ -952,39 +1057,33 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var, /* Set the source address for the next refresh */ base_addr_y = ch->dma_handle + new_pan_offset; - if (yuv) { + if (ch->format->yuv) { /* Set y offset */ - c_offset = var->yoffset * info->fix.line_length - * (info->var.bits_per_pixel - 8) / 8; - base_addr_c = ch->dma_handle - + info->var.xres * info->var.yres_virtual + c_offset = var->yoffset * ch->pitch + * (ch->format->bpp - 8) / 8; + base_addr_c = ch->dma_handle + ch->xres * ch->yres_virtual + c_offset; /* Set x offset */ - if (sh_mobile_format_fourcc(&info->var) == V4L2_PIX_FMT_NV24) + if (ch->format->fourcc == V4L2_PIX_FMT_NV24) base_addr_c += 2 * var->xoffset; else base_addr_c += var->xoffset; } - if (ch->meram_enabled) { - struct sh_mobile_meram_cfg *cfg; + if (ch->meram) { struct sh_mobile_meram_info *mdev; - int ret; - cfg = ch->cfg.meram_cfg; mdev = priv->meram_dev; - ret = mdev->ops->meram_update(mdev, cfg, + mdev->ops->meram_update(mdev, ch->meram, base_addr_y, base_addr_c, &base_addr_y, &base_addr_c); - if (ret) - return ret; } ch->base_addr_y = base_addr_y; ch->base_addr_c = base_addr_c; lcdc_write_chan_mirror(ch, LDSA1R, base_addr_y); - if (yuv) + if (ch->format->yuv) lcdc_write_chan_mirror(ch, LDSA2R, base_addr_c); if (lcdc_chan_is_sublcd(ch)) @@ -999,27 +1098,6 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var, return 0; } -static int sh_mobile_wait_for_vsync(struct fb_info *info) -{ - struct sh_mobile_lcdc_chan *ch = info->par; - unsigned long ldintr; - int ret; - - /* Enable VSync End interrupt and be careful not to acknowledge any - * pending interrupt. - */ - ldintr = lcdc_read(ch->lcdc, _LDINTR); - ldintr |= LDINTR_VEE | LDINTR_STATUS_MASK; - lcdc_write(ch->lcdc, _LDINTR, ldintr); - - ret = wait_for_completion_interruptible_timeout(&ch->vsync_completion, - msecs_to_jiffies(100)); - if (!ret) - return -ETIMEDOUT; - - return 0; -} - static int sh_mobile_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) { @@ -1027,7 +1105,7 @@ static int sh_mobile_ioctl(struct fb_info *info, unsigned int cmd, switch (cmd) { case FBIO_WAITFORVSYNC: - retval = sh_mobile_wait_for_vsync(info); + retval = sh_mobile_wait_for_vsync(info->par); break; default: @@ -1040,7 +1118,8 @@ static int sh_mobile_ioctl(struct fb_info *info, unsigned int cmd, static void sh_mobile_fb_reconfig(struct fb_info *info) { struct sh_mobile_lcdc_chan *ch = info->par; - struct fb_videomode mode1, mode2; + struct fb_var_screeninfo var; + struct fb_videomode mode; struct fb_event event; int evnt = FB_EVENT_MODE_CHANGE_ALL; @@ -1048,14 +1127,19 @@ static void sh_mobile_fb_reconfig(struct fb_info *info) /* More framebuffer users are active */ return; - fb_var_to_videomode(&mode1, &ch->display_var); - fb_var_to_videomode(&mode2, &info->var); + fb_var_to_videomode(&mode, &info->var); - if (fb_mode_is_equal(&mode1, &mode2)) + if (fb_mode_is_equal(&ch->display.mode, &mode)) return; /* Display has been re-plugged, framebuffer is free now, reconfigure */ - if (fb_set_var(info, &ch->display_var) < 0) + var = info->var; + fb_videomode_to_var(&var, &ch->display.mode); + var.width = ch->display.width; + var.height = ch->display.height; + var.activate = FB_ACTIVATE_NOW; + + if (fb_set_var(info, &var) < 0) /* Couldn't reconfigure, hopefully, can continue as before */ return; @@ -1065,7 +1149,7 @@ static void sh_mobile_fb_reconfig(struct fb_info *info) * user event, we have to call the chain ourselves. */ event.info = info; - event.data = &mode1; + event.data = &ch->display.mode; fb_notifier_call_chain(evnt, &event); } @@ -1124,8 +1208,8 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in * distance between two modes is defined as the size of the * non-overlapping parts of the two rectangles. */ - for (i = 0; i < ch->cfg.num_cfg; ++i) { - const struct fb_videomode *mode = &ch->cfg.lcd_cfg[i]; + for (i = 0; i < ch->cfg->num_modes; ++i) { + const struct fb_videomode *mode = &ch->cfg->lcd_modes[i]; unsigned int dist; /* We can only round up. */ @@ -1144,7 +1228,7 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in } /* If no available mode can be used, return an error. */ - if (ch->cfg.num_cfg != 0) { + if (ch->cfg->num_modes != 0) { if (best_dist == (unsigned int)-1) return -EINVAL; @@ -1161,32 +1245,17 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in var->yres_virtual = var->yres; if (sh_mobile_format_is_fourcc(var)) { - switch (var->grayscale) { - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV21: - var->bits_per_pixel = 12; - break; - case V4L2_PIX_FMT_RGB565: - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV61: - var->bits_per_pixel = 16; - break; - case V4L2_PIX_FMT_BGR24: - case V4L2_PIX_FMT_NV24: - case V4L2_PIX_FMT_NV42: - var->bits_per_pixel = 24; - break; - case V4L2_PIX_FMT_BGR32: - var->bits_per_pixel = 32; - break; - default: + const struct sh_mobile_lcdc_format_info *format; + + format = sh_mobile_format_info(var->grayscale); + if (format == NULL) return -EINVAL; - } + var->bits_per_pixel = format->bpp; /* Default to RGB and JPEG color-spaces for RGB and YUV formats * respectively. */ - if (!sh_mobile_format_is_yuv(var)) + if (!format->yuv) var->colorspace = V4L2_COLORSPACE_SRGB; else if (var->colorspace != V4L2_COLORSPACE_REC709) var->colorspace = V4L2_COLORSPACE_JPEG; @@ -1246,22 +1315,28 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in static int sh_mobile_set_par(struct fb_info *info) { struct sh_mobile_lcdc_chan *ch = info->par; - u32 line_length = info->fix.line_length; int ret; sh_mobile_lcdc_stop(ch->lcdc); - if (sh_mobile_format_is_yuv(&info->var)) - info->fix.line_length = info->var.xres; + ch->format = sh_mobile_format_info(sh_mobile_format_fourcc(&info->var)); + ch->colorspace = info->var.colorspace; + + ch->xres = info->var.xres; + ch->xres_virtual = info->var.xres_virtual; + ch->yres = info->var.yres; + ch->yres_virtual = info->var.yres_virtual; + + if (ch->format->yuv) + ch->pitch = info->var.xres; else - info->fix.line_length = info->var.xres - * info->var.bits_per_pixel / 8; + ch->pitch = info->var.xres * ch->format->bpp / 8; ret = sh_mobile_lcdc_start(ch->lcdc); - if (ret < 0) { + if (ret < 0) dev_err(info->dev, "%s: unable to restart LCDC\n", __func__); - info->fix.line_length = line_length; - } + + info->fix.line_length = ch->pitch; if (sh_mobile_format_is_fourcc(&info->var)) { info->fix.type = FB_TYPE_FOURCC; @@ -1290,8 +1365,8 @@ static int sh_mobile_lcdc_blank(int blank, struct fb_info *info) /* blank the screen? */ if (blank > FB_BLANK_UNBLANK && ch->blank_status == FB_BLANK_UNBLANK) { struct fb_fillrect rect = { - .width = info->var.xres, - .height = info->var.yres, + .width = ch->xres, + .height = ch->yres, }; sh_mobile_lcdc_fillrect(info, &rect); } @@ -1307,8 +1382,8 @@ static int sh_mobile_lcdc_blank(int blank, struct fb_info *info) * mode will reenable the clocks and update the screen in time, * so it does not need this. */ if (!info->fbdefio) { - sh_mobile_wait_for_vsync(info); - sh_mobile_wait_for_vsync(info); + sh_mobile_wait_for_vsync(ch); + sh_mobile_wait_for_vsync(ch); } sh_mobile_lcdc_clk_off(p); } @@ -1334,25 +1409,161 @@ static struct fb_ops sh_mobile_lcdc_ops = { .fb_set_par = sh_mobile_set_par, }; +static void +sh_mobile_lcdc_channel_fb_unregister(struct sh_mobile_lcdc_chan *ch) +{ + if (ch->info && ch->info->dev) + unregister_framebuffer(ch->info); +} + +static int __devinit +sh_mobile_lcdc_channel_fb_register(struct sh_mobile_lcdc_chan *ch) +{ + struct fb_info *info = ch->info; + int ret; + + if (info->fbdefio) { + ch->sglist = vmalloc(sizeof(struct scatterlist) * + ch->fb_size >> PAGE_SHIFT); + if (!ch->sglist) { + dev_err(ch->lcdc->dev, "cannot allocate sglist\n"); + return -ENOMEM; + } + } + + info->bl_dev = ch->bl; + + ret = register_framebuffer(info); + if (ret < 0) + return ret; + + dev_info(ch->lcdc->dev, "registered %s/%s as %dx%d %dbpp.\n", + dev_name(ch->lcdc->dev), (ch->cfg->chan == LCDC_CHAN_MAINLCD) ? + "mainlcd" : "sublcd", info->var.xres, info->var.yres, + info->var.bits_per_pixel); + + /* deferred io mode: disable clock to save power */ + if (info->fbdefio || info->state == FBINFO_STATE_SUSPENDED) + sh_mobile_lcdc_clk_off(ch->lcdc); + + return ret; +} + +static void +sh_mobile_lcdc_channel_fb_cleanup(struct sh_mobile_lcdc_chan *ch) +{ + struct fb_info *info = ch->info; + + if (!info || !info->device) + return; + + if (ch->sglist) + vfree(ch->sglist); + + fb_dealloc_cmap(&info->cmap); + framebuffer_release(info); +} + +static int __devinit +sh_mobile_lcdc_channel_fb_init(struct sh_mobile_lcdc_chan *ch, + const struct fb_videomode *mode, + unsigned int num_modes) +{ + struct sh_mobile_lcdc_priv *priv = ch->lcdc; + struct fb_var_screeninfo *var; + struct fb_info *info; + int ret; + + /* Allocate and initialize the frame buffer device. Create the modes + * list and allocate the color map. + */ + info = framebuffer_alloc(0, priv->dev); + if (info == NULL) { + dev_err(priv->dev, "unable to allocate fb_info\n"); + return -ENOMEM; + } + + ch->info = info; + + info->flags = FBINFO_FLAG_DEFAULT; + info->fbops = &sh_mobile_lcdc_ops; + info->device = priv->dev; + info->screen_base = ch->fb_mem; + info->pseudo_palette = &ch->pseudo_palette; + info->par = ch; + + fb_videomode_to_modelist(mode, num_modes, &info->modelist); + + ret = fb_alloc_cmap(&info->cmap, PALETTE_NR, 0); + if (ret < 0) { + dev_err(priv->dev, "unable to allocate cmap\n"); + return ret; + } + + /* Initialize fixed screen information. Restrict pan to 2 lines steps + * for NV12 and NV21. + */ + info->fix = sh_mobile_lcdc_fix; + info->fix.smem_start = ch->dma_handle; + info->fix.smem_len = ch->fb_size; + info->fix.line_length = ch->pitch; + + if (ch->format->yuv) + info->fix.visual = FB_VISUAL_FOURCC; + else + info->fix.visual = FB_VISUAL_TRUECOLOR; + + if (ch->format->fourcc == V4L2_PIX_FMT_NV12 || + ch->format->fourcc == V4L2_PIX_FMT_NV21) + info->fix.ypanstep = 2; + + /* Initialize variable screen information using the first mode as + * default. The default Y virtual resolution is twice the panel size to + * allow for double-buffering. + */ + var = &info->var; + fb_videomode_to_var(var, mode); + var->width = ch->cfg->panel_cfg.width; + var->height = ch->cfg->panel_cfg.height; + var->yres_virtual = var->yres * 2; + var->activate = FB_ACTIVATE_NOW; + + /* Use the legacy API by default for RGB formats, and the FOURCC API + * for YUV formats. + */ + if (!ch->format->yuv) + var->bits_per_pixel = ch->format->bpp; + else + var->grayscale = ch->format->fourcc; + + ret = sh_mobile_check_var(var, info); + if (ret) + return ret; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Backlight + */ + static int sh_mobile_lcdc_update_bl(struct backlight_device *bdev) { struct sh_mobile_lcdc_chan *ch = bl_get_data(bdev); - struct sh_mobile_lcdc_board_cfg *cfg = &ch->cfg.board_cfg; int brightness = bdev->props.brightness; if (bdev->props.power != FB_BLANK_UNBLANK || bdev->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK)) brightness = 0; - return cfg->set_brightness(cfg->board_data, brightness); + return ch->cfg->bl_info.set_brightness(brightness); } static int sh_mobile_lcdc_get_brightness(struct backlight_device *bdev) { struct sh_mobile_lcdc_chan *ch = bl_get_data(bdev); - struct sh_mobile_lcdc_board_cfg *cfg = &ch->cfg.board_cfg; - return cfg->get_brightness(cfg->board_data); + return ch->cfg->bl_info.get_brightness(); } static int sh_mobile_lcdc_check_fb(struct backlight_device *bdev, @@ -1373,7 +1584,7 @@ static struct backlight_device *sh_mobile_lcdc_bl_probe(struct device *parent, { struct backlight_device *bl; - bl = backlight_device_register(ch->cfg.bl_info.name, parent, ch, + bl = backlight_device_register(ch->cfg->bl_info.name, parent, ch, &sh_mobile_lcdc_bl_ops, NULL); if (IS_ERR(bl)) { dev_err(parent, "unable to register backlight device: %ld\n", @@ -1381,7 +1592,7 @@ static struct backlight_device *sh_mobile_lcdc_bl_probe(struct device *parent, return NULL; } - bl->props.max_brightness = ch->cfg.bl_info.max_brightness; + bl->props.max_brightness = ch->cfg->bl_info.max_brightness; bl->props.brightness = bl->props.max_brightness; backlight_update_status(bl); @@ -1393,6 +1604,10 @@ static void sh_mobile_lcdc_bl_remove(struct backlight_device *bdev) backlight_device_unregister(bdev); } +/* ----------------------------------------------------------------------------- + * Power management + */ + static int sh_mobile_lcdc_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); @@ -1436,6 +1651,10 @@ static const struct dev_pm_ops sh_mobile_lcdc_dev_pm_ops = { .runtime_resume = sh_mobile_lcdc_runtime_resume, }; +/* ----------------------------------------------------------------------------- + * Framebuffer notifier + */ + /* locking: called with info->lock held */ static int sh_mobile_lcdc_notify(struct notifier_block *nb, unsigned long action, void *data) @@ -1443,7 +1662,6 @@ static int sh_mobile_lcdc_notify(struct notifier_block *nb, struct fb_event *event = data; struct fb_info *info = event->info; struct sh_mobile_lcdc_chan *ch = info->par; - struct sh_mobile_lcdc_board_cfg *board_cfg = &ch->cfg.board_cfg; if (&ch->lcdc->notifier != nb) return NOTIFY_DONE; @@ -1453,10 +1671,7 @@ static int sh_mobile_lcdc_notify(struct notifier_block *nb, switch(action) { case FB_EVENT_SUSPEND: - if (board_cfg->display_off && try_module_get(board_cfg->owner)) { - board_cfg->display_off(board_cfg->board_data); - module_put(board_cfg->owner); - } + sh_mobile_lcdc_display_off(ch); sh_mobile_lcdc_stop(ch->lcdc); break; case FB_EVENT_RESUME: @@ -1464,47 +1679,60 @@ static int sh_mobile_lcdc_notify(struct notifier_block *nb, sh_mobile_fb_reconfig(info); mutex_unlock(&ch->open_lock); - /* HDMI must be enabled before LCDC configuration */ - if (board_cfg->display_on && try_module_get(board_cfg->owner)) { - board_cfg->display_on(board_cfg->board_data, info); - module_put(board_cfg->owner); - } - + sh_mobile_lcdc_display_on(ch); sh_mobile_lcdc_start(ch->lcdc); } return NOTIFY_OK; } +/* ----------------------------------------------------------------------------- + * Probe/remove and driver init/exit + */ + +static const struct fb_videomode default_720p __devinitconst = { + .name = "HDMI 720p", + .xres = 1280, + .yres = 720, + + .left_margin = 220, + .right_margin = 110, + .hsync_len = 40, + + .upper_margin = 20, + .lower_margin = 5, + .vsync_len = 5, + + .pixclock = 13468, + .refresh = 60, + .sync = FB_SYNC_VERT_HIGH_ACT | FB_SYNC_HOR_HIGH_ACT, +}; + static int sh_mobile_lcdc_remove(struct platform_device *pdev) { struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev); - struct fb_info *info; int i; fb_unregister_client(&priv->notifier); for (i = 0; i < ARRAY_SIZE(priv->ch); i++) - if (priv->ch[i].info && priv->ch[i].info->dev) - unregister_framebuffer(priv->ch[i].info); + sh_mobile_lcdc_channel_fb_unregister(&priv->ch[i]); sh_mobile_lcdc_stop(priv); for (i = 0; i < ARRAY_SIZE(priv->ch); i++) { - info = priv->ch[i].info; + struct sh_mobile_lcdc_chan *ch = &priv->ch[i]; - if (!info || !info->device) - continue; + if (ch->tx_dev) { + ch->tx_dev->lcdc = NULL; + module_put(ch->cfg->tx_dev->dev.driver->owner); + } - if (priv->ch[i].sglist) - vfree(priv->ch[i].sglist); + sh_mobile_lcdc_channel_fb_cleanup(ch); - if (info->screen_base) - dma_free_coherent(&pdev->dev, info->fix.smem_len, - info->screen_base, - priv->ch[i].dma_handle); - fb_dealloc_cmap(&info->cmap); - framebuffer_release(info); + if (ch->fb_mem) + dma_free_coherent(&pdev->dev, ch->fb_size, + ch->fb_mem, ch->dma_handle); } for (i = 0; i < ARRAY_SIZE(priv->ch); i++) { @@ -1512,11 +1740,10 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev) sh_mobile_lcdc_bl_remove(priv->ch[i].bl); } - if (priv->dot_clk) + if (priv->dot_clk) { + pm_runtime_disable(&pdev->dev); clk_put(priv->dot_clk); - - if (priv->dev) - pm_runtime_disable(priv->dev); + } if (priv->base) iounmap(priv->base); @@ -1527,49 +1754,82 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev) return 0; } -static int __devinit sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_chan *ch, - struct device *dev) +static int __devinit sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *ch) { - struct sh_mobile_lcdc_chan_cfg *cfg = &ch->cfg; + int interface_type = ch->cfg->interface_type; + + switch (interface_type) { + case RGB8: + case RGB9: + case RGB12A: + case RGB12B: + case RGB16: + case RGB18: + case RGB24: + case SYS8A: + case SYS8B: + case SYS8C: + case SYS8D: + case SYS9: + case SYS12: + case SYS16A: + case SYS16B: + case SYS16C: + case SYS18: + case SYS24: + break; + default: + return -EINVAL; + } + + /* SUBLCD only supports SYS interface */ + if (lcdc_chan_is_sublcd(ch)) { + if (!(interface_type & LDMT1R_IFM)) + return -EINVAL; + + interface_type &= ~LDMT1R_IFM; + } + + ch->ldmt1r_value = interface_type; + return 0; +} + +static int __devinit +sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv, + struct sh_mobile_lcdc_chan *ch) +{ + const struct sh_mobile_lcdc_format_info *format; + const struct sh_mobile_lcdc_chan_cfg *cfg = ch->cfg; const struct fb_videomode *max_mode; const struct fb_videomode *mode; - struct fb_var_screeninfo *var; - struct fb_info *info; + unsigned int num_modes; unsigned int max_size; - int num_cfg; - void *buf; - int ret; - int i; + unsigned int i; mutex_init(&ch->open_lock); + ch->notify = sh_mobile_lcdc_display_notify; - /* Allocate the frame buffer device. */ - ch->info = framebuffer_alloc(0, dev); - if (!ch->info) { - dev_err(dev, "unable to allocate fb_info\n"); - return -ENOMEM; + /* Validate the format. */ + format = sh_mobile_format_info(cfg->fourcc); + if (format == NULL) { + dev_err(priv->dev, "Invalid FOURCC %08x.\n", cfg->fourcc); + return -EINVAL; } - info = ch->info; - info->fbops = &sh_mobile_lcdc_ops; - info->par = ch; - info->pseudo_palette = &ch->pseudo_palette; - info->flags = FBINFO_FLAG_DEFAULT; - /* Iterate through the modes to validate them and find the highest * resolution. */ max_mode = NULL; max_size = 0; - for (i = 0, mode = cfg->lcd_cfg; i < cfg->num_cfg; i++, mode++) { + for (i = 0, mode = cfg->lcd_modes; i < cfg->num_modes; i++, mode++) { unsigned int size = mode->yres * mode->xres; /* NV12/NV21 buffers must have even number of lines */ if ((cfg->fourcc == V4L2_PIX_FMT_NV12 || cfg->fourcc == V4L2_PIX_FMT_NV21) && (mode->yres & 0x1)) { - dev_err(dev, "yres must be multiple of 2 for YCbCr420 " - "mode.\n"); + dev_err(priv->dev, "yres must be multiple of 2 for " + "YCbCr420 mode.\n"); return -EINVAL; } @@ -1582,93 +1842,59 @@ static int __devinit sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_chan *ch, if (!max_size) max_size = MAX_XRES * MAX_YRES; else - dev_dbg(dev, "Found largest videomode %ux%u\n", + dev_dbg(priv->dev, "Found largest videomode %ux%u\n", max_mode->xres, max_mode->yres); - /* Create the mode list. */ - if (cfg->lcd_cfg == NULL) { + if (cfg->lcd_modes == NULL) { mode = &default_720p; - num_cfg = 1; + num_modes = 1; } else { - mode = cfg->lcd_cfg; - num_cfg = cfg->num_cfg; + mode = cfg->lcd_modes; + num_modes = cfg->num_modes; } - fb_videomode_to_modelist(mode, num_cfg, &info->modelist); + /* Use the first mode as default. */ + ch->format = format; + ch->xres = mode->xres; + ch->xres_virtual = mode->xres; + ch->yres = mode->yres; + ch->yres_virtual = mode->yres * 2; - /* Initialize variable screen information using the first mode as - * default. The default Y virtual resolution is twice the panel size to - * allow for double-buffering. - */ - var = &info->var; - fb_videomode_to_var(var, mode); - var->width = cfg->lcd_size_cfg.width; - var->height = cfg->lcd_size_cfg.height; - var->yres_virtual = var->yres * 2; - var->activate = FB_ACTIVATE_NOW; - - switch (cfg->fourcc) { - case V4L2_PIX_FMT_RGB565: - var->bits_per_pixel = 16; - break; - case V4L2_PIX_FMT_BGR24: - var->bits_per_pixel = 24; - break; - case V4L2_PIX_FMT_BGR32: - var->bits_per_pixel = 32; - break; - default: - var->grayscale = cfg->fourcc; - break; + if (!format->yuv) { + ch->colorspace = V4L2_COLORSPACE_SRGB; + ch->pitch = ch->xres * format->bpp / 8; + } else { + ch->colorspace = V4L2_COLORSPACE_REC709; + ch->pitch = ch->xres; } - /* Make sure the memory size check won't fail. smem_len is initialized - * later based on var. - */ - info->fix.smem_len = UINT_MAX; - ret = sh_mobile_check_var(var, info); - if (ret) - return ret; - - max_size = max_size * var->bits_per_pixel / 8 * 2; + ch->display.width = cfg->panel_cfg.width; + ch->display.height = cfg->panel_cfg.height; + ch->display.mode = *mode; - /* Allocate frame buffer memory and color map. */ - buf = dma_alloc_coherent(dev, max_size, &ch->dma_handle, GFP_KERNEL); - if (!buf) { - dev_err(dev, "unable to allocate buffer\n"); + /* Allocate frame buffer memory. */ + ch->fb_size = max_size * format->bpp / 8 * 2; + ch->fb_mem = dma_alloc_coherent(priv->dev, ch->fb_size, &ch->dma_handle, + GFP_KERNEL); + if (ch->fb_mem == NULL) { + dev_err(priv->dev, "unable to allocate buffer\n"); return -ENOMEM; } - ret = fb_alloc_cmap(&info->cmap, PALETTE_NR, 0); - if (ret < 0) { - dev_err(dev, "unable to allocate cmap\n"); - dma_free_coherent(dev, max_size, buf, ch->dma_handle); - return ret; - } - - /* Initialize fixed screen information. Restrict pan to 2 lines steps - * for NV12 and NV21. - */ - info->fix = sh_mobile_lcdc_fix; - info->fix.smem_start = ch->dma_handle; - info->fix.smem_len = max_size; - if (cfg->fourcc == V4L2_PIX_FMT_NV12 || - cfg->fourcc == V4L2_PIX_FMT_NV21) - info->fix.ypanstep = 2; - - if (sh_mobile_format_is_yuv(var)) { - info->fix.line_length = var->xres; - info->fix.visual = FB_VISUAL_FOURCC; - } else { - info->fix.line_length = var->xres * var->bits_per_pixel / 8; - info->fix.visual = FB_VISUAL_TRUECOLOR; + /* Initialize the transmitter device if present. */ + if (cfg->tx_dev) { + if (!cfg->tx_dev->dev.driver || + !try_module_get(cfg->tx_dev->dev.driver->owner)) { + dev_warn(priv->dev, + "unable to get transmitter device\n"); + return -EINVAL; + } + ch->tx_dev = platform_get_drvdata(cfg->tx_dev); + ch->tx_dev->lcdc = ch; + ch->tx_dev->def_mode = *mode; } - info->screen_base = buf; - info->device = dev; - ch->display_var = *var; - - return 0; + return sh_mobile_lcdc_channel_fb_init(ch, mode, num_modes); } static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) @@ -1698,6 +1924,8 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) return -ENOMEM; } + priv->dev = &pdev->dev; + priv->meram_dev = pdata->meram_dev; platform_set_drvdata(pdev, priv); error = request_irq(i, sh_mobile_lcdc_irq, 0, @@ -1714,7 +1942,7 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) struct sh_mobile_lcdc_chan *ch = priv->ch + num_channels; ch->lcdc = priv; - memcpy(&ch->cfg, &pdata->ch[i], sizeof(pdata->ch[i])); + ch->cfg = &pdata->ch[i]; error = sh_mobile_lcdc_check_interface(ch); if (error) { @@ -1726,7 +1954,7 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) ch->pan_offset = 0; /* probe the backlight is there is one defined */ - if (ch->cfg.bl_info.max_brightness) + if (ch->cfg->bl_info.max_brightness) ch->bl = sh_mobile_lcdc_bl_probe(&pdev->dev, ch); switch (pdata->ch[i].chan) { @@ -1757,18 +1985,19 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) if (!priv->base) goto err1; - error = sh_mobile_lcdc_setup_clocks(pdev, pdata->clock_source, priv); + error = sh_mobile_lcdc_setup_clocks(priv, pdata->clock_source); if (error) { dev_err(&pdev->dev, "unable to setup clocks\n"); goto err1; } - priv->meram_dev = pdata->meram_dev; + /* Enable runtime PM. */ + pm_runtime_enable(&pdev->dev); for (i = 0; i < num_channels; i++) { struct sh_mobile_lcdc_chan *ch = priv->ch + i; - error = sh_mobile_lcdc_channel_init(ch, &pdev->dev); + error = sh_mobile_lcdc_channel_init(priv, ch); if (error) goto err1; } @@ -1781,31 +2010,10 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) for (i = 0; i < num_channels; i++) { struct sh_mobile_lcdc_chan *ch = priv->ch + i; - struct fb_info *info = ch->info; - - if (info->fbdefio) { - ch->sglist = vmalloc(sizeof(struct scatterlist) * - info->fix.smem_len >> PAGE_SHIFT); - if (!ch->sglist) { - dev_err(&pdev->dev, "cannot allocate sglist\n"); - goto err1; - } - } - - info->bl_dev = ch->bl; - error = register_framebuffer(info); - if (error < 0) + error = sh_mobile_lcdc_channel_fb_register(ch); + if (error) goto err1; - - dev_info(info->dev, "registered %s/%s as %dx%d %dbpp.\n", - pdev->name, (ch->cfg.chan == LCDC_CHAN_MAINLCD) ? - "mainlcd" : "sublcd", info->var.xres, info->var.yres, - info->var.bits_per_pixel); - - /* deferred io mode: disable clock to save power */ - if (info->fbdefio || info->state == FBINFO_STATE_SUSPENDED) - sh_mobile_lcdc_clk_off(priv); } /* Failure ignored */ diff --git a/drivers/video/sh_mobile_lcdcfb.h b/drivers/video/sh_mobile_lcdcfb.h index a58a0f38848b..da1c26e78a57 100644 --- a/drivers/video/sh_mobile_lcdcfb.h +++ b/drivers/video/sh_mobile_lcdcfb.h @@ -14,9 +14,35 @@ enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R, #define PALETTE_NR 16 -struct sh_mobile_lcdc_priv; -struct fb_info; struct backlight_device; +struct fb_info; +struct module; +struct sh_mobile_lcdc_chan; +struct sh_mobile_lcdc_entity; +struct sh_mobile_lcdc_format_info; +struct sh_mobile_lcdc_priv; + +#define SH_MOBILE_LCDC_DISPLAY_DISCONNECTED 0 +#define SH_MOBILE_LCDC_DISPLAY_CONNECTED 1 + +struct sh_mobile_lcdc_entity_ops { + /* Display */ + int (*display_on)(struct sh_mobile_lcdc_entity *entity); + void (*display_off)(struct sh_mobile_lcdc_entity *entity); +}; + +enum sh_mobile_lcdc_entity_event { + SH_MOBILE_LCDC_EVENT_DISPLAY_CONNECT, + SH_MOBILE_LCDC_EVENT_DISPLAY_DISCONNECT, + SH_MOBILE_LCDC_EVENT_DISPLAY_MODE, +}; + +struct sh_mobile_lcdc_entity { + struct module *owner; + const struct sh_mobile_lcdc_entity_ops *ops; + struct sh_mobile_lcdc_chan *lcdc; + struct fb_videomode def_mode; +}; /* * struct sh_mobile_lcdc_chan - LCDC display channel @@ -27,29 +53,57 @@ struct backlight_device; */ struct sh_mobile_lcdc_chan { struct sh_mobile_lcdc_priv *lcdc; + struct sh_mobile_lcdc_entity *tx_dev; + const struct sh_mobile_lcdc_chan_cfg *cfg; + unsigned long *reg_offs; unsigned long ldmt1r_value; unsigned long enabled; /* ME and SE in LDCNT2R */ - struct sh_mobile_lcdc_chan_cfg cfg; - u32 pseudo_palette[PALETTE_NR]; - struct fb_info *info; - struct backlight_device *bl; + void *meram; + + struct mutex open_lock; /* protects the use counter */ + int use_count; + + void *fb_mem; + unsigned long fb_size; + dma_addr_t dma_handle; - struct fb_deferred_io defio; - struct scatterlist *sglist; - unsigned long frame_end; unsigned long pan_offset; + + unsigned long frame_end; wait_queue_head_t frame_end_wait; struct completion vsync_completion; - struct fb_var_screeninfo display_var; - int use_count; - int blank_status; - struct mutex open_lock; /* protects the use counter */ - int meram_enabled; + + const struct sh_mobile_lcdc_format_info *format; + u32 colorspace; + unsigned int xres; + unsigned int xres_virtual; + unsigned int yres; + unsigned int yres_virtual; + unsigned int pitch; unsigned long base_addr_y; unsigned long base_addr_c; - unsigned int pitch; + + int (*notify)(struct sh_mobile_lcdc_chan *ch, + enum sh_mobile_lcdc_entity_event event, + const struct fb_videomode *mode, + const struct fb_monspecs *monspec); + + /* Backlight */ + struct backlight_device *bl; + + /* FB */ + struct fb_info *info; + u32 pseudo_palette[PALETTE_NR]; + struct { + unsigned int width; + unsigned int height; + struct fb_videomode mode; + } display; + struct fb_deferred_io defio; + struct scatterlist *sglist; + int blank_status; }; #endif diff --git a/drivers/video/sh_mobile_meram.c b/drivers/video/sh_mobile_meram.c index f45d83ecfd21..82ba830bf95d 100644 --- a/drivers/video/sh_mobile_meram.c +++ b/drivers/video/sh_mobile_meram.c @@ -9,16 +9,22 @@ * for more details. */ +#include <linux/device.h> +#include <linux/err.h> +#include <linux/genalloc.h> +#include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/device.h> +#include <linux/platform_device.h> #include <linux/pm_runtime.h> -#include <linux/io.h> #include <linux/slab.h> -#include <linux/platform_device.h> + #include <video/sh_mobile_meram.h> -/* meram registers */ +/* ----------------------------------------------------------------------------- + * MERAM registers + */ + #define MEVCR1 0x4 #define MEVCR1_RST (1 << 31) #define MEVCR1_WD (1 << 30) @@ -81,16 +87,14 @@ ((yszm1) << MExxBSIZE_YSZM1_SHIFT) | \ ((xszm1) << MExxBSIZE_XSZM1_SHIFT)) -#define SH_MOBILE_MERAM_ICB_NUM 32 - -static unsigned long common_regs[] = { +static const unsigned long common_regs[] = { MEVCR1, MEQSEL1, MEQSEL2, }; -#define CMN_REGS_SIZE ARRAY_SIZE(common_regs) +#define MERAM_REGS_SIZE ARRAY_SIZE(common_regs) -static unsigned long icb_regs[] = { +static const unsigned long icb_regs[] = { MExxCTL, MExxBSIZE, MExxMNCF, @@ -100,216 +104,269 @@ static unsigned long icb_regs[] = { }; #define ICB_REGS_SIZE ARRAY_SIZE(icb_regs) +/* + * sh_mobile_meram_icb - MERAM ICB information + * @regs: Registers cache + * @index: ICB index + * @offset: MERAM block offset + * @size: MERAM block size in KiB + * @cache_unit: Bytes to cache per ICB + * @pixelformat: Video pixel format of the data stored in the ICB + * @current_reg: Which of Start Address Register A (0) or B (1) is in use + */ +struct sh_mobile_meram_icb { + unsigned long regs[ICB_REGS_SIZE]; + unsigned int index; + unsigned long offset; + unsigned int size; + + unsigned int cache_unit; + unsigned int pixelformat; + unsigned int current_reg; +}; + +#define MERAM_ICB_NUM 32 + +struct sh_mobile_meram_fb_plane { + struct sh_mobile_meram_icb *marker; + struct sh_mobile_meram_icb *cache; +}; + +struct sh_mobile_meram_fb_cache { + unsigned int nplanes; + struct sh_mobile_meram_fb_plane planes[2]; +}; + +/* + * sh_mobile_meram_priv - MERAM device + * @base: Registers base address + * @meram: MERAM physical address + * @regs: Registers cache + * @lock: Protects used_icb and icbs + * @used_icb: Bitmask of used ICBs + * @icbs: ICBs + * @pool: Allocation pool to manage the MERAM + */ struct sh_mobile_meram_priv { - void __iomem *base; - struct mutex lock; - unsigned long used_icb; - int used_meram_cache_regions; - unsigned long used_meram_cache[SH_MOBILE_MERAM_ICB_NUM]; - unsigned long cmn_saved_regs[CMN_REGS_SIZE]; - unsigned long icb_saved_regs[ICB_REGS_SIZE * SH_MOBILE_MERAM_ICB_NUM]; + void __iomem *base; + unsigned long meram; + unsigned long regs[MERAM_REGS_SIZE]; + + struct mutex lock; + unsigned long used_icb; + struct sh_mobile_meram_icb icbs[MERAM_ICB_NUM]; + + struct gen_pool *pool; }; /* settings */ -#define MERAM_SEC_LINE 15 -#define MERAM_LINE_WIDTH 2048 +#define MERAM_GRANULARITY 1024 +#define MERAM_SEC_LINE 15 +#define MERAM_LINE_WIDTH 2048 -/* - * MERAM/ICB access functions +/* ----------------------------------------------------------------------------- + * Registers access */ #define MERAM_ICB_OFFSET(base, idx, off) ((base) + (off) + (idx) * 0x20) -static inline void meram_write_icb(void __iomem *base, int idx, int off, - unsigned long val) +static inline void meram_write_icb(void __iomem *base, unsigned int idx, + unsigned int off, unsigned long val) { iowrite32(val, MERAM_ICB_OFFSET(base, idx, off)); } -static inline unsigned long meram_read_icb(void __iomem *base, int idx, int off) +static inline unsigned long meram_read_icb(void __iomem *base, unsigned int idx, + unsigned int off) { return ioread32(MERAM_ICB_OFFSET(base, idx, off)); } -static inline void meram_write_reg(void __iomem *base, int off, - unsigned long val) +static inline void meram_write_reg(void __iomem *base, unsigned int off, + unsigned long val) { iowrite32(val, base + off); } -static inline unsigned long meram_read_reg(void __iomem *base, int off) +static inline unsigned long meram_read_reg(void __iomem *base, unsigned int off) { return ioread32(base + off); } -/* - * register ICB - */ - -#define MERAM_CACHE_START(p) ((p) >> 16) -#define MERAM_CACHE_END(p) ((p) & 0xffff) -#define MERAM_CACHE_SET(o, s) ((((o) & 0xffff) << 16) | \ - (((o) + (s) - 1) & 0xffff)) - -/* - * check if there's no overlaps in MERAM allocation. +/* ----------------------------------------------------------------------------- + * Allocation */ -static inline int meram_check_overlap(struct sh_mobile_meram_priv *priv, - struct sh_mobile_meram_icb *new) +/* Allocate ICBs and MERAM for a plane. */ +static int __meram_alloc(struct sh_mobile_meram_priv *priv, + struct sh_mobile_meram_fb_plane *plane, + size_t size) { - int i; - int used_start, used_end, meram_start, meram_end; + unsigned long mem; + unsigned long idx; - /* valid ICB? */ - if (new->marker_icb & ~0x1f || new->cache_icb & ~0x1f) - return 1; + idx = find_first_zero_bit(&priv->used_icb, 28); + if (idx == 28) + return -ENOMEM; + plane->cache = &priv->icbs[idx]; - if (test_bit(new->marker_icb, &priv->used_icb) || - test_bit(new->cache_icb, &priv->used_icb)) - return 1; + idx = find_next_zero_bit(&priv->used_icb, 32, 28); + if (idx == 32) + return -ENOMEM; + plane->marker = &priv->icbs[idx]; - for (i = 0; i < priv->used_meram_cache_regions; i++) { - used_start = MERAM_CACHE_START(priv->used_meram_cache[i]); - used_end = MERAM_CACHE_END(priv->used_meram_cache[i]); - meram_start = new->meram_offset; - meram_end = new->meram_offset + new->meram_size; + mem = gen_pool_alloc(priv->pool, size * 1024); + if (mem == 0) + return -ENOMEM; - if ((meram_start >= used_start && meram_start < used_end) || - (meram_end > used_start && meram_end < used_end)) - return 1; - } + __set_bit(plane->marker->index, &priv->used_icb); + __set_bit(plane->cache->index, &priv->used_icb); + + plane->marker->offset = mem - priv->meram; + plane->marker->size = size; return 0; } -/* - * mark the specified ICB as used - */ +/* Free ICBs and MERAM for a plane. */ +static void __meram_free(struct sh_mobile_meram_priv *priv, + struct sh_mobile_meram_fb_plane *plane) +{ + gen_pool_free(priv->pool, priv->meram + plane->marker->offset, + plane->marker->size * 1024); -static inline void meram_mark(struct sh_mobile_meram_priv *priv, - struct sh_mobile_meram_icb *new) + __clear_bit(plane->marker->index, &priv->used_icb); + __clear_bit(plane->cache->index, &priv->used_icb); +} + +/* Is this a YCbCr(NV12, NV16 or NV24) colorspace? */ +static int is_nvcolor(int cspace) { - int n; + if (cspace == SH_MOBILE_MERAM_PF_NV || + cspace == SH_MOBILE_MERAM_PF_NV24) + return 1; + return 0; +} - if (new->marker_icb < 0 || new->cache_icb < 0) - return; +/* Allocate memory for the ICBs and mark them as used. */ +static struct sh_mobile_meram_fb_cache * +meram_alloc(struct sh_mobile_meram_priv *priv, + const struct sh_mobile_meram_cfg *cfg, + int pixelformat) +{ + struct sh_mobile_meram_fb_cache *cache; + unsigned int nplanes = is_nvcolor(pixelformat) ? 2 : 1; + int ret; - __set_bit(new->marker_icb, &priv->used_icb); - __set_bit(new->cache_icb, &priv->used_icb); + if (cfg->icb[0].meram_size == 0) + return ERR_PTR(-EINVAL); - n = priv->used_meram_cache_regions; + if (nplanes == 2 && cfg->icb[1].meram_size == 0) + return ERR_PTR(-EINVAL); - priv->used_meram_cache[n] = MERAM_CACHE_SET(new->meram_offset, - new->meram_size); + cache = kzalloc(sizeof(*cache), GFP_KERNEL); + if (cache == NULL) + return ERR_PTR(-ENOMEM); - priv->used_meram_cache_regions++; -} + cache->nplanes = nplanes; -/* - * unmark the specified ICB as used - */ + ret = __meram_alloc(priv, &cache->planes[0], cfg->icb[0].meram_size); + if (ret < 0) + goto error; -static inline void meram_unmark(struct sh_mobile_meram_priv *priv, - struct sh_mobile_meram_icb *icb) -{ - int i; - unsigned long pattern; - - if (icb->marker_icb < 0 || icb->cache_icb < 0) - return; - - __clear_bit(icb->marker_icb, &priv->used_icb); - __clear_bit(icb->cache_icb, &priv->used_icb); - - pattern = MERAM_CACHE_SET(icb->meram_offset, icb->meram_size); - for (i = 0; i < priv->used_meram_cache_regions; i++) { - if (priv->used_meram_cache[i] == pattern) { - while (i < priv->used_meram_cache_regions - 1) { - priv->used_meram_cache[i] = - priv->used_meram_cache[i + 1] ; - i++; - } - priv->used_meram_cache[i] = 0; - priv->used_meram_cache_regions--; - break; - } + cache->planes[0].marker->current_reg = 1; + cache->planes[0].marker->pixelformat = pixelformat; + + if (cache->nplanes == 1) + return cache; + + ret = __meram_alloc(priv, &cache->planes[1], cfg->icb[1].meram_size); + if (ret < 0) { + __meram_free(priv, &cache->planes[0]); + goto error; } + + return cache; + +error: + kfree(cache); + return ERR_PTR(-ENOMEM); } -/* - * is this a YCbCr(NV12, NV16 or NV24) colorspace - */ -static inline int is_nvcolor(int cspace) +/* Unmark the specified ICB as used. */ +static void meram_free(struct sh_mobile_meram_priv *priv, + struct sh_mobile_meram_fb_cache *cache) { - if (cspace == SH_MOBILE_MERAM_PF_NV || - cspace == SH_MOBILE_MERAM_PF_NV24) - return 1; - return 0; + __meram_free(priv, &cache->planes[0]); + if (cache->nplanes == 2) + __meram_free(priv, &cache->planes[1]); + + kfree(cache); } -/* - * set the next address to fetch - */ -static inline void meram_set_next_addr(struct sh_mobile_meram_priv *priv, - struct sh_mobile_meram_cfg *cfg, - unsigned long base_addr_y, - unsigned long base_addr_c) +/* Set the next address to fetch. */ +static void meram_set_next_addr(struct sh_mobile_meram_priv *priv, + struct sh_mobile_meram_fb_cache *cache, + unsigned long base_addr_y, + unsigned long base_addr_c) { + struct sh_mobile_meram_icb *icb = cache->planes[0].marker; unsigned long target; - target = (cfg->current_reg) ? MExxSARA : MExxSARB; - cfg->current_reg ^= 1; + icb->current_reg ^= 1; + target = icb->current_reg ? MExxSARB : MExxSARA; /* set the next address to fetch */ - meram_write_icb(priv->base, cfg->icb[0].cache_icb, target, + meram_write_icb(priv->base, cache->planes[0].cache->index, target, base_addr_y); - meram_write_icb(priv->base, cfg->icb[0].marker_icb, target, - base_addr_y + cfg->icb[0].cache_unit); - - if (is_nvcolor(cfg->pixelformat)) { - meram_write_icb(priv->base, cfg->icb[1].cache_icb, target, - base_addr_c); - meram_write_icb(priv->base, cfg->icb[1].marker_icb, target, - base_addr_c + cfg->icb[1].cache_unit); + meram_write_icb(priv->base, cache->planes[0].marker->index, target, + base_addr_y + cache->planes[0].marker->cache_unit); + + if (cache->nplanes == 2) { + meram_write_icb(priv->base, cache->planes[1].cache->index, + target, base_addr_c); + meram_write_icb(priv->base, cache->planes[1].marker->index, + target, base_addr_c + + cache->planes[1].marker->cache_unit); } } -/* - * get the next ICB address - */ -static inline void meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata, - struct sh_mobile_meram_cfg *cfg, - unsigned long *icb_addr_y, - unsigned long *icb_addr_c) +/* Get the next ICB address. */ +static void +meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata, + struct sh_mobile_meram_fb_cache *cache, + unsigned long *icb_addr_y, unsigned long *icb_addr_c) { + struct sh_mobile_meram_icb *icb = cache->planes[0].marker; unsigned long icb_offset; if (pdata->addr_mode == SH_MOBILE_MERAM_MODE0) - icb_offset = 0x80000000 | (cfg->current_reg << 29); + icb_offset = 0x80000000 | (icb->current_reg << 29); else - icb_offset = 0xc0000000 | (cfg->current_reg << 23); + icb_offset = 0xc0000000 | (icb->current_reg << 23); - *icb_addr_y = icb_offset | (cfg->icb[0].marker_icb << 24); - if (is_nvcolor(cfg->pixelformat)) - *icb_addr_c = icb_offset | (cfg->icb[1].marker_icb << 24); + *icb_addr_y = icb_offset | (cache->planes[0].marker->index << 24); + if (cache->nplanes == 2) + *icb_addr_c = icb_offset + | (cache->planes[1].marker->index << 24); } #define MERAM_CALC_BYTECOUNT(x, y) \ (((x) * (y) + (MERAM_LINE_WIDTH - 1)) & ~(MERAM_LINE_WIDTH - 1)) -/* - * initialize MERAM - */ - +/* Initialize MERAM. */ static int meram_init(struct sh_mobile_meram_priv *priv, - struct sh_mobile_meram_icb *icb, - int xres, int yres, int *out_pitch) + struct sh_mobile_meram_fb_plane *plane, + unsigned int xres, unsigned int yres, + unsigned int *out_pitch) { + struct sh_mobile_meram_icb *marker = plane->marker; unsigned long total_byte_count = MERAM_CALC_BYTECOUNT(xres, yres); unsigned long bnm; - int lcdc_pitch, xpitch, line_cnt; - int save_lines; + unsigned int lcdc_pitch; + unsigned int xpitch; + unsigned int line_cnt; + unsigned int save_lines; /* adjust pitch to 1024, 2048, 4096 or 8192 */ lcdc_pitch = (xres - 1) | 1023; @@ -322,13 +379,13 @@ static int meram_init(struct sh_mobile_meram_priv *priv, lcdc_pitch = xpitch = MERAM_LINE_WIDTH; line_cnt = total_byte_count >> 11; *out_pitch = xres; - save_lines = (icb->meram_size / 16 / MERAM_SEC_LINE); + save_lines = plane->marker->size / 16 / MERAM_SEC_LINE; save_lines *= MERAM_SEC_LINE; } else { xpitch = xres; line_cnt = yres; *out_pitch = lcdc_pitch; - save_lines = icb->meram_size / (lcdc_pitch >> 10) / 2; + save_lines = plane->marker->size / (lcdc_pitch >> 10) / 2; save_lines &= 0xff; } bnm = (save_lines - 1) << 16; @@ -336,19 +393,20 @@ static int meram_init(struct sh_mobile_meram_priv *priv, /* TODO: we better to check if we have enough MERAM buffer size */ /* set up ICB */ - meram_write_icb(priv->base, icb->cache_icb, MExxBSIZE, + meram_write_icb(priv->base, plane->cache->index, MExxBSIZE, MERAM_MExxBSIZE_VAL(0x0, line_cnt - 1, xpitch - 1)); - meram_write_icb(priv->base, icb->marker_icb, MExxBSIZE, + meram_write_icb(priv->base, plane->marker->index, MExxBSIZE, MERAM_MExxBSIZE_VAL(0xf, line_cnt - 1, xpitch - 1)); - meram_write_icb(priv->base, icb->cache_icb, MExxMNCF, bnm); - meram_write_icb(priv->base, icb->marker_icb, MExxMNCF, bnm); + meram_write_icb(priv->base, plane->cache->index, MExxMNCF, bnm); + meram_write_icb(priv->base, plane->marker->index, MExxMNCF, bnm); - meram_write_icb(priv->base, icb->cache_icb, MExxSBSIZE, xpitch); - meram_write_icb(priv->base, icb->marker_icb, MExxSBSIZE, xpitch); + meram_write_icb(priv->base, plane->cache->index, MExxSBSIZE, xpitch); + meram_write_icb(priv->base, plane->marker->index, MExxSBSIZE, xpitch); /* save a cache unit size */ - icb->cache_unit = xres * save_lines; + plane->cache->cache_unit = xres * save_lines; + plane->marker->cache_unit = xres * save_lines; /* * Set MERAM for framebuffer @@ -356,13 +414,13 @@ static int meram_init(struct sh_mobile_meram_priv *priv, * we also chain the cache_icb and the marker_icb. * we also split the allocated MERAM buffer between two ICBs. */ - meram_write_icb(priv->base, icb->cache_icb, MExxCTL, - MERAM_MExxCTL_VAL(icb->marker_icb, icb->meram_offset) | - MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM | + meram_write_icb(priv->base, plane->cache->index, MExxCTL, + MERAM_MExxCTL_VAL(plane->marker->index, marker->offset) + | MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM | MExxCTL_MD_FB); - meram_write_icb(priv->base, icb->marker_icb, MExxCTL, - MERAM_MExxCTL_VAL(icb->cache_icb, icb->meram_offset + - icb->meram_size / 2) | + meram_write_icb(priv->base, plane->marker->index, MExxCTL, + MERAM_MExxCTL_VAL(plane->cache->index, marker->offset + + plane->marker->size / 2) | MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM | MExxCTL_MD_FB); @@ -370,239 +428,175 @@ static int meram_init(struct sh_mobile_meram_priv *priv, } static void meram_deinit(struct sh_mobile_meram_priv *priv, - struct sh_mobile_meram_icb *icb) + struct sh_mobile_meram_fb_plane *plane) { /* disable ICB */ - meram_write_icb(priv->base, icb->cache_icb, MExxCTL, + meram_write_icb(priv->base, plane->cache->index, MExxCTL, MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF); - meram_write_icb(priv->base, icb->marker_icb, MExxCTL, + meram_write_icb(priv->base, plane->marker->index, MExxCTL, MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF); - icb->cache_unit = 0; + + plane->cache->cache_unit = 0; + plane->marker->cache_unit = 0; } -/* - * register the ICB +/* ----------------------------------------------------------------------------- + * Registration/unregistration */ -static int sh_mobile_meram_register(struct sh_mobile_meram_info *pdata, - struct sh_mobile_meram_cfg *cfg, - int xres, int yres, int pixelformat, - unsigned long base_addr_y, - unsigned long base_addr_c, - unsigned long *icb_addr_y, - unsigned long *icb_addr_c, - int *pitch) +static void *sh_mobile_meram_register(struct sh_mobile_meram_info *pdata, + const struct sh_mobile_meram_cfg *cfg, + unsigned int xres, unsigned int yres, + unsigned int pixelformat, + unsigned int *pitch) { - struct platform_device *pdev; - struct sh_mobile_meram_priv *priv; - int n, out_pitch; - int error = 0; - - if (!pdata || !pdata->priv || !pdata->pdev || !cfg) - return -EINVAL; + struct sh_mobile_meram_fb_cache *cache; + struct sh_mobile_meram_priv *priv = pdata->priv; + struct platform_device *pdev = pdata->pdev; + unsigned int out_pitch; if (pixelformat != SH_MOBILE_MERAM_PF_NV && pixelformat != SH_MOBILE_MERAM_PF_NV24 && pixelformat != SH_MOBILE_MERAM_PF_RGB) - return -EINVAL; - - priv = pdata->priv; - pdev = pdata->pdev; + return ERR_PTR(-EINVAL); - dev_dbg(&pdev->dev, "registering %dx%d (%s) (y=%08lx, c=%08lx)", - xres, yres, (!pixelformat) ? "yuv" : "rgb", - base_addr_y, base_addr_c); + dev_dbg(&pdev->dev, "registering %dx%d (%s)", xres, yres, + !pixelformat ? "yuv" : "rgb"); /* we can't handle wider than 8192px */ if (xres > 8192) { dev_err(&pdev->dev, "width exceeding the limit (> 8192)."); - return -EINVAL; - } - - /* do we have at least one ICB config? */ - if (cfg->icb[0].marker_icb < 0 || cfg->icb[0].cache_icb < 0) { - dev_err(&pdev->dev, "at least one ICB is required."); - return -EINVAL; + return ERR_PTR(-EINVAL); } mutex_lock(&priv->lock); - if (priv->used_meram_cache_regions + 2 > SH_MOBILE_MERAM_ICB_NUM) { - dev_err(&pdev->dev, "no more ICB available."); - error = -EINVAL; - goto err; - } - - /* make sure that there's no overlaps */ - if (meram_check_overlap(priv, &cfg->icb[0])) { - dev_err(&pdev->dev, "conflicting config detected."); - error = -EINVAL; + /* We now register the ICBs and allocate the MERAM regions. */ + cache = meram_alloc(priv, cfg, pixelformat); + if (IS_ERR(cache)) { + dev_err(&pdev->dev, "MERAM allocation failed (%ld).", + PTR_ERR(cache)); goto err; } - n = 1; - - /* do the same if we have the second ICB set */ - if (cfg->icb[1].marker_icb >= 0 && cfg->icb[1].cache_icb >= 0) { - if (meram_check_overlap(priv, &cfg->icb[1])) { - dev_err(&pdev->dev, "conflicting config detected."); - error = -EINVAL; - goto err; - } - n = 2; - } - - if (is_nvcolor(pixelformat) && n != 2) { - dev_err(&pdev->dev, "requires two ICB sets for planar Y/C."); - error = -EINVAL; - goto err; - } - - /* we now register the ICB */ - cfg->pixelformat = pixelformat; - meram_mark(priv, &cfg->icb[0]); - if (is_nvcolor(pixelformat)) - meram_mark(priv, &cfg->icb[1]); /* initialize MERAM */ - meram_init(priv, &cfg->icb[0], xres, yres, &out_pitch); + meram_init(priv, &cache->planes[0], xres, yres, &out_pitch); *pitch = out_pitch; if (pixelformat == SH_MOBILE_MERAM_PF_NV) - meram_init(priv, &cfg->icb[1], xres, (yres + 1) / 2, + meram_init(priv, &cache->planes[1], xres, (yres + 1) / 2, &out_pitch); else if (pixelformat == SH_MOBILE_MERAM_PF_NV24) - meram_init(priv, &cfg->icb[1], 2 * xres, (yres + 1) / 2, + meram_init(priv, &cache->planes[1], 2 * xres, (yres + 1) / 2, &out_pitch); - cfg->current_reg = 1; - meram_set_next_addr(priv, cfg, base_addr_y, base_addr_c); - meram_get_next_icb_addr(pdata, cfg, icb_addr_y, icb_addr_c); - - dev_dbg(&pdev->dev, "registered - can access via y=%08lx, c=%08lx", - *icb_addr_y, *icb_addr_c); - err: mutex_unlock(&priv->lock); - return error; + return cache; } -static int sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata, - struct sh_mobile_meram_cfg *cfg) +static void +sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata, void *data) { - struct sh_mobile_meram_priv *priv; - - if (!pdata || !pdata->priv || !cfg) - return -EINVAL; - - priv = pdata->priv; + struct sh_mobile_meram_fb_cache *cache = data; + struct sh_mobile_meram_priv *priv = pdata->priv; mutex_lock(&priv->lock); - /* deinit & unmark */ - if (is_nvcolor(cfg->pixelformat)) { - meram_deinit(priv, &cfg->icb[1]); - meram_unmark(priv, &cfg->icb[1]); - } - meram_deinit(priv, &cfg->icb[0]); - meram_unmark(priv, &cfg->icb[0]); + /* deinit & free */ + meram_deinit(priv, &cache->planes[0]); + if (cache->nplanes == 2) + meram_deinit(priv, &cache->planes[1]); - mutex_unlock(&priv->lock); + meram_free(priv, cache); - return 0; + mutex_unlock(&priv->lock); } -static int sh_mobile_meram_update(struct sh_mobile_meram_info *pdata, - struct sh_mobile_meram_cfg *cfg, - unsigned long base_addr_y, - unsigned long base_addr_c, - unsigned long *icb_addr_y, - unsigned long *icb_addr_c) +static void +sh_mobile_meram_update(struct sh_mobile_meram_info *pdata, void *data, + unsigned long base_addr_y, unsigned long base_addr_c, + unsigned long *icb_addr_y, unsigned long *icb_addr_c) { - struct sh_mobile_meram_priv *priv; - - if (!pdata || !pdata->priv || !cfg) - return -EINVAL; - - priv = pdata->priv; + struct sh_mobile_meram_fb_cache *cache = data; + struct sh_mobile_meram_priv *priv = pdata->priv; mutex_lock(&priv->lock); - meram_set_next_addr(priv, cfg, base_addr_y, base_addr_c); - meram_get_next_icb_addr(pdata, cfg, icb_addr_y, icb_addr_c); + meram_set_next_addr(priv, cache, base_addr_y, base_addr_c); + meram_get_next_icb_addr(pdata, cache, icb_addr_y, icb_addr_c); mutex_unlock(&priv->lock); - - return 0; } -static int sh_mobile_meram_runtime_suspend(struct device *dev) +static struct sh_mobile_meram_ops sh_mobile_meram_ops = { + .module = THIS_MODULE, + .meram_register = sh_mobile_meram_register, + .meram_unregister = sh_mobile_meram_unregister, + .meram_update = sh_mobile_meram_update, +}; + +/* ----------------------------------------------------------------------------- + * Power management + */ + +static int sh_mobile_meram_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev); - int k, j; + unsigned int i, j; - for (k = 0; k < CMN_REGS_SIZE; k++) - priv->cmn_saved_regs[k] = meram_read_reg(priv->base, - common_regs[k]); + for (i = 0; i < MERAM_REGS_SIZE; i++) + priv->regs[i] = meram_read_reg(priv->base, common_regs[i]); - for (j = 0; j < 32; j++) { - if (!test_bit(j, &priv->used_icb)) + for (i = 0; i < 32; i++) { + if (!test_bit(i, &priv->used_icb)) continue; - for (k = 0; k < ICB_REGS_SIZE; k++) { - priv->icb_saved_regs[j * ICB_REGS_SIZE + k] = - meram_read_icb(priv->base, j, icb_regs[k]); + for (j = 0; j < ICB_REGS_SIZE; j++) { + priv->icbs[i].regs[j] = + meram_read_icb(priv->base, i, icb_regs[j]); /* Reset ICB on resume */ - if (icb_regs[k] == MExxCTL) - priv->icb_saved_regs[j * ICB_REGS_SIZE + k] |= + if (icb_regs[j] == MExxCTL) + priv->icbs[i].regs[j] |= MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF; } } return 0; } -static int sh_mobile_meram_runtime_resume(struct device *dev) +static int sh_mobile_meram_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev); - int k, j; + unsigned int i, j; - for (j = 0; j < 32; j++) { - if (!test_bit(j, &priv->used_icb)) + for (i = 0; i < 32; i++) { + if (!test_bit(i, &priv->used_icb)) continue; - for (k = 0; k < ICB_REGS_SIZE; k++) { - meram_write_icb(priv->base, j, icb_regs[k], - priv->icb_saved_regs[j * ICB_REGS_SIZE + k]); - } + for (j = 0; j < ICB_REGS_SIZE; j++) + meram_write_icb(priv->base, i, icb_regs[j], + priv->icbs[i].regs[j]); } - for (k = 0; k < CMN_REGS_SIZE; k++) - meram_write_reg(priv->base, common_regs[k], - priv->cmn_saved_regs[k]); + for (i = 0; i < MERAM_REGS_SIZE; i++) + meram_write_reg(priv->base, common_regs[i], priv->regs[i]); return 0; } -static const struct dev_pm_ops sh_mobile_meram_dev_pm_ops = { - .runtime_suspend = sh_mobile_meram_runtime_suspend, - .runtime_resume = sh_mobile_meram_runtime_resume, -}; - -static struct sh_mobile_meram_ops sh_mobile_meram_ops = { - .module = THIS_MODULE, - .meram_register = sh_mobile_meram_register, - .meram_unregister = sh_mobile_meram_unregister, - .meram_update = sh_mobile_meram_update, -}; +static UNIVERSAL_DEV_PM_OPS(sh_mobile_meram_dev_pm_ops, + sh_mobile_meram_suspend, + sh_mobile_meram_resume, NULL); -/* - * initialize MERAM +/* ----------------------------------------------------------------------------- + * Probe/remove and driver init/exit */ -static int sh_mobile_meram_remove(struct platform_device *pdev); - static int __devinit sh_mobile_meram_probe(struct platform_device *pdev) { struct sh_mobile_meram_priv *priv; struct sh_mobile_meram_info *pdata = pdev->dev.platform_data; - struct resource *res; + struct resource *regs; + struct resource *meram; + unsigned int i; int error; if (!pdata) { @@ -610,8 +604,9 @@ static int __devinit sh_mobile_meram_probe(struct platform_device *pdev) return -EINVAL; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + meram = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (regs == NULL || meram == NULL) { dev_err(&pdev->dev, "cannot get platform resources\n"); return -ENOENT; } @@ -622,32 +617,74 @@ static int __devinit sh_mobile_meram_probe(struct platform_device *pdev) return -ENOMEM; } - platform_set_drvdata(pdev, priv); - - /* initialize private data */ + /* Initialize private data. */ mutex_init(&priv->lock); - priv->base = ioremap_nocache(res->start, resource_size(res)); + priv->used_icb = pdata->reserved_icbs; + + for (i = 0; i < MERAM_ICB_NUM; ++i) + priv->icbs[i].index = i; + + pdata->ops = &sh_mobile_meram_ops; + pdata->priv = priv; + pdata->pdev = pdev; + + /* Request memory regions and remap the registers. */ + if (!request_mem_region(regs->start, resource_size(regs), pdev->name)) { + dev_err(&pdev->dev, "MERAM registers region already claimed\n"); + error = -EBUSY; + goto err_req_regs; + } + + if (!request_mem_region(meram->start, resource_size(meram), + pdev->name)) { + dev_err(&pdev->dev, "MERAM memory region already claimed\n"); + error = -EBUSY; + goto err_req_meram; + } + + priv->base = ioremap_nocache(regs->start, resource_size(regs)); if (!priv->base) { dev_err(&pdev->dev, "ioremap failed\n"); error = -EFAULT; - goto err; + goto err_ioremap; } - pdata->ops = &sh_mobile_meram_ops; - pdata->priv = priv; - pdata->pdev = pdev; + + priv->meram = meram->start; + + /* Create and initialize the MERAM memory pool. */ + priv->pool = gen_pool_create(ilog2(MERAM_GRANULARITY), -1); + if (priv->pool == NULL) { + error = -ENOMEM; + goto err_genpool; + } + + error = gen_pool_add(priv->pool, meram->start, resource_size(meram), + -1); + if (error < 0) + goto err_genpool; /* initialize ICB addressing mode */ if (pdata->addr_mode == SH_MOBILE_MERAM_MODE1) meram_write_reg(priv->base, MEVCR1, MEVCR1_AMD1); + platform_set_drvdata(pdev, priv); pm_runtime_enable(&pdev->dev); dev_info(&pdev->dev, "sh_mobile_meram initialized."); return 0; -err: - sh_mobile_meram_remove(pdev); +err_genpool: + if (priv->pool) + gen_pool_destroy(priv->pool); + iounmap(priv->base); +err_ioremap: + release_mem_region(meram->start, resource_size(meram)); +err_req_meram: + release_mem_region(regs->start, resource_size(regs)); +err_req_regs: + mutex_destroy(&priv->lock); + kfree(priv); return error; } @@ -656,11 +693,16 @@ err: static int sh_mobile_meram_remove(struct platform_device *pdev) { struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev); + struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct resource *meram = platform_get_resource(pdev, IORESOURCE_MEM, 1); pm_runtime_disable(&pdev->dev); - if (priv->base) - iounmap(priv->base); + gen_pool_destroy(priv->pool); + + iounmap(priv->base); + release_mem_region(meram->start, resource_size(meram)); + release_mem_region(regs->start, resource_size(regs)); mutex_destroy(&priv->lock); diff --git a/drivers/video/udlfb.c b/drivers/video/udlfb.c index a19773149bd7..a159b63e18b9 100644 --- a/drivers/video/udlfb.c +++ b/drivers/video/udlfb.c @@ -72,6 +72,7 @@ MODULE_DEVICE_TABLE(usb, id_table); static bool console = 1; /* Allow fbcon to open framebuffer */ static bool fb_defio = 1; /* Detect mmap writes using page faults */ static bool shadow = 1; /* Optionally disable shadow framebuffer */ +static int pixel_limit; /* Optionally force a pixel resolution limit */ /* dlfb keeps a list of urbs for efficient bulk transfers */ static void dlfb_urb_completion(struct urb *urb); @@ -918,10 +919,6 @@ static void dlfb_free(struct kref *kref) { struct dlfb_data *dev = container_of(kref, struct dlfb_data, kref); - /* this function will wait for all in-flight urbs to complete */ - if (dev->urbs.count > 0) - dlfb_free_urb_list(dev); - if (dev->backing_buffer) vfree(dev->backing_buffer); @@ -940,35 +937,42 @@ static void dlfb_release_urb_work(struct work_struct *work) up(&unode->dev->urbs.limit_sem); } -static void dlfb_free_framebuffer_work(struct work_struct *work) +static void dlfb_free_framebuffer(struct dlfb_data *dev) { - struct dlfb_data *dev = container_of(work, struct dlfb_data, - free_framebuffer_work.work); struct fb_info *info = dev->info; - int node = info->node; - unregister_framebuffer(info); + if (info) { + int node = info->node; - if (info->cmap.len != 0) - fb_dealloc_cmap(&info->cmap); - if (info->monspecs.modedb) - fb_destroy_modedb(info->monspecs.modedb); - if (info->screen_base) - vfree(info->screen_base); + unregister_framebuffer(info); - fb_destroy_modelist(&info->modelist); + if (info->cmap.len != 0) + fb_dealloc_cmap(&info->cmap); + if (info->monspecs.modedb) + fb_destroy_modedb(info->monspecs.modedb); + if (info->screen_base) + vfree(info->screen_base); - dev->info = 0; + fb_destroy_modelist(&info->modelist); - /* Assume info structure is freed after this point */ - framebuffer_release(info); + dev->info = NULL; - pr_warn("fb_info for /dev/fb%d has been freed\n", node); + /* Assume info structure is freed after this point */ + framebuffer_release(info); + + pr_warn("fb_info for /dev/fb%d has been freed\n", node); + } /* ref taken in probe() as part of registering framebfufer */ kref_put(&dev->kref, dlfb_free); } +static void dlfb_free_framebuffer_work(struct work_struct *work) +{ + struct dlfb_data *dev = container_of(work, struct dlfb_data, + free_framebuffer_work.work); + dlfb_free_framebuffer(dev); +} /* * Assumes caller is holding info->lock mutex (for open and release at least) */ @@ -1012,7 +1016,8 @@ static int dlfb_is_valid_mode(struct fb_videomode *mode, return 0; } - pr_info("%dx%d valid mode\n", mode->xres, mode->yres); + pr_info("%dx%d @ %d Hz valid mode\n", mode->xres, mode->yres, + mode->refresh); return 1; } @@ -1427,19 +1432,22 @@ static ssize_t edid_store( struct device *fbdev = container_of(kobj, struct device, kobj); struct fb_info *fb_info = dev_get_drvdata(fbdev); struct dlfb_data *dev = fb_info->par; + int ret; /* We only support write of entire EDID at once, no offset*/ if ((src_size != EDID_LENGTH) || (src_off != 0)) - return 0; + return -EINVAL; - dlfb_setup_modes(dev, fb_info, src, src_size); + ret = dlfb_setup_modes(dev, fb_info, src, src_size); + if (ret) + return ret; - if (dev->edid && (memcmp(src, dev->edid, src_size) == 0)) { - pr_info("sysfs written EDID is new default\n"); - dlfb_ops_set_par(fb_info); - return src_size; - } else - return 0; + if (!dev->edid || memcmp(src, dev->edid, src_size)) + return -EINVAL; + + pr_info("sysfs written EDID is new default\n"); + dlfb_ops_set_par(fb_info); + return src_size; } static ssize_t metrics_reset_store(struct device *fbdev, @@ -1537,7 +1545,7 @@ static int dlfb_parse_vendor_descriptor(struct dlfb_data *dev, u8 length; u16 key; - key = *((u16 *) desc); + key = le16_to_cpu(*((u16 *) desc)); desc += sizeof(u16); length = *desc; desc++; @@ -1570,14 +1578,15 @@ success: kfree(buf); return true; } + +static void dlfb_init_framebuffer_work(struct work_struct *work); + static int dlfb_usb_probe(struct usb_interface *interface, const struct usb_device_id *id) { struct usb_device *usbdev; struct dlfb_data *dev = 0; - struct fb_info *info = 0; int retval = -ENOMEM; - int i; /* usb initialization */ @@ -1589,9 +1598,7 @@ static int dlfb_usb_probe(struct usb_interface *interface, goto error; } - /* we need to wait for both usb and fbdev to spin down on disconnect */ kref_init(&dev->kref); /* matching kref_put in usb .disconnect fn */ - kref_get(&dev->kref); /* matching kref_put in free_framebuffer_work */ dev->udev = usbdev; dev->gdev = &usbdev->dev; /* our generic struct device * */ @@ -1613,16 +1620,53 @@ static int dlfb_usb_probe(struct usb_interface *interface, goto error; } + if (pixel_limit) { + pr_warn("DL chip limit of %d overriden" + " by module param to %d\n", + dev->sku_pixel_limit, pixel_limit); + dev->sku_pixel_limit = pixel_limit; + } + + if (!dlfb_alloc_urb_list(dev, WRITES_IN_FLIGHT, MAX_TRANSFER)) { retval = -ENOMEM; pr_err("dlfb_alloc_urb_list failed\n"); goto error; } + kref_get(&dev->kref); /* matching kref_put in free_framebuffer_work */ + /* We don't register a new USB class. Our client interface is fbdev */ + /* Workitem keep things fast & simple during USB enumeration */ + INIT_DELAYED_WORK(&dev->init_framebuffer_work, + dlfb_init_framebuffer_work); + schedule_delayed_work(&dev->init_framebuffer_work, 0); + + return 0; + +error: + if (dev) { + + kref_put(&dev->kref, dlfb_free); /* ref for framebuffer */ + kref_put(&dev->kref, dlfb_free); /* last ref from kref_init */ + + /* dev has been deallocated. Do not dereference */ + } + + return retval; +} + +static void dlfb_init_framebuffer_work(struct work_struct *work) +{ + struct dlfb_data *dev = container_of(work, struct dlfb_data, + init_framebuffer_work.work); + struct fb_info *info; + int retval; + int i; + /* allocates framebuffer driver structure, not framebuffer memory */ - info = framebuffer_alloc(0, &interface->dev); + info = framebuffer_alloc(0, dev->gdev); if (!info) { retval = -ENOMEM; pr_err("framebuffer_alloc failed\n"); @@ -1668,15 +1712,13 @@ static int dlfb_usb_probe(struct usb_interface *interface, for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) { retval = device_create_file(info->dev, &fb_device_attrs[i]); if (retval) { - pr_err("device_create_file failed %d\n", retval); - goto err_del_attrs; + pr_warn("device_create_file failed %d\n", retval); } } retval = device_create_bin_file(info->dev, &edid_attr); if (retval) { - pr_err("device_create_bin_file failed %d\n", retval); - goto err_del_attrs; + pr_warn("device_create_bin_file failed %d\n", retval); } pr_info("DisplayLink USB device /dev/fb%d attached. %dx%d resolution." @@ -1684,38 +1726,10 @@ static int dlfb_usb_probe(struct usb_interface *interface, info->var.xres, info->var.yres, ((dev->backing_buffer) ? info->fix.smem_len * 2 : info->fix.smem_len) >> 10); - return 0; - -err_del_attrs: - for (i -= 1; i >= 0; i--) - device_remove_file(info->dev, &fb_device_attrs[i]); + return; error: - if (dev) { - - if (info) { - if (info->cmap.len != 0) - fb_dealloc_cmap(&info->cmap); - if (info->monspecs.modedb) - fb_destroy_modedb(info->monspecs.modedb); - if (info->screen_base) - vfree(info->screen_base); - - fb_destroy_modelist(&info->modelist); - - framebuffer_release(info); - } - - if (dev->backing_buffer) - vfree(dev->backing_buffer); - - kref_put(&dev->kref, dlfb_free); /* ref for framebuffer */ - kref_put(&dev->kref, dlfb_free); /* last ref from kref_init */ - - /* dev has been deallocated. Do not dereference */ - } - - return retval; + dlfb_free_framebuffer(dev); } static void dlfb_usb_disconnect(struct usb_interface *interface) @@ -1735,12 +1749,20 @@ static void dlfb_usb_disconnect(struct usb_interface *interface) /* When non-active we'll update virtual framebuffer, but no new urbs */ atomic_set(&dev->usb_active, 0); - /* remove udlfb's sysfs interfaces */ - for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) - device_remove_file(info->dev, &fb_device_attrs[i]); - device_remove_bin_file(info->dev, &edid_attr); + /* this function will wait for all in-flight urbs to complete */ + dlfb_free_urb_list(dev); + + if (info) { + /* remove udlfb's sysfs interfaces */ + for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) + device_remove_file(info->dev, &fb_device_attrs[i]); + device_remove_bin_file(info->dev, &edid_attr); + unlink_framebuffer(info); + } usb_set_intfdata(interface, NULL); + dev->udev = NULL; + dev->gdev = NULL; /* if clients still have us open, will be freed on last close */ if (dev->fb_count == 0) @@ -1806,12 +1828,12 @@ static void dlfb_free_urb_list(struct dlfb_data *dev) int ret; unsigned long flags; - pr_notice("Waiting for completes and freeing all render urbs\n"); + pr_notice("Freeing all render urbs\n"); /* keep waiting and freeing, until we've got 'em all */ while (count--) { - /* Getting interrupted means a leak, but ok at shutdown*/ + /* Getting interrupted means a leak, but ok at disconnect */ ret = down_interruptible(&dev->urbs.limit_sem); if (ret) break; @@ -1833,6 +1855,7 @@ static void dlfb_free_urb_list(struct dlfb_data *dev) kfree(node); } + dev->urbs.count = 0; } static int dlfb_alloc_urb_list(struct dlfb_data *dev, int count, size_t size) @@ -1948,6 +1971,9 @@ MODULE_PARM_DESC(fb_defio, "Page fault detection of mmap writes"); module_param(shadow, bool, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP); MODULE_PARM_DESC(shadow, "Shadow vid mem. Disable to save mem but lose perf"); +module_param(pixel_limit, int, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP); +MODULE_PARM_DESC(pixel_limit, "Force limit on max mode (in x*y pixels)"); + MODULE_AUTHOR("Roberto De Ioris <roberto@unbit.it>, " "Jaya Kumar <jayakumar.lkml@gmail.com>, " "Bernie Thompson <bernie@plugable.com>"); diff --git a/drivers/video/uvesafb.c b/drivers/video/uvesafb.c index e7f69ef572dc..260cca7ddb41 100644 --- a/drivers/video/uvesafb.c +++ b/drivers/video/uvesafb.c @@ -121,7 +121,7 @@ static int uvesafb_helper_start(void) NULL, }; - return call_usermodehelper(v86d_path, argv, envp, 1); + return call_usermodehelper(v86d_path, argv, envp, UMH_WAIT_PROC); } /* @@ -362,7 +362,7 @@ static u8 *uvesafb_vbe_state_save(struct uvesafb_par *par) state = kmalloc(par->vbe_state_size, GFP_KERNEL); if (!state) - return NULL; + return ERR_PTR(-ENOMEM); task = uvesafb_prep(); if (!task) { @@ -1172,9 +1172,17 @@ static int uvesafb_open(struct fb_info *info, int user) { struct uvesafb_par *par = info->par; int cnt = atomic_read(&par->ref_count); + u8 *buf = NULL; - if (!cnt && par->vbe_state_size) - par->vbe_state_orig = uvesafb_vbe_state_save(par); + if (!cnt && par->vbe_state_size) { + buf = uvesafb_vbe_state_save(par); + if (IS_ERR(buf)) { + printk(KERN_WARNING "uvesafb: save hardware state" + "failed, error code is %ld!\n", PTR_ERR(buf)); + } else { + par->vbe_state_orig = buf; + } + } atomic_inc(&par->ref_count); return 0; diff --git a/drivers/video/via/Makefile b/drivers/video/via/Makefile index 5108136e8776..159f26e6adb5 100644 --- a/drivers/video/via/Makefile +++ b/drivers/video/via/Makefile @@ -6,4 +6,7 @@ obj-$(CONFIG_FB_VIA) += viafb.o viafb-y :=viafbdev.o hw.o via_i2c.o dvi.o lcd.o ioctl.o accel.o \ via_utility.o vt1636.o global.o tblDPASetting.o viamode.o \ - via-core.o via-gpio.o via_modesetting.o via_clock.o + via-core.o via-gpio.o via_modesetting.o via_clock.o \ + via_aux.o via_aux_edid.o via_aux_vt1636.o via_aux_vt1632.o \ + via_aux_vt1631.o via_aux_vt1625.o via_aux_vt1622.o via_aux_vt1621.o \ + via_aux_sii164.o via_aux_ch7301.o diff --git a/drivers/video/via/chip.h b/drivers/video/via/chip.h index 3ebf20c06eef..d32a5076c20f 100644 --- a/drivers/video/via/chip.h +++ b/drivers/video/via/chip.h @@ -146,9 +146,6 @@ struct tmds_setting_information { struct lvds_setting_information { int iga_path; - int h_active; - int v_active; - int bpp; int lcd_panel_hres; int lcd_panel_vres; int display_method; diff --git a/drivers/video/via/dvi.c b/drivers/video/via/dvi.c index 9138e517267c..6be72f0ba21d 100644 --- a/drivers/video/via/dvi.c +++ b/drivers/video/via/dvi.c @@ -172,10 +172,11 @@ static int tmds_register_read_bytes(int index, u8 *buff, int buff_len) } /* DVI Set Mode */ -void viafb_dvi_set_mode(const struct fb_var_screeninfo *var, int iga) +void viafb_dvi_set_mode(const struct fb_var_screeninfo *var, + u16 cxres, u16 cyres, int iga) { struct fb_var_screeninfo dvi_var = *var; - struct crt_mode_table *rb_mode; + const struct fb_videomode *rb_mode; int maxPixelClock; maxPixelClock = viaparinfo->shared->tmds_setting_info.max_pixel_clock; @@ -185,7 +186,7 @@ void viafb_dvi_set_mode(const struct fb_var_screeninfo *var, int iga) viafb_fill_var_timing_info(&dvi_var, rb_mode); } - viafb_fill_crtc_timing(&dvi_var, iga); + viafb_fill_crtc_timing(&dvi_var, cxres, cyres, iga); } /* Sense DVI Connector */ diff --git a/drivers/video/via/dvi.h b/drivers/video/via/dvi.h index e2116aaf797a..db757850c216 100644 --- a/drivers/video/via/dvi.h +++ b/drivers/video/via/dvi.h @@ -59,6 +59,7 @@ void viafb_dvi_enable(void); bool __devinit viafb_tmds_trasmitter_identify(void); void __devinit viafb_init_dvi_size(struct tmds_chip_information *tmds_chip, struct tmds_setting_information *tmds_setting); -void viafb_dvi_set_mode(const struct fb_var_screeninfo *var, int iga); +void viafb_dvi_set_mode(const struct fb_var_screeninfo *var, + u16 cxres, u16 cyres, int iga); #endif /* __DVI_H__ */ diff --git a/drivers/video/via/hw.c b/drivers/video/via/hw.c index 8497727d66de..898590db5e14 100644 --- a/drivers/video/via/hw.c +++ b/drivers/video/via/hw.c @@ -1467,28 +1467,32 @@ void viafb_set_vclock(u32 clk, int set_iga) via_write_misc_reg_mask(0x0C, 0x0C); /* select external clock */ } -static struct display_timing var_to_timing(const struct fb_var_screeninfo *var) +struct display_timing var_to_timing(const struct fb_var_screeninfo *var, + u16 cxres, u16 cyres) { struct display_timing timing; + u16 dx = (var->xres - cxres) / 2, dy = (var->yres - cyres) / 2; - timing.hor_addr = var->xres; - timing.hor_sync_start = timing.hor_addr + var->right_margin; + timing.hor_addr = cxres; + timing.hor_sync_start = timing.hor_addr + var->right_margin + dx; timing.hor_sync_end = timing.hor_sync_start + var->hsync_len; - timing.hor_total = timing.hor_sync_end + var->left_margin; - timing.hor_blank_start = timing.hor_addr; - timing.hor_blank_end = timing.hor_total; - timing.ver_addr = var->yres; - timing.ver_sync_start = timing.ver_addr + var->lower_margin; + timing.hor_total = timing.hor_sync_end + var->left_margin + dx; + timing.hor_blank_start = timing.hor_addr + dx; + timing.hor_blank_end = timing.hor_total - dx; + timing.ver_addr = cyres; + timing.ver_sync_start = timing.ver_addr + var->lower_margin + dy; timing.ver_sync_end = timing.ver_sync_start + var->vsync_len; - timing.ver_total = timing.ver_sync_end + var->upper_margin; - timing.ver_blank_start = timing.ver_addr; - timing.ver_blank_end = timing.ver_total; + timing.ver_total = timing.ver_sync_end + var->upper_margin + dy; + timing.ver_blank_start = timing.ver_addr + dy; + timing.ver_blank_end = timing.ver_total - dy; return timing; } -void viafb_fill_crtc_timing(const struct fb_var_screeninfo *var, int iga) +void viafb_fill_crtc_timing(const struct fb_var_screeninfo *var, + u16 cxres, u16 cyres, int iga) { - struct display_timing crt_reg = var_to_timing(var); + struct display_timing crt_reg = var_to_timing(var, + cxres ? cxres : var->xres, cyres ? cyres : var->yres); if (iga == IGA1) via_set_primary_timing(&crt_reg); @@ -1526,13 +1530,6 @@ void viafb_update_device_setting(int hres, int vres, int bpp, int flag) if (flag == 0) { viaparinfo->tmds_setting_info->h_active = hres; viaparinfo->tmds_setting_info->v_active = vres; - - viaparinfo->lvds_setting_info->h_active = hres; - viaparinfo->lvds_setting_info->v_active = vres; - viaparinfo->lvds_setting_info->bpp = bpp; - viaparinfo->lvds_setting_info2->h_active = hres; - viaparinfo->lvds_setting_info2->v_active = vres; - viaparinfo->lvds_setting_info2->bpp = bpp; } else { if (viaparinfo->tmds_setting_info->iga_path == IGA2) { @@ -1540,16 +1537,6 @@ void viafb_update_device_setting(int hres, int vres, int bpp, int flag) viaparinfo->tmds_setting_info->v_active = vres; } - if (viaparinfo->lvds_setting_info->iga_path == IGA2) { - viaparinfo->lvds_setting_info->h_active = hres; - viaparinfo->lvds_setting_info->v_active = vres; - viaparinfo->lvds_setting_info->bpp = bpp; - } - if (IGA2 == viaparinfo->lvds_setting_info2->iga_path) { - viaparinfo->lvds_setting_info2->h_active = hres; - viaparinfo->lvds_setting_info2->v_active = vres; - viaparinfo->lvds_setting_info2->bpp = bpp; - } } } @@ -1758,13 +1745,13 @@ static void set_display_channel(void) } } -static u8 get_sync(struct fb_info *info) +static u8 get_sync(struct fb_var_screeninfo *var) { u8 polarity = 0; - if (!(info->var.sync & FB_SYNC_HOR_HIGH_ACT)) + if (!(var->sync & FB_SYNC_HOR_HIGH_ACT)) polarity |= VIA_HSYNC_NEGATIVE; - if (!(info->var.sync & FB_SYNC_VERT_HIGH_ACT)) + if (!(var->sync & FB_SYNC_VERT_HIGH_ACT)) polarity |= VIA_VSYNC_NEGATIVE; return polarity; } @@ -1844,9 +1831,9 @@ static void hw_init(void) load_fix_bit_crtc_reg(); } -int viafb_setmode(int video_bpp, int video_bpp1) +int viafb_setmode(void) { - int j; + int j, cxres = 0, cyres = 0; int port; u32 devices = viaparinfo->shared->iga1_devices | viaparinfo->shared->iga2_devices; @@ -1895,6 +1882,8 @@ int viafb_setmode(int video_bpp, int video_bpp1) } else if (viafb_SAMM_ON) { viafb_fill_var_timing_info(&var2, viafb_get_best_mode( viafb_second_xres, viafb_second_yres, viafb_refresh1)); + cxres = viafbinfo->var.xres; + cyres = viafbinfo->var.yres; var2.bits_per_pixel = viafbinfo->var.bits_per_pixel; } @@ -1902,9 +1891,9 @@ int viafb_setmode(int video_bpp, int video_bpp1) if (viafb_CRT_ON) { if (viaparinfo->shared->iga2_devices & VIA_CRT && viafb_SAMM_ON) - viafb_fill_crtc_timing(&var2, IGA2); + viafb_fill_crtc_timing(&var2, cxres, cyres, IGA2); else - viafb_fill_crtc_timing(&viafbinfo->var, + viafb_fill_crtc_timing(&viafbinfo->var, 0, 0, (viaparinfo->shared->iga1_devices & VIA_CRT) ? IGA1 : IGA2); @@ -1922,17 +1911,17 @@ int viafb_setmode(int video_bpp, int video_bpp1) if (viafb_DVI_ON) { if (viaparinfo->shared->tmds_setting_info.iga_path == IGA2 && viafb_SAMM_ON) - viafb_dvi_set_mode(&var2, IGA2); + viafb_dvi_set_mode(&var2, cxres, cyres, IGA2); else - viafb_dvi_set_mode(&viafbinfo->var, + viafb_dvi_set_mode(&viafbinfo->var, 0, 0, viaparinfo->tmds_setting_info->iga_path); } if (viafb_LCD_ON) { if (viafb_SAMM_ON && (viaparinfo->lvds_setting_info->iga_path == IGA2)) { - viaparinfo->lvds_setting_info->bpp = video_bpp1; - viafb_lcd_set_mode(viaparinfo->lvds_setting_info, + viafb_lcd_set_mode(&var2, cxres, cyres, + viaparinfo->lvds_setting_info, &viaparinfo->chip_info->lvds_chip_info); } else { /* IGA1 doesn't have LCD scaling, so set it center. */ @@ -1940,16 +1929,16 @@ int viafb_setmode(int video_bpp, int video_bpp1) viaparinfo->lvds_setting_info->display_method = LCD_CENTERING; } - viaparinfo->lvds_setting_info->bpp = video_bpp; - viafb_lcd_set_mode(viaparinfo->lvds_setting_info, + viafb_lcd_set_mode(&viafbinfo->var, 0, 0, + viaparinfo->lvds_setting_info, &viaparinfo->chip_info->lvds_chip_info); } } if (viafb_LCD2_ON) { if (viafb_SAMM_ON && (viaparinfo->lvds_setting_info2->iga_path == IGA2)) { - viaparinfo->lvds_setting_info2->bpp = video_bpp1; - viafb_lcd_set_mode(viaparinfo->lvds_setting_info2, + viafb_lcd_set_mode(&var2, cxres, cyres, + viaparinfo->lvds_setting_info2, &viaparinfo->chip_info->lvds_chip_info2); } else { /* IGA1 doesn't have LCD scaling, so set it center. */ @@ -1957,8 +1946,8 @@ int viafb_setmode(int video_bpp, int video_bpp1) viaparinfo->lvds_setting_info2->display_method = LCD_CENTERING; } - viaparinfo->lvds_setting_info2->bpp = video_bpp; - viafb_lcd_set_mode(viaparinfo->lvds_setting_info2, + viafb_lcd_set_mode(&viafbinfo->var, 0, 0, + viaparinfo->lvds_setting_info2, &viaparinfo->chip_info->lvds_chip_info2); } } @@ -1971,7 +1960,7 @@ int viafb_setmode(int video_bpp, int video_bpp1) if (!viafb_hotplug) { viafb_hotplug_Xres = viafbinfo->var.xres; viafb_hotplug_Yres = viafbinfo->var.yres; - viafb_hotplug_bpp = video_bpp; + viafb_hotplug_bpp = viafbinfo->var.bits_per_pixel; viafb_hotplug_refresh = viafb_refresh; if (viafb_DVI_ON) @@ -1980,13 +1969,13 @@ int viafb_setmode(int video_bpp, int video_bpp1) viafb_DeviceStatus = CRT_Device; } device_on(); - if (!viafb_dual_fb) - via_set_sync_polarity(devices, get_sync(viafbinfo)); + if (!viafb_SAMM_ON) + via_set_sync_polarity(devices, get_sync(&viafbinfo->var)); else { via_set_sync_polarity(viaparinfo->shared->iga1_devices, - get_sync(viafbinfo)); + get_sync(&viafbinfo->var)); via_set_sync_polarity(viaparinfo->shared->iga2_devices, - get_sync(viafbinfo1)); + get_sync(&var2)); } clock.set_engine_pll_state(VIA_STATE_ON); @@ -2023,20 +2012,20 @@ int viafb_setmode(int video_bpp, int video_bpp1) int viafb_get_refresh(int hres, int vres, u32 long_refresh) { - struct crt_mode_table *best; + const struct fb_videomode *best; best = viafb_get_best_mode(hres, vres, long_refresh); if (!best) return 60; - if (abs(best->refresh_rate - long_refresh) > 3) { + if (abs(best->refresh - long_refresh) > 3) { if (hres == 1200 && vres == 900) return 49; /* OLPC DCON only supports 50 Hz */ else return 60; } - return best->refresh_rate; + return best->refresh; } static void device_off(void) @@ -2129,26 +2118,17 @@ void viafb_set_dpa_gfx(int output_interface, struct GFX_DPA_SETTING\ } } -/*According var's xres, yres fill var's other timing information*/ void viafb_fill_var_timing_info(struct fb_var_screeninfo *var, - struct crt_mode_table *mode) + const struct fb_videomode *mode) { - struct display_timing crt_reg; - - crt_reg = mode->crtc; - var->pixclock = 1000000000 / (crt_reg.hor_total * crt_reg.ver_total) - * 1000 / mode->refresh_rate; - var->left_margin = - crt_reg.hor_total - (crt_reg.hor_sync_start + crt_reg.hor_sync_end); - var->right_margin = crt_reg.hor_sync_start - crt_reg.hor_addr; - var->hsync_len = crt_reg.hor_sync_end; - var->upper_margin = - crt_reg.ver_total - (crt_reg.ver_sync_start + crt_reg.ver_sync_end); - var->lower_margin = crt_reg.ver_sync_start - crt_reg.ver_addr; - var->vsync_len = crt_reg.ver_sync_end; - var->sync = 0; - if (mode->h_sync_polarity == POSITIVE) - var->sync |= FB_SYNC_HOR_HIGH_ACT; - if (mode->v_sync_polarity == POSITIVE) - var->sync |= FB_SYNC_VERT_HIGH_ACT; + var->pixclock = mode->pixclock; + var->xres = mode->xres; + var->yres = mode->yres; + var->left_margin = mode->left_margin; + var->right_margin = mode->right_margin; + var->hsync_len = mode->hsync_len; + var->upper_margin = mode->upper_margin; + var->lower_margin = mode->lower_margin; + var->vsync_len = mode->vsync_len; + var->sync = mode->sync; } diff --git a/drivers/video/via/hw.h b/drivers/video/via/hw.h index 4db5b6e8d8d0..6be243cfc823 100644 --- a/drivers/video/via/hw.h +++ b/drivers/video/via/hw.h @@ -637,7 +637,10 @@ extern int viafb_LCD_ON; extern int viafb_DVI_ON; extern int viafb_hotplug; -void viafb_fill_crtc_timing(const struct fb_var_screeninfo *var, int iga); +struct display_timing var_to_timing(const struct fb_var_screeninfo *var, + u16 cxres, u16 cyres); +void viafb_fill_crtc_timing(const struct fb_var_screeninfo *var, + u16 cxres, u16 cyres, int iga); void viafb_set_vclock(u32 CLK, int set_iga); void viafb_load_reg(int timing_value, int viafb_load_reg_num, struct io_register *reg, @@ -657,9 +660,9 @@ void viafb_load_FIFO_reg(int set_iga, int hor_active, int ver_active); void viafb_set_dpa_gfx(int output_interface, struct GFX_DPA_SETTING\ *p_gfx_dpa_setting); -int viafb_setmode(int video_bpp, int video_bpp1); +int viafb_setmode(void); void viafb_fill_var_timing_info(struct fb_var_screeninfo *var, - struct crt_mode_table *mode); + const struct fb_videomode *mode); void __devinit viafb_init_chip_info(int chip_type); void __devinit viafb_init_dac(int set_iga); int viafb_get_refresh(int hres, int vres, u32 float_refresh); diff --git a/drivers/video/via/lcd.c b/drivers/video/via/lcd.c index 5f3b4e394e82..165037910536 100644 --- a/drivers/video/via/lcd.c +++ b/drivers/video/via/lcd.c @@ -53,10 +53,6 @@ static void __devinit fp_id_to_vindex(int panel_id); static int lvds_register_read(int index); static void load_lcd_scaling(int set_hres, int set_vres, int panel_hres, int panel_vres); -static void via_pitch_alignment_patch_lcd( - struct lvds_setting_information *plvds_setting_info, - struct lvds_chip_information - *plvds_chip_info); static void lcd_patch_skew_dvp0(struct lvds_setting_information *plvds_setting_info, struct lvds_chip_information *plvds_chip_info); @@ -79,9 +75,6 @@ static void check_diport_of_integrated_lvds( struct lvds_chip_information *plvds_chip_info, struct lvds_setting_information *plvds_setting_info); -static struct display_timing lcd_centering_timging(struct display_timing - mode_crt_reg, - struct display_timing panel_crt_reg); static inline bool check_lvds_chip(int device_id_subaddr, int device_id) { @@ -454,20 +447,17 @@ static void load_lcd_scaling(int set_hres, int set_vres, int panel_hres, } } -static void via_pitch_alignment_patch_lcd( - struct lvds_setting_information *plvds_setting_info, - struct lvds_chip_information - *plvds_chip_info) +static void via_pitch_alignment_patch_lcd(int iga_path, int hres, int bpp) { unsigned char cr13, cr35, cr65, cr66, cr67; unsigned long dwScreenPitch = 0; unsigned long dwPitch; - dwPitch = plvds_setting_info->h_active * (plvds_setting_info->bpp >> 3); + dwPitch = hres * (bpp >> 3); if (dwPitch & 0x1F) { dwScreenPitch = ((dwPitch + 31) & ~31) >> 3; - if (plvds_setting_info->iga_path == IGA2) { - if (plvds_setting_info->bpp > 8) { + if (iga_path == IGA2) { + if (bpp > 8) { cr66 = (unsigned char)(dwScreenPitch & 0xFF); viafb_write_reg(CR66, VIACR, cr66); cr67 = viafb_read_reg(VIACR, CR67) & 0xFC; @@ -485,7 +475,7 @@ static void via_pitch_alignment_patch_lcd( cr65 += 2; viafb_write_reg(CR65, VIACR, cr65); } else { - if (plvds_setting_info->bpp > 8) { + if (bpp > 8) { cr13 = (unsigned char)(dwScreenPitch & 0xFF); viafb_write_reg(CR13, VIACR, cr13); cr35 = viafb_read_reg(VIACR, CR35) & 0x1F; @@ -548,49 +538,45 @@ static void lcd_patch_skew(struct lvds_setting_information } /* LCD Set Mode */ -void viafb_lcd_set_mode(struct lvds_setting_information *plvds_setting_info, +void viafb_lcd_set_mode(const struct fb_var_screeninfo *var, u16 cxres, + u16 cyres, struct lvds_setting_information *plvds_setting_info, struct lvds_chip_information *plvds_chip_info) { int set_iga = plvds_setting_info->iga_path; - int mode_bpp = plvds_setting_info->bpp; - int set_hres = plvds_setting_info->h_active; - int set_vres = plvds_setting_info->v_active; + int mode_bpp = var->bits_per_pixel; + int set_hres = cxres ? cxres : var->xres; + int set_vres = cyres ? cyres : var->yres; int panel_hres = plvds_setting_info->lcd_panel_hres; int panel_vres = plvds_setting_info->lcd_panel_vres; u32 clock; - struct display_timing mode_crt_reg, panel_crt_reg, timing; - struct crt_mode_table *mode_crt_table, *panel_crt_table; + struct display_timing timing; + struct fb_var_screeninfo panel_var; + const struct fb_videomode *mode_crt_table, *panel_crt_table; DEBUG_MSG(KERN_INFO "viafb_lcd_set_mode!!\n"); /* Get mode table */ mode_crt_table = viafb_get_best_mode(set_hres, set_vres, 60); - mode_crt_reg = mode_crt_table->crtc; /* Get panel table Pointer */ panel_crt_table = viafb_get_best_mode(panel_hres, panel_vres, 60); - panel_crt_reg = panel_crt_table->crtc; + viafb_fill_var_timing_info(&panel_var, panel_crt_table); DEBUG_MSG(KERN_INFO "bellow viafb_lcd_set_mode!!\n"); if (VT1636_LVDS == plvds_chip_info->lvds_chip_name) viafb_init_lvds_vt1636(plvds_setting_info, plvds_chip_info); - clock = panel_crt_reg.hor_total * panel_crt_reg.ver_total - * panel_crt_table->refresh_rate; + clock = PICOS2KHZ(panel_crt_table->pixclock) * 1000; plvds_setting_info->vclk = clock; if (set_iga == IGA2 && (set_hres < panel_hres || set_vres < panel_vres) && plvds_setting_info->display_method == LCD_EXPANDSION) { - timing = panel_crt_reg; + timing = var_to_timing(&panel_var, panel_hres, panel_vres); load_lcd_scaling(set_hres, set_vres, panel_hres, panel_vres); } else { - timing = lcd_centering_timging(mode_crt_reg, panel_crt_reg); + timing = var_to_timing(&panel_var, set_hres, set_vres); if (set_iga == IGA2) /* disable scaling */ via_write_reg_mask(VIACR, 0x79, 0x00, BIT0 + BIT1 + BIT2); } - timing.hor_blank_end += timing.hor_blank_start; - timing.hor_sync_end += timing.hor_sync_start; - timing.ver_blank_end += timing.ver_blank_start; - timing.ver_sync_end += timing.ver_sync_start; if (set_iga == IGA1) via_set_primary_timing(&timing); else if (set_iga == IGA2) @@ -613,7 +599,8 @@ void viafb_lcd_set_mode(struct lvds_setting_information *plvds_setting_info, viafb_write_reg_mask(CR6A, VIACR, 0x01, BIT0); /* Patch for non 32bit alignment mode */ - via_pitch_alignment_patch_lcd(plvds_setting_info, plvds_chip_info); + via_pitch_alignment_patch_lcd(plvds_setting_info->iga_path, set_hres, + var->bits_per_pixel); } static void integrated_lvds_disable(struct lvds_setting_information @@ -973,37 +960,6 @@ void __devinit viafb_init_lvds_output_interface(struct lvds_chip_information } } -static struct display_timing lcd_centering_timging(struct display_timing - mode_crt_reg, - struct display_timing panel_crt_reg) -{ - struct display_timing crt_reg; - - crt_reg.hor_total = panel_crt_reg.hor_total; - crt_reg.hor_addr = mode_crt_reg.hor_addr; - crt_reg.hor_blank_start = - (panel_crt_reg.hor_addr - mode_crt_reg.hor_addr) / 2 + - crt_reg.hor_addr; - crt_reg.hor_blank_end = panel_crt_reg.hor_blank_end; - crt_reg.hor_sync_start = - (panel_crt_reg.hor_sync_start - - panel_crt_reg.hor_blank_start) + crt_reg.hor_blank_start; - crt_reg.hor_sync_end = panel_crt_reg.hor_sync_end; - - crt_reg.ver_total = panel_crt_reg.ver_total; - crt_reg.ver_addr = mode_crt_reg.ver_addr; - crt_reg.ver_blank_start = - (panel_crt_reg.ver_addr - mode_crt_reg.ver_addr) / 2 + - crt_reg.ver_addr; - crt_reg.ver_blank_end = panel_crt_reg.ver_blank_end; - crt_reg.ver_sync_start = - (panel_crt_reg.ver_sync_start - - panel_crt_reg.ver_blank_start) + crt_reg.ver_blank_start; - crt_reg.ver_sync_end = panel_crt_reg.ver_sync_end; - - return crt_reg; -} - bool viafb_lcd_get_mobile_state(bool *mobile) { unsigned char __iomem *romptr, *tableptr, *biosptr; diff --git a/drivers/video/via/lcd.h b/drivers/video/via/lcd.h index 77ca7b862e68..8f3e4e06156c 100644 --- a/drivers/video/via/lcd.h +++ b/drivers/video/via/lcd.h @@ -76,7 +76,8 @@ void __devinit viafb_init_lvds_output_interface(struct lvds_chip_information *plvds_chip_info, struct lvds_setting_information *plvds_setting_info); -void viafb_lcd_set_mode(struct lvds_setting_information *plvds_setting_info, +void viafb_lcd_set_mode(const struct fb_var_screeninfo *var, u16 cxres, + u16 cyres, struct lvds_setting_information *plvds_setting_info, struct lvds_chip_information *plvds_chip_info); bool __devinit viafb_lvds_trasmitter_identify(void); void viafb_init_lvds_output_interface(struct lvds_chip_information diff --git a/drivers/video/via/share.h b/drivers/video/via/share.h index c01c1c162726..3158dfc90bed 100644 --- a/drivers/video/via/share.h +++ b/drivers/video/via/share.h @@ -283,337 +283,6 @@ #define HW_LAYOUT_LCD1_LCD2 0x04 #define HW_LAYOUT_LCD_EXTERNAL_LCD2 0x10 -/* Definition Refresh Rate */ -#define REFRESH_49 49 -#define REFRESH_50 50 -#define REFRESH_60 60 -#define REFRESH_75 75 -#define REFRESH_85 85 -#define REFRESH_100 100 -#define REFRESH_120 120 - -/* Definition Sync Polarity*/ -#define NEGATIVE 1 -#define POSITIVE 0 - -/*480x640@60 Sync Polarity (GTF) -*/ -#define M480X640_R60_HSP NEGATIVE -#define M480X640_R60_VSP POSITIVE - -/*640x480@60 Sync Polarity (VESA Mode) -*/ -#define M640X480_R60_HSP NEGATIVE -#define M640X480_R60_VSP NEGATIVE - -/*640x480@75 Sync Polarity (VESA Mode) -*/ -#define M640X480_R75_HSP NEGATIVE -#define M640X480_R75_VSP NEGATIVE - -/*640x480@85 Sync Polarity (VESA Mode) -*/ -#define M640X480_R85_HSP NEGATIVE -#define M640X480_R85_VSP NEGATIVE - -/*640x480@100 Sync Polarity (GTF Mode) -*/ -#define M640X480_R100_HSP NEGATIVE -#define M640X480_R100_VSP POSITIVE - -/*640x480@120 Sync Polarity (GTF Mode) -*/ -#define M640X480_R120_HSP NEGATIVE -#define M640X480_R120_VSP POSITIVE - -/*720x480@60 Sync Polarity (GTF Mode) -*/ -#define M720X480_R60_HSP NEGATIVE -#define M720X480_R60_VSP POSITIVE - -/*720x576@60 Sync Polarity (GTF Mode) -*/ -#define M720X576_R60_HSP NEGATIVE -#define M720X576_R60_VSP POSITIVE - -/*800x600@60 Sync Polarity (VESA Mode) -*/ -#define M800X600_R60_HSP POSITIVE -#define M800X600_R60_VSP POSITIVE - -/*800x600@75 Sync Polarity (VESA Mode) -*/ -#define M800X600_R75_HSP POSITIVE -#define M800X600_R75_VSP POSITIVE - -/*800x600@85 Sync Polarity (VESA Mode) -*/ -#define M800X600_R85_HSP POSITIVE -#define M800X600_R85_VSP POSITIVE - -/*800x600@100 Sync Polarity (GTF Mode) -*/ -#define M800X600_R100_HSP NEGATIVE -#define M800X600_R100_VSP POSITIVE - -/*800x600@120 Sync Polarity (GTF Mode) -*/ -#define M800X600_R120_HSP NEGATIVE -#define M800X600_R120_VSP POSITIVE - -/*800x480@60 Sync Polarity (CVT Mode) -*/ -#define M800X480_R60_HSP NEGATIVE -#define M800X480_R60_VSP POSITIVE - -/*848x480@60 Sync Polarity (CVT Mode) -*/ -#define M848X480_R60_HSP NEGATIVE -#define M848X480_R60_VSP POSITIVE - -/*852x480@60 Sync Polarity (GTF Mode) -*/ -#define M852X480_R60_HSP NEGATIVE -#define M852X480_R60_VSP POSITIVE - -/*1024x512@60 Sync Polarity (GTF Mode) -*/ -#define M1024X512_R60_HSP NEGATIVE -#define M1024X512_R60_VSP POSITIVE - -/*1024x600@60 Sync Polarity (GTF Mode) -*/ -#define M1024X600_R60_HSP NEGATIVE -#define M1024X600_R60_VSP POSITIVE - -/*1024x768@60 Sync Polarity (VESA Mode) -*/ -#define M1024X768_R60_HSP NEGATIVE -#define M1024X768_R60_VSP NEGATIVE - -/*1024x768@75 Sync Polarity (VESA Mode) -*/ -#define M1024X768_R75_HSP POSITIVE -#define M1024X768_R75_VSP POSITIVE - -/*1024x768@85 Sync Polarity (VESA Mode) -*/ -#define M1024X768_R85_HSP POSITIVE -#define M1024X768_R85_VSP POSITIVE - -/*1024x768@100 Sync Polarity (GTF Mode) -*/ -#define M1024X768_R100_HSP NEGATIVE -#define M1024X768_R100_VSP POSITIVE - -/*1152x864@75 Sync Polarity (VESA Mode) -*/ -#define M1152X864_R75_HSP POSITIVE -#define M1152X864_R75_VSP POSITIVE - -/*1280x720@60 Sync Polarity (GTF Mode) -*/ -#define M1280X720_R60_HSP NEGATIVE -#define M1280X720_R60_VSP POSITIVE - -/* 1280x768@50 Sync Polarity (GTF Mode) */ -#define M1280X768_R50_HSP NEGATIVE -#define M1280X768_R50_VSP POSITIVE - -/*1280x768@60 Sync Polarity (GTF Mode) -*/ -#define M1280X768_R60_HSP NEGATIVE -#define M1280X768_R60_VSP POSITIVE - -/*1280x800@60 Sync Polarity (CVT Mode) -*/ -#define M1280X800_R60_HSP NEGATIVE -#define M1280X800_R60_VSP POSITIVE - -/*1280x960@60 Sync Polarity (VESA Mode) -*/ -#define M1280X960_R60_HSP POSITIVE -#define M1280X960_R60_VSP POSITIVE - -/*1280x1024@60 Sync Polarity (VESA Mode) -*/ -#define M1280X1024_R60_HSP POSITIVE -#define M1280X1024_R60_VSP POSITIVE - -/* 1360x768@60 Sync Polarity (CVT Mode) */ -#define M1360X768_R60_HSP POSITIVE -#define M1360X768_R60_VSP POSITIVE - -/* 1360x768@60 Sync Polarity (CVT Reduce Blanking Mode) */ -#define M1360X768_RB_R60_HSP POSITIVE -#define M1360X768_RB_R60_VSP NEGATIVE - -/* 1368x768@50 Sync Polarity (GTF Mode) */ -#define M1368X768_R50_HSP NEGATIVE -#define M1368X768_R50_VSP POSITIVE - -/* 1368x768@60 Sync Polarity (VESA Mode) */ -#define M1368X768_R60_HSP NEGATIVE -#define M1368X768_R60_VSP POSITIVE - -/*1280x1024@75 Sync Polarity (VESA Mode) -*/ -#define M1280X1024_R75_HSP POSITIVE -#define M1280X1024_R75_VSP POSITIVE - -/*1280x1024@85 Sync Polarity (VESA Mode) -*/ -#define M1280X1024_R85_HSP POSITIVE -#define M1280X1024_R85_VSP POSITIVE - -/*1440x1050@60 Sync Polarity (GTF Mode) -*/ -#define M1440X1050_R60_HSP NEGATIVE -#define M1440X1050_R60_VSP POSITIVE - -/*1600x1200@60 Sync Polarity (VESA Mode) -*/ -#define M1600X1200_R60_HSP POSITIVE -#define M1600X1200_R60_VSP POSITIVE - -/*1600x1200@75 Sync Polarity (VESA Mode) -*/ -#define M1600X1200_R75_HSP POSITIVE -#define M1600X1200_R75_VSP POSITIVE - -/* 1680x1050@60 Sync Polarity (CVT Mode) */ -#define M1680x1050_R60_HSP NEGATIVE -#define M1680x1050_R60_VSP NEGATIVE - -/* 1680x1050@60 Sync Polarity (CVT Reduce Blanking Mode) */ -#define M1680x1050_RB_R60_HSP POSITIVE -#define M1680x1050_RB_R60_VSP NEGATIVE - -/* 1680x1050@75 Sync Polarity (CVT Mode) */ -#define M1680x1050_R75_HSP NEGATIVE -#define M1680x1050_R75_VSP POSITIVE - -/*1920x1080@60 Sync Polarity (CVT Mode) -*/ -#define M1920X1080_R60_HSP NEGATIVE -#define M1920X1080_R60_VSP POSITIVE - -/* 1920x1080@60 Sync Polarity (CVT Reduce Blanking Mode) */ -#define M1920X1080_RB_R60_HSP POSITIVE -#define M1920X1080_RB_R60_VSP NEGATIVE - -/*1920x1440@60 Sync Polarity (VESA Mode) -*/ -#define M1920X1440_R60_HSP NEGATIVE -#define M1920X1440_R60_VSP POSITIVE - -/*1920x1440@75 Sync Polarity (VESA Mode) -*/ -#define M1920X1440_R75_HSP NEGATIVE -#define M1920X1440_R75_VSP POSITIVE - -#if 0 -/* 1400x1050@60 Sync Polarity (VESA Mode) */ -#define M1400X1050_R60_HSP NEGATIVE -#define M1400X1050_R60_VSP NEGATIVE -#endif - -/* 1400x1050@60 Sync Polarity (CVT Mode) */ -#define M1400X1050_R60_HSP NEGATIVE -#define M1400X1050_R60_VSP POSITIVE - -/* 1400x1050@60 Sync Polarity (CVT Reduce Blanking Mode) */ -#define M1400X1050_RB_R60_HSP POSITIVE -#define M1400X1050_RB_R60_VSP NEGATIVE - -/* 1400x1050@75 Sync Polarity (CVT Mode) */ -#define M1400X1050_R75_HSP NEGATIVE -#define M1400X1050_R75_VSP POSITIVE - -/* 960x600@60 Sync Polarity (CVT Mode) */ -#define M960X600_R60_HSP NEGATIVE -#define M960X600_R60_VSP POSITIVE - -/* 1000x600@60 Sync Polarity (GTF Mode) */ -#define M1000X600_R60_HSP NEGATIVE -#define M1000X600_R60_VSP POSITIVE - -/* 1024x576@60 Sync Polarity (GTF Mode) */ -#define M1024X576_R60_HSP NEGATIVE -#define M1024X576_R60_VSP POSITIVE - -/*1024x600@60 Sync Polarity (GTF Mode)*/ -#define M1024X600_R60_HSP NEGATIVE -#define M1024X600_R60_VSP POSITIVE - -/* 1088x612@60 Sync Polarity (CVT Mode) */ -#define M1088X612_R60_HSP NEGATIVE -#define M1088X612_R60_VSP POSITIVE - -/* 1152x720@60 Sync Polarity (CVT Mode) */ -#define M1152X720_R60_HSP NEGATIVE -#define M1152X720_R60_VSP POSITIVE - -/* 1200x720@60 Sync Polarity (GTF Mode) */ -#define M1200X720_R60_HSP NEGATIVE -#define M1200X720_R60_VSP POSITIVE - -/* 1200x900@60 Sync Polarity (DCON) */ -#define M1200X900_R60_HSP POSITIVE -#define M1200X900_R60_VSP POSITIVE - -/* 1280x600@60 Sync Polarity (GTF Mode) */ -#define M1280x600_R60_HSP NEGATIVE -#define M1280x600_R60_VSP POSITIVE - -/* 1280x720@50 Sync Polarity (GTF Mode) */ -#define M1280X720_R50_HSP NEGATIVE -#define M1280X720_R50_VSP POSITIVE - -/* 1440x900@60 Sync Polarity (CVT Mode) */ -#define M1440X900_R60_HSP NEGATIVE -#define M1440X900_R60_VSP POSITIVE - -/* 1440x900@75 Sync Polarity (CVT Mode) */ -#define M1440X900_R75_HSP NEGATIVE -#define M1440X900_R75_VSP POSITIVE - -/* 1440x900@60 Sync Polarity (CVT Reduce Blanking Mode) */ -#define M1440X900_RB_R60_HSP POSITIVE -#define M1440X900_RB_R60_VSP NEGATIVE - -/* 1600x900@60 Sync Polarity (CVT Mode) */ -#define M1600X900_R60_HSP NEGATIVE -#define M1600X900_R60_VSP POSITIVE - -/* 1600x900@60 Sync Polarity (CVT Reduce Blanking Mode) */ -#define M1600X900_RB_R60_HSP POSITIVE -#define M1600X900_RB_R60_VSP NEGATIVE - -/* 1600x1024@60 Sync Polarity (GTF Mode) */ -#define M1600X1024_R60_HSP NEGATIVE -#define M1600X1024_R60_VSP POSITIVE - -/* 1792x1344@60 Sync Polarity (DMT Mode) */ -#define M1792x1344_R60_HSP NEGATIVE -#define M1792x1344_R60_VSP POSITIVE - -/* 1856x1392@60 Sync Polarity (DMT Mode) */ -#define M1856x1392_R60_HSP NEGATIVE -#define M1856x1392_R60_VSP POSITIVE - -/* 1920x1200@60 Sync Polarity (CVT Mode) */ -#define M1920X1200_R60_HSP NEGATIVE -#define M1920X1200_R60_VSP POSITIVE - -/* 1920x1200@60 Sync Polarity (CVT Reduce Blanking Mode) */ -#define M1920X1200_RB_R60_HSP POSITIVE -#define M1920X1200_RB_R60_VSP NEGATIVE - -/* 2048x1536@60 Sync Polarity (CVT Mode) */ -#define M2048x1536_R60_HSP NEGATIVE -#define M2048x1536_R60_VSP POSITIVE - /* Definition CRTC Timing Index */ #define H_TOTAL_INDEX 0 #define H_ADDR_INDEX 1 diff --git a/drivers/video/via/via_aux.c b/drivers/video/via/via_aux.c new file mode 100644 index 000000000000..4a0a55cdac3d --- /dev/null +++ b/drivers/video/via/via_aux.c @@ -0,0 +1,88 @@ +/* + * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE.See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/* + * infrastructure for devices connected via I2C + */ + +#include <linux/slab.h> +#include "via_aux.h" + + +struct via_aux_bus *via_aux_probe(struct i2c_adapter *adap) +{ + struct via_aux_bus *bus; + + if (!adap) + return NULL; + + bus = kmalloc(sizeof(*bus), GFP_KERNEL); + if (!bus) + return NULL; + + bus->adap = adap; + INIT_LIST_HEAD(&bus->drivers); + + via_aux_edid_probe(bus); + via_aux_vt1636_probe(bus); + via_aux_vt1632_probe(bus); + via_aux_vt1631_probe(bus); + via_aux_vt1625_probe(bus); + via_aux_vt1622_probe(bus); + via_aux_vt1621_probe(bus); + via_aux_sii164_probe(bus); + via_aux_ch7301_probe(bus); + + return bus; +} + +void via_aux_free(struct via_aux_bus *bus) +{ + struct via_aux_drv *pos, *n; + + if (!bus) + return; + + list_for_each_entry_safe(pos, n, &bus->drivers, chain) { + if (pos->cleanup) + pos->cleanup(pos); + + list_del(&pos->chain); + kfree(pos->data); + kfree(pos); + } + + kfree(bus); +} + +const struct fb_videomode *via_aux_get_preferred_mode(struct via_aux_bus *bus) +{ + struct via_aux_drv *pos; + const struct fb_videomode *mode = NULL; + + if (!bus) + return NULL; + + list_for_each_entry(pos, &bus->drivers, chain) { + if (pos->get_preferred_mode) + mode = pos->get_preferred_mode(pos); + } + + return mode; +} diff --git a/drivers/video/via/via_aux.h b/drivers/video/via/via_aux.h new file mode 100644 index 000000000000..a8de3f038cea --- /dev/null +++ b/drivers/video/via/via_aux.h @@ -0,0 +1,93 @@ +/* + * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE.See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/* + * infrastructure for devices connected via I2C + */ + +#ifndef __VIA_AUX_H__ +#define __VIA_AUX_H__ + + +#include <linux/list.h> +#include <linux/i2c.h> +#include <linux/fb.h> + + +struct via_aux_bus { + struct i2c_adapter *adap; /* the I2C device to access the bus */ + struct list_head drivers; /* drivers for devices on this bus */ +}; + +struct via_aux_drv { + struct list_head chain; /* chain to support multiple drivers */ + + struct via_aux_bus *bus; /* the I2C bus used */ + u8 addr; /* the I2C slave address */ + + const char *name; /* human readable name of the driver */ + void *data; /* private data of this driver */ + + void (*cleanup)(struct via_aux_drv *drv); + const struct fb_videomode* (*get_preferred_mode) + (struct via_aux_drv *drv); +}; + + +struct via_aux_bus *via_aux_probe(struct i2c_adapter *adap); +void via_aux_free(struct via_aux_bus *bus); +const struct fb_videomode *via_aux_get_preferred_mode(struct via_aux_bus *bus); + + +static inline bool via_aux_add(struct via_aux_drv *drv) +{ + struct via_aux_drv *data = kmalloc(sizeof(*data), GFP_KERNEL); + + if (!data) + return false; + + *data = *drv; + list_add_tail(&data->chain, &data->bus->drivers); + return true; +} + +static inline bool via_aux_read(struct via_aux_drv *drv, u8 start, u8 *buf, + u8 len) +{ + struct i2c_msg msg[2] = { + {.addr = drv->addr, .flags = 0, .len = 1, .buf = &start}, + {.addr = drv->addr, .flags = I2C_M_RD, .len = len, .buf = buf} }; + + return i2c_transfer(drv->bus->adap, msg, 2) == 2; +} + + +/* probe functions of existing drivers - should only be called in via_aux.c */ +void via_aux_ch7301_probe(struct via_aux_bus *bus); +void via_aux_edid_probe(struct via_aux_bus *bus); +void via_aux_sii164_probe(struct via_aux_bus *bus); +void via_aux_vt1636_probe(struct via_aux_bus *bus); +void via_aux_vt1632_probe(struct via_aux_bus *bus); +void via_aux_vt1631_probe(struct via_aux_bus *bus); +void via_aux_vt1625_probe(struct via_aux_bus *bus); +void via_aux_vt1622_probe(struct via_aux_bus *bus); +void via_aux_vt1621_probe(struct via_aux_bus *bus); + + +#endif /* __VIA_AUX_H__ */ diff --git a/drivers/video/via/via_aux_ch7301.c b/drivers/video/via/via_aux_ch7301.c new file mode 100644 index 000000000000..1cbe5037a6b0 --- /dev/null +++ b/drivers/video/via/via_aux_ch7301.c @@ -0,0 +1,50 @@ +/* + * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE.See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/* + * driver for Chrontel CH7301 DVI Transmitter + */ + +#include <linux/slab.h> +#include "via_aux.h" + + +static const char *name = "CH7301 DVI Transmitter"; + + +static void probe(struct via_aux_bus *bus, u8 addr) +{ + struct via_aux_drv drv = { + .bus = bus, + .addr = addr, + .name = name}; + u8 tmp; + + if (!via_aux_read(&drv, 0x4B, &tmp, 1) || tmp != 0x17) + return; + + printk(KERN_INFO "viafb: Found %s at address 0x%x\n", name, addr); + via_aux_add(&drv); +} + +void via_aux_ch7301_probe(struct via_aux_bus *bus) +{ + probe(bus, 0x75); + probe(bus, 0x76); +} diff --git a/drivers/video/via/via_aux_edid.c b/drivers/video/via/via_aux_edid.c new file mode 100644 index 000000000000..754d4509033f --- /dev/null +++ b/drivers/video/via/via_aux_edid.c @@ -0,0 +1,100 @@ +/* + * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE.See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/* + * generic EDID driver + */ + +#include <linux/slab.h> +#include <linux/fb.h> +#include "via_aux.h" +#include "../edid.h" + + +static const char *name = "EDID"; + + +static void query_edid(struct via_aux_drv *drv) +{ + struct fb_monspecs *spec = drv->data; + unsigned char edid[EDID_LENGTH]; + bool valid = false; + + if (spec) { + fb_destroy_modedb(spec->modedb); + } else { + spec = kmalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return; + } + + spec->version = spec->revision = 0; + if (via_aux_read(drv, 0x00, edid, EDID_LENGTH)) { + fb_edid_to_monspecs(edid, spec); + valid = spec->version || spec->revision; + } + + if (!valid) { + kfree(spec); + spec = NULL; + } else + printk(KERN_DEBUG "EDID: %s %s\n", spec->manufacturer, spec->monitor); + + drv->data = spec; +} + +static const struct fb_videomode *get_preferred_mode(struct via_aux_drv *drv) +{ + struct fb_monspecs *spec = drv->data; + int i; + + if (!spec || !spec->modedb || !(spec->misc & FB_MISC_1ST_DETAIL)) + return NULL; + + for (i = 0; i < spec->modedb_len; i++) { + if (spec->modedb[i].flag & FB_MODE_IS_FIRST && + spec->modedb[i].flag & FB_MODE_IS_DETAILED) + return &spec->modedb[i]; + } + + return NULL; +} + +static void cleanup(struct via_aux_drv *drv) +{ + struct fb_monspecs *spec = drv->data; + + if (spec) + fb_destroy_modedb(spec->modedb); +} + +void via_aux_edid_probe(struct via_aux_bus *bus) +{ + struct via_aux_drv drv = { + .bus = bus, + .addr = 0x50, + .name = name, + .cleanup = cleanup, + .get_preferred_mode = get_preferred_mode}; + + query_edid(&drv); + + /* as EDID devices can be connected/disconnected just add the driver */ + via_aux_add(&drv); +} diff --git a/drivers/video/via/via_aux_sii164.c b/drivers/video/via/via_aux_sii164.c new file mode 100644 index 000000000000..ca1b35f033b1 --- /dev/null +++ b/drivers/video/via/via_aux_sii164.c @@ -0,0 +1,54 @@ +/* + * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE.See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/* + * driver for Silicon Image SiI 164 PanelLink Transmitter + */ + +#include <linux/slab.h> +#include "via_aux.h" + + +static const char *name = "SiI 164 PanelLink Transmitter"; + + +static void probe(struct via_aux_bus *bus, u8 addr) +{ + struct via_aux_drv drv = { + .bus = bus, + .addr = addr, + .name = name}; + /* check vendor id and device id */ + const u8 id[] = {0x01, 0x00, 0x06, 0x00}, len = ARRAY_SIZE(id); + u8 tmp[len]; + + if (!via_aux_read(&drv, 0x00, tmp, len) || memcmp(id, tmp, len)) + return; + + printk(KERN_INFO "viafb: Found %s at address 0x%x\n", name, addr); + via_aux_add(&drv); +} + +void via_aux_sii164_probe(struct via_aux_bus *bus) +{ + u8 i; + + for (i = 0x38; i <= 0x3F; i++) + probe(bus, i); +} diff --git a/drivers/video/via/via_aux_vt1621.c b/drivers/video/via/via_aux_vt1621.c new file mode 100644 index 000000000000..38eca8479898 --- /dev/null +++ b/drivers/video/via/via_aux_vt1621.c @@ -0,0 +1,44 @@ +/* + * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE.See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/* + * driver for VIA VT1621(M) TV Encoder + */ + +#include <linux/slab.h> +#include "via_aux.h" + + +static const char *name = "VT1621(M) TV Encoder"; + + +void via_aux_vt1621_probe(struct via_aux_bus *bus) +{ + struct via_aux_drv drv = { + .bus = bus, + .addr = 0x20, + .name = name}; + u8 tmp; + + if (!via_aux_read(&drv, 0x1B, &tmp, 1) || tmp != 0x02) + return; + + printk(KERN_INFO "viafb: Found %s\n", name); + via_aux_add(&drv); +} diff --git a/drivers/video/via/via_aux_vt1622.c b/drivers/video/via/via_aux_vt1622.c new file mode 100644 index 000000000000..8c79c68ba683 --- /dev/null +++ b/drivers/video/via/via_aux_vt1622.c @@ -0,0 +1,50 @@ +/* + * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE.See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/* + * driver for VIA VT1622(M) Digital TV Encoder + */ + +#include <linux/slab.h> +#include "via_aux.h" + + +static const char *name = "VT1622(M) Digital TV Encoder"; + + +static void probe(struct via_aux_bus *bus, u8 addr) +{ + struct via_aux_drv drv = { + .bus = bus, + .addr = addr, + .name = name}; + u8 tmp; + + if (!via_aux_read(&drv, 0x1B, &tmp, 1) || tmp != 0x03) + return; + + printk(KERN_INFO "viafb: Found %s at address 0x%x\n", name, addr); + via_aux_add(&drv); +} + +void via_aux_vt1622_probe(struct via_aux_bus *bus) +{ + probe(bus, 0x20); + probe(bus, 0x21); +} diff --git a/drivers/video/via/via_aux_vt1625.c b/drivers/video/via/via_aux_vt1625.c new file mode 100644 index 000000000000..03eb30165d36 --- /dev/null +++ b/drivers/video/via/via_aux_vt1625.c @@ -0,0 +1,50 @@ +/* + * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE.See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/* + * driver for VIA VT1625(M) HDTV Encoder + */ + +#include <linux/slab.h> +#include "via_aux.h" + + +static const char *name = "VT1625(M) HDTV Encoder"; + + +static void probe(struct via_aux_bus *bus, u8 addr) +{ + struct via_aux_drv drv = { + .bus = bus, + .addr = addr, + .name = name}; + u8 tmp; + + if (!via_aux_read(&drv, 0x1B, &tmp, 1) || tmp != 0x50) + return; + + printk(KERN_INFO "viafb: Found %s at address 0x%x\n", name, addr); + via_aux_add(&drv); +} + +void via_aux_vt1625_probe(struct via_aux_bus *bus) +{ + probe(bus, 0x20); + probe(bus, 0x21); +} diff --git a/drivers/video/via/via_aux_vt1631.c b/drivers/video/via/via_aux_vt1631.c new file mode 100644 index 000000000000..06e742f1f723 --- /dev/null +++ b/drivers/video/via/via_aux_vt1631.c @@ -0,0 +1,46 @@ +/* + * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE.See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/* + * driver for VIA VT1631 LVDS Transmitter + */ + +#include <linux/slab.h> +#include "via_aux.h" + + +static const char *name = "VT1631 LVDS Transmitter"; + + +void via_aux_vt1631_probe(struct via_aux_bus *bus) +{ + struct via_aux_drv drv = { + .bus = bus, + .addr = 0x38, + .name = name}; + /* check vendor id and device id */ + const u8 id[] = {0x06, 0x11, 0x91, 0x31}, len = ARRAY_SIZE(id); + u8 tmp[len]; + + if (!via_aux_read(&drv, 0x00, tmp, len) || memcmp(id, tmp, len)) + return; + + printk(KERN_INFO "viafb: Found %s\n", name); + via_aux_add(&drv); +} diff --git a/drivers/video/via/via_aux_vt1632.c b/drivers/video/via/via_aux_vt1632.c new file mode 100644 index 000000000000..d24f4cd97401 --- /dev/null +++ b/drivers/video/via/via_aux_vt1632.c @@ -0,0 +1,54 @@ +/* + * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE.See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/* + * driver for VIA VT1632 DVI Transmitter + */ + +#include <linux/slab.h> +#include "via_aux.h" + + +static const char *name = "VT1632 DVI Transmitter"; + + +static void probe(struct via_aux_bus *bus, u8 addr) +{ + struct via_aux_drv drv = { + .bus = bus, + .addr = addr, + .name = name}; + /* check vendor id and device id */ + const u8 id[] = {0x06, 0x11, 0x92, 0x31}, len = ARRAY_SIZE(id); + u8 tmp[len]; + + if (!via_aux_read(&drv, 0x00, tmp, len) || memcmp(id, tmp, len)) + return; + + printk(KERN_INFO "viafb: Found %s at address 0x%x\n", name, addr); + via_aux_add(&drv); +} + +void via_aux_vt1632_probe(struct via_aux_bus *bus) +{ + u8 i; + + for (i = 0x08; i <= 0x0F; i++) + probe(bus, i); +} diff --git a/drivers/video/via/via_aux_vt1636.c b/drivers/video/via/via_aux_vt1636.c new file mode 100644 index 000000000000..9e015c101d4d --- /dev/null +++ b/drivers/video/via/via_aux_vt1636.c @@ -0,0 +1,46 @@ +/* + * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE.See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/* + * driver for VIA VT1636 LVDS Transmitter + */ + +#include <linux/slab.h> +#include "via_aux.h" + + +static const char *name = "VT1636 LVDS Transmitter"; + + +void via_aux_vt1636_probe(struct via_aux_bus *bus) +{ + struct via_aux_drv drv = { + .bus = bus, + .addr = 0x40, + .name = name}; + /* check vendor id and device id */ + const u8 id[] = {0x06, 0x11, 0x45, 0x33}, len = ARRAY_SIZE(id); + u8 tmp[len]; + + if (!via_aux_read(&drv, 0x00, tmp, len) || memcmp(id, tmp, len)) + return; + + printk(KERN_INFO "viafb: Found %s\n", name); + via_aux_add(&drv); +} diff --git a/drivers/video/via/via_i2c.c b/drivers/video/via/via_i2c.c index 78f1405dbab7..dd53058bbbb7 100644 --- a/drivers/video/via/via_i2c.c +++ b/drivers/video/via/via_i2c.c @@ -51,7 +51,7 @@ static void via_i2c_setscl(void *data, int state) val |= 0x01; break; case VIA_PORT_GPIO: - val |= 0x80; + val |= 0x82; break; default: printk(KERN_ERR "viafb_i2c: specify wrong i2c type.\n"); @@ -67,6 +67,9 @@ static int via_i2c_getscl(void *data) int ret = 0; spin_lock_irqsave(&i2c_vdev->reg_lock, flags); + if (adap_data->type == VIA_PORT_GPIO) + via_write_reg_mask(adap_data->io_port, adap_data->ioport_index, + 0, 0x80); if (via_read_reg(adap_data->io_port, adap_data->ioport_index) & 0x08) ret = 1; spin_unlock_irqrestore(&i2c_vdev->reg_lock, flags); @@ -80,6 +83,9 @@ static int via_i2c_getsda(void *data) int ret = 0; spin_lock_irqsave(&i2c_vdev->reg_lock, flags); + if (adap_data->type == VIA_PORT_GPIO) + via_write_reg_mask(adap_data->io_port, adap_data->ioport_index, + 0, 0x40); if (via_read_reg(adap_data->io_port, adap_data->ioport_index) & 0x04) ret = 1; spin_unlock_irqrestore(&i2c_vdev->reg_lock, flags); @@ -103,7 +109,7 @@ static void via_i2c_setsda(void *data, int state) val |= 0x01; break; case VIA_PORT_GPIO: - val |= 0x40; + val |= 0x42; break; default: printk(KERN_ERR "viafb_i2c: specify wrong i2c type.\n"); diff --git a/drivers/video/via/viafbdev.c b/drivers/video/via/viafbdev.c index a13c258bd32f..0c8837565bc7 100644 --- a/drivers/video/via/viafbdev.c +++ b/drivers/video/via/viafbdev.c @@ -24,6 +24,7 @@ #include <linux/slab.h> #include <linux/stat.h> #include <linux/via-core.h> +#include <linux/via_i2c.h> #include <asm/olpc.h> #define _MASTER_FILE @@ -286,26 +287,22 @@ static int viafb_set_par(struct fb_info *info) viafb_second_yres, viafb_bpp1, 1); } - refresh = viafb_get_refresh(info->var.xres, info->var.yres, - get_var_refresh(&info->var)); - if (viafb_get_best_mode(viafbinfo->var.xres, viafbinfo->var.yres, - refresh)) { - if (viafb_dual_fb && viapar->iga_path == IGA2) { - viafb_bpp1 = info->var.bits_per_pixel; - viafb_refresh1 = refresh; - } else { - viafb_bpp = info->var.bits_per_pixel; - viafb_refresh = refresh; - } - - if (info->var.accel_flags & FB_ACCELF_TEXT) - info->flags &= ~FBINFO_HWACCEL_DISABLED; - else - info->flags |= FBINFO_HWACCEL_DISABLED; - viafb_setmode(info->var.bits_per_pixel, viafb_bpp1); - viafb_pan_display(&info->var, info); + refresh = get_var_refresh(&info->var); + if (viafb_dual_fb && viapar->iga_path == IGA2) { + viafb_bpp1 = info->var.bits_per_pixel; + viafb_refresh1 = refresh; + } else { + viafb_bpp = info->var.bits_per_pixel; + viafb_refresh = refresh; } + if (info->var.accel_flags & FB_ACCELF_TEXT) + info->flags &= ~FBINFO_HWACCEL_DISABLED; + else + info->flags |= FBINFO_HWACCEL_DISABLED; + viafb_setmode(); + viafb_pan_display(&info->var, info); + return 0; } @@ -1670,12 +1667,23 @@ static void viafb_remove_proc(struct viafb_shared *shared) } #undef IS_VT1636 -static int parse_mode(const char *str, u32 *xres, u32 *yres) +static int parse_mode(const char *str, u32 devices, u32 *xres, u32 *yres) { + const struct fb_videomode *mode = NULL; char *ptr; if (!str) { - if (machine_is_olpc()) { + if (devices == VIA_CRT) + mode = via_aux_get_preferred_mode( + viaparinfo->shared->i2c_26); + else if (devices == VIA_DVP1) + mode = via_aux_get_preferred_mode( + viaparinfo->shared->i2c_31); + + if (mode) { + *xres = mode->xres; + *yres = mode->yres; + } else if (machine_is_olpc()) { *xres = 1200; *yres = 900; } else { @@ -1729,6 +1737,31 @@ static struct viafb_pm_hooks viafb_fb_pm_hooks = { #endif +static void __devinit i2c_bus_probe(struct viafb_shared *shared) +{ + /* should be always CRT */ + printk(KERN_INFO "viafb: Probing I2C bus 0x26\n"); + shared->i2c_26 = via_aux_probe(viafb_find_i2c_adapter(VIA_PORT_26)); + + /* seems to be usually DVP1 */ + printk(KERN_INFO "viafb: Probing I2C bus 0x31\n"); + shared->i2c_31 = via_aux_probe(viafb_find_i2c_adapter(VIA_PORT_31)); + + /* FIXME: what is this? */ + if (!machine_is_olpc()) { + printk(KERN_INFO "viafb: Probing I2C bus 0x2C\n"); + shared->i2c_2C = via_aux_probe(viafb_find_i2c_adapter(VIA_PORT_2C)); + } + + printk(KERN_INFO "viafb: Finished I2C bus probing"); +} + +static void i2c_bus_free(struct viafb_shared *shared) +{ + via_aux_free(shared->i2c_26); + via_aux_free(shared->i2c_31); + via_aux_free(shared->i2c_2C); +} int __devinit via_fb_pci_probe(struct viafb_dev *vdev) { @@ -1762,6 +1795,7 @@ int __devinit via_fb_pci_probe(struct viafb_dev *vdev) &viaparinfo->shared->lvds_setting_info2; viaparinfo->chip_info = &viaparinfo->shared->chip_info; + i2c_bus_probe(viaparinfo->shared); if (viafb_dual_fb) viafb_SAMM_ON = 1; parse_lcd_port(); @@ -1804,10 +1838,11 @@ int __devinit via_fb_pci_probe(struct viafb_dev *vdev) viafb_second_size * 1024 * 1024; } - parse_mode(viafb_mode, &default_xres, &default_yres); + parse_mode(viafb_mode, viaparinfo->shared->iga1_devices, + &default_xres, &default_yres); if (viafb_SAMM_ON == 1) - parse_mode(viafb_mode1, &viafb_second_xres, - &viafb_second_yres); + parse_mode(viafb_mode1, viaparinfo->shared->iga2_devices, + &viafb_second_xres, &viafb_second_yres); default_var.xres = default_xres; default_var.yres = default_yres; @@ -1915,6 +1950,7 @@ out_fb1_release: if (viafbinfo1) framebuffer_release(viafbinfo1); out_fb_release: + i2c_bus_free(viaparinfo->shared); framebuffer_release(viafbinfo); return rc; } @@ -1927,6 +1963,7 @@ void __devexit via_fb_pci_remove(struct pci_dev *pdev) if (viafb_dual_fb) unregister_framebuffer(viafbinfo1); viafb_remove_proc(viaparinfo->shared); + i2c_bus_free(viaparinfo->shared); framebuffer_release(viafbinfo); if (viafb_dual_fb) framebuffer_release(viafbinfo1); @@ -2033,9 +2070,9 @@ int __init viafb_init(void) if (r < 0) return r; #endif - if (parse_mode(viafb_mode, &dummy_x, &dummy_y) + if (parse_mode(viafb_mode, 0, &dummy_x, &dummy_y) || !viafb_get_best_mode(dummy_x, dummy_y, viafb_refresh) - || parse_mode(viafb_mode1, &dummy_x, &dummy_y) + || parse_mode(viafb_mode1, 0, &dummy_x, &dummy_y) || !viafb_get_best_mode(dummy_x, dummy_y, viafb_refresh1) || viafb_bpp < 0 || viafb_bpp > 32 || viafb_bpp1 < 0 || viafb_bpp1 > 32 diff --git a/drivers/video/via/viafbdev.h b/drivers/video/via/viafbdev.h index d9440635d1d4..f6b2ddf56e94 100644 --- a/drivers/video/via/viafbdev.h +++ b/drivers/video/via/viafbdev.h @@ -26,6 +26,7 @@ #include <linux/fb.h> #include <linux/spinlock.h> +#include "via_aux.h" #include "ioctl.h" #include "share.h" #include "chip.h" @@ -48,6 +49,11 @@ struct viafb_shared { struct proc_dir_entry *iga2_proc_entry; struct viafb_dev *vdev; /* Global dev info */ + /* I2C busses that may have auxiliary devices */ + struct via_aux_bus *i2c_26; + struct via_aux_bus *i2c_31; + struct via_aux_bus *i2c_2C; + /* All the information will be needed to set engine */ struct tmds_setting_information tmds_setting_info; struct lvds_setting_information lvds_setting_info; diff --git a/drivers/video/via/viamode.c b/drivers/video/via/viamode.c index 0911cac1b2ff..0666ab01cf4a 100644 --- a/drivers/video/via/viamode.c +++ b/drivers/video/via/viamode.c @@ -268,591 +268,78 @@ struct VPITTable VPIT = { /* Mode Table */ /********************/ -/* 480x640 */ -static struct crt_mode_table CRTM480x640[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M480X640_R60_HSP, M480X640_R60_VSP, - {624, 480, 480, 144, 504, 48, 663, 640, 640, 23, 641, 3} } /* GTF*/ -}; - -/* 640x480*/ -static struct crt_mode_table CRTM640x480[] = { - /*r_rate,hsp,vsp */ - /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M640X480_R60_HSP, M640X480_R60_VSP, - {800, 640, 640, 160, 656, 96, 525, 480, 480, 45, 490, 2} }, - {REFRESH_75, M640X480_R75_HSP, M640X480_R75_VSP, - {840, 640, 640, 200, 656, 64, 500, 480, 480, 20, 481, 3} }, - {REFRESH_85, M640X480_R85_HSP, M640X480_R85_VSP, - {832, 640, 640, 192, 696, 56, 509, 480, 480, 29, 481, 3} }, - {REFRESH_100, M640X480_R100_HSP, M640X480_R100_VSP, - {848, 640, 640, 208, 680, 64, 509, 480, 480, 29, 481, 3} }, /*GTF*/ - {REFRESH_120, M640X480_R120_HSP, M640X480_R120_VSP, - {848, 640, 640, 208, 680, 64, 515, 480, 480, 35, 481, 3} } /*GTF*/ -}; - -/*720x480 (GTF)*/ -static struct crt_mode_table CRTM720x480[] = { - /*r_rate,hsp,vsp */ - /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M720X480_R60_HSP, M720X480_R60_VSP, - {896, 720, 720, 176, 736, 72, 497, 480, 480, 17, 481, 3} } - -}; - -/*720x576 (GTF)*/ -static struct crt_mode_table CRTM720x576[] = { - /*r_rate,hsp,vsp */ - /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M720X576_R60_HSP, M720X576_R60_VSP, - {912, 720, 720, 192, 744, 72, 597, 576, 576, 21, 577, 3} } -}; - -/* 800x480 (CVT) */ -static struct crt_mode_table CRTM800x480[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M800X480_R60_HSP, M800X480_R60_VSP, - {992, 800, 800, 192, 824, 72, 500, 480, 480, 20, 483, 7} } -}; - -/* 800x600*/ -static struct crt_mode_table CRTM800x600[] = { - /*r_rate,hsp,vsp */ - /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M800X600_R60_HSP, M800X600_R60_VSP, - {1056, 800, 800, 256, 840, 128, 628, 600, 600, 28, 601, 4} }, - {REFRESH_75, M800X600_R75_HSP, M800X600_R75_VSP, - {1056, 800, 800, 256, 816, 80, 625, 600, 600, 25, 601, 3} }, - {REFRESH_85, M800X600_R85_HSP, M800X600_R85_VSP, - {1048, 800, 800, 248, 832, 64, 631, 600, 600, 31, 601, 3} }, - {REFRESH_100, M800X600_R100_HSP, M800X600_R100_VSP, - {1072, 800, 800, 272, 848, 88, 636, 600, 600, 36, 601, 3} }, - {REFRESH_120, M800X600_R120_HSP, M800X600_R120_VSP, - {1088, 800, 800, 288, 856, 88, 643, 600, 600, 43, 601, 3} } -}; - -/* 848x480 (CVT) */ -static struct crt_mode_table CRTM848x480[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M848X480_R60_HSP, M848X480_R60_VSP, - {1056, 848, 848, 208, 872, 80, 500, 480, 480, 20, 483, 5} } -}; - -/*856x480 (GTF) convert to 852x480*/ -static struct crt_mode_table CRTM852x480[] = { - /*r_rate,hsp,vsp */ - /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M852X480_R60_HSP, M852X480_R60_VSP, - {1064, 856, 856, 208, 872, 88, 497, 480, 480, 17, 481, 3} } -}; - -/*1024x512 (GTF)*/ -static struct crt_mode_table CRTM1024x512[] = { - /*r_rate,hsp,vsp */ - /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1024X512_R60_HSP, M1024X512_R60_VSP, - {1296, 1024, 1024, 272, 1056, 104, 531, 512, 512, 19, 513, 3} } - -}; - -/* 1024x600*/ -static struct crt_mode_table CRTM1024x600[] = { - /*r_rate,hsp,vsp */ - /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1024X600_R60_HSP, M1024X600_R60_VSP, - {1312, 1024, 1024, 288, 1064, 104, 622, 600, 600, 22, 601, 3} }, -}; - -/* 1024x768*/ -static struct crt_mode_table CRTM1024x768[] = { - /*r_rate,hsp,vsp */ - /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1024X768_R60_HSP, M1024X768_R60_VSP, - {1344, 1024, 1024, 320, 1048, 136, 806, 768, 768, 38, 771, 6} }, - {REFRESH_75, M1024X768_R75_HSP, M1024X768_R75_VSP, - {1312, 1024, 1024, 288, 1040, 96, 800, 768, 768, 32, 769, 3} }, - {REFRESH_85, M1024X768_R85_HSP, M1024X768_R85_VSP, - {1376, 1024, 1024, 352, 1072, 96, 808, 768, 768, 40, 769, 3} }, - {REFRESH_100, M1024X768_R100_HSP, M1024X768_R100_VSP, - {1392, 1024, 1024, 368, 1096, 112, 814, 768, 768, 46, 769, 3} } -}; - -/* 1152x864*/ -static struct crt_mode_table CRTM1152x864[] = { - /*r_rate,hsp,vsp */ - /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_75, M1152X864_R75_HSP, M1152X864_R75_VSP, - {1600, 1152, 1152, 448, 1216, 128, 900, 864, 864, 36, 865, 3} } - -}; - -/* 1280x720 (HDMI 720P)*/ -static struct crt_mode_table CRTM1280x720[] = { - /*r_rate,hsp,vsp */ - /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1280X720_R60_HSP, M1280X720_R60_VSP, - {1648, 1280, 1280, 368, 1392, 40, 750, 720, 720, 30, 725, 5} }, - {REFRESH_50, M1280X720_R50_HSP, M1280X720_R50_VSP, - {1632, 1280, 1280, 352, 1328, 128, 741, 720, 720, 21, 721, 3} } -}; - -/*1280x768 (GTF)*/ -static struct crt_mode_table CRTM1280x768[] = { - /*r_rate,hsp,vsp */ - /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1280X768_R60_HSP, M1280X768_R60_VSP, - {1680, 1280, 1280, 400, 1344, 136, 795, 768, 768, 27, 769, 3} }, - {REFRESH_50, M1280X768_R50_HSP, M1280X768_R50_VSP, - {1648, 1280, 1280, 368, 1336, 128, 791, 768, 768, 23, 769, 3} } -}; - -/* 1280x800 (CVT) */ -static struct crt_mode_table CRTM1280x800[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1280X800_R60_HSP, M1280X800_R60_VSP, - {1680, 1280, 1280, 400, 1352, 128, 831, 800, 800, 31, 803, 6} } -}; - -/*1280x960*/ -static struct crt_mode_table CRTM1280x960[] = { - /*r_rate,hsp,vsp */ - /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1280X960_R60_HSP, M1280X960_R60_VSP, - {1800, 1280, 1280, 520, 1376, 112, 1000, 960, 960, 40, 961, 3} } -}; - -/* 1280x1024*/ -static struct crt_mode_table CRTM1280x1024[] = { - /*r_rate,hsp,vsp */ - /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1280X1024_R60_HSP, M1280X1024_R60_VSP, - {1688, 1280, 1280, 408, 1328, 112, 1066, 1024, 1024, 42, 1025, - 3} }, - {REFRESH_75, M1280X1024_R75_HSP, M1280X1024_R75_VSP, - {1688, 1280, 1280, 408, 1296, 144, 1066, 1024, 1024, 42, 1025, - 3} }, - {REFRESH_85, M1280X1024_R85_HSP, M1280X1024_R85_VSP, - {1728, 1280, 1280, 448, 1344, 160, 1072, 1024, 1024, 48, 1025, 3} } -}; - -/* 1368x768 (GTF) */ -static struct crt_mode_table CRTM1368x768[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1368X768_R60_HSP, M1368X768_R60_VSP, - {1800, 1368, 1368, 432, 1440, 144, 795, 768, 768, 27, 769, 3} } -}; - -/*1440x1050 (GTF)*/ -static struct crt_mode_table CRTM1440x1050[] = { - /*r_rate,hsp,vsp */ - /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1440X1050_R60_HSP, M1440X1050_R60_VSP, - {1936, 1440, 1440, 496, 1536, 152, 1077, 1040, 1040, 37, 1041, 3} } -}; - -/* 1600x1200*/ -static struct crt_mode_table CRTM1600x1200[] = { - /*r_rate,hsp,vsp */ - /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1600X1200_R60_HSP, M1600X1200_R60_VSP, - {2160, 1600, 1600, 560, 1664, 192, 1250, 1200, 1200, 50, 1201, - 3} }, - {REFRESH_75, M1600X1200_R75_HSP, M1600X1200_R75_VSP, - {2160, 1600, 1600, 560, 1664, 192, 1250, 1200, 1200, 50, 1201, 3} } - -}; - -/* 1680x1050 (CVT) */ -static struct crt_mode_table CRTM1680x1050[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1680x1050_R60_HSP, M1680x1050_R60_VSP, - {2240, 1680, 1680, 560, 1784, 176, 1089, 1050, 1050, 39, 1053, - 6} }, - {REFRESH_75, M1680x1050_R75_HSP, M1680x1050_R75_VSP, - {2272, 1680, 1680, 592, 1800, 176, 1099, 1050, 1050, 49, 1053, 6} } -}; - -/* 1680x1050 (CVT Reduce Blanking) */ -static struct crt_mode_table CRTM1680x1050_RB[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1680x1050_RB_R60_HSP, M1680x1050_RB_R60_VSP, - {1840, 1680, 1680, 160, 1728, 32, 1080, 1050, 1050, 30, 1053, 6} } -}; - -/* 1920x1080 (CVT)*/ -static struct crt_mode_table CRTM1920x1080[] = { - /*r_rate,hsp,vsp */ - /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1920X1080_R60_HSP, M1920X1080_R60_VSP, - {2576, 1920, 1920, 656, 2048, 200, 1120, 1080, 1080, 40, 1083, 5} } -}; - -/* 1920x1080 (CVT with Reduce Blanking) */ -static struct crt_mode_table CRTM1920x1080_RB[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1920X1080_RB_R60_HSP, M1920X1080_RB_R60_VSP, - {2080, 1920, 1920, 160, 1968, 32, 1111, 1080, 1080, 31, 1083, 5} } -}; - -/* 1920x1440*/ -static struct crt_mode_table CRTM1920x1440[] = { - /*r_rate,hsp,vsp */ - /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1920X1440_R60_HSP, M1920X1440_R60_VSP, - {2600, 1920, 1920, 680, 2048, 208, 1500, 1440, 1440, 60, 1441, - 3} }, - {REFRESH_75, M1920X1440_R75_HSP, M1920X1440_R75_VSP, - {2640, 1920, 1920, 720, 2064, 224, 1500, 1440, 1440, 60, 1441, 3} } -}; - -/* 1400x1050 (CVT) */ -static struct crt_mode_table CRTM1400x1050[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1400X1050_R60_HSP, M1400X1050_R60_VSP, - {1864, 1400, 1400, 464, 1488, 144, 1089, 1050, 1050, 39, 1053, - 4} }, - {REFRESH_75, M1400X1050_R75_HSP, M1400X1050_R75_VSP, - {1896, 1400, 1400, 496, 1504, 144, 1099, 1050, 1050, 49, 1053, 4} } -}; - -/* 1400x1050 (CVT Reduce Blanking) */ -static struct crt_mode_table CRTM1400x1050_RB[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1400X1050_RB_R60_HSP, M1400X1050_RB_R60_VSP, - {1560, 1400, 1400, 160, 1448, 32, 1080, 1050, 1050, 30, 1053, 4} } -}; - -/* 960x600 (CVT) */ -static struct crt_mode_table CRTM960x600[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M960X600_R60_HSP, M960X600_R60_VSP, - {1216, 960, 960, 256, 992, 96, 624, 600, 600, 24, 603, 6} } -}; - -/* 1000x600 (GTF) */ -static struct crt_mode_table CRTM1000x600[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1000X600_R60_HSP, M1000X600_R60_VSP, - {1288, 1000, 1000, 288, 1040, 104, 622, 600, 600, 22, 601, 3} } -}; - -/* 1024x576 (GTF) */ -static struct crt_mode_table CRTM1024x576[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1024X576_R60_HSP, M1024X576_R60_VSP, - {1312, 1024, 1024, 288, 1064, 104, 597, 576, 576, 21, 577, 3} } -}; - -/* 1088x612 (CVT) */ -static struct crt_mode_table CRTM1088x612[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1088X612_R60_HSP, M1088X612_R60_VSP, - {1392, 1088, 1088, 304, 1136, 104, 636, 612, 612, 24, 615, 5} } -}; - -/* 1152x720 (CVT) */ -static struct crt_mode_table CRTM1152x720[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1152X720_R60_HSP, M1152X720_R60_VSP, - {1488, 1152, 1152, 336, 1208, 112, 748, 720, 720, 28, 723, 6} } -}; - -/* 1200x720 (GTF) */ -static struct crt_mode_table CRTM1200x720[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1200X720_R60_HSP, M1200X720_R60_VSP, - {1568, 1200, 1200, 368, 1256, 128, 746, 720, 720, 26, 721, 3} } -}; - -/* 1200x900 (DCON) */ -static struct crt_mode_table DCON1200x900[] = { - /* r_rate, hsp, vsp */ - {REFRESH_49, M1200X900_R60_HSP, M1200X900_R60_VSP, - /* The correct htotal is 1240, but this doesn't raster on VX855. */ - /* Via suggested changing to a multiple of 16, hence 1264. */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {1264, 1200, 1200, 64, 1211, 32, 912, 900, 900, 12, 901, 10} } -}; - -/* 1280x600 (GTF) */ -static struct crt_mode_table CRTM1280x600[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1280x600_R60_HSP, M1280x600_R60_VSP, - {1648, 1280, 1280, 368, 1336, 128, 622, 600, 600, 22, 601, 3} } -}; - -/* 1360x768 (CVT) */ -static struct crt_mode_table CRTM1360x768[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1360X768_R60_HSP, M1360X768_R60_VSP, - {1776, 1360, 1360, 416, 1432, 136, 798, 768, 768, 30, 771, 5} } -}; - -/* 1360x768 (CVT Reduce Blanking) */ -static struct crt_mode_table CRTM1360x768_RB[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1360X768_RB_R60_HSP, M1360X768_RB_R60_VSP, - {1520, 1360, 1360, 160, 1408, 32, 790, 768, 768, 22, 771, 5} } -}; - -/* 1366x768 (GTF) */ -static struct crt_mode_table CRTM1366x768[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1368X768_R60_HSP, M1368X768_R60_VSP, - {1800, 1368, 1368, 432, 1440, 144, 795, 768, 768, 27, 769, 3} }, - {REFRESH_50, M1368X768_R50_HSP, M1368X768_R50_VSP, - {1768, 1368, 1368, 400, 1424, 144, 791, 768, 768, 23, 769, 3} } -}; - -/* 1440x900 (CVT) */ -static struct crt_mode_table CRTM1440x900[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1440X900_R60_HSP, M1440X900_R60_VSP, - {1904, 1440, 1440, 464, 1520, 152, 934, 900, 900, 34, 903, 6} }, - {REFRESH_75, M1440X900_R75_HSP, M1440X900_R75_VSP, - {1936, 1440, 1440, 496, 1536, 152, 942, 900, 900, 42, 903, 6} } -}; - -/* 1440x900 (CVT Reduce Blanking) */ -static struct crt_mode_table CRTM1440x900_RB[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1440X900_RB_R60_HSP, M1440X900_RB_R60_VSP, - {1600, 1440, 1440, 160, 1488, 32, 926, 900, 900, 26, 903, 6} } -}; - -/* 1600x900 (CVT) */ -static struct crt_mode_table CRTM1600x900[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1600X900_R60_HSP, M1600X900_R60_VSP, - {2112, 1600, 1600, 512, 1688, 168, 934, 900, 900, 34, 903, 5} } -}; - -/* 1600x900 (CVT Reduce Blanking) */ -static struct crt_mode_table CRTM1600x900_RB[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1600X900_RB_R60_HSP, M1600X900_RB_R60_VSP, - {1760, 1600, 1600, 160, 1648, 32, 926, 900, 900, 26, 903, 5} } -}; - -/* 1600x1024 (GTF) */ -static struct crt_mode_table CRTM1600x1024[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1600X1024_R60_HSP, M1600X1024_R60_VSP, - {2144, 1600, 1600, 544, 1704, 168, 1060, 1024, 1024, 36, 1025, 3} } -}; - -/* 1792x1344 (DMT) */ -static struct crt_mode_table CRTM1792x1344[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1792x1344_R60_HSP, M1792x1344_R60_VSP, - {2448, 1792, 1792, 656, 1920, 200, 1394, 1344, 1344, 50, 1345, 3} } -}; - -/* 1856x1392 (DMT) */ -static struct crt_mode_table CRTM1856x1392[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1856x1392_R60_HSP, M1856x1392_R60_VSP, - {2528, 1856, 1856, 672, 1952, 224, 1439, 1392, 1392, 47, 1393, 3} } -}; - -/* 1920x1200 (CVT) */ -static struct crt_mode_table CRTM1920x1200[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1920X1200_R60_HSP, M1920X1200_R60_VSP, - {2592, 1920, 1920, 672, 2056, 200, 1245, 1200, 1200, 45, 1203, 6} } -}; - -/* 1920x1200 (CVT with Reduce Blanking) */ -static struct crt_mode_table CRTM1920x1200_RB[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M1920X1200_RB_R60_HSP, M1920X1200_RB_R60_VSP, - {2080, 1920, 1920, 160, 1968, 32, 1235, 1200, 1200, 35, 1203, 6} } -}; - -/* 2048x1536 (CVT) */ -static struct crt_mode_table CRTM2048x1536[] = { - /* r_rate, hsp, vsp */ - /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */ - {REFRESH_60, M2048x1536_R60_HSP, M2048x1536_R60_VSP, - {2800, 2048, 2048, 752, 2200, 224, 1592, 1536, 1536, 56, 1539, 4} } -}; - -static struct VideoModeTable viafb_modes[] = { - /* Display : 480x640 (GTF) */ - {CRTM480x640, ARRAY_SIZE(CRTM480x640)}, - - /* Display : 640x480 */ - {CRTM640x480, ARRAY_SIZE(CRTM640x480)}, - - /* Display : 720x480 (GTF) */ - {CRTM720x480, ARRAY_SIZE(CRTM720x480)}, - - /* Display : 720x576 (GTF) */ - {CRTM720x576, ARRAY_SIZE(CRTM720x576)}, - - /* Display : 800x600 */ - {CRTM800x600, ARRAY_SIZE(CRTM800x600)}, - - /* Display : 800x480 (CVT) */ - {CRTM800x480, ARRAY_SIZE(CRTM800x480)}, - - /* Display : 848x480 (CVT) */ - {CRTM848x480, ARRAY_SIZE(CRTM848x480)}, - - /* Display : 852x480 (GTF) */ - {CRTM852x480, ARRAY_SIZE(CRTM852x480)}, - - /* Display : 1024x512 (GTF) */ - {CRTM1024x512, ARRAY_SIZE(CRTM1024x512)}, - - /* Display : 1024x600 */ - {CRTM1024x600, ARRAY_SIZE(CRTM1024x600)}, - - /* Display : 1024x768 */ - {CRTM1024x768, ARRAY_SIZE(CRTM1024x768)}, - - /* Display : 1152x864 */ - {CRTM1152x864, ARRAY_SIZE(CRTM1152x864)}, - - /* Display : 1280x768 (GTF) */ - {CRTM1280x768, ARRAY_SIZE(CRTM1280x768)}, - - /* Display : 960x600 (CVT) */ - {CRTM960x600, ARRAY_SIZE(CRTM960x600)}, - - /* Display : 1000x600 (GTF) */ - {CRTM1000x600, ARRAY_SIZE(CRTM1000x600)}, - - /* Display : 1024x576 (GTF) */ - {CRTM1024x576, ARRAY_SIZE(CRTM1024x576)}, - - /* Display : 1088x612 (GTF) */ - {CRTM1088x612, ARRAY_SIZE(CRTM1088x612)}, - - /* Display : 1152x720 (CVT) */ - {CRTM1152x720, ARRAY_SIZE(CRTM1152x720)}, - - /* Display : 1200x720 (GTF) */ - {CRTM1200x720, ARRAY_SIZE(CRTM1200x720)}, - - /* Display : 1200x900 (DCON) */ - {DCON1200x900, ARRAY_SIZE(DCON1200x900)}, - - /* Display : 1280x600 (GTF) */ - {CRTM1280x600, ARRAY_SIZE(CRTM1280x600)}, - - /* Display : 1280x800 (CVT) */ - {CRTM1280x800, ARRAY_SIZE(CRTM1280x800)}, - - /* Display : 1280x960 */ - {CRTM1280x960, ARRAY_SIZE(CRTM1280x960)}, - - /* Display : 1280x1024 */ - {CRTM1280x1024, ARRAY_SIZE(CRTM1280x1024)}, - - /* Display : 1360x768 (CVT) */ - {CRTM1360x768, ARRAY_SIZE(CRTM1360x768)}, - - /* Display : 1366x768 */ - {CRTM1366x768, ARRAY_SIZE(CRTM1366x768)}, - - /* Display : 1368x768 (GTF) */ - {CRTM1368x768, ARRAY_SIZE(CRTM1368x768)}, - - /* Display : 1440x900 (CVT) */ - {CRTM1440x900, ARRAY_SIZE(CRTM1440x900)}, - - /* Display : 1440x1050 (GTF) */ - {CRTM1440x1050, ARRAY_SIZE(CRTM1440x1050)}, - - /* Display : 1600x900 (CVT) */ - {CRTM1600x900, ARRAY_SIZE(CRTM1600x900)}, - - /* Display : 1600x1024 (GTF) */ - {CRTM1600x1024, ARRAY_SIZE(CRTM1600x1024)}, - - /* Display : 1600x1200 */ - {CRTM1600x1200, ARRAY_SIZE(CRTM1600x1200)}, - - /* Display : 1680x1050 (CVT) */ - {CRTM1680x1050, ARRAY_SIZE(CRTM1680x1050)}, - - /* Display : 1792x1344 (DMT) */ - {CRTM1792x1344, ARRAY_SIZE(CRTM1792x1344)}, - - /* Display : 1856x1392 (DMT) */ - {CRTM1856x1392, ARRAY_SIZE(CRTM1856x1392)}, - - /* Display : 1920x1440 */ - {CRTM1920x1440, ARRAY_SIZE(CRTM1920x1440)}, - - /* Display : 2048x1536 */ - {CRTM2048x1536, ARRAY_SIZE(CRTM2048x1536)}, - - /* Display : 1280x720 */ - {CRTM1280x720, ARRAY_SIZE(CRTM1280x720)}, - - /* Display : 1920x1080 (CVT) */ - {CRTM1920x1080, ARRAY_SIZE(CRTM1920x1080)}, - - /* Display : 1920x1200 (CVT) */ - {CRTM1920x1200, ARRAY_SIZE(CRTM1920x1200)}, - - /* Display : 1400x1050 (CVT) */ - {CRTM1400x1050, ARRAY_SIZE(CRTM1400x1050)} -}; - -static struct VideoModeTable viafb_rb_modes[] = { - /* Display : 1360x768 (CVT Reduce Blanking) */ - {CRTM1360x768_RB, ARRAY_SIZE(CRTM1360x768_RB)}, - - /* Display : 1440x900 (CVT Reduce Blanking) */ - {CRTM1440x900_RB, ARRAY_SIZE(CRTM1440x900_RB)}, - - /* Display : 1400x1050 (CVT Reduce Blanking) */ - {CRTM1400x1050_RB, ARRAY_SIZE(CRTM1400x1050_RB)}, - - /* Display : 1600x900 (CVT Reduce Blanking) */ - {CRTM1600x900_RB, ARRAY_SIZE(CRTM1600x900_RB)}, - - /* Display : 1680x1050 (CVT Reduce Blanking) */ - {CRTM1680x1050_RB, ARRAY_SIZE(CRTM1680x1050_RB)}, - - /* Display : 1920x1080 (CVT Reduce Blanking) */ - {CRTM1920x1080_RB, ARRAY_SIZE(CRTM1920x1080_RB)}, - - /* Display : 1920x1200 (CVT Reduce Blanking) */ - {CRTM1920x1200_RB, ARRAY_SIZE(CRTM1920x1200_RB)} -}; +static const struct fb_videomode viafb_modes[] = { + {NULL, 60, 480, 640, 40285, 72, 24, 19, 1, 48, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 640, 480, 39682, 48, 16, 33, 10, 96, 2, 0, 0, 0}, + {NULL, 75, 640, 480, 31746, 120, 16, 16, 1, 64, 3, 0, 0, 0}, + {NULL, 85, 640, 480, 27780, 80, 56, 25, 1, 56, 3, 0, 0, 0}, + {NULL, 100, 640, 480, 23167, 104, 40, 25, 1, 64, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 120, 640, 480, 19081, 104, 40, 31, 1, 64, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 720, 480, 37426, 88, 16, 13, 1, 72, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 720, 576, 30611, 96, 24, 17, 1, 72, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 800, 600, 25131, 88, 40, 23, 1, 128, 4, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 75, 800, 600, 20202, 160, 16, 21, 1, 80, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 85, 800, 600, 17790, 152, 32, 27, 1, 64, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 100, 800, 600, 14667, 136, 48, 32, 1, 88, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 120, 800, 600, 11911, 144, 56, 39, 1, 88, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 800, 480, 33602, 96, 24, 10, 3, 72, 7, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 848, 480, 31565, 104, 24, 12, 3, 80, 5, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 856, 480, 31517, 104, 16, 13, 1, 88, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1024, 512, 24218, 136, 32, 15, 1, 104, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1024, 600, 20423, 144, 40, 18, 1, 104, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1024, 768, 15385, 160, 24, 29, 3, 136, 6, 0, 0, 0}, + {NULL, 75, 1024, 768, 12703, 176, 16, 28, 1, 96, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 85, 1024, 768, 10581, 208, 48, 36, 1, 96, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 100, 1024, 768, 8825, 184, 72, 42, 1, 112, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 75, 1152, 864, 9259, 256, 64, 32, 1, 128, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1280, 768, 12478, 200, 64, 23, 1, 136, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 50, 1280, 768, 15342, 184, 56, 19, 1, 128, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 960, 600, 21964, 128, 32, 15, 3, 96, 6, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1000, 600, 20803, 144, 40, 18, 1, 104, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1024, 576, 21278, 144, 40, 17, 1, 104, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1088, 612, 18825, 152, 48, 16, 3, 104, 5, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1152, 720, 14974, 168, 56, 19, 3, 112, 6, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1200, 720, 14248, 184, 56, 22, 1, 128, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 49, 1200, 900, 17703, 21, 11, 1, 1, 32, 10, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1280, 600, 16259, 184, 56, 18, 1, 128, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1280, 800, 11938, 200, 72, 22, 3, 128, 6, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1280, 960, 9259, 312, 96, 36, 1, 112, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1280, 1024, 9262, 248, 48, 38, 1, 112, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 75, 1280, 1024, 7409, 248, 16, 38, 1, 144, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 85, 1280, 1024, 6351, 224, 64, 44, 1, 160, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1360, 768, 11759, 208, 72, 22, 3, 136, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1368, 768, 11646, 216, 72, 23, 1, 144, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 50, 1368, 768, 14301, 200, 56, 19, 1, 144, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1368, 768, 11646, 216, 72, 23, 1, 144, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1440, 900, 9372, 232, 80, 25, 3, 152, 6, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 75, 1440, 900, 7311, 248, 96, 33, 3, 152, 6, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1440, 1040, 7993, 248, 96, 33, 1, 152, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1600, 900, 8449, 256, 88, 26, 3, 168, 5, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1600, 1024, 7333, 272, 104, 32, 1, 168, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1600, 1200, 6172, 304, 64, 46, 1, 192, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 75, 1600, 1200, 4938, 304, 64, 46, 1, 192, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1680, 1050, 6832, 280, 104, 30, 3, 176, 6, 0, 0, 0}, + {NULL, 75, 1680, 1050, 5339, 296, 120, 40, 3, 176, 6, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1792, 1344, 4883, 328, 128, 46, 1, 200, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1856, 1392, 4581, 352, 96, 43, 1, 224, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1920, 1440, 4273, 344, 128, 56, 1, 208, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 75, 1920, 1440, 3367, 352, 144, 56, 1, 224, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 2048, 1536, 3738, 376, 152, 49, 3, 224, 4, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1280, 720, 13484, 216, 112, 20, 5, 40, 5, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 50, 1280, 720, 16538, 176, 48, 17, 1, 128, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1920, 1080, 5776, 328, 128, 32, 3, 200, 5, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1920, 1200, 5164, 336, 136, 36, 3, 200, 6, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 60, 1400, 1050, 8210, 232, 88, 32, 3, 144, 4, FB_SYNC_VERT_HIGH_ACT, 0, 0}, + {NULL, 75, 1400, 1050, 6398, 248, 104, 42, 3, 144, 4, FB_SYNC_VERT_HIGH_ACT, 0, 0} }; + +static const struct fb_videomode viafb_rb_modes[] = { + {NULL, 60, 1360, 768, 13879, 80, 48, 14, 3, 32, 5, FB_SYNC_HOR_HIGH_ACT, 0, 0}, + {NULL, 60, 1440, 900, 11249, 80, 48, 17, 3, 32, 6, FB_SYNC_HOR_HIGH_ACT, 0, 0}, + {NULL, 60, 1400, 1050, 9892, 80, 48, 23, 3, 32, 4, FB_SYNC_HOR_HIGH_ACT, 0, 0}, + {NULL, 60, 1600, 900, 10226, 80, 48, 18, 3, 32, 5, FB_SYNC_HOR_HIGH_ACT, 0, 0}, + {NULL, 60, 1680, 1050, 8387, 80, 48, 21, 3, 32, 6, FB_SYNC_HOR_HIGH_ACT, 0, 0}, + {NULL, 60, 1920, 1080, 7212, 80, 48, 23, 3, 32, 5, FB_SYNC_HOR_HIGH_ACT, 0, 0}, + {NULL, 60, 1920, 1200, 6488, 80, 48, 26, 3, 32, 6, FB_SYNC_HOR_HIGH_ACT, 0, 0} }; int NUM_TOTAL_CN400_ModeXregs = ARRAY_SIZE(CN400_ModeXregs); int NUM_TOTAL_CN700_ModeXregs = ARRAY_SIZE(CN700_ModeXregs); @@ -863,56 +350,34 @@ int NUM_TOTAL_CLE266_ModeXregs = ARRAY_SIZE(CLE266_ModeXregs); int NUM_TOTAL_PATCH_MODE = ARRAY_SIZE(res_patch_table); -static struct VideoModeTable *get_modes(struct VideoModeTable *vmt, int n, - int hres, int vres) -{ - int i; - - for (i = 0; i < n; i++) - if (vmt[i].mode_array && - vmt[i].crtc[0].crtc.hor_addr == hres && - vmt[i].crtc[0].crtc.ver_addr == vres) - return &viafb_modes[i]; - - return NULL; -} - -static struct crt_mode_table *get_best_mode(struct VideoModeTable *vmt, - int refresh) +static const struct fb_videomode *get_best_mode( + const struct fb_videomode *modes, int n, + int hres, int vres, int refresh) { - struct crt_mode_table *best; + const struct fb_videomode *best = NULL; int i; - if (!vmt) - return NULL; + for (i = 0; i < n; i++) { + if (modes[i].xres != hres || modes[i].yres != vres) + continue; - best = &vmt->crtc[0]; - for (i = 1; i < vmt->mode_array; i++) { - if (abs(vmt->crtc[i].refresh_rate - refresh) - < abs(best->refresh_rate - refresh)) - best = &vmt->crtc[i]; + if (!best || abs(modes[i].refresh - refresh) < + abs(best->refresh - refresh)) + best = &modes[i]; } return best; } -static struct VideoModeTable *viafb_get_mode(int hres, int vres) -{ - return get_modes(viafb_modes, ARRAY_SIZE(viafb_modes), hres, vres); -} - -struct crt_mode_table *viafb_get_best_mode(int hres, int vres, int refresh) +const struct fb_videomode *viafb_get_best_mode(int hres, int vres, int refresh) { - return get_best_mode(viafb_get_mode(hres, vres), refresh); + return get_best_mode(viafb_modes, ARRAY_SIZE(viafb_modes), + hres, vres, refresh); } -static struct VideoModeTable *viafb_get_rb_mode(int hres, int vres) -{ - return get_modes(viafb_rb_modes, ARRAY_SIZE(viafb_rb_modes), hres, - vres); -} - -struct crt_mode_table *viafb_get_best_rb_mode(int hres, int vres, int refresh) +const struct fb_videomode *viafb_get_best_rb_mode(int hres, int vres, + int refresh) { - return get_best_mode(viafb_get_rb_mode(hres, vres), refresh); + return get_best_mode(viafb_rb_modes, ARRAY_SIZE(viafb_rb_modes), + hres, vres, refresh); } diff --git a/drivers/video/via/viamode.h b/drivers/video/via/viamode.h index 5917a2b00e1b..dd19106698e7 100644 --- a/drivers/video/via/viamode.h +++ b/drivers/video/via/viamode.h @@ -31,11 +31,6 @@ struct VPITTable { unsigned char AR[StdAR]; }; -struct VideoModeTable { - struct crt_mode_table *crtc; - int mode_array; -}; - struct patch_table { int table_length; struct io_reg *io_reg_table; @@ -60,7 +55,9 @@ extern struct io_reg PM1024x768[]; extern struct patch_table res_patch_table[]; extern struct VPITTable VPIT; -struct crt_mode_table *viafb_get_best_mode(int hres, int vres, int refresh); -struct crt_mode_table *viafb_get_best_rb_mode(int hres, int vres, int refresh); +const struct fb_videomode *viafb_get_best_mode(int hres, int vres, + int refresh); +const struct fb_videomode *viafb_get_best_rb_mode(int hres, int vres, + int refresh); #endif /* __VIAMODE_H__ */ |