diff options
Diffstat (limited to 'drivers/video')
83 files changed, 7409 insertions, 626 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index e7068c50880..4c1546f71d5 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -21,8 +21,6 @@ source "drivers/gpu/vga/Kconfig" source "drivers/gpu/drm/Kconfig" -source "drivers/gpu/stub/Kconfig" - config VGASTATE tristate default n @@ -33,6 +31,30 @@ config VIDEO_OUTPUT_CONTROL This framework adds support for low-level control of the video output switch. +config DISPLAY_TIMING + bool + +config VIDEOMODE + bool + +config OF_DISPLAY_TIMING + bool "Enable device tree display timing support" + depends on OF + select DISPLAY_TIMING + help + helper to parse display timings from the devicetree + +config OF_VIDEOMODE + bool "Enable device tree videomode support" + depends on OF + select VIDEOMODE + select OF_DISPLAY_TIMING + help + helper to get videomodes from the devicetree + +config HDMI + bool + menuconfig FB tristate "Support for frame buffer devices" ---help--- @@ -364,7 +386,7 @@ config FB_SA1100 Y here. config FB_IMX - tristate "Freescale i.MX LCD support" + tristate "Freescale i.MX1/21/25/27 LCD support" depends on FB && IMX_HAVE_PLATFORM_IMX_FB select FB_CFB_FILLRECT select FB_CFB_COPYAREA @@ -1112,8 +1134,8 @@ config FB_RIVA_BACKLIGHT 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 + tristate "Intel740 support" + depends on FB && PCI select FB_MODE_HELPERS select FB_CFB_FILLRECT select FB_CFB_COPYAREA @@ -1124,8 +1146,8 @@ config FB_I740 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 + tristate "Intel 810/815 support" + depends on FB && PCI && X86_32 && AGP_INTEL select FB_MODE_HELPERS select FB_CFB_FILLRECT select FB_CFB_COPYAREA @@ -1187,8 +1209,8 @@ config FB_CARILLO_RANCH This driver supports the LE80578 (Carillo Ranch) board config FB_INTEL - tristate "Intel 830M/845G/852GM/855GM/865G/915G/945G/945GM/965G/965GM support (EXPERIMENTAL)" - depends on EXPERIMENTAL && FB && PCI && X86 && AGP_INTEL && EXPERT + tristate "Intel 830M/845G/852GM/855GM/865G/915G/945G/945GM/965G/965GM support" + depends on FB && PCI && X86 && AGP_INTEL && EXPERT select FB_MODE_HELPERS select FB_CFB_FILLRECT select FB_CFB_COPYAREA @@ -1432,7 +1454,7 @@ config FB_ATY_CT is at <http://support.ati.com/products/pc/mach64/mach64.html>. config FB_ATY_GENERIC_LCD - bool "Mach64 generic LCD support (EXPERIMENTAL)" + bool "Mach64 generic LCD support" depends on FB_ATY_CT help Say Y if you have a laptop with an ATI Rage LT PRO, Rage Mobility, @@ -1479,7 +1501,7 @@ config FB_S3_DDC config FB_SAVAGE tristate "S3 Savage support" - depends on FB && PCI && EXPERIMENTAL + depends on FB && PCI select FB_MODE_HELPERS select FB_CFB_FILLRECT select FB_CFB_COPYAREA @@ -1633,15 +1655,15 @@ config FB_3DFX module will be called tdfxfb. config FB_3DFX_ACCEL - bool "3Dfx Acceleration functions (EXPERIMENTAL)" - depends on FB_3DFX && EXPERIMENTAL + bool "3Dfx Acceleration functions" + depends on FB_3DFX ---help--- This will compile the 3Dfx Banshee/Voodoo3/VSA-100 frame buffer device driver with acceleration functions. config FB_3DFX_I2C bool "Enable DDC/I2C support" - depends on FB_3DFX && EXPERIMENTAL + depends on FB_3DFX select FB_DDC default y help @@ -1714,8 +1736,8 @@ config FB_ARK and ICS 5342 RAMDAC. config FB_PM3 - tristate "Permedia3 support (EXPERIMENTAL)" - depends on FB && PCI && EXPERIMENTAL + tristate "Permedia3 support" + depends on FB && PCI select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT @@ -2025,7 +2047,8 @@ config FB_TMIO_ACCELL config FB_S3C tristate "Samsung S3C framebuffer support" - depends on FB && (S3C_DEV_FB || S5P_DEV_FIMD0) + depends on FB && (CPU_S3C2416 || ARCH_S3C64XX || ARCH_S5P64X0 || \ + ARCH_S5PC100 || ARCH_S5PV210 || ARCH_EXYNOS) select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT @@ -2183,6 +2206,15 @@ config FB_XILINX framebuffer. ML300 carries a 640*480 LCD display on the board, ML403 uses a standard DB15 VGA connector. +config FB_GOLDFISH + tristate "Goldfish Framebuffer" + depends on FB + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + ---help--- + Framebuffer driver for Goldfish Virtual Platform + config FB_COBALT tristate "Cobalt server LCD frame buffer support" depends on FB && (MIPS_COBALT || MIPS_SEAD3) @@ -2422,6 +2454,7 @@ config FB_PUV3_UNIGFX source "drivers/video/omap/Kconfig" source "drivers/video/omap2/Kconfig" source "drivers/video/exynos/Kconfig" +source "drivers/video/mmp/Kconfig" source "drivers/video/backlight/Kconfig" if VT diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 768a137a1ba..9df387334cb 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -5,6 +5,7 @@ # Each configuration option enables a list of files. obj-$(CONFIG_VGASTATE) += vgastate.o +obj-$(CONFIG_HDMI) += hdmi.o obj-y += fb_notify.o obj-$(CONFIG_FB) += fb.o fb-y := fbmem.o fbmon.o fbcmap.o fbsysfs.o \ @@ -98,6 +99,7 @@ obj-$(CONFIG_FB_ATMEL) += atmel_lcdfb.o obj-$(CONFIG_FB_PVR2) += pvr2fb.o obj-$(CONFIG_FB_VOODOO1) += sstfb.o obj-$(CONFIG_FB_ARMCLCD) += amba-clcd.o +obj-$(CONFIG_FB_GOLDFISH) += goldfishfb.o obj-$(CONFIG_FB_68328) += 68328fb.o obj-$(CONFIG_FB_GBE) += gbefb.o obj-$(CONFIG_FB_CIRRUS) += cirrusfb.o @@ -105,6 +107,7 @@ obj-$(CONFIG_FB_ASILIANT) += asiliantfb.o obj-$(CONFIG_FB_PXA) += pxafb.o obj-$(CONFIG_FB_PXA168) += pxa168fb.o obj-$(CONFIG_PXA3XX_GCU) += pxa3xx-gcu.o +obj-$(CONFIG_MMP_DISP) += mmp/ obj-$(CONFIG_FB_W100) += w100fb.o obj-$(CONFIG_FB_TMIO) += tmiofb.o obj-$(CONFIG_FB_AU1100) += au1100fb.o @@ -168,3 +171,7 @@ obj-$(CONFIG_FB_VIRTUAL) += vfb.o #video output switch sysfs driver obj-$(CONFIG_VIDEO_OUTPUT_CONTROL) += output.o +obj-$(CONFIG_DISPLAY_TIMING) += display_timing.o +obj-$(CONFIG_OF_DISPLAY_TIMING) += of_display_timing.o +obj-$(CONFIG_VIDEOMODE) += videomode.o +obj-$(CONFIG_OF_VIDEOMODE) += of_videomode.o diff --git a/drivers/video/auo_k190x.c b/drivers/video/auo_k190x.c index 97f79356141..53846cb534d 100644 --- a/drivers/video/auo_k190x.c +++ b/drivers/video/auo_k190x.c @@ -11,6 +11,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/gpio.h> +#include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/fb.h> #include <linux/delay.h> diff --git a/drivers/video/backlight/88pm860x_bl.c b/drivers/video/backlight/88pm860x_bl.c index c072ed9aea3..2cd63507ed7 100644 --- a/drivers/video/backlight/88pm860x_bl.c +++ b/drivers/video/backlight/88pm860x_bl.c @@ -165,8 +165,10 @@ static int pm860x_backlight_dt_init(struct platform_device *pdev, struct pm860x_backlight_data *data, char *name) { - struct device_node *nproot = pdev->dev.parent->of_node, *np; + struct device_node *nproot, *np; int iset = 0; + + nproot = of_node_get(pdev->dev.parent->of_node); if (!nproot) return -ENODEV; nproot = of_find_node_by_name(nproot, "backlights"); @@ -184,6 +186,7 @@ static int pm860x_backlight_dt_init(struct platform_device *pdev, break; } } + of_node_put(nproot); return 0; } #else diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 765a945f8ea..db10d0120d2 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -126,6 +126,21 @@ config LCD_AMS369FG06 If you have an AMS369FG06 AMOLED Panel, say Y to enable its LCD control driver. +config LCD_LMS501KF03 + tristate "LMS501KF03 LCD Driver" + depends on SPI + default n + help + If you have an LMS501KF03 LCD Panel, say Y to enable its + LCD control driver. + +config LCD_HX8357 + tristate "Himax HX-8357 LCD Driver" + depends on SPI + help + If you have a HX-8357 LCD panel, say Y to enable its LCD control + driver. + endif # LCD_CLASS_DEVICE # @@ -366,9 +381,15 @@ 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 + This supports TI LP8550, LP8551, LP8552, LP8553, LP8556 and LP8557 backlight driver. +config BACKLIGHT_LP8788 + tristate "Backlight driver for TI LP8788 MFD" + depends on BACKLIGHT_CLASS_DEVICE && MFD_LP8788 + help + This supports TI LP8788 backlight driver. + config BACKLIGHT_OT200 tristate "Backlight driver for ot200 visualisation device" depends on BACKLIGHT_CLASS_DEVICE && CS5535_MFGPT && GPIO_CS5535 @@ -390,6 +411,13 @@ config BACKLIGHT_TPS65217 If you have a Texas Instruments TPS65217 say Y to enable the backlight driver. +config BACKLIGHT_AS3711 + tristate "AS3711 Backlight" + depends on BACKLIGHT_CLASS_DEVICE && MFD_AS3711 + help + If you have an Austrian Microsystems AS3711 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 e7ce7291635..96c4d620c5c 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -1,47 +1,51 @@ # Backlight & LCD drivers -obj-$(CONFIG_LCD_CLASS_DEVICE) += lcd.o -obj-$(CONFIG_LCD_CORGI) += corgi_lcd.o -obj-$(CONFIG_LCD_HP700) += jornada720_lcd.o -obj-$(CONFIG_LCD_L4F00242T03) += l4f00242t03.o -obj-$(CONFIG_LCD_LMS283GF05) += lms283gf05.o -obj-$(CONFIG_LCD_LTV350QV) += ltv350qv.o -obj-$(CONFIG_LCD_ILI9320) += ili9320.o -obj-$(CONFIG_LCD_PLATFORM) += platform_lcd.o -obj-$(CONFIG_LCD_VGG2432A4) += vgg2432a4.o -obj-$(CONFIG_LCD_TDO24M) += tdo24m.o -obj-$(CONFIG_LCD_TOSA) += tosa_lcd.o -obj-$(CONFIG_LCD_S6E63M0) += s6e63m0.o -obj-$(CONFIG_LCD_LD9040) += ld9040.o -obj-$(CONFIG_LCD_AMS369FG06) += ams369fg06.o +obj-$(CONFIG_LCD_AMS369FG06) += ams369fg06.o +obj-$(CONFIG_LCD_CLASS_DEVICE) += lcd.o +obj-$(CONFIG_LCD_CORGI) += corgi_lcd.o +obj-$(CONFIG_LCD_HP700) += jornada720_lcd.o +obj-$(CONFIG_LCD_HX8357) += hx8357.o +obj-$(CONFIG_LCD_ILI9320) += ili9320.o +obj-$(CONFIG_LCD_L4F00242T03) += l4f00242t03.o +obj-$(CONFIG_LCD_LD9040) += ld9040.o +obj-$(CONFIG_LCD_LMS283GF05) += lms283gf05.o +obj-$(CONFIG_LCD_LMS501KF03) += lms501kf03.o +obj-$(CONFIG_LCD_LTV350QV) += ltv350qv.o +obj-$(CONFIG_LCD_PLATFORM) += platform_lcd.o +obj-$(CONFIG_LCD_S6E63M0) += s6e63m0.o +obj-$(CONFIG_LCD_TDO24M) += tdo24m.o +obj-$(CONFIG_LCD_TOSA) += tosa_lcd.o +obj-$(CONFIG_LCD_VGG2432A4) += vgg2432a4.o -obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o -obj-$(CONFIG_BACKLIGHT_ATMEL_PWM) += atmel-pwm-bl.o -obj-$(CONFIG_BACKLIGHT_EP93XX) += ep93xx_bl.o -obj-$(CONFIG_BACKLIGHT_GENERIC) += generic_bl.o -obj-$(CONFIG_BACKLIGHT_HP700) += jornada720_bl.o -obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o -obj-$(CONFIG_BACKLIGHT_LM3533) += lm3533_bl.o -obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o -obj-$(CONFIG_BACKLIGHT_LM3630) += lm3630_bl.o -obj-$(CONFIG_BACKLIGHT_LM3639) += lm3639_bl.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_CARILLO_RANCH) += cr_bllcd.o -obj-$(CONFIG_BACKLIGHT_PWM) += pwm_bl.o -obj-$(CONFIG_BACKLIGHT_DA903X) += da903x_bl.o -obj-$(CONFIG_BACKLIGHT_DA9052) += da9052_bl.o -obj-$(CONFIG_BACKLIGHT_MAX8925) += max8925_bl.o -obj-$(CONFIG_BACKLIGHT_APPLE) += apple_bl.o -obj-$(CONFIG_BACKLIGHT_TOSA) += tosa_bl.o -obj-$(CONFIG_BACKLIGHT_SAHARA) += kb3886_bl.o -obj-$(CONFIG_BACKLIGHT_WM831X) += wm831x_bl.o -obj-$(CONFIG_BACKLIGHT_ADP5520) += adp5520_bl.o -obj-$(CONFIG_BACKLIGHT_ADP8860) += adp8860_bl.o -obj-$(CONFIG_BACKLIGHT_ADP8870) += adp8870_bl.o -obj-$(CONFIG_BACKLIGHT_88PM860X) += 88pm860x_bl.o +obj-$(CONFIG_BACKLIGHT_88PM860X) += 88pm860x_bl.o +obj-$(CONFIG_BACKLIGHT_AAT2870) += aat2870_bl.o +obj-$(CONFIG_BACKLIGHT_ADP5520) += adp5520_bl.o +obj-$(CONFIG_BACKLIGHT_ADP8860) += adp8860_bl.o +obj-$(CONFIG_BACKLIGHT_ADP8870) += adp8870_bl.o +obj-$(CONFIG_BACKLIGHT_APPLE) += apple_bl.o +obj-$(CONFIG_BACKLIGHT_AS3711) += as3711_bl.o +obj-$(CONFIG_BACKLIGHT_ATMEL_PWM) += atmel-pwm-bl.o +obj-$(CONFIG_BACKLIGHT_CARILLO_RANCH) += cr_bllcd.o +obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o +obj-$(CONFIG_BACKLIGHT_DA903X) += da903x_bl.o +obj-$(CONFIG_BACKLIGHT_DA9052) += da9052_bl.o +obj-$(CONFIG_BACKLIGHT_EP93XX) += ep93xx_bl.o +obj-$(CONFIG_BACKLIGHT_GENERIC) += generic_bl.o +obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o +obj-$(CONFIG_BACKLIGHT_HP700) += jornada720_bl.o +obj-$(CONFIG_BACKLIGHT_LM3533) += lm3533_bl.o +obj-$(CONFIG_BACKLIGHT_LM3630) += lm3630_bl.o +obj-$(CONFIG_BACKLIGHT_LM3639) += lm3639_bl.o +obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o +obj-$(CONFIG_BACKLIGHT_LP855X) += lp855x_bl.o +obj-$(CONFIG_BACKLIGHT_LP8788) += lp8788_bl.o +obj-$(CONFIG_BACKLIGHT_MAX8925) += max8925_bl.o +obj-$(CONFIG_BACKLIGHT_OMAP1) += omap1_bl.o +obj-$(CONFIG_BACKLIGHT_OT200) += ot200_bl.o +obj-$(CONFIG_BACKLIGHT_PANDORA) += pandora_bl.o obj-$(CONFIG_BACKLIGHT_PCF50633) += pcf50633-backlight.o -obj-$(CONFIG_BACKLIGHT_AAT2870) += aat2870_bl.o -obj-$(CONFIG_BACKLIGHT_OT200) += ot200_bl.o -obj-$(CONFIG_BACKLIGHT_TPS65217) += tps65217_bl.o +obj-$(CONFIG_BACKLIGHT_PWM) += pwm_bl.o +obj-$(CONFIG_BACKLIGHT_SAHARA) += kb3886_bl.o +obj-$(CONFIG_BACKLIGHT_TOSA) += tosa_bl.o +obj-$(CONFIG_BACKLIGHT_TPS65217) += tps65217_bl.o +obj-$(CONFIG_BACKLIGHT_WM831X) += wm831x_bl.o diff --git a/drivers/video/backlight/aat2870_bl.c b/drivers/video/backlight/aat2870_bl.c index 7ff752288b9..c6fc668d623 100644 --- a/drivers/video/backlight/aat2870_bl.c +++ b/drivers/video/backlight/aat2870_bl.c @@ -74,7 +74,7 @@ static int aat2870_bl_get_brightness(struct backlight_device *bd) static int aat2870_bl_update_status(struct backlight_device *bd) { - struct aat2870_bl_driver_data *aat2870_bl = dev_get_drvdata(&bd->dev); + struct aat2870_bl_driver_data *aat2870_bl = bl_get_data(bd); struct aat2870_data *aat2870 = dev_get_drvdata(aat2870_bl->pdev->dev.parent); int brightness = bd->props.brightness; diff --git a/drivers/video/backlight/adp8860_bl.c b/drivers/video/backlight/adp8860_bl.c index 6bb72c0cb80..a77c9cad332 100644 --- a/drivers/video/backlight/adp8860_bl.c +++ b/drivers/video/backlight/adp8860_bl.c @@ -783,7 +783,7 @@ static int adp8860_i2c_suspend(struct i2c_client *client, pm_message_t message) static int adp8860_i2c_resume(struct i2c_client *client) { - adp8860_set_bits(client, ADP8860_MDCR, NSTBY); + adp8860_set_bits(client, ADP8860_MDCR, NSTBY | BLEN); return 0; } diff --git a/drivers/video/backlight/adp8870_bl.c b/drivers/video/backlight/adp8870_bl.c index 63c882b8177..712c25a0d8f 100644 --- a/drivers/video/backlight/adp8870_bl.c +++ b/drivers/video/backlight/adp8870_bl.c @@ -957,7 +957,7 @@ static int adp8870_i2c_suspend(struct i2c_client *client, pm_message_t message) static int adp8870_i2c_resume(struct i2c_client *client) { - adp8870_set_bits(client, ADP8870_MDCR, NSTBY); + adp8870_set_bits(client, ADP8870_MDCR, NSTBY | BLEN); return 0; } diff --git a/drivers/video/backlight/ams369fg06.c b/drivers/video/backlight/ams369fg06.c index f57e1905236..c02aa2c2575 100644 --- a/drivers/video/backlight/ams369fg06.c +++ b/drivers/video/backlight/ams369fg06.c @@ -10,25 +10,16 @@ * 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/wait.h> -#include <linux/module.h> -#include <linux/fb.h> +#include <linux/backlight.h> #include <linux/delay.h> +#include <linux/fb.h> #include <linux/gpio.h> -#include <linux/spi/spi.h> #include <linux/lcd.h> -#include <linux/backlight.h> +#include <linux/module.h> +#include <linux/spi/spi.h> +#include <linux/wait.h> #define SLEEPMSEC 0x1000 #define ENDDEF 0x2000 @@ -210,8 +201,9 @@ static int ams369fg06_panel_send_sequence(struct ams369fg06 *lcd, ret = ams369fg06_spi_write(lcd, wbuf[i], wbuf[i+1]); if (ret) break; - } else - mdelay(wbuf[i+1]); + } else { + msleep(wbuf[i+1]); + } i += 2; } @@ -313,41 +305,29 @@ static int ams369fg06_ldi_disable(struct ams369fg06 *lcd) static int ams369fg06_power_is_on(int power) { - return ((power) <= FB_BLANK_NORMAL); + return power <= FB_BLANK_NORMAL; } static int ams369fg06_power_on(struct ams369fg06 *lcd) { int ret = 0; - struct lcd_platform_data *pd = NULL; - struct backlight_device *bd = NULL; + struct lcd_platform_data *pd; + struct backlight_device *bd; pd = lcd->lcd_pd; - if (!pd) { - dev_err(lcd->dev, "platform data is NULL.\n"); - return -EFAULT; - } - bd = lcd->bd; - if (!bd) { - dev_err(lcd->dev, "backlight device is NULL.\n"); - return -EFAULT; - } - if (!pd->power_on) { - dev_err(lcd->dev, "power_on is NULL.\n"); - return -EFAULT; - } else { + if (pd->power_on) { pd->power_on(lcd->ld, 1); - mdelay(pd->power_on_delay); + msleep(pd->power_on_delay); } if (!pd->reset) { dev_err(lcd->dev, "reset is NULL.\n"); - return -EFAULT; + return -EINVAL; } else { pd->reset(lcd->ld); - mdelay(pd->reset_delay); + msleep(pd->reset_delay); } ret = ams369fg06_ldi_init(lcd); @@ -374,14 +354,10 @@ static int ams369fg06_power_on(struct ams369fg06 *lcd) static int ams369fg06_power_off(struct ams369fg06 *lcd) { - int ret = 0; - struct lcd_platform_data *pd = NULL; + int ret; + struct lcd_platform_data *pd; pd = lcd->lcd_pd; - if (!pd) { - dev_err(lcd->dev, "platform data is NULL\n"); - return -EFAULT; - } ret = ams369fg06_ldi_disable(lcd); if (ret) { @@ -389,12 +365,9 @@ static int ams369fg06_power_off(struct ams369fg06 *lcd) return -EIO; } - mdelay(pd->power_off_delay); + msleep(pd->power_off_delay); - if (!pd->power_on) { - dev_err(lcd->dev, "power_on is NULL.\n"); - return -EFAULT; - } else + if (pd->power_on) pd->power_on(lcd->ld, 0); return 0; @@ -446,7 +419,7 @@ static int ams369fg06_set_brightness(struct backlight_device *bd) { int ret = 0; int brightness = bd->props.brightness; - struct ams369fg06 *lcd = dev_get_drvdata(&bd->dev); + struct ams369fg06 *lcd = bl_get_data(bd); if (brightness < MIN_BRIGHTNESS || brightness > bd->props.max_brightness) { @@ -501,7 +474,7 @@ static int ams369fg06_probe(struct spi_device *spi) lcd->lcd_pd = spi->dev.platform_data; if (!lcd->lcd_pd) { dev_err(&spi->dev, "platform data is NULL\n"); - return -EFAULT; + return -EINVAL; } ld = lcd_device_register("ams369fg06", &spi->dev, lcd, @@ -534,10 +507,11 @@ static int ams369fg06_probe(struct spi_device *spi) lcd->power = FB_BLANK_POWERDOWN; ams369fg06_power(lcd, FB_BLANK_UNBLANK); - } else + } else { lcd->power = FB_BLANK_UNBLANK; + } - dev_set_drvdata(&spi->dev, lcd); + spi_set_drvdata(spi, lcd); dev_info(&spi->dev, "ams369fg06 panel driver has been probed.\n"); @@ -550,7 +524,7 @@ out_lcd_unregister: static int ams369fg06_remove(struct spi_device *spi) { - struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev); + struct ams369fg06 *lcd = spi_get_drvdata(spi); ams369fg06_power(lcd, FB_BLANK_POWERDOWN); backlight_device_unregister(lcd->bd); @@ -560,44 +534,26 @@ static int ams369fg06_remove(struct spi_device *spi) } #if defined(CONFIG_PM) -static unsigned int before_power; - static int ams369fg06_suspend(struct spi_device *spi, pm_message_t mesg) { - int ret = 0; - struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev); + struct ams369fg06 *lcd = spi_get_drvdata(spi); dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power); - before_power = lcd->power; - /* * when lcd panel is suspend, lcd panel becomes off * regardless of status. */ - ret = ams369fg06_power(lcd, FB_BLANK_POWERDOWN); - - return ret; + return ams369fg06_power(lcd, FB_BLANK_POWERDOWN); } static int ams369fg06_resume(struct spi_device *spi) { - int ret = 0; - struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev); + struct ams369fg06 *lcd = spi_get_drvdata(spi); - /* - * after suspended, if lcd panel status is FB_BLANK_UNBLANK - * (at that time, before_power is FB_BLANK_UNBLANK) then - * it changes that status to FB_BLANK_POWERDOWN to get lcd on. - */ - if (before_power == FB_BLANK_UNBLANK) - lcd->power = FB_BLANK_POWERDOWN; - - dev_dbg(&spi->dev, "before_power = %d\n", before_power); + lcd->power = FB_BLANK_POWERDOWN; - ret = ams369fg06_power(lcd, before_power); - - return ret; + return ams369fg06_power(lcd, FB_BLANK_UNBLANK); } #else #define ams369fg06_suspend NULL @@ -606,7 +562,7 @@ static int ams369fg06_resume(struct spi_device *spi) static void ams369fg06_shutdown(struct spi_device *spi) { - struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev); + struct ams369fg06 *lcd = spi_get_drvdata(spi); ams369fg06_power(lcd, FB_BLANK_POWERDOWN); } diff --git a/drivers/video/backlight/apple_bl.c b/drivers/video/backlight/apple_bl.c index f088d4c0738..d8432967668 100644 --- a/drivers/video/backlight/apple_bl.c +++ b/drivers/video/backlight/apple_bl.c @@ -196,7 +196,7 @@ static int apple_bl_add(struct acpi_device *dev) return 0; } -static int apple_bl_remove(struct acpi_device *dev, int type) +static int apple_bl_remove(struct acpi_device *dev) { backlight_device_unregister(apple_backlight_device); diff --git a/drivers/video/backlight/as3711_bl.c b/drivers/video/backlight/as3711_bl.c new file mode 100644 index 00000000000..41d52fe5254 --- /dev/null +++ b/drivers/video/backlight/as3711_bl.c @@ -0,0 +1,380 @@ +/* + * AS3711 PMIC backlight driver, using DCDC Step Up Converters + * + * Copyright (C) 2012 Renesas Electronics Corporation + * Author: Guennadi Liakhovetski, <g.liakhovetski@gmx.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License as + * published by the Free Software Foundation + */ + +#include <linux/backlight.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/fb.h> +#include <linux/kernel.h> +#include <linux/mfd/as3711.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +enum as3711_bl_type { + AS3711_BL_SU1, + AS3711_BL_SU2, +}; + +struct as3711_bl_data { + bool powered; + const char *fb_name; + struct device *fb_dev; + enum as3711_bl_type type; + int brightness; + struct backlight_device *bl; +}; + +struct as3711_bl_supply { + struct as3711_bl_data su1; + struct as3711_bl_data su2; + const struct as3711_bl_pdata *pdata; + struct as3711 *as3711; +}; + +static struct as3711_bl_supply *to_supply(struct as3711_bl_data *su) +{ + switch (su->type) { + case AS3711_BL_SU1: + return container_of(su, struct as3711_bl_supply, su1); + case AS3711_BL_SU2: + return container_of(su, struct as3711_bl_supply, su2); + } + return NULL; +} + +static int as3711_set_brightness_auto_i(struct as3711_bl_data *data, + unsigned int brightness) +{ + struct as3711_bl_supply *supply = to_supply(data); + struct as3711 *as3711 = supply->as3711; + const struct as3711_bl_pdata *pdata = supply->pdata; + int ret = 0; + + /* Only all equal current values are supported */ + if (pdata->su2_auto_curr1) + ret = regmap_write(as3711->regmap, AS3711_CURR1_VALUE, + brightness); + if (!ret && pdata->su2_auto_curr2) + ret = regmap_write(as3711->regmap, AS3711_CURR2_VALUE, + brightness); + if (!ret && pdata->su2_auto_curr3) + ret = regmap_write(as3711->regmap, AS3711_CURR3_VALUE, + brightness); + + return ret; +} + +static int as3711_set_brightness_v(struct as3711 *as3711, + unsigned int brightness, + unsigned int reg) +{ + if (brightness > 31) + return -EINVAL; + + return regmap_update_bits(as3711->regmap, reg, 0xf0, + brightness << 4); +} + +static int as3711_bl_su2_reset(struct as3711_bl_supply *supply) +{ + struct as3711 *as3711 = supply->as3711; + int ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_5, + 3, supply->pdata->su2_fbprot); + if (!ret) + ret = regmap_update_bits(as3711->regmap, + AS3711_STEPUP_CONTROL_2, 1, 0); + if (!ret) + ret = regmap_update_bits(as3711->regmap, + AS3711_STEPUP_CONTROL_2, 1, 1); + return ret; +} + +/* + * Someone with less fragile or less expensive hardware could try to simplify + * the brightness adjustment procedure. + */ +static int as3711_bl_update_status(struct backlight_device *bl) +{ + struct as3711_bl_data *data = bl_get_data(bl); + struct as3711_bl_supply *supply = to_supply(data); + struct as3711 *as3711 = supply->as3711; + int brightness = bl->props.brightness; + int ret = 0; + + dev_dbg(&bl->dev, "%s(): brightness %u, pwr %x, blank %x, state %x\n", + __func__, bl->props.brightness, bl->props.power, + bl->props.fb_blank, bl->props.state); + + if (bl->props.power != FB_BLANK_UNBLANK || + bl->props.fb_blank != FB_BLANK_UNBLANK || + bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK)) + brightness = 0; + + if (data->type == AS3711_BL_SU1) { + ret = as3711_set_brightness_v(as3711, brightness, + AS3711_STEPUP_CONTROL_1); + } else { + const struct as3711_bl_pdata *pdata = supply->pdata; + + switch (pdata->su2_feedback) { + case AS3711_SU2_VOLTAGE: + ret = as3711_set_brightness_v(as3711, brightness, + AS3711_STEPUP_CONTROL_2); + break; + case AS3711_SU2_CURR_AUTO: + ret = as3711_set_brightness_auto_i(data, brightness / 4); + if (ret < 0) + return ret; + if (brightness) { + ret = as3711_bl_su2_reset(supply); + if (ret < 0) + return ret; + udelay(500); + ret = as3711_set_brightness_auto_i(data, brightness); + } else { + ret = regmap_update_bits(as3711->regmap, + AS3711_STEPUP_CONTROL_2, 1, 0); + } + break; + /* Manual one current feedback pin below */ + case AS3711_SU2_CURR1: + ret = regmap_write(as3711->regmap, AS3711_CURR1_VALUE, + brightness); + break; + case AS3711_SU2_CURR2: + ret = regmap_write(as3711->regmap, AS3711_CURR2_VALUE, + brightness); + break; + case AS3711_SU2_CURR3: + ret = regmap_write(as3711->regmap, AS3711_CURR3_VALUE, + brightness); + break; + default: + ret = -EINVAL; + } + } + if (!ret) + data->brightness = brightness; + + return ret; +} + +static int as3711_bl_get_brightness(struct backlight_device *bl) +{ + struct as3711_bl_data *data = bl_get_data(bl); + + return data->brightness; +} + +static const struct backlight_ops as3711_bl_ops = { + .update_status = as3711_bl_update_status, + .get_brightness = as3711_bl_get_brightness, +}; + +static int as3711_bl_init_su2(struct as3711_bl_supply *supply) +{ + struct as3711 *as3711 = supply->as3711; + const struct as3711_bl_pdata *pdata = supply->pdata; + u8 ctl = 0; + int ret; + + dev_dbg(as3711->dev, "%s(): use %u\n", __func__, pdata->su2_feedback); + + /* Turn SU2 off */ + ret = regmap_write(as3711->regmap, AS3711_STEPUP_CONTROL_2, 0); + if (ret < 0) + return ret; + + switch (pdata->su2_feedback) { + case AS3711_SU2_VOLTAGE: + ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_4, 3, 0); + break; + case AS3711_SU2_CURR1: + ctl = 1; + ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_4, 3, 1); + break; + case AS3711_SU2_CURR2: + ctl = 4; + ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_4, 3, 2); + break; + case AS3711_SU2_CURR3: + ctl = 0x10; + ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_4, 3, 3); + break; + case AS3711_SU2_CURR_AUTO: + if (pdata->su2_auto_curr1) + ctl = 2; + if (pdata->su2_auto_curr2) + ctl |= 8; + if (pdata->su2_auto_curr3) + ctl |= 0x20; + ret = 0; + break; + default: + return -EINVAL; + } + + if (!ret) + ret = regmap_write(as3711->regmap, AS3711_CURR_CONTROL, ctl); + + return ret; +} + +static int as3711_bl_register(struct platform_device *pdev, + unsigned int max_brightness, struct as3711_bl_data *su) +{ + struct backlight_properties props = {.type = BACKLIGHT_RAW,}; + struct backlight_device *bl; + + /* max tuning I = 31uA for voltage- and 38250uA for current-feedback */ + props.max_brightness = max_brightness; + + bl = backlight_device_register(su->type == AS3711_BL_SU1 ? + "as3711-su1" : "as3711-su2", + &pdev->dev, su, + &as3711_bl_ops, &props); + if (IS_ERR(bl)) { + dev_err(&pdev->dev, "failed to register backlight\n"); + return PTR_ERR(bl); + } + + bl->props.brightness = props.max_brightness; + + backlight_update_status(bl); + + su->bl = bl; + + return 0; +} + +static int as3711_backlight_probe(struct platform_device *pdev) +{ + struct as3711_bl_pdata *pdata = dev_get_platdata(&pdev->dev); + struct as3711 *as3711 = dev_get_drvdata(pdev->dev.parent); + struct as3711_bl_supply *supply; + struct as3711_bl_data *su; + unsigned int max_brightness; + int ret; + + if (!pdata || (!pdata->su1_fb && !pdata->su2_fb)) { + dev_err(&pdev->dev, "No platform data, exiting...\n"); + return -ENODEV; + } + + /* + * Due to possible hardware damage I chose to block all modes, + * unsupported on my hardware. Anyone, wishing to use any of those modes + * will have to first review the code, then activate and test it. + */ + if (pdata->su1_fb || + pdata->su2_fbprot != AS3711_SU2_GPIO4 || + pdata->su2_feedback != AS3711_SU2_CURR_AUTO) { + dev_warn(&pdev->dev, + "Attention! An untested mode has been chosen!\n" + "Please, review the code, enable, test, and report success:-)\n"); + return -EINVAL; + } + + supply = devm_kzalloc(&pdev->dev, sizeof(*supply), GFP_KERNEL); + if (!supply) + return -ENOMEM; + + supply->as3711 = as3711; + supply->pdata = pdata; + + if (pdata->su1_fb) { + su = &supply->su1; + su->fb_name = pdata->su1_fb; + su->type = AS3711_BL_SU1; + + max_brightness = min(pdata->su1_max_uA, 31); + ret = as3711_bl_register(pdev, max_brightness, su); + if (ret < 0) + return ret; + } + + if (pdata->su2_fb) { + su = &supply->su2; + su->fb_name = pdata->su2_fb; + su->type = AS3711_BL_SU2; + + switch (pdata->su2_fbprot) { + case AS3711_SU2_GPIO2: + case AS3711_SU2_GPIO3: + case AS3711_SU2_GPIO4: + case AS3711_SU2_LX_SD4: + break; + default: + ret = -EINVAL; + goto esu2; + } + + switch (pdata->su2_feedback) { + case AS3711_SU2_VOLTAGE: + max_brightness = min(pdata->su2_max_uA, 31); + break; + case AS3711_SU2_CURR1: + case AS3711_SU2_CURR2: + case AS3711_SU2_CURR3: + case AS3711_SU2_CURR_AUTO: + max_brightness = min(pdata->su2_max_uA / 150, 255); + break; + default: + ret = -EINVAL; + goto esu2; + } + + ret = as3711_bl_init_su2(supply); + if (ret < 0) + return ret; + + ret = as3711_bl_register(pdev, max_brightness, su); + if (ret < 0) + goto esu2; + } + + platform_set_drvdata(pdev, supply); + + return 0; + +esu2: + backlight_device_unregister(supply->su1.bl); + return ret; +} + +static int as3711_backlight_remove(struct platform_device *pdev) +{ + struct as3711_bl_supply *supply = platform_get_drvdata(pdev); + + backlight_device_unregister(supply->su1.bl); + backlight_device_unregister(supply->su2.bl); + + return 0; +} + +static struct platform_driver as3711_backlight_driver = { + .driver = { + .name = "as3711-backlight", + .owner = THIS_MODULE, + }, + .probe = as3711_backlight_probe, + .remove = as3711_backlight_remove, +}; + +module_platform_driver(as3711_backlight_driver); + +MODULE_DESCRIPTION("Backlight Driver for AS3711 PMICs"); +MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:as3711-backlight"); diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c index 345f6660d4b..c74e7aa4673 100644 --- a/drivers/video/backlight/backlight.c +++ b/drivers/video/backlight/backlight.c @@ -371,7 +371,7 @@ void backlight_device_unregister(struct backlight_device *bd) EXPORT_SYMBOL(backlight_device_unregister); #ifdef CONFIG_OF -static int of_parent_match(struct device *dev, void *data) +static int of_parent_match(struct device *dev, const void *data) { return dev->parent && dev->parent->of_node == data; } diff --git a/drivers/video/backlight/corgi_lcd.c b/drivers/video/backlight/corgi_lcd.c index e323fcbe884..aa782f30298 100644 --- a/drivers/video/backlight/corgi_lcd.c +++ b/drivers/video/backlight/corgi_lcd.c @@ -337,7 +337,7 @@ static void corgi_lcd_power_off(struct corgi_lcd *lcd) static int corgi_lcd_set_mode(struct lcd_device *ld, struct fb_videomode *m) { - struct corgi_lcd *lcd = dev_get_drvdata(&ld->dev); + struct corgi_lcd *lcd = lcd_get_data(ld); int mode = CORGI_LCD_MODE_QVGA; if (m->xres == 640 || m->xres == 480) @@ -364,7 +364,7 @@ static int corgi_lcd_set_mode(struct lcd_device *ld, struct fb_videomode *m) static int corgi_lcd_set_power(struct lcd_device *ld, int power) { - struct corgi_lcd *lcd = dev_get_drvdata(&ld->dev); + struct corgi_lcd *lcd = lcd_get_data(ld); if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power)) corgi_lcd_power_on(lcd); @@ -378,7 +378,7 @@ static int corgi_lcd_set_power(struct lcd_device *ld, int power) static int corgi_lcd_get_power(struct lcd_device *ld) { - struct corgi_lcd *lcd = dev_get_drvdata(&ld->dev); + struct corgi_lcd *lcd = lcd_get_data(ld); return lcd->power; } @@ -391,7 +391,7 @@ static struct lcd_ops corgi_lcd_ops = { static int corgi_bl_get_intensity(struct backlight_device *bd) { - struct corgi_lcd *lcd = dev_get_drvdata(&bd->dev); + struct corgi_lcd *lcd = bl_get_data(bd); return lcd->intensity; } @@ -423,7 +423,7 @@ static int corgi_bl_set_intensity(struct corgi_lcd *lcd, int intensity) static int corgi_bl_update_status(struct backlight_device *bd) { - struct corgi_lcd *lcd = dev_get_drvdata(&bd->dev); + struct corgi_lcd *lcd = bl_get_data(bd); int intensity = bd->props.brightness; if (bd->props.power != FB_BLANK_UNBLANK) @@ -460,7 +460,7 @@ static const struct backlight_ops corgi_bl_ops = { #ifdef CONFIG_PM static int corgi_lcd_suspend(struct spi_device *spi, pm_message_t state) { - struct corgi_lcd *lcd = dev_get_drvdata(&spi->dev); + struct corgi_lcd *lcd = spi_get_drvdata(spi); corgibl_flags |= CORGIBL_SUSPENDED; corgi_bl_set_intensity(lcd, 0); @@ -470,7 +470,7 @@ static int corgi_lcd_suspend(struct spi_device *spi, pm_message_t state) static int corgi_lcd_resume(struct spi_device *spi) { - struct corgi_lcd *lcd = dev_get_drvdata(&spi->dev); + struct corgi_lcd *lcd = spi_get_drvdata(spi); corgibl_flags &= ~CORGIBL_SUSPENDED; corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_UNBLANK); @@ -577,7 +577,7 @@ static int corgi_lcd_probe(struct spi_device *spi) lcd->kick_battery = pdata->kick_battery; - dev_set_drvdata(&spi->dev, lcd); + spi_set_drvdata(spi, lcd); corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_UNBLANK); backlight_update_status(lcd->bl_dev); @@ -594,7 +594,7 @@ err_unregister_lcd: static int corgi_lcd_remove(struct spi_device *spi) { - struct corgi_lcd *lcd = dev_get_drvdata(&spi->dev); + struct corgi_lcd *lcd = spi_get_drvdata(spi); lcd->bl_dev->props.power = FB_BLANK_UNBLANK; lcd->bl_dev->props.brightness = 0; diff --git a/drivers/video/backlight/hx8357.c b/drivers/video/backlight/hx8357.c new file mode 100644 index 00000000000..a0482b567bf --- /dev/null +++ b/drivers/video/backlight/hx8357.c @@ -0,0 +1,497 @@ +/* + * Driver for the Himax HX-8357 LCD Controller + * + * Copyright 2012 Free Electrons + * + * Licensed under the GPLv2 or later. + */ + +#include <linux/delay.h> +#include <linux/lcd.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/spi/spi.h> + +#define HX8357_NUM_IM_PINS 3 + +#define HX8357_SWRESET 0x01 +#define HX8357_GET_RED_CHANNEL 0x06 +#define HX8357_GET_GREEN_CHANNEL 0x07 +#define HX8357_GET_BLUE_CHANNEL 0x08 +#define HX8357_GET_POWER_MODE 0x0a +#define HX8357_GET_MADCTL 0x0b +#define HX8357_GET_PIXEL_FORMAT 0x0c +#define HX8357_GET_DISPLAY_MODE 0x0d +#define HX8357_GET_SIGNAL_MODE 0x0e +#define HX8357_GET_DIAGNOSTIC_RESULT 0x0f +#define HX8357_ENTER_SLEEP_MODE 0x10 +#define HX8357_EXIT_SLEEP_MODE 0x11 +#define HX8357_ENTER_PARTIAL_MODE 0x12 +#define HX8357_ENTER_NORMAL_MODE 0x13 +#define HX8357_EXIT_INVERSION_MODE 0x20 +#define HX8357_ENTER_INVERSION_MODE 0x21 +#define HX8357_SET_DISPLAY_OFF 0x28 +#define HX8357_SET_DISPLAY_ON 0x29 +#define HX8357_SET_COLUMN_ADDRESS 0x2a +#define HX8357_SET_PAGE_ADDRESS 0x2b +#define HX8357_WRITE_MEMORY_START 0x2c +#define HX8357_READ_MEMORY_START 0x2e +#define HX8357_SET_PARTIAL_AREA 0x30 +#define HX8357_SET_SCROLL_AREA 0x33 +#define HX8357_SET_TEAR_OFF 0x34 +#define HX8357_SET_TEAR_ON 0x35 +#define HX8357_SET_ADDRESS_MODE 0x36 +#define HX8357_SET_SCROLL_START 0x37 +#define HX8357_EXIT_IDLE_MODE 0x38 +#define HX8357_ENTER_IDLE_MODE 0x39 +#define HX8357_SET_PIXEL_FORMAT 0x3a +#define HX8357_SET_PIXEL_FORMAT_DBI_3BIT (0x1) +#define HX8357_SET_PIXEL_FORMAT_DBI_16BIT (0x5) +#define HX8357_SET_PIXEL_FORMAT_DBI_18BIT (0x6) +#define HX8357_SET_PIXEL_FORMAT_DPI_3BIT (0x1 << 4) +#define HX8357_SET_PIXEL_FORMAT_DPI_16BIT (0x5 << 4) +#define HX8357_SET_PIXEL_FORMAT_DPI_18BIT (0x6 << 4) +#define HX8357_WRITE_MEMORY_CONTINUE 0x3c +#define HX8357_READ_MEMORY_CONTINUE 0x3e +#define HX8357_SET_TEAR_SCAN_LINES 0x44 +#define HX8357_GET_SCAN_LINES 0x45 +#define HX8357_READ_DDB_START 0xa1 +#define HX8357_SET_DISPLAY_MODE 0xb4 +#define HX8357_SET_DISPLAY_MODE_RGB_THROUGH (0x3) +#define HX8357_SET_DISPLAY_MODE_RGB_INTERFACE (1 << 4) +#define HX8357_SET_PANEL_DRIVING 0xc0 +#define HX8357_SET_DISPLAY_FRAME 0xc5 +#define HX8357_SET_RGB 0xc6 +#define HX8357_SET_RGB_ENABLE_HIGH (1 << 1) +#define HX8357_SET_GAMMA 0xc8 +#define HX8357_SET_POWER 0xd0 +#define HX8357_SET_VCOM 0xd1 +#define HX8357_SET_POWER_NORMAL 0xd2 +#define HX8357_SET_PANEL_RELATED 0xe9 + +struct hx8357_data { + unsigned im_pins[HX8357_NUM_IM_PINS]; + unsigned reset; + struct spi_device *spi; + int state; +}; + +static u8 hx8357_seq_power[] = { + HX8357_SET_POWER, 0x44, 0x41, 0x06, +}; + +static u8 hx8357_seq_vcom[] = { + HX8357_SET_VCOM, 0x40, 0x10, +}; + +static u8 hx8357_seq_power_normal[] = { + HX8357_SET_POWER_NORMAL, 0x05, 0x12, +}; + +static u8 hx8357_seq_panel_driving[] = { + HX8357_SET_PANEL_DRIVING, 0x14, 0x3b, 0x00, 0x02, 0x11, +}; + +static u8 hx8357_seq_display_frame[] = { + HX8357_SET_DISPLAY_FRAME, 0x0c, +}; + +static u8 hx8357_seq_panel_related[] = { + HX8357_SET_PANEL_RELATED, 0x01, +}; + +static u8 hx8357_seq_undefined1[] = { + 0xea, 0x03, 0x00, 0x00, +}; + +static u8 hx8357_seq_undefined2[] = { + 0xeb, 0x40, 0x54, 0x26, 0xdb, +}; + +static u8 hx8357_seq_gamma[] = { + HX8357_SET_GAMMA, 0x00, 0x15, 0x00, 0x22, 0x00, + 0x08, 0x77, 0x26, 0x77, 0x22, 0x04, 0x00, +}; + +static u8 hx8357_seq_address_mode[] = { + HX8357_SET_ADDRESS_MODE, 0xc0, +}; + +static u8 hx8357_seq_pixel_format[] = { + HX8357_SET_PIXEL_FORMAT, + HX8357_SET_PIXEL_FORMAT_DPI_18BIT | + HX8357_SET_PIXEL_FORMAT_DBI_18BIT, +}; + +static u8 hx8357_seq_column_address[] = { + HX8357_SET_COLUMN_ADDRESS, 0x00, 0x00, 0x01, 0x3f, +}; + +static u8 hx8357_seq_page_address[] = { + HX8357_SET_PAGE_ADDRESS, 0x00, 0x00, 0x01, 0xdf, +}; + +static u8 hx8357_seq_rgb[] = { + HX8357_SET_RGB, 0x02, +}; + +static u8 hx8357_seq_display_mode[] = { + HX8357_SET_DISPLAY_MODE, + HX8357_SET_DISPLAY_MODE_RGB_THROUGH | + HX8357_SET_DISPLAY_MODE_RGB_INTERFACE, +}; + +static int hx8357_spi_write_then_read(struct lcd_device *lcdev, + u8 *txbuf, u16 txlen, + u8 *rxbuf, u16 rxlen) +{ + struct hx8357_data *lcd = lcd_get_data(lcdev); + struct spi_message msg; + struct spi_transfer xfer[2]; + u16 *local_txbuf = NULL; + int ret = 0; + + memset(xfer, 0, sizeof(xfer)); + spi_message_init(&msg); + + if (txlen) { + int i; + + local_txbuf = kcalloc(txlen, sizeof(*local_txbuf), GFP_KERNEL); + + if (!local_txbuf) + return -ENOMEM; + + for (i = 0; i < txlen; i++) { + local_txbuf[i] = txbuf[i]; + if (i > 0) + local_txbuf[i] |= 1 << 8; + } + + xfer[0].len = 2 * txlen; + xfer[0].bits_per_word = 9; + xfer[0].tx_buf = local_txbuf; + spi_message_add_tail(&xfer[0], &msg); + } + + if (rxlen) { + xfer[1].len = rxlen; + xfer[1].bits_per_word = 8; + xfer[1].rx_buf = rxbuf; + spi_message_add_tail(&xfer[1], &msg); + } + + ret = spi_sync(lcd->spi, &msg); + if (ret < 0) + dev_err(&lcdev->dev, "Couldn't send SPI data\n"); + + if (txlen) + kfree(local_txbuf); + + return ret; +} + +static inline int hx8357_spi_write_array(struct lcd_device *lcdev, + u8 *value, u8 len) +{ + return hx8357_spi_write_then_read(lcdev, value, len, NULL, 0); +} + +static inline int hx8357_spi_write_byte(struct lcd_device *lcdev, + u8 value) +{ + return hx8357_spi_write_then_read(lcdev, &value, 1, NULL, 0); +} + +static int hx8357_enter_standby(struct lcd_device *lcdev) +{ + int ret; + + ret = hx8357_spi_write_byte(lcdev, HX8357_SET_DISPLAY_OFF); + if (ret < 0) + return ret; + + usleep_range(10000, 12000); + + ret = hx8357_spi_write_byte(lcdev, HX8357_ENTER_SLEEP_MODE); + if (ret < 0) + return ret; + + msleep(120); + + return 0; +} + +static int hx8357_exit_standby(struct lcd_device *lcdev) +{ + int ret; + + ret = hx8357_spi_write_byte(lcdev, HX8357_EXIT_SLEEP_MODE); + if (ret < 0) + return ret; + + msleep(120); + + ret = hx8357_spi_write_byte(lcdev, HX8357_SET_DISPLAY_ON); + if (ret < 0) + return ret; + + return 0; +} + +static int hx8357_lcd_init(struct lcd_device *lcdev) +{ + struct hx8357_data *lcd = lcd_get_data(lcdev); + int ret; + + /* + * Set the interface selection pins to SPI mode, with three + * wires + */ + gpio_set_value_cansleep(lcd->im_pins[0], 1); + gpio_set_value_cansleep(lcd->im_pins[1], 0); + gpio_set_value_cansleep(lcd->im_pins[2], 1); + + /* Reset the screen */ + gpio_set_value(lcd->reset, 1); + usleep_range(10000, 12000); + gpio_set_value(lcd->reset, 0); + usleep_range(10000, 12000); + gpio_set_value(lcd->reset, 1); + msleep(120); + + ret = hx8357_spi_write_array(lcdev, hx8357_seq_power, + ARRAY_SIZE(hx8357_seq_power)); + if (ret < 0) + return ret; + + ret = hx8357_spi_write_array(lcdev, hx8357_seq_vcom, + ARRAY_SIZE(hx8357_seq_vcom)); + if (ret < 0) + return ret; + + ret = hx8357_spi_write_array(lcdev, hx8357_seq_power_normal, + ARRAY_SIZE(hx8357_seq_power_normal)); + if (ret < 0) + return ret; + + ret = hx8357_spi_write_array(lcdev, hx8357_seq_panel_driving, + ARRAY_SIZE(hx8357_seq_panel_driving)); + if (ret < 0) + return ret; + + ret = hx8357_spi_write_array(lcdev, hx8357_seq_display_frame, + ARRAY_SIZE(hx8357_seq_display_frame)); + if (ret < 0) + return ret; + + ret = hx8357_spi_write_array(lcdev, hx8357_seq_panel_related, + ARRAY_SIZE(hx8357_seq_panel_related)); + if (ret < 0) + return ret; + + ret = hx8357_spi_write_array(lcdev, hx8357_seq_undefined1, + ARRAY_SIZE(hx8357_seq_undefined1)); + if (ret < 0) + return ret; + + ret = hx8357_spi_write_array(lcdev, hx8357_seq_undefined2, + ARRAY_SIZE(hx8357_seq_undefined2)); + if (ret < 0) + return ret; + + ret = hx8357_spi_write_array(lcdev, hx8357_seq_gamma, + ARRAY_SIZE(hx8357_seq_gamma)); + if (ret < 0) + return ret; + + ret = hx8357_spi_write_array(lcdev, hx8357_seq_address_mode, + ARRAY_SIZE(hx8357_seq_address_mode)); + if (ret < 0) + return ret; + + ret = hx8357_spi_write_array(lcdev, hx8357_seq_pixel_format, + ARRAY_SIZE(hx8357_seq_pixel_format)); + if (ret < 0) + return ret; + + ret = hx8357_spi_write_array(lcdev, hx8357_seq_column_address, + ARRAY_SIZE(hx8357_seq_column_address)); + if (ret < 0) + return ret; + + ret = hx8357_spi_write_array(lcdev, hx8357_seq_page_address, + ARRAY_SIZE(hx8357_seq_page_address)); + if (ret < 0) + return ret; + + ret = hx8357_spi_write_array(lcdev, hx8357_seq_rgb, + ARRAY_SIZE(hx8357_seq_rgb)); + if (ret < 0) + return ret; + + ret = hx8357_spi_write_array(lcdev, hx8357_seq_display_mode, + ARRAY_SIZE(hx8357_seq_display_mode)); + if (ret < 0) + return ret; + + ret = hx8357_spi_write_byte(lcdev, HX8357_EXIT_SLEEP_MODE); + if (ret < 0) + return ret; + + msleep(120); + + ret = hx8357_spi_write_byte(lcdev, HX8357_SET_DISPLAY_ON); + if (ret < 0) + return ret; + + usleep_range(5000, 7000); + + ret = hx8357_spi_write_byte(lcdev, HX8357_WRITE_MEMORY_START); + if (ret < 0) + return ret; + + return 0; +} + +#define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL) + +static int hx8357_set_power(struct lcd_device *lcdev, int power) +{ + struct hx8357_data *lcd = lcd_get_data(lcdev); + int ret = 0; + + if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->state)) + ret = hx8357_exit_standby(lcdev); + else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->state)) + ret = hx8357_enter_standby(lcdev); + + if (ret == 0) + lcd->state = power; + else + dev_warn(&lcdev->dev, "failed to set power mode %d\n", power); + + return ret; +} + +static int hx8357_get_power(struct lcd_device *lcdev) +{ + struct hx8357_data *lcd = lcd_get_data(lcdev); + + return lcd->state; +} + +static struct lcd_ops hx8357_ops = { + .set_power = hx8357_set_power, + .get_power = hx8357_get_power, +}; + +static int hx8357_probe(struct spi_device *spi) +{ + struct lcd_device *lcdev; + struct hx8357_data *lcd; + int i, ret; + + lcd = devm_kzalloc(&spi->dev, sizeof(*lcd), GFP_KERNEL); + if (!lcd) { + dev_err(&spi->dev, "Couldn't allocate lcd internal structure!\n"); + return -ENOMEM; + } + + ret = spi_setup(spi); + if (ret < 0) { + dev_err(&spi->dev, "SPI setup failed.\n"); + return ret; + } + + lcd->spi = spi; + + lcd->reset = of_get_named_gpio(spi->dev.of_node, "gpios-reset", 0); + if (!gpio_is_valid(lcd->reset)) { + dev_err(&spi->dev, "Missing dt property: gpios-reset\n"); + return -EINVAL; + } + + ret = devm_gpio_request_one(&spi->dev, lcd->reset, + GPIOF_OUT_INIT_HIGH, + "hx8357-reset"); + if (ret) { + dev_err(&spi->dev, + "failed to request gpio %d: %d\n", + lcd->reset, ret); + return -EINVAL; + } + + for (i = 0; i < HX8357_NUM_IM_PINS; i++) { + lcd->im_pins[i] = of_get_named_gpio(spi->dev.of_node, + "im-gpios", i); + if (lcd->im_pins[i] == -EPROBE_DEFER) { + dev_info(&spi->dev, "GPIO requested is not here yet, deferring the probe\n"); + return -EPROBE_DEFER; + } + if (!gpio_is_valid(lcd->im_pins[i])) { + dev_err(&spi->dev, "Missing dt property: im-gpios\n"); + return -EINVAL; + } + + ret = devm_gpio_request_one(&spi->dev, lcd->im_pins[i], + GPIOF_OUT_INIT_LOW, "im_pins"); + if (ret) { + dev_err(&spi->dev, "failed to request gpio %d: %d\n", + lcd->im_pins[i], ret); + return -EINVAL; + } + } + + lcdev = lcd_device_register("mxsfb", &spi->dev, lcd, &hx8357_ops); + if (IS_ERR(lcdev)) { + ret = PTR_ERR(lcdev); + return ret; + } + spi_set_drvdata(spi, lcdev); + + ret = hx8357_lcd_init(lcdev); + if (ret) { + dev_err(&spi->dev, "Couldn't initialize panel\n"); + goto init_error; + } + + dev_info(&spi->dev, "Panel probed\n"); + + return 0; + +init_error: + lcd_device_unregister(lcdev); + return ret; +} + +static int hx8357_remove(struct spi_device *spi) +{ + struct lcd_device *lcdev = spi_get_drvdata(spi); + + lcd_device_unregister(lcdev); + return 0; +} + +static const struct of_device_id hx8357_dt_ids[] = { + { .compatible = "himax,hx8357" }, + {}, +}; +MODULE_DEVICE_TABLE(of, hx8357_dt_ids); + +static struct spi_driver hx8357_driver = { + .probe = hx8357_probe, + .remove = hx8357_remove, + .driver = { + .name = "hx8357", + .of_match_table = of_match_ptr(hx8357_dt_ids), + }, +}; + +module_spi_driver(hx8357_driver); + +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); +MODULE_DESCRIPTION("Himax HX-8357 LCD Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/backlight/l4f00242t03.c b/drivers/video/backlight/l4f00242t03.c index 9a35196d12d..fb615577132 100644 --- a/drivers/video/backlight/l4f00242t03.c +++ b/drivers/video/backlight/l4f00242t03.c @@ -49,7 +49,7 @@ static void l4f00242t03_reset(unsigned int gpio) static void l4f00242t03_lcd_init(struct spi_device *spi) { struct l4f00242t03_pdata *pdata = spi->dev.platform_data; - struct l4f00242t03_priv *priv = dev_get_drvdata(&spi->dev); + struct l4f00242t03_priv *priv = spi_get_drvdata(spi); const u16 cmd[] = { 0x36, param(0), 0x3A, param(0x60) }; dev_dbg(&spi->dev, "initializing LCD\n"); @@ -70,7 +70,7 @@ static void l4f00242t03_lcd_init(struct spi_device *spi) static void l4f00242t03_lcd_powerdown(struct spi_device *spi) { struct l4f00242t03_pdata *pdata = spi->dev.platform_data; - struct l4f00242t03_priv *priv = dev_get_drvdata(&spi->dev); + struct l4f00242t03_priv *priv = spi_get_drvdata(spi); dev_dbg(&spi->dev, "Powering down LCD\n"); @@ -168,7 +168,7 @@ static int l4f00242t03_probe(struct spi_device *spi) return -ENOMEM; } - dev_set_drvdata(&spi->dev, priv); + spi_set_drvdata(spi, priv); spi->bits_per_word = 9; spi_setup(spi); @@ -190,27 +190,24 @@ static int l4f00242t03_probe(struct spi_device *spi) return ret; } - priv->io_reg = regulator_get(&spi->dev, "vdd"); + priv->io_reg = devm_regulator_get(&spi->dev, "vdd"); if (IS_ERR(priv->io_reg)) { dev_err(&spi->dev, "%s: Unable to get the IO regulator\n", __func__); return PTR_ERR(priv->io_reg); } - priv->core_reg = regulator_get(&spi->dev, "vcore"); + priv->core_reg = devm_regulator_get(&spi->dev, "vcore"); if (IS_ERR(priv->core_reg)) { - ret = PTR_ERR(priv->core_reg); dev_err(&spi->dev, "%s: Unable to get the core regulator\n", __func__); - goto err1; + return PTR_ERR(priv->core_reg); } priv->ld = lcd_device_register("l4f00242t03", &spi->dev, priv, &l4f_ops); - if (IS_ERR(priv->ld)) { - ret = PTR_ERR(priv->ld); - goto err2; - } + if (IS_ERR(priv->ld)) + return PTR_ERR(priv->ld); /* Init the LCD */ l4f00242t03_lcd_init(spi); @@ -220,33 +217,22 @@ static int l4f00242t03_probe(struct spi_device *spi) dev_info(&spi->dev, "Epson l4f00242t03 lcd probed.\n"); return 0; - -err2: - regulator_put(priv->core_reg); -err1: - regulator_put(priv->io_reg); - - return ret; } static int l4f00242t03_remove(struct spi_device *spi) { - struct l4f00242t03_priv *priv = dev_get_drvdata(&spi->dev); + struct l4f00242t03_priv *priv = spi_get_drvdata(spi); l4f00242t03_lcd_power_set(priv->ld, FB_BLANK_POWERDOWN); lcd_device_unregister(priv->ld); - - dev_set_drvdata(&spi->dev, NULL); - - regulator_put(priv->io_reg); - regulator_put(priv->core_reg); + spi_set_drvdata(spi, NULL); return 0; } static void l4f00242t03_shutdown(struct spi_device *spi) { - struct l4f00242t03_priv *priv = dev_get_drvdata(&spi->dev); + struct l4f00242t03_priv *priv = spi_get_drvdata(spi); if (priv) l4f00242t03_lcd_power_set(priv->ld, FB_BLANK_POWERDOWN); diff --git a/drivers/video/backlight/ld9040.c b/drivers/video/backlight/ld9040.c index 1cb35241851..1b642f5f381 100644 --- a/drivers/video/backlight/ld9040.c +++ b/drivers/video/backlight/ld9040.c @@ -9,29 +9,20 @@ * 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/wait.h> -#include <linux/fb.h> +#include <linux/backlight.h> #include <linux/delay.h> +#include <linux/fb.h> #include <linux/gpio.h> -#include <linux/spi/spi.h> -#include <linux/irq.h> #include <linux/interrupt.h> +#include <linux/irq.h> #include <linux/kernel.h> #include <linux/lcd.h> -#include <linux/backlight.h> #include <linux/module.h> #include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> +#include <linux/wait.h> #include "ld9040_gamma.h" @@ -43,7 +34,6 @@ #define MIN_BRIGHTNESS 0 #define MAX_BRIGHTNESS 24 -#define power_is_on(pwr) ((pwr) <= FB_BLANK_NORMAL) struct ld9040 { struct device *dev; @@ -78,7 +68,7 @@ static void ld9040_regulator_enable(struct ld9040 *lcd) lcd->enabled = true; } - mdelay(pd->power_on_delay); + msleep(pd->power_on_delay); out: mutex_unlock(&lcd->lock); } @@ -474,8 +464,9 @@ static int ld9040_panel_send_sequence(struct ld9040 *lcd, ret = ld9040_spi_write(lcd, wbuf[i], wbuf[i+1]); if (ret) break; - } else - udelay(wbuf[i+1]*1000); + } else { + msleep(wbuf[i+1]); + } i += 2; } @@ -513,14 +504,9 @@ gamma_err: static int ld9040_gamma_ctl(struct ld9040 *lcd, int gamma) { - int ret = 0; - - ret = _ld9040_gamma_ctl(lcd, gamma_table.gamma_22_table[gamma]); - - return ret; + return _ld9040_gamma_ctl(lcd, gamma_table.gamma_22_table[gamma]); } - static int ld9040_ldi_init(struct ld9040 *lcd) { int ret, i; @@ -539,7 +525,7 @@ static int ld9040_ldi_init(struct ld9040 *lcd) for (i = 0; i < ARRAY_SIZE(init_seq); i++) { ret = ld9040_panel_send_sequence(lcd, init_seq[i]); /* workaround: minimum delay time for transferring CMD */ - udelay(300); + usleep_range(300, 310); if (ret) break; } @@ -549,11 +535,7 @@ static int ld9040_ldi_init(struct ld9040 *lcd) static int ld9040_ldi_enable(struct ld9040 *lcd) { - int ret = 0; - - ret = ld9040_panel_send_sequence(lcd, seq_display_on); - - return ret; + return ld9040_panel_send_sequence(lcd, seq_display_on); } static int ld9040_ldi_disable(struct ld9040 *lcd) @@ -566,25 +548,27 @@ static int ld9040_ldi_disable(struct ld9040 *lcd) return ret; } +static int ld9040_power_is_on(int power) +{ + return power <= FB_BLANK_NORMAL; +} + static int ld9040_power_on(struct ld9040 *lcd) { int ret = 0; - struct lcd_platform_data *pd = NULL; + struct lcd_platform_data *pd; + pd = lcd->lcd_pd; - if (!pd) { - dev_err(lcd->dev, "platform data is NULL.\n"); - return -EFAULT; - } /* lcd power on */ ld9040_regulator_enable(lcd); if (!pd->reset) { dev_err(lcd->dev, "reset is NULL.\n"); - return -EFAULT; + return -EINVAL; } else { pd->reset(lcd->ld); - mdelay(pd->reset_delay); + msleep(pd->reset_delay); } ret = ld9040_ldi_init(lcd); @@ -604,14 +588,10 @@ static int ld9040_power_on(struct ld9040 *lcd) static int ld9040_power_off(struct ld9040 *lcd) { - int ret = 0; - struct lcd_platform_data *pd = NULL; + int ret; + struct lcd_platform_data *pd; pd = lcd->lcd_pd; - if (!pd) { - dev_err(lcd->dev, "platform data is NULL.\n"); - return -EFAULT; - } ret = ld9040_ldi_disable(lcd); if (ret) { @@ -619,7 +599,7 @@ static int ld9040_power_off(struct ld9040 *lcd) return -EIO; } - mdelay(pd->power_off_delay); + msleep(pd->power_off_delay); /* lcd power off */ ld9040_regulator_disable(lcd); @@ -631,9 +611,9 @@ static int ld9040_power(struct ld9040 *lcd, int power) { int ret = 0; - if (power_is_on(power) && !power_is_on(lcd->power)) + if (ld9040_power_is_on(power) && !ld9040_power_is_on(lcd->power)) ret = ld9040_power_on(lcd); - else if (!power_is_on(power) && power_is_on(lcd->power)) + else if (!ld9040_power_is_on(power) && ld9040_power_is_on(lcd->power)) ret = ld9040_power_off(lcd); if (!ret) @@ -698,7 +678,6 @@ static const struct backlight_ops ld9040_backlight_ops = { .update_status = ld9040_set_brightness, }; - static int ld9040_probe(struct spi_device *spi) { int ret = 0; @@ -726,22 +705,20 @@ static int ld9040_probe(struct spi_device *spi) lcd->lcd_pd = spi->dev.platform_data; if (!lcd->lcd_pd) { dev_err(&spi->dev, "platform data is NULL.\n"); - return -EFAULT; + return -EINVAL; } mutex_init(&lcd->lock); - ret = regulator_bulk_get(lcd->dev, ARRAY_SIZE(supplies), supplies); + ret = devm_regulator_bulk_get(lcd->dev, ARRAY_SIZE(supplies), supplies); if (ret) { dev_err(lcd->dev, "Failed to get regulators: %d\n", ret); return ret; } ld = lcd_device_register("ld9040", &spi->dev, lcd, &ld9040_lcd_ops); - if (IS_ERR(ld)) { - ret = PTR_ERR(ld); - goto out_free_regulator; - } + if (IS_ERR(ld)) + return PTR_ERR(ld); lcd->ld = ld; @@ -772,30 +749,28 @@ static int ld9040_probe(struct spi_device *spi) lcd->power = FB_BLANK_POWERDOWN; ld9040_power(lcd, FB_BLANK_UNBLANK); - } else + } else { lcd->power = FB_BLANK_UNBLANK; + } - dev_set_drvdata(&spi->dev, lcd); + spi_set_drvdata(spi, lcd); dev_info(&spi->dev, "ld9040 panel driver has been probed.\n"); return 0; out_unregister_lcd: lcd_device_unregister(lcd->ld); -out_free_regulator: - regulator_bulk_free(ARRAY_SIZE(supplies), supplies); return ret; } static int ld9040_remove(struct spi_device *spi) { - struct ld9040 *lcd = dev_get_drvdata(&spi->dev); + struct ld9040 *lcd = spi_get_drvdata(spi); ld9040_power(lcd, FB_BLANK_POWERDOWN); backlight_device_unregister(lcd->bd); lcd_device_unregister(lcd->ld); - regulator_bulk_free(ARRAY_SIZE(supplies), supplies); return 0; } @@ -803,8 +778,7 @@ static int ld9040_remove(struct spi_device *spi) #if defined(CONFIG_PM) static int ld9040_suspend(struct spi_device *spi, pm_message_t mesg) { - int ret = 0; - struct ld9040 *lcd = dev_get_drvdata(&spi->dev); + struct ld9040 *lcd = spi_get_drvdata(spi); dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power); @@ -812,21 +786,16 @@ static int ld9040_suspend(struct spi_device *spi, pm_message_t mesg) * when lcd panel is suspend, lcd panel becomes off * regardless of status. */ - ret = ld9040_power(lcd, FB_BLANK_POWERDOWN); - - return ret; + return ld9040_power(lcd, FB_BLANK_POWERDOWN); } static int ld9040_resume(struct spi_device *spi) { - int ret = 0; - struct ld9040 *lcd = dev_get_drvdata(&spi->dev); + struct ld9040 *lcd = spi_get_drvdata(spi); lcd->power = FB_BLANK_POWERDOWN; - ret = ld9040_power(lcd, FB_BLANK_UNBLANK); - - return ret; + return ld9040_power(lcd, FB_BLANK_UNBLANK); } #else #define ld9040_suspend NULL @@ -836,7 +805,7 @@ static int ld9040_resume(struct spi_device *spi) /* Power down all displays on reboot, poweroff or halt. */ static void ld9040_shutdown(struct spi_device *spi) { - struct ld9040 *lcd = dev_get_drvdata(&spi->dev); + struct ld9040 *lcd = spi_get_drvdata(spi); ld9040_power(lcd, FB_BLANK_POWERDOWN); } diff --git a/drivers/video/backlight/lm3630_bl.c b/drivers/video/backlight/lm3630_bl.c index a6d637b5c68..76a62e978fc 100644 --- a/drivers/video/backlight/lm3630_bl.c +++ b/drivers/video/backlight/lm3630_bl.c @@ -320,7 +320,7 @@ static int lm3630_backlight_register(struct lm3630_chip_data *pchip, backlight_device_register(name, pchip->dev, pchip, &lm3630_bank_a_ops, &props); if (IS_ERR(pchip->bled1)) - return -EIO; + return PTR_ERR(pchip->bled1); break; case BLED_2: props.brightness = pdata->init_brt_led2; @@ -329,7 +329,7 @@ static int lm3630_backlight_register(struct lm3630_chip_data *pchip, backlight_device_register(name, pchip->dev, pchip, &lm3630_bank_b_ops, &props); if (IS_ERR(pchip->bled2)) - return -EIO; + return PTR_ERR(pchip->bled2); break; } return 0; diff --git a/drivers/video/backlight/lm3639_bl.c b/drivers/video/backlight/lm3639_bl.c index 7ab2d2a04e4..053964da8dd 100644 --- a/drivers/video/backlight/lm3639_bl.c +++ b/drivers/video/backlight/lm3639_bl.c @@ -350,14 +350,13 @@ static int lm3639_probe(struct i2c_client *client, &lm3639_bled_ops, &props); if (IS_ERR(pchip->bled)) { dev_err(&client->dev, "fail : backlight register\n"); - ret = -EIO; + ret = PTR_ERR(pchip->bled); goto err_out; } ret = device_create_file(&(pchip->bled->dev), &dev_attr_bled_mode); if (ret < 0) { dev_err(&client->dev, "failed : add sysfs entries\n"); - ret = -EIO; goto err_bled_mode; } @@ -369,7 +368,6 @@ static int lm3639_probe(struct i2c_client *client, &client->dev, &pchip->cdev_flash); if (ret < 0) { dev_err(&client->dev, "fail : flash register\n"); - ret = -EIO; goto err_flash; } @@ -381,7 +379,6 @@ static int lm3639_probe(struct i2c_client *client, &client->dev, &pchip->cdev_torch); if (ret < 0) { dev_err(&client->dev, "fail : torch register\n"); - ret = -EIO; goto err_torch; } diff --git a/drivers/video/backlight/lms283gf05.c b/drivers/video/backlight/lms283gf05.c index 55819b38470..4eec47261cd 100644 --- a/drivers/video/backlight/lms283gf05.c +++ b/drivers/video/backlight/lms283gf05.c @@ -180,7 +180,7 @@ static int lms283gf05_probe(struct spi_device *spi) st->spi = spi; st->ld = ld; - dev_set_drvdata(&spi->dev, st); + spi_set_drvdata(spi, st); /* kick in the LCD */ if (pdata) @@ -192,7 +192,7 @@ static int lms283gf05_probe(struct spi_device *spi) static int lms283gf05_remove(struct spi_device *spi) { - struct lms283gf05_state *st = dev_get_drvdata(&spi->dev); + struct lms283gf05_state *st = spi_get_drvdata(spi); lcd_device_unregister(st->ld); diff --git a/drivers/video/backlight/lms501kf03.c b/drivers/video/backlight/lms501kf03.c new file mode 100644 index 00000000000..b43882abefa --- /dev/null +++ b/drivers/video/backlight/lms501kf03.c @@ -0,0 +1,441 @@ +/* + * lms501kf03 TFT LCD panel 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/backlight.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/gpio.h> +#include <linux/lcd.h> +#include <linux/module.h> +#include <linux/spi/spi.h> +#include <linux/wait.h> + +#define COMMAND_ONLY 0x00 +#define DATA_ONLY 0x01 + +struct lms501kf03 { + struct device *dev; + struct spi_device *spi; + unsigned int power; + struct lcd_device *ld; + struct lcd_platform_data *lcd_pd; +}; + +static const unsigned char seq_password[] = { + 0xb9, 0xff, 0x83, 0x69, +}; + +static const unsigned char seq_power[] = { + 0xb1, 0x01, 0x00, 0x34, 0x06, 0x00, 0x14, 0x14, 0x20, 0x28, + 0x12, 0x12, 0x17, 0x0a, 0x01, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, +}; + +static const unsigned char seq_display[] = { + 0xb2, 0x00, 0x2b, 0x03, 0x03, 0x70, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x03, 0x00, 0x01, +}; + +static const unsigned char seq_rgb_if[] = { + 0xb3, 0x09, +}; + +static const unsigned char seq_display_inv[] = { + 0xb4, 0x01, 0x08, 0x77, 0x0e, 0x06, +}; + +static const unsigned char seq_vcom[] = { + 0xb6, 0x4c, 0x2e, +}; + +static const unsigned char seq_gate[] = { + 0xd5, 0x00, 0x05, 0x03, 0x29, 0x01, 0x07, 0x17, 0x68, 0x13, + 0x37, 0x20, 0x31, 0x8a, 0x46, 0x9b, 0x57, 0x13, 0x02, 0x75, + 0xb9, 0x64, 0xa8, 0x07, 0x0f, 0x04, 0x07, +}; + +static const unsigned char seq_panel[] = { + 0xcc, 0x02, +}; + +static const unsigned char seq_col_mod[] = { + 0x3a, 0x77, +}; + +static const unsigned char seq_w_gamma[] = { + 0xe0, 0x00, 0x04, 0x09, 0x0f, 0x1f, 0x3f, 0x1f, 0x2f, 0x0a, + 0x0f, 0x10, 0x16, 0x18, 0x16, 0x17, 0x0d, 0x15, 0x00, 0x04, + 0x09, 0x0f, 0x38, 0x3f, 0x20, 0x39, 0x0a, 0x0f, 0x10, 0x16, + 0x18, 0x16, 0x17, 0x0d, 0x15, +}; + +static const unsigned char seq_rgb_gamma[] = { + 0xc1, 0x01, 0x03, 0x07, 0x0f, 0x1a, 0x22, 0x2c, 0x33, 0x3c, + 0x46, 0x4f, 0x58, 0x60, 0x69, 0x71, 0x79, 0x82, 0x89, 0x92, + 0x9a, 0xa1, 0xa9, 0xb1, 0xb9, 0xc1, 0xc9, 0xcf, 0xd6, 0xde, + 0xe5, 0xec, 0xf3, 0xf9, 0xff, 0xdd, 0x39, 0x07, 0x1c, 0xcb, + 0xab, 0x5f, 0x49, 0x80, 0x03, 0x07, 0x0f, 0x19, 0x20, 0x2a, + 0x31, 0x39, 0x42, 0x4b, 0x53, 0x5b, 0x63, 0x6b, 0x73, 0x7b, + 0x83, 0x8a, 0x92, 0x9b, 0xa2, 0xaa, 0xb2, 0xba, 0xc2, 0xca, + 0xd0, 0xd8, 0xe1, 0xe8, 0xf0, 0xf8, 0xff, 0xf7, 0xd8, 0xbe, + 0xa7, 0x39, 0x40, 0x85, 0x8c, 0xc0, 0x04, 0x07, 0x0c, 0x17, + 0x1c, 0x23, 0x2b, 0x34, 0x3b, 0x43, 0x4c, 0x54, 0x5b, 0x63, + 0x6a, 0x73, 0x7a, 0x82, 0x8a, 0x91, 0x98, 0xa1, 0xa8, 0xb0, + 0xb7, 0xc1, 0xc9, 0xcf, 0xd9, 0xe3, 0xea, 0xf4, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static const unsigned char seq_up_dn[] = { + 0x36, 0x10, +}; + +static const unsigned char seq_sleep_in[] = { + 0x10, +}; + +static const unsigned char seq_sleep_out[] = { + 0x11, +}; + +static const unsigned char seq_display_on[] = { + 0x29, +}; + +static const unsigned char seq_display_off[] = { + 0x10, +}; + +static int lms501kf03_spi_write_byte(struct lms501kf03 *lcd, int addr, int data) +{ + u16 buf[1]; + struct spi_message msg; + + struct spi_transfer xfer = { + .len = 2, + .tx_buf = buf, + }; + + buf[0] = (addr << 8) | data; + + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + return spi_sync(lcd->spi, &msg); +} + +static int lms501kf03_spi_write(struct lms501kf03 *lcd, unsigned char address, + unsigned char command) +{ + return lms501kf03_spi_write_byte(lcd, address, command); +} + +static int lms501kf03_panel_send_sequence(struct lms501kf03 *lcd, + const unsigned char *wbuf, + unsigned int len) +{ + int ret = 0, i = 0; + + while (i < len) { + if (i == 0) + ret = lms501kf03_spi_write(lcd, COMMAND_ONLY, wbuf[i]); + else + ret = lms501kf03_spi_write(lcd, DATA_ONLY, wbuf[i]); + if (ret) + break; + i += 1; + } + + return ret; +} + +static int lms501kf03_ldi_init(struct lms501kf03 *lcd) +{ + int ret, i; + static const unsigned char *init_seq[] = { + seq_password, + seq_power, + seq_display, + seq_rgb_if, + seq_display_inv, + seq_vcom, + seq_gate, + seq_panel, + seq_col_mod, + seq_w_gamma, + seq_rgb_gamma, + seq_sleep_out, + }; + + static const unsigned int size_seq[] = { + ARRAY_SIZE(seq_password), + ARRAY_SIZE(seq_power), + ARRAY_SIZE(seq_display), + ARRAY_SIZE(seq_rgb_if), + ARRAY_SIZE(seq_display_inv), + ARRAY_SIZE(seq_vcom), + ARRAY_SIZE(seq_gate), + ARRAY_SIZE(seq_panel), + ARRAY_SIZE(seq_col_mod), + ARRAY_SIZE(seq_w_gamma), + ARRAY_SIZE(seq_rgb_gamma), + ARRAY_SIZE(seq_sleep_out), + }; + + for (i = 0; i < ARRAY_SIZE(init_seq); i++) { + ret = lms501kf03_panel_send_sequence(lcd, init_seq[i], + size_seq[i]); + if (ret) + break; + } + /* + * According to the datasheet, 120ms delay time is required. + * After sleep out sequence, command is blocked for 120ms. + * Thus, LDI should wait for 120ms. + */ + msleep(120); + + return ret; +} + +static int lms501kf03_ldi_enable(struct lms501kf03 *lcd) +{ + return lms501kf03_panel_send_sequence(lcd, seq_display_on, + ARRAY_SIZE(seq_display_on)); +} + +static int lms501kf03_ldi_disable(struct lms501kf03 *lcd) +{ + return lms501kf03_panel_send_sequence(lcd, seq_display_off, + ARRAY_SIZE(seq_display_off)); +} + +static int lms501kf03_power_is_on(int power) +{ + return (power) <= FB_BLANK_NORMAL; +} + +static int lms501kf03_power_on(struct lms501kf03 *lcd) +{ + int ret = 0; + struct lcd_platform_data *pd; + + pd = lcd->lcd_pd; + + if (!pd->power_on) { + dev_err(lcd->dev, "power_on is NULL.\n"); + return -EINVAL; + } else { + pd->power_on(lcd->ld, 1); + msleep(pd->power_on_delay); + } + + if (!pd->reset) { + dev_err(lcd->dev, "reset is NULL.\n"); + return -EINVAL; + } else { + pd->reset(lcd->ld); + msleep(pd->reset_delay); + } + + ret = lms501kf03_ldi_init(lcd); + if (ret) { + dev_err(lcd->dev, "failed to initialize ldi.\n"); + return ret; + } + + ret = lms501kf03_ldi_enable(lcd); + if (ret) { + dev_err(lcd->dev, "failed to enable ldi.\n"); + return ret; + } + + return 0; +} + +static int lms501kf03_power_off(struct lms501kf03 *lcd) +{ + int ret = 0; + struct lcd_platform_data *pd; + + pd = lcd->lcd_pd; + + ret = lms501kf03_ldi_disable(lcd); + if (ret) { + dev_err(lcd->dev, "lcd setting failed.\n"); + return -EIO; + } + + msleep(pd->power_off_delay); + + pd->power_on(lcd->ld, 0); + + return 0; +} + +static int lms501kf03_power(struct lms501kf03 *lcd, int power) +{ + int ret = 0; + + if (lms501kf03_power_is_on(power) && + !lms501kf03_power_is_on(lcd->power)) + ret = lms501kf03_power_on(lcd); + else if (!lms501kf03_power_is_on(power) && + lms501kf03_power_is_on(lcd->power)) + ret = lms501kf03_power_off(lcd); + + if (!ret) + lcd->power = power; + + return ret; +} + +static int lms501kf03_get_power(struct lcd_device *ld) +{ + struct lms501kf03 *lcd = lcd_get_data(ld); + + return lcd->power; +} + +static int lms501kf03_set_power(struct lcd_device *ld, int power) +{ + struct lms501kf03 *lcd = lcd_get_data(ld); + + 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; + } + + return lms501kf03_power(lcd, power); +} + +static struct lcd_ops lms501kf03_lcd_ops = { + .get_power = lms501kf03_get_power, + .set_power = lms501kf03_set_power, +}; + +static int lms501kf03_probe(struct spi_device *spi) +{ + struct lms501kf03 *lcd = NULL; + struct lcd_device *ld = NULL; + int ret = 0; + + lcd = devm_kzalloc(&spi->dev, sizeof(struct lms501kf03), GFP_KERNEL); + if (!lcd) + return -ENOMEM; + + /* lms501kf03 lcd panel uses 3-wire 9-bit SPI Mode. */ + spi->bits_per_word = 9; + + ret = spi_setup(spi); + if (ret < 0) { + dev_err(&spi->dev, "spi setup failed.\n"); + return ret; + } + + lcd->spi = spi; + lcd->dev = &spi->dev; + + lcd->lcd_pd = spi->dev.platform_data; + if (!lcd->lcd_pd) { + dev_err(&spi->dev, "platform data is NULL\n"); + return -EINVAL; + } + + ld = lcd_device_register("lms501kf03", &spi->dev, lcd, + &lms501kf03_lcd_ops); + if (IS_ERR(ld)) + return PTR_ERR(ld); + + lcd->ld = ld; + + if (!lcd->lcd_pd->lcd_enabled) { + /* + * if lcd panel was off from bootloader then + * current lcd status is powerdown and then + * it enables lcd panel. + */ + lcd->power = FB_BLANK_POWERDOWN; + + lms501kf03_power(lcd, FB_BLANK_UNBLANK); + } else { + lcd->power = FB_BLANK_UNBLANK; + } + + spi_set_drvdata(spi, lcd); + + dev_info(&spi->dev, "lms501kf03 panel driver has been probed.\n"); + + return 0; +} + +static int lms501kf03_remove(struct spi_device *spi) +{ + struct lms501kf03 *lcd = spi_get_drvdata(spi); + + lms501kf03_power(lcd, FB_BLANK_POWERDOWN); + lcd_device_unregister(lcd->ld); + + return 0; +} + +#if defined(CONFIG_PM) + +static int lms501kf03_suspend(struct spi_device *spi, pm_message_t mesg) +{ + struct lms501kf03 *lcd = spi_get_drvdata(spi); + + dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power); + + /* + * when lcd panel is suspend, lcd panel becomes off + * regardless of status. + */ + return lms501kf03_power(lcd, FB_BLANK_POWERDOWN); +} + +static int lms501kf03_resume(struct spi_device *spi) +{ + struct lms501kf03 *lcd = spi_get_drvdata(spi); + + lcd->power = FB_BLANK_POWERDOWN; + + return lms501kf03_power(lcd, FB_BLANK_UNBLANK); +} +#else +#define lms501kf03_suspend NULL +#define lms501kf03_resume NULL +#endif + +static void lms501kf03_shutdown(struct spi_device *spi) +{ + struct lms501kf03 *lcd = spi_get_drvdata(spi); + + lms501kf03_power(lcd, FB_BLANK_POWERDOWN); +} + +static struct spi_driver lms501kf03_driver = { + .driver = { + .name = "lms501kf03", + .owner = THIS_MODULE, + }, + .probe = lms501kf03_probe, + .remove = lms501kf03_remove, + .shutdown = lms501kf03_shutdown, + .suspend = lms501kf03_suspend, + .resume = lms501kf03_resume, +}; + +module_spi_driver(lms501kf03_driver); + +MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>"); +MODULE_DESCRIPTION("lms501kf03 LCD Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/backlight/lp855x_bl.c b/drivers/video/backlight/lp855x_bl.c index 6e4db0c874c..7ae9ae6f465 100644 --- a/drivers/video/backlight/lp855x_bl.c +++ b/drivers/video/backlight/lp855x_bl.c @@ -17,21 +17,48 @@ #include <linux/platform_data/lp855x.h> #include <linux/pwm.h> -/* Registers */ -#define BRIGHTNESS_CTRL 0x00 -#define DEVICE_CTRL 0x01 -#define EEPROM_START 0xA0 -#define EEPROM_END 0xA7 -#define EPROM_START 0xA0 -#define EPROM_END 0xAF +/* LP8550/1/2/3/6 Registers */ +#define LP855X_BRIGHTNESS_CTRL 0x00 +#define LP855X_DEVICE_CTRL 0x01 +#define LP855X_EEPROM_START 0xA0 +#define LP855X_EEPROM_END 0xA7 +#define LP8556_EPROM_START 0xA0 +#define LP8556_EPROM_END 0xAF + +/* LP8557 Registers */ +#define LP8557_BL_CMD 0x00 +#define LP8557_BL_MASK 0x01 +#define LP8557_BL_ON 0x01 +#define LP8557_BL_OFF 0x00 +#define LP8557_BRIGHTNESS_CTRL 0x04 +#define LP8557_CONFIG 0x10 +#define LP8557_EPROM_START 0x10 +#define LP8557_EPROM_END 0x1E #define BUF_SIZE 20 #define DEFAULT_BL_NAME "lcd-backlight" #define MAX_BRIGHTNESS 255 +struct lp855x; + +/* + * struct lp855x_device_config + * @pre_init_device: init device function call before updating the brightness + * @reg_brightness: register address for brigthenss control + * @reg_devicectrl: register address for device control + * @post_init_device: late init device function call + */ +struct lp855x_device_config { + int (*pre_init_device)(struct lp855x *); + u8 reg_brightness; + u8 reg_devicectrl; + int (*post_init_device)(struct lp855x *); +}; + struct lp855x { const char *chipname; enum lp855x_chip_id chip_id; + struct lp855x_device_config *cfg; struct i2c_client *client; struct backlight_device *bl; struct device *dev; @@ -39,9 +66,15 @@ struct lp855x { struct pwm_device *pwm; }; -static int lp855x_read_byte(struct lp855x *lp, u8 reg, u8 *data) +static int lp855x_write_byte(struct lp855x *lp, u8 reg, u8 data) +{ + return i2c_smbus_write_byte_data(lp->client, reg, data); +} + +static int lp855x_update_bit(struct lp855x *lp, u8 reg, u8 mask, u8 data) { int ret; + u8 tmp; ret = i2c_smbus_read_byte_data(lp->client, reg); if (ret < 0) { @@ -49,13 +82,11 @@ static int lp855x_read_byte(struct lp855x *lp, u8 reg, u8 *data) return ret; } - *data = (u8)ret; - return 0; -} + tmp = (u8)ret; + tmp &= ~mask; + tmp |= data & mask; -static int lp855x_write_byte(struct lp855x *lp, u8 reg, u8 data) -{ - return i2c_smbus_write_byte_data(lp->client, reg, data); + return lp855x_write_byte(lp, reg, tmp); } static bool lp855x_is_valid_rom_area(struct lp855x *lp, u8 addr) @@ -67,12 +98,16 @@ static bool lp855x_is_valid_rom_area(struct lp855x *lp, u8 addr) case LP8551: case LP8552: case LP8553: - start = EEPROM_START; - end = EEPROM_END; + start = LP855X_EEPROM_START; + end = LP855X_EEPROM_END; break; case LP8556: - start = EPROM_START; - end = EPROM_END; + start = LP8556_EPROM_START; + end = LP8556_EPROM_END; + break; + case LP8557: + start = LP8557_EPROM_START; + end = LP8557_EPROM_END; break; default: return false; @@ -81,21 +116,76 @@ static bool lp855x_is_valid_rom_area(struct lp855x *lp, u8 addr) return (addr >= start && addr <= end); } -static int lp855x_init_registers(struct lp855x *lp) +static int lp8557_bl_off(struct lp855x *lp) +{ + /* BL_ON = 0 before updating EPROM settings */ + return lp855x_update_bit(lp, LP8557_BL_CMD, LP8557_BL_MASK, + LP8557_BL_OFF); +} + +static int lp8557_bl_on(struct lp855x *lp) +{ + /* BL_ON = 1 after updating EPROM settings */ + return lp855x_update_bit(lp, LP8557_BL_CMD, LP8557_BL_MASK, + LP8557_BL_ON); +} + +static struct lp855x_device_config lp855x_dev_cfg = { + .reg_brightness = LP855X_BRIGHTNESS_CTRL, + .reg_devicectrl = LP855X_DEVICE_CTRL, +}; + +static struct lp855x_device_config lp8557_dev_cfg = { + .reg_brightness = LP8557_BRIGHTNESS_CTRL, + .reg_devicectrl = LP8557_CONFIG, + .pre_init_device = lp8557_bl_off, + .post_init_device = lp8557_bl_on, +}; + +/* + * Device specific configuration flow + * + * a) pre_init_device(optional) + * b) update the brightness register + * c) update device control register + * d) update ROM area(optional) + * e) post_init_device(optional) + * + */ +static int lp855x_configure(struct lp855x *lp) { u8 val, addr; int i, ret; struct lp855x_platform_data *pd = lp->pdata; + switch (lp->chip_id) { + case LP8550 ... LP8556: + lp->cfg = &lp855x_dev_cfg; + break; + case LP8557: + lp->cfg = &lp8557_dev_cfg; + break; + default: + return -EINVAL; + } + + if (lp->cfg->pre_init_device) { + ret = lp->cfg->pre_init_device(lp); + if (ret) { + dev_err(lp->dev, "pre init device err: %d\n", ret); + goto err; + } + } + val = pd->initial_brightness; - ret = lp855x_write_byte(lp, BRIGHTNESS_CTRL, val); + ret = lp855x_write_byte(lp, lp->cfg->reg_brightness, val); if (ret) - return ret; + goto err; val = pd->device_control; - ret = lp855x_write_byte(lp, DEVICE_CTRL, val); + ret = lp855x_write_byte(lp, lp->cfg->reg_devicectrl, val); if (ret) - return ret; + goto err; if (pd->load_new_rom_data && pd->size_program) { for (i = 0; i < pd->size_program; i++) { @@ -106,10 +196,21 @@ static int lp855x_init_registers(struct lp855x *lp) ret = lp855x_write_byte(lp, addr, val); if (ret) - return ret; + goto err; + } + } + + if (lp->cfg->post_init_device) { + ret = lp->cfg->post_init_device(lp); + if (ret) { + dev_err(lp->dev, "post init device err: %d\n", ret); + goto err; } } + return 0; + +err: return ret; } @@ -151,7 +252,7 @@ static int lp855x_bl_update_status(struct backlight_device *bl) } else if (mode == REGISTER_BASED) { u8 val = bl->props.brightness; - lp855x_write_byte(lp, BRIGHTNESS_CTRL, val); + lp855x_write_byte(lp, lp->cfg->reg_brightness, val); } return 0; @@ -159,16 +260,6 @@ static int lp855x_bl_update_status(struct backlight_device *bl) 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 == REGISTER_BASED) { - u8 val = 0; - - lp855x_read_byte(lp, BRIGHTNESS_CTRL, &val); - bl->props.brightness = val; - } - return bl->props.brightness; } @@ -271,11 +362,10 @@ static int lp855x_probe(struct i2c_client *cl, const struct i2c_device_id *id) lp->chip_id = id->driver_data; i2c_set_clientdata(cl, lp); - ret = lp855x_init_registers(lp); + ret = lp855x_configure(lp); if (ret) { - dev_err(lp->dev, "i2c communication err: %d", ret); - if (mode == REGISTER_BASED) - goto err_dev; + dev_err(lp->dev, "device config err: %d", ret); + goto err_dev; } ret = lp855x_backlight_register(lp); @@ -318,6 +408,7 @@ static const struct i2c_device_id lp855x_ids[] = { {"lp8552", LP8552}, {"lp8553", LP8553}, {"lp8556", LP8556}, + {"lp8557", LP8557}, { } }; MODULE_DEVICE_TABLE(i2c, lp855x_ids); diff --git a/drivers/video/backlight/lp8788_bl.c b/drivers/video/backlight/lp8788_bl.c new file mode 100644 index 00000000000..4bb8b4f140c --- /dev/null +++ b/drivers/video/backlight/lp8788_bl.c @@ -0,0 +1,333 @@ +/* + * TI LP8788 MFD - backlight driver + * + * Copyright 2012 Texas Instruments + * + * Author: Milo(Woogyom) Kim <milo.kim@ti.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/backlight.h> +#include <linux/err.h> +#include <linux/mfd/lp8788.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pwm.h> +#include <linux/slab.h> + +/* Register address */ +#define LP8788_BL_CONFIG 0x96 +#define LP8788_BL_EN BIT(0) +#define LP8788_BL_PWM_INPUT_EN BIT(5) +#define LP8788_BL_FULLSCALE_SHIFT 2 +#define LP8788_BL_DIM_MODE_SHIFT 1 +#define LP8788_BL_PWM_POLARITY_SHIFT 6 + +#define LP8788_BL_BRIGHTNESS 0x97 + +#define LP8788_BL_RAMP 0x98 +#define LP8788_BL_RAMP_RISE_SHIFT 4 + +#define MAX_BRIGHTNESS 127 +#define DEFAULT_BL_NAME "lcd-backlight" + +struct lp8788_bl_config { + enum lp8788_bl_ctrl_mode bl_mode; + enum lp8788_bl_dim_mode dim_mode; + enum lp8788_bl_full_scale_current full_scale; + enum lp8788_bl_ramp_step rise_time; + enum lp8788_bl_ramp_step fall_time; + enum pwm_polarity pwm_pol; +}; + +struct lp8788_bl { + struct lp8788 *lp; + struct backlight_device *bl_dev; + struct lp8788_backlight_platform_data *pdata; + enum lp8788_bl_ctrl_mode mode; + struct pwm_device *pwm; +}; + +struct lp8788_bl_config default_bl_config = { + .bl_mode = LP8788_BL_REGISTER_ONLY, + .dim_mode = LP8788_DIM_EXPONENTIAL, + .full_scale = LP8788_FULLSCALE_1900uA, + .rise_time = LP8788_RAMP_8192us, + .fall_time = LP8788_RAMP_8192us, + .pwm_pol = PWM_POLARITY_NORMAL, +}; + +static inline bool is_brightness_ctrl_by_pwm(enum lp8788_bl_ctrl_mode mode) +{ + return (mode == LP8788_BL_COMB_PWM_BASED); +} + +static inline bool is_brightness_ctrl_by_register(enum lp8788_bl_ctrl_mode mode) +{ + return (mode == LP8788_BL_REGISTER_ONLY || + mode == LP8788_BL_COMB_REGISTER_BASED); +} + +static int lp8788_backlight_configure(struct lp8788_bl *bl) +{ + struct lp8788_backlight_platform_data *pdata = bl->pdata; + struct lp8788_bl_config *cfg = &default_bl_config; + int ret; + u8 val; + + /* + * Update chip configuration if platform data exists, + * otherwise use the default settings. + */ + if (pdata) { + cfg->bl_mode = pdata->bl_mode; + cfg->dim_mode = pdata->dim_mode; + cfg->full_scale = pdata->full_scale; + cfg->rise_time = pdata->rise_time; + cfg->fall_time = pdata->fall_time; + cfg->pwm_pol = pdata->pwm_pol; + } + + /* Brightness ramp up/down */ + val = (cfg->rise_time << LP8788_BL_RAMP_RISE_SHIFT) | cfg->fall_time; + ret = lp8788_write_byte(bl->lp, LP8788_BL_RAMP, val); + if (ret) + return ret; + + /* Fullscale current setting */ + val = (cfg->full_scale << LP8788_BL_FULLSCALE_SHIFT) | + (cfg->dim_mode << LP8788_BL_DIM_MODE_SHIFT); + + /* Brightness control mode */ + switch (cfg->bl_mode) { + case LP8788_BL_REGISTER_ONLY: + val |= LP8788_BL_EN; + break; + case LP8788_BL_COMB_PWM_BASED: + case LP8788_BL_COMB_REGISTER_BASED: + val |= LP8788_BL_EN | LP8788_BL_PWM_INPUT_EN | + (cfg->pwm_pol << LP8788_BL_PWM_POLARITY_SHIFT); + break; + default: + dev_err(bl->lp->dev, "invalid mode: %d\n", cfg->bl_mode); + return -EINVAL; + } + + bl->mode = cfg->bl_mode; + + return lp8788_write_byte(bl->lp, LP8788_BL_CONFIG, val); +} + +static void lp8788_pwm_ctrl(struct lp8788_bl *bl, int br, int max_br) +{ + unsigned int period; + unsigned int duty; + struct device *dev; + struct pwm_device *pwm; + + if (!bl->pdata) + return; + + period = bl->pdata->period_ns; + duty = br * period / max_br; + dev = bl->lp->dev; + + /* request PWM device with the consumer name */ + if (!bl->pwm) { + pwm = devm_pwm_get(dev, LP8788_DEV_BACKLIGHT); + if (IS_ERR(pwm)) { + dev_err(dev, "can not get PWM device\n"); + return; + } + + bl->pwm = pwm; + } + + pwm_config(bl->pwm, duty, period); + if (duty) + pwm_enable(bl->pwm); + else + pwm_disable(bl->pwm); +} + +static int lp8788_bl_update_status(struct backlight_device *bl_dev) +{ + struct lp8788_bl *bl = bl_get_data(bl_dev); + enum lp8788_bl_ctrl_mode mode = bl->mode; + + if (bl_dev->props.state & BL_CORE_SUSPENDED) + bl_dev->props.brightness = 0; + + if (is_brightness_ctrl_by_pwm(mode)) { + int brt = bl_dev->props.brightness; + int max = bl_dev->props.max_brightness; + + lp8788_pwm_ctrl(bl, brt, max); + } else if (is_brightness_ctrl_by_register(mode)) { + u8 brt = bl_dev->props.brightness; + + lp8788_write_byte(bl->lp, LP8788_BL_BRIGHTNESS, brt); + } + + return 0; +} + +static int lp8788_bl_get_brightness(struct backlight_device *bl_dev) +{ + return bl_dev->props.brightness; +} + +static const struct backlight_ops lp8788_bl_ops = { + .options = BL_CORE_SUSPENDRESUME, + .update_status = lp8788_bl_update_status, + .get_brightness = lp8788_bl_get_brightness, +}; + +static int lp8788_backlight_register(struct lp8788_bl *bl) +{ + struct backlight_device *bl_dev; + struct backlight_properties props; + struct lp8788_backlight_platform_data *pdata = bl->pdata; + int init_brt; + char *name; + + props.type = BACKLIGHT_PLATFORM; + props.max_brightness = MAX_BRIGHTNESS; + + /* Initial brightness */ + if (pdata) + init_brt = min_t(int, pdata->initial_brightness, + props.max_brightness); + else + init_brt = 0; + + props.brightness = init_brt; + + /* Backlight device name */ + if (!pdata || !pdata->name) + name = DEFAULT_BL_NAME; + else + name = pdata->name; + + bl_dev = backlight_device_register(name, bl->lp->dev, bl, + &lp8788_bl_ops, &props); + if (IS_ERR(bl_dev)) + return PTR_ERR(bl_dev); + + bl->bl_dev = bl_dev; + + return 0; +} + +static void lp8788_backlight_unregister(struct lp8788_bl *bl) +{ + struct backlight_device *bl_dev = bl->bl_dev; + + if (bl_dev) + backlight_device_unregister(bl_dev); +} + +static ssize_t lp8788_get_bl_ctl_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct lp8788_bl *bl = dev_get_drvdata(dev); + enum lp8788_bl_ctrl_mode mode = bl->mode; + char *strmode; + + if (is_brightness_ctrl_by_pwm(mode)) + strmode = "PWM based"; + else if (is_brightness_ctrl_by_register(mode)) + strmode = "Register based"; + else + strmode = "Invalid mode"; + + return scnprintf(buf, PAGE_SIZE, "%s\n", strmode); +} + +static DEVICE_ATTR(bl_ctl_mode, S_IRUGO, lp8788_get_bl_ctl_mode, NULL); + +static struct attribute *lp8788_attributes[] = { + &dev_attr_bl_ctl_mode.attr, + NULL, +}; + +static const struct attribute_group lp8788_attr_group = { + .attrs = lp8788_attributes, +}; + +static int lp8788_backlight_probe(struct platform_device *pdev) +{ + struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent); + struct lp8788_bl *bl; + int ret; + + bl = devm_kzalloc(lp->dev, sizeof(struct lp8788_bl), GFP_KERNEL); + if (!bl) + return -ENOMEM; + + bl->lp = lp; + if (lp->pdata) + bl->pdata = lp->pdata->bl_pdata; + + platform_set_drvdata(pdev, bl); + + ret = lp8788_backlight_configure(bl); + if (ret) { + dev_err(lp->dev, "backlight config err: %d\n", ret); + goto err_dev; + } + + ret = lp8788_backlight_register(bl); + if (ret) { + dev_err(lp->dev, "register backlight err: %d\n", ret); + goto err_dev; + } + + ret = sysfs_create_group(&pdev->dev.kobj, &lp8788_attr_group); + if (ret) { + dev_err(lp->dev, "register sysfs err: %d\n", ret); + goto err_sysfs; + } + + backlight_update_status(bl->bl_dev); + + return 0; + +err_sysfs: + lp8788_backlight_unregister(bl); +err_dev: + return ret; +} + +static int lp8788_backlight_remove(struct platform_device *pdev) +{ + struct lp8788_bl *bl = platform_get_drvdata(pdev); + struct backlight_device *bl_dev = bl->bl_dev; + + bl_dev->props.brightness = 0; + backlight_update_status(bl_dev); + sysfs_remove_group(&pdev->dev.kobj, &lp8788_attr_group); + lp8788_backlight_unregister(bl); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver lp8788_bl_driver = { + .probe = lp8788_backlight_probe, + .remove = lp8788_backlight_remove, + .driver = { + .name = LP8788_DEV_BACKLIGHT, + .owner = THIS_MODULE, + }, +}; +module_platform_driver(lp8788_bl_driver); + +MODULE_DESCRIPTION("Texas Instruments LP8788 Backlight Driver"); +MODULE_AUTHOR("Milo Kim"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:lp8788-backlight"); diff --git a/drivers/video/backlight/ltv350qv.c b/drivers/video/backlight/ltv350qv.c index 226d813edf0..c0b4b8f2de9 100644 --- a/drivers/video/backlight/ltv350qv.c +++ b/drivers/video/backlight/ltv350qv.c @@ -252,7 +252,7 @@ static int ltv350qv_probe(struct spi_device *spi) if (ret) goto out_unregister; - dev_set_drvdata(&spi->dev, lcd); + spi_set_drvdata(spi, lcd); return 0; @@ -263,7 +263,7 @@ out_unregister: static int ltv350qv_remove(struct spi_device *spi) { - struct ltv350qv *lcd = dev_get_drvdata(&spi->dev); + struct ltv350qv *lcd = spi_get_drvdata(spi); ltv350qv_power(lcd, FB_BLANK_POWERDOWN); lcd_device_unregister(lcd->ld); @@ -274,14 +274,14 @@ static int ltv350qv_remove(struct spi_device *spi) #ifdef CONFIG_PM static int ltv350qv_suspend(struct spi_device *spi, pm_message_t state) { - struct ltv350qv *lcd = dev_get_drvdata(&spi->dev); + struct ltv350qv *lcd = spi_get_drvdata(spi); return ltv350qv_power(lcd, FB_BLANK_POWERDOWN); } static int ltv350qv_resume(struct spi_device *spi) { - struct ltv350qv *lcd = dev_get_drvdata(&spi->dev); + struct ltv350qv *lcd = spi_get_drvdata(spi); return ltv350qv_power(lcd, FB_BLANK_UNBLANK); } @@ -293,7 +293,7 @@ static int ltv350qv_resume(struct spi_device *spi) /* Power down all displays on reboot, poweroff or halt */ static void ltv350qv_shutdown(struct spi_device *spi) { - struct ltv350qv *lcd = dev_get_drvdata(&spi->dev); + struct ltv350qv *lcd = spi_get_drvdata(spi); ltv350qv_power(lcd, FB_BLANK_POWERDOWN); } diff --git a/drivers/video/backlight/max8925_bl.c b/drivers/video/backlight/max8925_bl.c index 2c9bce050aa..5ca11b066b7 100644 --- a/drivers/video/backlight/max8925_bl.c +++ b/drivers/video/backlight/max8925_bl.c @@ -101,6 +101,29 @@ static const struct backlight_ops max8925_backlight_ops = { .get_brightness = max8925_backlight_get_brightness, }; +#ifdef CONFIG_OF +static int max8925_backlight_dt_init(struct platform_device *pdev, + struct max8925_backlight_pdata *pdata) +{ + struct device_node *nproot = pdev->dev.parent->of_node, *np; + int dual_string; + + if (!nproot) + return -ENODEV; + np = of_find_node_by_name(nproot, "backlight"); + if (!np) { + dev_err(&pdev->dev, "failed to find backlight node\n"); + return -ENODEV; + } + + of_property_read_u32(np, "maxim,max8925-dual-string", &dual_string); + pdata->dual_string = dual_string; + return 0; +} +#else +#define max8925_backlight_dt_init(x, y) (-1) +#endif + static int max8925_backlight_probe(struct platform_device *pdev) { struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); @@ -147,6 +170,13 @@ static int max8925_backlight_probe(struct platform_device *pdev) platform_set_drvdata(pdev, bl); value = 0; + if (pdev->dev.parent->of_node && !pdata) { + pdata = devm_kzalloc(&pdev->dev, + sizeof(struct max8925_backlight_pdata), + GFP_KERNEL); + max8925_backlight_dt_init(pdev, pdata); + } + if (pdata) { if (pdata->lxw_scl) value |= (1 << 7); @@ -158,7 +188,6 @@ static int max8925_backlight_probe(struct platform_device *pdev) ret = max8925_set_bits(chip->i2c, data->reg_mode_cntl, 0xfe, value); if (ret < 0) goto out_brt; - backlight_update_status(bl); return 0; out_brt: diff --git a/drivers/video/backlight/omap1_bl.c b/drivers/video/backlight/omap1_bl.c index af31c269baa..62711016306 100644 --- a/drivers/video/backlight/omap1_bl.c +++ b/drivers/video/backlight/omap1_bl.c @@ -77,7 +77,7 @@ static void omapbl_blank(struct omap_backlight *bl, int mode) static int omapbl_suspend(struct platform_device *pdev, pm_message_t state) { struct backlight_device *dev = platform_get_drvdata(pdev); - struct omap_backlight *bl = dev_get_drvdata(&dev->dev); + struct omap_backlight *bl = bl_get_data(dev); omapbl_blank(bl, FB_BLANK_POWERDOWN); return 0; @@ -86,7 +86,7 @@ static int omapbl_suspend(struct platform_device *pdev, pm_message_t state) static int omapbl_resume(struct platform_device *pdev) { struct backlight_device *dev = platform_get_drvdata(pdev); - struct omap_backlight *bl = dev_get_drvdata(&dev->dev); + struct omap_backlight *bl = bl_get_data(dev); omapbl_blank(bl, bl->powermode); return 0; @@ -98,7 +98,7 @@ static int omapbl_resume(struct platform_device *pdev) static int omapbl_set_power(struct backlight_device *dev, int state) { - struct omap_backlight *bl = dev_get_drvdata(&dev->dev); + struct omap_backlight *bl = bl_get_data(dev); omapbl_blank(bl, state); bl->powermode = state; @@ -108,7 +108,7 @@ static int omapbl_set_power(struct backlight_device *dev, int state) static int omapbl_update_status(struct backlight_device *dev) { - struct omap_backlight *bl = dev_get_drvdata(&dev->dev); + struct omap_backlight *bl = bl_get_data(dev); if (bl->current_intensity != dev->props.brightness) { if (bl->powermode == FB_BLANK_UNBLANK) @@ -124,7 +124,7 @@ static int omapbl_update_status(struct backlight_device *dev) static int omapbl_get_intensity(struct backlight_device *dev) { - struct omap_backlight *bl = dev_get_drvdata(&dev->dev); + struct omap_backlight *bl = bl_get_data(dev); return bl->current_intensity; } diff --git a/drivers/video/backlight/ot200_bl.c b/drivers/video/backlight/ot200_bl.c index 469cf0f109d..fdbb6ee5027 100644 --- a/drivers/video/backlight/ot200_bl.c +++ b/drivers/video/backlight/ot200_bl.c @@ -14,6 +14,7 @@ #include <linux/fb.h> #include <linux/backlight.h> #include <linux/gpio.h> +#include <linux/platform_device.h> #include <linux/cs5535.h> static struct cs5535_mfgpt_timer *pwm_timer; diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index 069983ca49f..fa00304a63d 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -37,14 +37,13 @@ struct pwm_bl_data { static int pwm_backlight_update_status(struct backlight_device *bl) { - struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev); + struct pwm_bl_data *pb = bl_get_data(bl); int brightness = bl->props.brightness; int max = bl->props.max_brightness; - if (bl->props.power != FB_BLANK_UNBLANK) - brightness = 0; - - if (bl->props.fb_blank != FB_BLANK_UNBLANK) + if (bl->props.power != FB_BLANK_UNBLANK || + bl->props.fb_blank != FB_BLANK_UNBLANK || + bl->props.state & BL_CORE_FBBLANK) brightness = 0; if (pb->notify) @@ -83,7 +82,7 @@ static int pwm_backlight_get_brightness(struct backlight_device *bl) static int pwm_backlight_check_fb(struct backlight_device *bl, struct fb_info *info) { - struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev); + struct pwm_bl_data *pb = bl_get_data(bl); return !pb->check_fb || pb->check_fb(pb->dev, info); } @@ -135,12 +134,6 @@ static int pwm_backlight_parse_dt(struct device *dev, if (ret < 0) return ret; - if (value >= data->max_brightness) { - dev_warn(dev, "invalid default brightness level: %u, using %u\n", - value, data->max_brightness - 1); - value = data->max_brightness - 1; - } - data->dft_brightness = value; data->max_brightness--; } @@ -249,6 +242,13 @@ static int pwm_backlight_probe(struct platform_device *pdev) goto err_alloc; } + if (data->dft_brightness > data->max_brightness) { + dev_warn(&pdev->dev, + "invalid default brightness level: %u, using %u\n", + data->dft_brightness, data->max_brightness); + data->dft_brightness = data->max_brightness; + } + bl->props.brightness = data->dft_brightness; backlight_update_status(bl); @@ -264,7 +264,7 @@ err_alloc: static int pwm_backlight_remove(struct platform_device *pdev) { struct backlight_device *bl = platform_get_drvdata(pdev); - struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev); + struct pwm_bl_data *pb = bl_get_data(bl); backlight_device_unregister(bl); pwm_config(pb->pwm, 0, pb->period); @@ -278,7 +278,7 @@ static int pwm_backlight_remove(struct platform_device *pdev) static int pwm_backlight_suspend(struct device *dev) { struct backlight_device *bl = dev_get_drvdata(dev); - struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev); + struct pwm_bl_data *pb = bl_get_data(bl); if (pb->notify) pb->notify(pb->dev, 0); diff --git a/drivers/video/backlight/s6e63m0.c b/drivers/video/backlight/s6e63m0.c index 3e1c1135f6d..9c2677f0ef7 100644 --- a/drivers/video/backlight/s6e63m0.c +++ b/drivers/video/backlight/s6e63m0.c @@ -9,28 +9,19 @@ * 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/wait.h> -#include <linux/fb.h> +#include <linux/backlight.h> #include <linux/delay.h> +#include <linux/fb.h> #include <linux/gpio.h> -#include <linux/spi/spi.h> -#include <linux/irq.h> #include <linux/interrupt.h> +#include <linux/irq.h> #include <linux/kernel.h> #include <linux/lcd.h> -#include <linux/backlight.h> #include <linux/module.h> +#include <linux/spi/spi.h> +#include <linux/wait.h> #include "s6e63m0_gamma.h" @@ -43,8 +34,6 @@ #define MIN_BRIGHTNESS 0 #define MAX_BRIGHTNESS 10 -#define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL) - struct s6e63m0 { struct device *dev; struct spi_device *spi; @@ -57,7 +46,7 @@ struct s6e63m0 { struct lcd_platform_data *lcd_pd; }; -static const unsigned short SEQ_PANEL_CONDITION_SET[] = { +static const unsigned short seq_panel_condition_set[] = { 0xF8, 0x01, DATA_ONLY, 0x27, DATA_ONLY, 0x27, @@ -76,7 +65,7 @@ static const unsigned short SEQ_PANEL_CONDITION_SET[] = { ENDDEF, 0x0000 }; -static const unsigned short SEQ_DISPLAY_CONDITION_SET[] = { +static const unsigned short seq_display_condition_set[] = { 0xf2, 0x02, DATA_ONLY, 0x03, DATA_ONLY, 0x1c, @@ -90,7 +79,7 @@ static const unsigned short SEQ_DISPLAY_CONDITION_SET[] = { ENDDEF, 0x0000 }; -static const unsigned short SEQ_GAMMA_SETTING[] = { +static const unsigned short seq_gamma_setting[] = { 0xfa, 0x00, DATA_ONLY, 0x18, DATA_ONLY, 0x08, @@ -119,7 +108,7 @@ static const unsigned short SEQ_GAMMA_SETTING[] = { ENDDEF, 0x0000 }; -static const unsigned short SEQ_ETC_CONDITION_SET[] = { +static const unsigned short seq_etc_condition_set[] = { 0xf6, 0x00, DATA_ONLY, 0x8c, DATA_ONLY, 0x07, @@ -318,47 +307,47 @@ static const unsigned short SEQ_ETC_CONDITION_SET[] = { ENDDEF, 0x0000 }; -static const unsigned short SEQ_ACL_ON[] = { +static const unsigned short seq_acl_on[] = { /* ACL on */ 0xc0, 0x01, ENDDEF, 0x0000 }; -static const unsigned short SEQ_ACL_OFF[] = { +static const unsigned short seq_acl_off[] = { /* ACL off */ 0xc0, 0x00, ENDDEF, 0x0000 }; -static const unsigned short SEQ_ELVSS_ON[] = { +static const unsigned short seq_elvss_on[] = { /* ELVSS on */ 0xb1, 0x0b, ENDDEF, 0x0000 }; -static const unsigned short SEQ_ELVSS_OFF[] = { +static const unsigned short seq_elvss_off[] = { /* ELVSS off */ 0xb1, 0x0a, ENDDEF, 0x0000 }; -static const unsigned short SEQ_STAND_BY_OFF[] = { +static const unsigned short seq_stand_by_off[] = { 0x11, COMMAND_ONLY, ENDDEF, 0x0000 }; -static const unsigned short SEQ_STAND_BY_ON[] = { +static const unsigned short seq_stand_by_on[] = { 0x10, COMMAND_ONLY, ENDDEF, 0x0000 }; -static const unsigned short SEQ_DISPLAY_ON[] = { +static const unsigned short seq_display_on[] = { 0x29, COMMAND_ONLY, ENDDEF, 0x0000 @@ -406,8 +395,9 @@ static int s6e63m0_panel_send_sequence(struct s6e63m0 *lcd, ret = s6e63m0_spi_write(lcd, wbuf[i], wbuf[i+1]); if (ret) break; - } else - udelay(wbuf[i+1]*1000); + } else { + msleep(wbuf[i+1]); + } i += 2; } @@ -457,12 +447,12 @@ static int s6e63m0_ldi_init(struct s6e63m0 *lcd) { int ret, i; const unsigned short *init_seq[] = { - SEQ_PANEL_CONDITION_SET, - SEQ_DISPLAY_CONDITION_SET, - SEQ_GAMMA_SETTING, - SEQ_ETC_CONDITION_SET, - SEQ_ACL_ON, - SEQ_ELVSS_ON, + seq_panel_condition_set, + seq_display_condition_set, + seq_gamma_setting, + seq_etc_condition_set, + seq_acl_on, + seq_elvss_on, }; for (i = 0; i < ARRAY_SIZE(init_seq); i++) { @@ -478,8 +468,8 @@ static int s6e63m0_ldi_enable(struct s6e63m0 *lcd) { int ret = 0, i; const unsigned short *enable_seq[] = { - SEQ_STAND_BY_OFF, - SEQ_DISPLAY_ON, + seq_stand_by_off, + seq_display_on, }; for (i = 0; i < ARRAY_SIZE(enable_seq); i++) { @@ -495,43 +485,39 @@ static int s6e63m0_ldi_disable(struct s6e63m0 *lcd) { int ret; - ret = s6e63m0_panel_send_sequence(lcd, SEQ_STAND_BY_ON); + ret = s6e63m0_panel_send_sequence(lcd, seq_stand_by_on); return ret; } +static int s6e63m0_power_is_on(int power) +{ + return power <= FB_BLANK_NORMAL; +} + static int s6e63m0_power_on(struct s6e63m0 *lcd) { int ret = 0; - struct lcd_platform_data *pd = NULL; - struct backlight_device *bd = NULL; + struct lcd_platform_data *pd; + struct backlight_device *bd; pd = lcd->lcd_pd; - if (!pd) { - dev_err(lcd->dev, "platform data is NULL.\n"); - return -EFAULT; - } - bd = lcd->bd; - if (!bd) { - dev_err(lcd->dev, "backlight device is NULL.\n"); - return -EFAULT; - } if (!pd->power_on) { dev_err(lcd->dev, "power_on is NULL.\n"); - return -EFAULT; + return -EINVAL; } else { pd->power_on(lcd->ld, 1); - mdelay(pd->power_on_delay); + msleep(pd->power_on_delay); } if (!pd->reset) { dev_err(lcd->dev, "reset is NULL.\n"); - return -EFAULT; + return -EINVAL; } else { pd->reset(lcd->ld); - mdelay(pd->reset_delay); + msleep(pd->reset_delay); } ret = s6e63m0_ldi_init(lcd); @@ -558,14 +544,10 @@ static int s6e63m0_power_on(struct s6e63m0 *lcd) static int s6e63m0_power_off(struct s6e63m0 *lcd) { - int ret = 0; - struct lcd_platform_data *pd = NULL; + int ret; + struct lcd_platform_data *pd; pd = lcd->lcd_pd; - if (!pd) { - dev_err(lcd->dev, "platform data is NULL.\n"); - return -EFAULT; - } ret = s6e63m0_ldi_disable(lcd); if (ret) { @@ -573,13 +555,9 @@ static int s6e63m0_power_off(struct s6e63m0 *lcd) return -EIO; } - mdelay(pd->power_off_delay); + msleep(pd->power_off_delay); - if (!pd->power_on) { - dev_err(lcd->dev, "power_on is NULL.\n"); - return -EFAULT; - } else - pd->power_on(lcd->ld, 0); + pd->power_on(lcd->ld, 0); return 0; } @@ -588,9 +566,9 @@ static int s6e63m0_power(struct s6e63m0 *lcd, int power) { int ret = 0; - if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power)) + if (s6e63m0_power_is_on(power) && !s6e63m0_power_is_on(lcd->power)) ret = s6e63m0_power_on(lcd); - else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power)) + else if (!s6e63m0_power_is_on(power) && s6e63m0_power_is_on(lcd->power)) ret = s6e63m0_power_off(lcd); if (!ret) @@ -760,7 +738,7 @@ static int s6e63m0_probe(struct spi_device *spi) lcd->lcd_pd = spi->dev.platform_data; if (!lcd->lcd_pd) { dev_err(&spi->dev, "platform data is NULL.\n"); - return -EFAULT; + return -EINVAL; } ld = lcd_device_register("s6e63m0", &spi->dev, lcd, &s6e63m0_lcd_ops); @@ -788,7 +766,7 @@ static int s6e63m0_probe(struct spi_device *spi) * know that. */ lcd->gamma_table_count = - sizeof(gamma_table) / (MAX_GAMMA_LEVEL * sizeof(int)); + sizeof(gamma_table) / (MAX_GAMMA_LEVEL * sizeof(int *)); ret = device_create_file(&(spi->dev), &dev_attr_gamma_mode); if (ret < 0) @@ -811,10 +789,11 @@ static int s6e63m0_probe(struct spi_device *spi) lcd->power = FB_BLANK_POWERDOWN; s6e63m0_power(lcd, FB_BLANK_UNBLANK); - } else + } else { lcd->power = FB_BLANK_UNBLANK; + } - dev_set_drvdata(&spi->dev, lcd); + spi_set_drvdata(spi, lcd); dev_info(&spi->dev, "s6e63m0 panel driver has been probed.\n"); @@ -827,7 +806,7 @@ out_lcd_unregister: static int s6e63m0_remove(struct spi_device *spi) { - struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev); + struct s6e63m0 *lcd = spi_get_drvdata(spi); s6e63m0_power(lcd, FB_BLANK_POWERDOWN); device_remove_file(&spi->dev, &dev_attr_gamma_table); @@ -839,44 +818,26 @@ static int s6e63m0_remove(struct spi_device *spi) } #if defined(CONFIG_PM) -static unsigned int before_power; - static int s6e63m0_suspend(struct spi_device *spi, pm_message_t mesg) { - int ret = 0; - struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev); + struct s6e63m0 *lcd = spi_get_drvdata(spi); dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power); - before_power = lcd->power; - /* * when lcd panel is suspend, lcd panel becomes off * regardless of status. */ - ret = s6e63m0_power(lcd, FB_BLANK_POWERDOWN); - - return ret; + return s6e63m0_power(lcd, FB_BLANK_POWERDOWN); } static int s6e63m0_resume(struct spi_device *spi) { - int ret = 0; - struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev); - - /* - * after suspended, if lcd panel status is FB_BLANK_UNBLANK - * (at that time, before_power is FB_BLANK_UNBLANK) then - * it changes that status to FB_BLANK_POWERDOWN to get lcd on. - */ - if (before_power == FB_BLANK_UNBLANK) - lcd->power = FB_BLANK_POWERDOWN; + struct s6e63m0 *lcd = spi_get_drvdata(spi); - dev_dbg(&spi->dev, "before_power = %d\n", before_power); + lcd->power = FB_BLANK_POWERDOWN; - ret = s6e63m0_power(lcd, before_power); - - return ret; + return s6e63m0_power(lcd, FB_BLANK_UNBLANK); } #else #define s6e63m0_suspend NULL @@ -886,7 +847,7 @@ static int s6e63m0_resume(struct spi_device *spi) /* Power down all displays on reboot, poweroff or halt. */ static void s6e63m0_shutdown(struct spi_device *spi) { - struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev); + struct s6e63m0 *lcd = spi_get_drvdata(spi); s6e63m0_power(lcd, FB_BLANK_POWERDOWN); } diff --git a/drivers/video/backlight/tdo24m.c b/drivers/video/backlight/tdo24m.c index ad2325f3d65..00162085eec 100644 --- a/drivers/video/backlight/tdo24m.c +++ b/drivers/video/backlight/tdo24m.c @@ -390,7 +390,7 @@ static int tdo24m_probe(struct spi_device *spi) if (IS_ERR(lcd->lcd_dev)) return PTR_ERR(lcd->lcd_dev); - dev_set_drvdata(&spi->dev, lcd); + spi_set_drvdata(spi, lcd); err = tdo24m_power(lcd, FB_BLANK_UNBLANK); if (err) goto out_unregister; @@ -404,7 +404,7 @@ out_unregister: static int tdo24m_remove(struct spi_device *spi) { - struct tdo24m *lcd = dev_get_drvdata(&spi->dev); + struct tdo24m *lcd = spi_get_drvdata(spi); tdo24m_power(lcd, FB_BLANK_POWERDOWN); lcd_device_unregister(lcd->lcd_dev); @@ -415,14 +415,14 @@ static int tdo24m_remove(struct spi_device *spi) #ifdef CONFIG_PM static int tdo24m_suspend(struct spi_device *spi, pm_message_t state) { - struct tdo24m *lcd = dev_get_drvdata(&spi->dev); + struct tdo24m *lcd = spi_get_drvdata(spi); return tdo24m_power(lcd, FB_BLANK_POWERDOWN); } static int tdo24m_resume(struct spi_device *spi) { - struct tdo24m *lcd = dev_get_drvdata(&spi->dev); + struct tdo24m *lcd = spi_get_drvdata(spi); return tdo24m_power(lcd, FB_BLANK_UNBLANK); } @@ -434,7 +434,7 @@ static int tdo24m_resume(struct spi_device *spi) /* Power down all displays on reboot, poweroff or halt */ static void tdo24m_shutdown(struct spi_device *spi) { - struct tdo24m *lcd = dev_get_drvdata(&spi->dev); + struct tdo24m *lcd = spi_get_drvdata(spi); tdo24m_power(lcd, FB_BLANK_POWERDOWN); } diff --git a/drivers/video/backlight/tosa_bl.c b/drivers/video/backlight/tosa_bl.c index 588682cc161..2326fa810c5 100644 --- a/drivers/video/backlight/tosa_bl.c +++ b/drivers/video/backlight/tosa_bl.c @@ -54,7 +54,7 @@ static void tosa_bl_set_backlight(struct tosa_bl_data *data, int brightness) static int tosa_bl_update_status(struct backlight_device *dev) { struct backlight_properties *props = &dev->props; - struct tosa_bl_data *data = dev_get_drvdata(&dev->dev); + struct tosa_bl_data *data = bl_get_data(dev); int power = max(props->power, props->fb_blank); int brightness = props->brightness; diff --git a/drivers/video/backlight/tosa_lcd.c b/drivers/video/backlight/tosa_lcd.c index 96bae941585..666fe2593ea 100644 --- a/drivers/video/backlight/tosa_lcd.c +++ b/drivers/video/backlight/tosa_lcd.c @@ -193,7 +193,7 @@ static int tosa_lcd_probe(struct spi_device *spi) return ret; data->spi = spi; - dev_set_drvdata(&spi->dev, data); + spi_set_drvdata(spi, data); ret = devm_gpio_request_one(&spi->dev, TOSA_GPIO_TG_ON, GPIOF_OUT_INIT_LOW, "tg #pwr"); @@ -220,13 +220,13 @@ static int tosa_lcd_probe(struct spi_device *spi) err_register: tosa_lcd_tg_off(data); err_gpio_tg: - dev_set_drvdata(&spi->dev, NULL); + spi_set_drvdata(spi, NULL); return ret; } static int tosa_lcd_remove(struct spi_device *spi) { - struct tosa_lcd_data *data = dev_get_drvdata(&spi->dev); + struct tosa_lcd_data *data = spi_get_drvdata(spi); lcd_device_unregister(data->lcd); @@ -235,7 +235,7 @@ static int tosa_lcd_remove(struct spi_device *spi) tosa_lcd_tg_off(data); - dev_set_drvdata(&spi->dev, NULL); + spi_set_drvdata(spi, NULL); return 0; } @@ -243,7 +243,7 @@ static int tosa_lcd_remove(struct spi_device *spi) #ifdef CONFIG_PM static int tosa_lcd_suspend(struct spi_device *spi, pm_message_t state) { - struct tosa_lcd_data *data = dev_get_drvdata(&spi->dev); + struct tosa_lcd_data *data = spi_get_drvdata(spi); tosa_lcd_tg_off(data); @@ -252,7 +252,7 @@ static int tosa_lcd_suspend(struct spi_device *spi, pm_message_t state) static int tosa_lcd_resume(struct spi_device *spi) { - struct tosa_lcd_data *data = dev_get_drvdata(&spi->dev); + struct tosa_lcd_data *data = spi_get_drvdata(spi); tosa_lcd_tg_init(data); if (POWER_IS_ON(data->lcd_power)) diff --git a/drivers/video/backlight/vgg2432a4.c b/drivers/video/backlight/vgg2432a4.c index 45e81b4cf8b..84d582f591d 100644 --- a/drivers/video/backlight/vgg2432a4.c +++ b/drivers/video/backlight/vgg2432a4.c @@ -208,12 +208,11 @@ static int vgg2432a4_lcd_init(struct ili9320 *lcd, #ifdef CONFIG_PM static int vgg2432a4_suspend(struct spi_device *spi, pm_message_t state) { - return ili9320_suspend(dev_get_drvdata(&spi->dev), state); + return ili9320_suspend(spi_get_drvdata(spi), state); } - static int vgg2432a4_resume(struct spi_device *spi) { - return ili9320_resume(dev_get_drvdata(&spi->dev)); + return ili9320_resume(spi_get_drvdata(spi)); } #else #define vgg2432a4_suspend NULL @@ -242,12 +241,12 @@ static int vgg2432a4_probe(struct spi_device *spi) static int vgg2432a4_remove(struct spi_device *spi) { - return ili9320_remove(dev_get_drvdata(&spi->dev)); + return ili9320_remove(spi_get_drvdata(spi)); } static void vgg2432a4_shutdown(struct spi_device *spi) { - ili9320_shutdown(dev_get_drvdata(&spi->dev)); + ili9320_shutdown(spi_get_drvdata(spi)); } static struct spi_driver vgg2432a4_driver = { diff --git a/drivers/video/clps711xfb.c b/drivers/video/clps711xfb.c index 5a7af0deced..f00980607b8 100644 --- a/drivers/video/clps711xfb.c +++ b/drivers/video/clps711xfb.c @@ -26,6 +26,7 @@ #include <linux/fb.h> #include <linux/init.h> #include <linux/delay.h> +#include <linux/platform_device.h> #include <mach/hardware.h> #include <asm/mach-types.h> diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig index e2c96d01d8f..bc922c47d04 100644 --- a/drivers/video/console/Kconfig +++ b/drivers/video/console/Kconfig @@ -46,7 +46,7 @@ config VGACON_SOFT_SCROLLBACK_SIZE config MDA_CONSOLE depends on !M68K && !PARISC && ISA - tristate "MDA text console (dual-headed) (EXPERIMENTAL)" + tristate "MDA text console (dual-headed)" ---help--- Say Y here if you have an old MDA or monochrome Hercules graphics adapter in your system acting as a second head ( = video card). You diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index fdefa8fd72c..3cd67592782 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -529,6 +529,33 @@ static int search_for_mapped_con(void) return retval; } +static int do_fbcon_takeover(int show_logo) +{ + int err, i; + + if (!num_registered_fb) + return -ENODEV; + + if (!show_logo) + logo_shown = FBCON_LOGO_DONTSHOW; + + for (i = first_fb_vc; i <= last_fb_vc; i++) + con2fb_map[i] = info_idx; + + err = do_take_over_console(&fb_con, first_fb_vc, last_fb_vc, + fbcon_is_default); + + if (err) { + for (i = first_fb_vc; i <= last_fb_vc; i++) + con2fb_map[i] = -1; + info_idx = -1; + } else { + fbcon_has_console_bind = 1; + } + + return err; +} + static int fbcon_takeover(int show_logo) { int err, i; @@ -815,6 +842,8 @@ static void con2fb_init_display(struct vc_data *vc, struct fb_info *info, * * Maps a virtual console @unit to a frame buffer device * @newidx. + * + * This should be called with the console lock held. */ static int set_con2fb_map(int unit, int newidx, int user) { @@ -832,7 +861,7 @@ static int set_con2fb_map(int unit, int newidx, int user) if (!search_for_mapped_con() || !con_is_bound(&fb_con)) { info_idx = newidx; - return fbcon_takeover(0); + return do_fbcon_takeover(0); } if (oldidx != -1) @@ -840,7 +869,6 @@ static int set_con2fb_map(int unit, int newidx, int user) found = search_fb_in_map(newidx); - console_lock(); con2fb_map[unit] = newidx; if (!err && !found) err = con2fb_acquire_newinfo(vc, info, unit, oldidx); @@ -867,7 +895,6 @@ static int set_con2fb_map(int unit, int newidx, int user) if (!search_fb_in_map(info_idx)) info_idx = newidx; - console_unlock(); return err; } @@ -990,7 +1017,7 @@ static const char *fbcon_startup(void) } /* Setup default font */ - if (!p->fontdata) { + if (!p->fontdata && !vc->vc_font.data) { if (!fontname[0] || !(font = find_font(fontname))) font = get_default_font(info->var.xres, info->var.yres, @@ -1000,6 +1027,8 @@ static const char *fbcon_startup(void) vc->vc_font.height = font->height; vc->vc_font.data = (void *)(p->fontdata = font->data); vc->vc_font.charcount = 256; /* FIXME Need to support more fonts */ + } else { + p->fontdata = vc->vc_font.data; } cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres); @@ -1159,9 +1188,9 @@ static void fbcon_init(struct vc_data *vc, int init) ops->p = &fb_display[fg_console]; } -static void fbcon_free_font(struct display *p) +static void fbcon_free_font(struct display *p, bool freefont) { - if (p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0)) + if (freefont && p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0)) kfree(p->fontdata - FONT_EXTRA_WORDS * sizeof(int)); p->fontdata = NULL; p->userfont = 0; @@ -1173,8 +1202,8 @@ static void fbcon_deinit(struct vc_data *vc) struct fb_info *info; struct fbcon_ops *ops; int idx; + bool free_font = true; - fbcon_free_font(p); idx = con2fb_map[vc->vc_num]; if (idx == -1) @@ -1185,6 +1214,8 @@ static void fbcon_deinit(struct vc_data *vc) if (!info) goto finished; + if (info->flags & FBINFO_MISC_FIRMWARE) + free_font = false; ops = info->fbcon_par; if (!ops) @@ -1196,6 +1227,8 @@ static void fbcon_deinit(struct vc_data *vc) ops->flags &= ~FBCON_FLAGS_INIT; finished: + fbcon_free_font(p, free_font); + if (!con_is_bound(&fb_con)) fbcon_exit(); @@ -1242,8 +1275,16 @@ static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height, if (!height || !width) return; - if (sy < vc->vc_top && vc->vc_top == logo_lines) + if (sy < vc->vc_top && vc->vc_top == logo_lines) { vc->vc_top = 0; + /* + * If the font dimensions are not an integral of the display + * dimensions then the ops->clear below won't end up clearing + * the margins. Call clear_margins here in case the logo + * bitmap stretched into the margin area. + */ + fbcon_clear_margins(vc, 0); + } /* Split blits that cross physical y_wrap boundary */ @@ -2977,7 +3018,7 @@ static int fbcon_unbind(void) { int ret; - ret = unbind_con_driver(&fb_con, first_fb_vc, last_fb_vc, + ret = do_unbind_con_driver(&fb_con, first_fb_vc, last_fb_vc, fbcon_is_default); if (!ret) @@ -2992,6 +3033,7 @@ static inline int fbcon_unbind(void) } #endif /* CONFIG_VT_HW_CONSOLE_BINDING */ +/* called with console_lock held */ static int fbcon_fb_unbind(int idx) { int i, new_idx = -1, ret = 0; @@ -3018,6 +3060,7 @@ static int fbcon_fb_unbind(int idx) return ret; } +/* called with console_lock held */ static int fbcon_fb_unregistered(struct fb_info *info) { int i, idx; @@ -3050,11 +3093,12 @@ static int fbcon_fb_unregistered(struct fb_info *info) primary_device = -1; if (!num_registered_fb) - unregister_con_driver(&fb_con); + do_unregister_con_driver(&fb_con); return 0; } +/* called with console_lock held */ static void fbcon_remap_all(int idx) { int i; @@ -3099,6 +3143,7 @@ static inline void fbcon_select_primary(struct fb_info *info) } #endif /* CONFIG_FRAMEBUFFER_DETECT_PRIMARY */ +/* called with console_lock held */ static int fbcon_fb_registered(struct fb_info *info) { int ret = 0, i, idx; @@ -3115,7 +3160,7 @@ static int fbcon_fb_registered(struct fb_info *info) } if (info_idx != -1) - ret = fbcon_takeover(1); + ret = do_fbcon_takeover(1); } else { for (i = first_fb_vc; i <= last_fb_vc; i++) { if (con2fb_map_boot[i] == idx) @@ -3251,6 +3296,7 @@ static int fbcon_event_notify(struct notifier_block *self, ret = fbcon_fb_unregistered(info); break; case FB_EVENT_SET_CONSOLE_MAP: + /* called with console lock held */ con2fb = event->data; ret = set_con2fb_map(con2fb->console - 1, con2fb->framebuffer, 1); diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index d449a74d4a3..5855d17d19a 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -1064,7 +1064,7 @@ static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512) unsigned short video_port_status = vga_video_port_reg + 6; int font_select = 0x00, beg, i; char *charmap; - + bool clear_attribs = false; if (vga_video_type != VIDEO_TYPE_EGAM) { charmap = (char *) VGA_MAP_MEM(colourmap, 0); beg = 0x0e; @@ -1169,12 +1169,6 @@ static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512) /* if 512 char mode is already enabled don't re-enable it. */ if ((set) && (ch512 != vga_512_chars)) { - /* attribute controller */ - for (i = 0; i < MAX_NR_CONSOLES; i++) { - struct vc_data *c = vc_cons[i].d; - if (c && c->vc_sw == &vga_con) - c->vc_hi_font_mask = ch512 ? 0x0800 : 0; - } vga_512_chars = ch512; /* 256-char: enable intensity bit 512-char: disable intensity bit */ @@ -1185,8 +1179,22 @@ static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512) it means, but it works, and it appears necessary */ inb_p(video_port_status); vga_wattr(state->vgabase, VGA_AR_ENABLE_DISPLAY, 0); + clear_attribs = true; } raw_spin_unlock_irq(&vga_lock); + + if (clear_attribs) { + for (i = 0; i < MAX_NR_CONSOLES; i++) { + struct vc_data *c = vc_cons[i].d; + if (c && c->vc_sw == &vga_con) { + /* force hi font mask to 0, so we always clear + the bit on either transition */ + c->vc_hi_font_mask = 0x00; + clear_buffer_attributes(c); + c->vc_hi_font_mask = ch512 ? 0x0800 : 0; + } + } + } return 0; } diff --git a/drivers/video/display_timing.c b/drivers/video/display_timing.c new file mode 100644 index 00000000000..5e1822cef57 --- /dev/null +++ b/drivers/video/display_timing.c @@ -0,0 +1,24 @@ +/* + * generic display timing functions + * + * Copyright (c) 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>, Pengutronix + * + * This file is released under the GPLv2 + */ + +#include <linux/export.h> +#include <linux/slab.h> +#include <video/display_timing.h> + +void display_timings_release(struct display_timings *disp) +{ + if (disp->timings) { + unsigned int i; + + for (i = 0; i < disp->num_timings; i++) + kfree(disp->timings[i]); + kfree(disp->timings); + } + kfree(disp); +} +EXPORT_SYMBOL_GPL(display_timings_release); diff --git a/drivers/video/exynos/exynos_dp_core.c b/drivers/video/exynos/exynos_dp_core.c index 4ef18e2e90c..de9d4da0e3d 100644 --- a/drivers/video/exynos/exynos_dp_core.c +++ b/drivers/video/exynos/exynos_dp_core.c @@ -965,10 +965,11 @@ static struct exynos_dp_platdata *exynos_dp_dt_parse_pdata(struct device *dev) static int exynos_dp_dt_parse_phydata(struct exynos_dp_device *dp) { - struct device_node *dp_phy_node; + struct device_node *dp_phy_node = of_node_get(dp->dev->of_node); u32 phy_base; + int ret = 0; - dp_phy_node = of_find_node_by_name(dp->dev->of_node, "dptx-phy"); + dp_phy_node = of_find_node_by_name(dp_phy_node, "dptx-phy"); if (!dp_phy_node) { dev_err(dp->dev, "could not find dptx-phy node\n"); return -ENODEV; @@ -976,22 +977,28 @@ static int exynos_dp_dt_parse_phydata(struct exynos_dp_device *dp) if (of_property_read_u32(dp_phy_node, "reg", &phy_base)) { dev_err(dp->dev, "faild to get reg for dptx-phy\n"); - return -EINVAL; + ret = -EINVAL; + goto err; } if (of_property_read_u32(dp_phy_node, "samsung,enable-mask", &dp->enable_mask)) { dev_err(dp->dev, "faild to get enable-mask for dptx-phy\n"); - return -EINVAL; + ret = -EINVAL; + goto err; } dp->phy_addr = ioremap(phy_base, SZ_4); if (!dp->phy_addr) { dev_err(dp->dev, "failed to ioremap dp-phy\n"); - return -ENOMEM; + ret = -ENOMEM; + goto err; } - return 0; +err: + of_node_put(dp_phy_node); + + return ret; } static void exynos_dp_phy_init(struct exynos_dp_device *dp) @@ -1076,11 +1083,9 @@ static int exynos_dp_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - dp->reg_base = devm_request_and_ioremap(&pdev->dev, res); - if (!dp->reg_base) { - dev_err(&pdev->dev, "failed to ioremap\n"); - return -ENOMEM; - } + dp->reg_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dp->reg_base)) + return PTR_ERR(dp->reg_base); dp->irq = platform_get_irq(pdev, 0); if (dp->irq == -ENXIO) { @@ -1119,10 +1124,7 @@ static int 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); - disable_irq(dp->irq); - - if (work_pending(&dp->hotplug_work)) - flush_work(&dp->hotplug_work); + flush_work(&dp->hotplug_work); if (pdev->dev.of_node) { if (dp->phy_addr) @@ -1144,8 +1146,9 @@ static int exynos_dp_suspend(struct device *dev) struct exynos_dp_platdata *pdata = dev->platform_data; struct exynos_dp_device *dp = dev_get_drvdata(dev); - if (work_pending(&dp->hotplug_work)) - flush_work(&dp->hotplug_work); + disable_irq(dp->irq); + + flush_work(&dp->hotplug_work); if (dev->of_node) { if (dp->phy_addr) diff --git a/drivers/video/exynos/exynos_mipi_dsi.c b/drivers/video/exynos/exynos_mipi_dsi.c index 4a17cdccef3..fac7df6d1ab 100644 --- a/drivers/video/exynos/exynos_mipi_dsi.c +++ b/drivers/video/exynos/exynos_mipi_dsi.c @@ -338,7 +338,8 @@ static int exynos_mipi_dsi_probe(struct platform_device *pdev) struct mipi_dsim_ddi *dsim_ddi; int ret = -EINVAL; - dsim = kzalloc(sizeof(struct mipi_dsim_device), GFP_KERNEL); + dsim = devm_kzalloc(&pdev->dev, sizeof(struct mipi_dsim_device), + GFP_KERNEL); if (!dsim) { dev_err(&pdev->dev, "failed to allocate dsim object.\n"); return -ENOMEM; @@ -352,13 +353,13 @@ static int exynos_mipi_dsi_probe(struct platform_device *pdev) 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; + return -EINVAL; } /* 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; + return -EINVAL; } dsim->dsim_config = dsim_config; @@ -366,41 +367,28 @@ static int exynos_mipi_dsi_probe(struct platform_device *pdev) mutex_init(&dsim->lock); - ret = regulator_bulk_get(&pdev->dev, ARRAY_SIZE(supplies), supplies); + ret = devm_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; + return ret; } - dsim->clock = clk_get(&pdev->dev, "dsim0"); + dsim->clock = devm_clk_get(&pdev->dev, "dsim0"); if (IS_ERR(dsim->clock)) { dev_err(&pdev->dev, "failed to get dsim clock source\n"); - ret = -ENODEV; - goto err_clock_get; + return -ENODEV; } 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"); - ret = -ENODEV; - 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)); + dsim->reg_base = devm_request_and_ioremap(&pdev->dev, res); if (!dsim->reg_base) { dev_err(&pdev->dev, "failed to remap io region\n"); ret = -ENOMEM; - goto err_ioremap; + goto error; } mutex_init(&dsim->lock); @@ -410,26 +398,27 @@ static int exynos_mipi_dsi_probe(struct platform_device *pdev) if (!dsim_ddi) { dev_err(&pdev->dev, "mipi_dsim_ddi object not found.\n"); ret = -EINVAL; - goto err_bind; + goto error; } dsim->irq = platform_get_irq(pdev, 0); - if (dsim->irq < 0) { + if (IS_ERR_VALUE(dsim->irq)) { dev_err(&pdev->dev, "failed to request dsim irq resource\n"); ret = -EINVAL; - goto err_platform_get_irq; + goto error; } init_completion(&dsim_wr_comp); init_completion(&dsim_rd_comp); platform_set_drvdata(pdev, dsim); - ret = request_irq(dsim->irq, exynos_mipi_dsi_interrupt_handler, + ret = devm_request_irq(&pdev->dev, dsim->irq, + exynos_mipi_dsi_interrupt_handler, IRQF_SHARED, dev_name(&pdev->dev), dsim); if (ret != 0) { dev_err(&pdev->dev, "failed to request dsim irq\n"); ret = -EINVAL; - goto err_bind; + goto error; } /* enable interrupts */ @@ -471,22 +460,8 @@ done: 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: +error: clk_disable(dsim->clock); - clk_put(dsim->clock); -err_clock_get: - kfree(dsim); - -err_platform_get_irq: return ret; } @@ -496,13 +471,7 @@ static int exynos_mipi_dsi_remove(struct platform_device *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) { @@ -518,9 +487,6 @@ static int exynos_mipi_dsi_remove(struct platform_device *pdev) } } - regulator_bulk_free(ARRAY_SIZE(supplies), supplies); - kfree(dsim); - return 0; } diff --git a/drivers/video/exynos/exynos_mipi_dsi_common.c b/drivers/video/exynos/exynos_mipi_dsi_common.c index 3cd29a4fc10..c70cb8926df 100644 --- a/drivers/video/exynos/exynos_mipi_dsi_common.c +++ b/drivers/video/exynos/exynos_mipi_dsi_common.c @@ -25,6 +25,7 @@ #include <linux/io.h> #include <linux/memory.h> #include <linux/delay.h> +#include <linux/irqreturn.h> #include <linux/kthread.h> #include <video/mipi_display.h> diff --git a/drivers/video/exynos/exynos_mipi_dsi_lowlevel.c b/drivers/video/exynos/exynos_mipi_dsi_lowlevel.c index 0ef38ce72af..95cb99a1fe2 100644 --- a/drivers/video/exynos/exynos_mipi_dsi_lowlevel.c +++ b/drivers/video/exynos/exynos_mipi_dsi_lowlevel.c @@ -21,6 +21,7 @@ #include <linux/fs.h> #include <linux/mm.h> #include <linux/ctype.h> +#include <linux/platform_device.h> #include <linux/io.h> #include <video/exynos_mipi_dsim.h> diff --git a/drivers/video/exynos/s6e8ax0.c b/drivers/video/exynos/s6e8ax0.c index 05d080b63bc..ca2602413aa 100644 --- a/drivers/video/exynos/s6e8ax0.c +++ b/drivers/video/exynos/s6e8ax0.c @@ -776,7 +776,7 @@ static int s6e8ax0_probe(struct mipi_dsim_lcd_device *dsim_dev) int ret; u8 mtp_id[3] = {0, }; - lcd = kzalloc(sizeof(struct s6e8ax0), GFP_KERNEL); + lcd = devm_kzalloc(&dsim_dev->dev, sizeof(struct s6e8ax0), GFP_KERNEL); if (!lcd) { dev_err(&dsim_dev->dev, "failed to allocate s6e8ax0 structure.\n"); return -ENOMEM; @@ -788,18 +788,17 @@ static int s6e8ax0_probe(struct mipi_dsim_lcd_device *dsim_dev) mutex_init(&lcd->lock); - ret = regulator_bulk_get(lcd->dev, ARRAY_SIZE(supplies), supplies); + ret = devm_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; + return ret; } 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; + return PTR_ERR(lcd->ld); } lcd->bd = backlight_device_register("s6e8ax0-bl", lcd->dev, lcd, @@ -838,11 +837,6 @@ static int s6e8ax0_probe(struct mipi_dsim_lcd_device *dsim_dev) err_backlight_register: lcd_device_unregister(lcd->ld); - -err_lcd_register: - regulator_bulk_free(ARRAY_SIZE(supplies), supplies); - kfree(lcd); - return ret; } diff --git a/drivers/video/fb_defio.c b/drivers/video/fb_defio.c index 88cad6b8b47..900aa4ecd61 100644 --- a/drivers/video/fb_defio.c +++ b/drivers/video/fb_defio.c @@ -69,7 +69,7 @@ static int fb_deferred_io_fault(struct vm_area_struct *vma, int fb_deferred_io_fsync(struct file *file, loff_t start, loff_t end, int datasync) { struct fb_info *info = file->private_data; - struct inode *inode = file->f_path.dentry->d_inode; + struct inode *inode = file_inode(file); int err = filemap_write_and_wait_range(inode->i_mapping, start, end); if (err) return err; diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 3ff0105a496..7c254084b6a 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -727,7 +727,7 @@ static const struct file_operations fb_proc_fops = { */ static struct fb_info *file_fb_info(struct file *file) { - struct inode *inode = file->f_path.dentry->d_inode; + struct inode *inode = file_inode(file); int fbidx = iminor(inode); struct fb_info *info = registered_fb[fbidx]; @@ -1177,8 +1177,10 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, event.data = &con2fb; if (!lock_fb_info(info)) return -ENODEV; + console_lock(); event.info = info; ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event); + console_unlock(); unlock_fb_info(info); break; case FBIOBLANK: @@ -1650,7 +1652,9 @@ static int do_register_framebuffer(struct fb_info *fb_info) event.info = fb_info; if (!lock_fb_info(fb_info)) return -ENODEV; + console_lock(); fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event); + console_unlock(); unlock_fb_info(fb_info); return 0; } @@ -1666,8 +1670,10 @@ static int do_unregister_framebuffer(struct fb_info *fb_info) if (!lock_fb_info(fb_info)) return -ENODEV; + console_lock(); event.info = fb_info; ret = fb_notifier_call_chain(FB_EVENT_FB_UNBIND, &event); + console_unlock(); unlock_fb_info(fb_info); if (ret) @@ -1682,7 +1688,9 @@ static int do_unregister_framebuffer(struct fb_info *fb_info) num_registered_fb--; fb_cleanup_device(fb_info); event.info = fb_info; + console_lock(); fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event); + console_unlock(); /* this may free fb info */ put_fb_info(fb_info); @@ -1853,11 +1861,8 @@ int fb_new_modelist(struct fb_info *info) err = 1; if (!list_empty(&info->modelist)) { - if (!lock_fb_info(info)) - return -ENODEV; event.info = info; err = fb_notifier_call_chain(FB_EVENT_NEW_MODELIST, &event); - unlock_fb_info(info); } return err; diff --git a/drivers/video/fbmon.c b/drivers/video/fbmon.c index cef65574db6..94ad0f71383 100644 --- a/drivers/video/fbmon.c +++ b/drivers/video/fbmon.c @@ -31,6 +31,8 @@ #include <linux/pci.h> #include <linux/slab.h> #include <video/edid.h> +#include <video/of_videomode.h> +#include <video/videomode.h> #ifdef CONFIG_PPC_OF #include <asm/prom.h> #include <asm/pci-bridge.h> @@ -1373,6 +1375,98 @@ int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var, struct fb_inf kfree(timings); return err; } + +#if IS_ENABLED(CONFIG_VIDEOMODE) +int fb_videomode_from_videomode(const struct videomode *vm, + struct fb_videomode *fbmode) +{ + unsigned int htotal, vtotal; + + fbmode->xres = vm->hactive; + fbmode->left_margin = vm->hback_porch; + fbmode->right_margin = vm->hfront_porch; + fbmode->hsync_len = vm->hsync_len; + + fbmode->yres = vm->vactive; + fbmode->upper_margin = vm->vback_porch; + fbmode->lower_margin = vm->vfront_porch; + fbmode->vsync_len = vm->vsync_len; + + /* prevent division by zero in KHZ2PICOS macro */ + fbmode->pixclock = vm->pixelclock ? + KHZ2PICOS(vm->pixelclock / 1000) : 0; + + fbmode->sync = 0; + fbmode->vmode = 0; + if (vm->dmt_flags & VESA_DMT_HSYNC_HIGH) + fbmode->sync |= FB_SYNC_HOR_HIGH_ACT; + if (vm->dmt_flags & VESA_DMT_HSYNC_HIGH) + fbmode->sync |= FB_SYNC_VERT_HIGH_ACT; + if (vm->data_flags & DISPLAY_FLAGS_INTERLACED) + fbmode->vmode |= FB_VMODE_INTERLACED; + if (vm->data_flags & DISPLAY_FLAGS_DOUBLESCAN) + fbmode->vmode |= FB_VMODE_DOUBLE; + fbmode->flag = 0; + + htotal = vm->hactive + vm->hfront_porch + vm->hback_porch + + vm->hsync_len; + vtotal = vm->vactive + vm->vfront_porch + vm->vback_porch + + vm->vsync_len; + /* prevent division by zero */ + if (htotal && vtotal) { + fbmode->refresh = vm->pixelclock / (htotal * vtotal); + /* a mode must have htotal and vtotal != 0 or it is invalid */ + } else { + fbmode->refresh = 0; + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(fb_videomode_from_videomode); +#endif + +#if IS_ENABLED(CONFIG_OF_VIDEOMODE) +static inline void dump_fb_videomode(const struct fb_videomode *m) +{ + pr_debug("fb_videomode = %ux%u@%uHz (%ukHz) %u %u %u %u %u %u %u %u %u\n", + m->xres, m->yres, m->refresh, m->pixclock, m->left_margin, + m->right_margin, m->upper_margin, m->lower_margin, + m->hsync_len, m->vsync_len, m->sync, m->vmode, m->flag); +} + +/** + * of_get_fb_videomode - get a fb_videomode from devicetree + * @np: device_node with the timing specification + * @fb: will be set to the return value + * @index: index into the list of display timings in devicetree + * + * DESCRIPTION: + * This function is expensive and should only be used, if only one mode is to be + * read from DT. To get multiple modes start with of_get_display_timings ond + * work with that instead. + */ +int of_get_fb_videomode(struct device_node *np, struct fb_videomode *fb, + int index) +{ + struct videomode vm; + int ret; + + ret = of_get_videomode(np, &vm, index); + if (ret) + return ret; + + fb_videomode_from_videomode(&vm, fb); + + pr_debug("%s: got %dx%d display mode from %s\n", + of_node_full_name(np), vm.hactive, vm.vactive, np->name); + dump_fb_videomode(fb); + + return 0; +} +EXPORT_SYMBOL_GPL(of_get_fb_videomode); +#endif + #else int fb_parse_edid(unsigned char *edid, struct fb_var_screeninfo *var) { diff --git a/drivers/video/fbsysfs.c b/drivers/video/fbsysfs.c index a55e3669d13..ef476b02fbe 100644 --- a/drivers/video/fbsysfs.c +++ b/drivers/video/fbsysfs.c @@ -177,6 +177,8 @@ static ssize_t store_modes(struct device *device, if (i * sizeof(struct fb_videomode) != count) return -EINVAL; + if (!lock_fb_info(fb_info)) + return -ENODEV; console_lock(); list_splice(&fb_info->modelist, &old_list); fb_videomode_to_modelist((const struct fb_videomode *)buf, i, @@ -188,6 +190,7 @@ static ssize_t store_modes(struct device *device, fb_destroy_modelist(&old_list); console_unlock(); + unlock_fb_info(fb_info); return 0; } diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c index 19cfd7a9256..41fbd9453c5 100644 --- a/drivers/video/fsl-diu-fb.c +++ b/drivers/video/fsl-diu-fb.c @@ -944,7 +944,7 @@ static u32 fsl_diu_get_pixel_format(unsigned int bits_per_pixel) #define PF_COMP_0_MASK 0x0000000F #define PF_COMP_0_SHIFT 0 -#define MAKE_PF(alpha, red, blue, green, size, c0, c1, c2, c3) \ +#define MAKE_PF(alpha, red, green, blue, size, c0, c1, c2, c3) \ cpu_to_le32(PF_BYTE_F | (alpha << PF_ALPHA_C_SHIFT) | \ (blue << PF_BLUE_C_SHIFT) | (green << PF_GREEN_C_SHIFT) | \ (red << PF_RED_C_SHIFT) | (c3 << PF_COMP_3_SHIFT) | \ @@ -954,10 +954,10 @@ static u32 fsl_diu_get_pixel_format(unsigned int bits_per_pixel) switch (bits_per_pixel) { case 32: /* 0x88883316 */ - return MAKE_PF(3, 2, 0, 1, 3, 8, 8, 8, 8); + return MAKE_PF(3, 2, 1, 0, 3, 8, 8, 8, 8); case 24: /* 0x88082219 */ - return MAKE_PF(4, 0, 1, 2, 2, 0, 8, 8, 8); + return MAKE_PF(4, 0, 1, 2, 2, 8, 8, 8, 0); case 16: /* 0x65053118 */ return MAKE_PF(4, 2, 1, 0, 1, 5, 6, 5, 0); @@ -1232,6 +1232,16 @@ static int fsl_diu_ioctl(struct fb_info *info, unsigned int cmd, return 0; } +static inline void fsl_diu_enable_interrupts(struct fsl_diu_data *data) +{ + u32 int_mask = INT_UNDRUN; /* enable underrun detection */ + + if (IS_ENABLED(CONFIG_NOT_COHERENT_CACHE)) + int_mask |= INT_VSYNC; /* enable vertical sync */ + + clrbits32(&data->diu_reg->int_mask, int_mask); +} + /* turn on fb if count == 1 */ static int fsl_diu_open(struct fb_info *info, int user) @@ -1251,19 +1261,7 @@ static int fsl_diu_open(struct fb_info *info, int user) if (res < 0) mfbi->count--; else { - struct fsl_diu_data *data = mfbi->parent; - -#ifdef CONFIG_NOT_COHERENT_CACHE - /* - * Enable underrun detection and vertical sync - * interrupts. - */ - clrbits32(&data->diu_reg->int_mask, - INT_UNDRUN | INT_VSYNC); -#else - /* Enable underrun detection */ - clrbits32(&data->diu_reg->int_mask, INT_UNDRUN); -#endif + fsl_diu_enable_interrupts(mfbi->parent); fsl_diu_enable_panel(info); } } @@ -1283,9 +1281,18 @@ static int fsl_diu_release(struct fb_info *info, int user) mfbi->count--; if (mfbi->count == 0) { struct fsl_diu_data *data = mfbi->parent; + bool disable = true; + int i; - /* Disable interrupts */ - out_be32(&data->diu_reg->int_mask, 0xffffffff); + /* Disable interrupts only if all AOIs are closed */ + for (i = 0; i < NUM_AOIS; i++) { + struct mfb_info *mi = data->fsl_diu_info[i].par; + + if (mi->count) + disable = false; + } + if (disable) + out_be32(&data->diu_reg->int_mask, 0xffffffff); fsl_diu_disable_panel(info); } @@ -1614,14 +1621,6 @@ static int fsl_diu_probe(struct platform_device *pdev) out_be32(&data->diu_reg->desc[1], data->dummy_ad.paddr); out_be32(&data->diu_reg->desc[2], data->dummy_ad.paddr); - for (i = 0; i < NUM_AOIS; i++) { - ret = install_fb(&data->fsl_diu_info[i]); - if (ret) { - dev_err(&pdev->dev, "could not register fb %d\n", i); - goto error; - } - } - /* * Older versions of U-Boot leave interrupts enabled, so disable * all of them and clear the status register. @@ -1630,12 +1629,21 @@ static int fsl_diu_probe(struct platform_device *pdev) in_be32(&data->diu_reg->int_status); ret = request_irq(data->irq, fsl_diu_isr, 0, "fsl-diu-fb", - &data->diu_reg); + data->diu_reg); if (ret) { dev_err(&pdev->dev, "could not claim irq\n"); goto error; } + for (i = 0; i < NUM_AOIS; i++) { + ret = install_fb(&data->fsl_diu_info[i]); + if (ret) { + dev_err(&pdev->dev, "could not register fb %d\n", i); + free_irq(data->irq, data->diu_reg); + goto error; + } + } + sysfs_attr_init(&data->dev_attr.attr); data->dev_attr.attr.name = "monitor"; data->dev_attr.attr.mode = S_IRUGO|S_IWUSR; @@ -1667,7 +1675,7 @@ static int fsl_diu_remove(struct platform_device *pdev) data = dev_get_drvdata(&pdev->dev); disable_lcdc(&data->fsl_diu_info[0]); - free_irq(data->irq, &data->diu_reg); + free_irq(data->irq, data->diu_reg); for (i = 0; i < NUM_AOIS; i++) uninstall_fb(&data->fsl_diu_info[i]); diff --git a/drivers/video/geode/Kconfig b/drivers/video/geode/Kconfig index c5d8ba4b9fc..21e351a1459 100644 --- a/drivers/video/geode/Kconfig +++ b/drivers/video/geode/Kconfig @@ -2,14 +2,14 @@ # Geode family framebuffer configuration # config FB_GEODE - bool "AMD Geode family framebuffer support (EXPERIMENTAL)" - depends on FB && PCI && EXPERIMENTAL && X86 + bool "AMD Geode family framebuffer support" + depends on FB && PCI && X86 ---help--- Say 'Y' here to allow you to select framebuffer drivers for the AMD Geode family of processors. config FB_GEODE_LX - tristate "AMD Geode LX framebuffer support (EXPERIMENTAL)" + tristate "AMD Geode LX framebuffer support" depends on FB && FB_GEODE select FB_CFB_FILLRECT select FB_CFB_COPYAREA @@ -24,8 +24,8 @@ config FB_GEODE_LX If unsure, say N. config FB_GEODE_GX - tristate "AMD Geode GX framebuffer support (EXPERIMENTAL)" - depends on FB && FB_GEODE && EXPERIMENTAL + tristate "AMD Geode GX framebuffer support" + depends on FB && FB_GEODE select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT @@ -39,8 +39,8 @@ config FB_GEODE_GX If unsure, say N. config FB_GEODE_GX1 - tristate "AMD Geode GX1 framebuffer support (EXPERIMENTAL)" - depends on FB && FB_GEODE && EXPERIMENTAL + tristate "AMD Geode GX1 framebuffer support" + depends on FB && FB_GEODE select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT diff --git a/drivers/video/goldfishfb.c b/drivers/video/goldfishfb.c new file mode 100644 index 00000000000..489abb32fc0 --- /dev/null +++ b/drivers/video/goldfishfb.c @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2007 Google, Inc. + * Copyright (C) 2012 Intel, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/dma-mapping.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/mm.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/platform_device.h> + +enum { + FB_GET_WIDTH = 0x00, + FB_GET_HEIGHT = 0x04, + FB_INT_STATUS = 0x08, + FB_INT_ENABLE = 0x0c, + FB_SET_BASE = 0x10, + FB_SET_ROTATION = 0x14, + FB_SET_BLANK = 0x18, + FB_GET_PHYS_WIDTH = 0x1c, + FB_GET_PHYS_HEIGHT = 0x20, + + FB_INT_VSYNC = 1U << 0, + FB_INT_BASE_UPDATE_DONE = 1U << 1 +}; + +struct goldfish_fb { + void __iomem *reg_base; + int irq; + spinlock_t lock; + wait_queue_head_t wait; + int base_update_count; + int rotation; + struct fb_info fb; + u32 cmap[16]; +}; + +static irqreturn_t goldfish_fb_interrupt(int irq, void *dev_id) +{ + unsigned long irq_flags; + struct goldfish_fb *fb = dev_id; + u32 status; + + spin_lock_irqsave(&fb->lock, irq_flags); + status = readl(fb->reg_base + FB_INT_STATUS); + if (status & FB_INT_BASE_UPDATE_DONE) { + fb->base_update_count++; + wake_up(&fb->wait); + } + spin_unlock_irqrestore(&fb->lock, irq_flags); + return status ? IRQ_HANDLED : IRQ_NONE; +} + +static inline u32 convert_bitfield(int val, struct fb_bitfield *bf) +{ + unsigned int mask = (1 << bf->length) - 1; + + return (val >> (16 - bf->length) & mask) << bf->offset; +} + +static int +goldfish_fb_setcolreg(unsigned int regno, unsigned int red, unsigned int green, + unsigned int blue, unsigned int transp, struct fb_info *info) +{ + struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb); + + if (regno < 16) { + fb->cmap[regno] = convert_bitfield(transp, &fb->fb.var.transp) | + convert_bitfield(blue, &fb->fb.var.blue) | + convert_bitfield(green, &fb->fb.var.green) | + convert_bitfield(red, &fb->fb.var.red); + return 0; + } else { + return 1; + } +} + +static int goldfish_fb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + if ((var->rotate & 1) != (info->var.rotate & 1)) { + if ((var->xres != info->var.yres) || + (var->yres != info->var.xres) || + (var->xres_virtual != info->var.yres) || + (var->yres_virtual > info->var.xres * 2) || + (var->yres_virtual < info->var.xres)) { + return -EINVAL; + } + } else { + if ((var->xres != info->var.xres) || + (var->yres != info->var.yres) || + (var->xres_virtual != info->var.xres) || + (var->yres_virtual > info->var.yres * 2) || + (var->yres_virtual < info->var.yres)) { + return -EINVAL; + } + } + if ((var->xoffset != info->var.xoffset) || + (var->bits_per_pixel != info->var.bits_per_pixel) || + (var->grayscale != info->var.grayscale)) { + return -EINVAL; + } + return 0; +} + +static int goldfish_fb_set_par(struct fb_info *info) +{ + struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb); + if (fb->rotation != fb->fb.var.rotate) { + info->fix.line_length = info->var.xres * 2; + fb->rotation = fb->fb.var.rotate; + writel(fb->rotation, fb->reg_base + FB_SET_ROTATION); + } + return 0; +} + + +static int goldfish_fb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + unsigned long irq_flags; + int base_update_count; + struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb); + + spin_lock_irqsave(&fb->lock, irq_flags); + base_update_count = fb->base_update_count; + writel(fb->fb.fix.smem_start + fb->fb.var.xres * 2 * var->yoffset, + fb->reg_base + FB_SET_BASE); + spin_unlock_irqrestore(&fb->lock, irq_flags); + wait_event_timeout(fb->wait, + fb->base_update_count != base_update_count, HZ / 15); + if (fb->base_update_count == base_update_count) + pr_err("goldfish_fb_pan_display: timeout wating for base update\n"); + return 0; +} + +static int goldfish_fb_blank(int blank, struct fb_info *info) +{ + struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb); + switch (blank) { + case FB_BLANK_NORMAL: + writel(1, fb->reg_base + FB_SET_BLANK); + break; + case FB_BLANK_UNBLANK: + writel(0, fb->reg_base + FB_SET_BLANK); + break; + } + return 0; +} + +static struct fb_ops goldfish_fb_ops = { + .owner = THIS_MODULE, + .fb_check_var = goldfish_fb_check_var, + .fb_set_par = goldfish_fb_set_par, + .fb_setcolreg = goldfish_fb_setcolreg, + .fb_pan_display = goldfish_fb_pan_display, + .fb_blank = goldfish_fb_blank, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + + +static int goldfish_fb_probe(struct platform_device *pdev) +{ + int ret; + struct resource *r; + struct goldfish_fb *fb; + size_t framesize; + u32 width, height; + dma_addr_t fbpaddr; + + fb = kzalloc(sizeof(*fb), GFP_KERNEL); + if (fb == NULL) { + ret = -ENOMEM; + goto err_fb_alloc_failed; + } + spin_lock_init(&fb->lock); + init_waitqueue_head(&fb->wait); + platform_set_drvdata(pdev, fb); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (r == NULL) { + ret = -ENODEV; + goto err_no_io_base; + } + fb->reg_base = ioremap(r->start, PAGE_SIZE); + if (fb->reg_base == NULL) { + ret = -ENOMEM; + goto err_no_io_base; + } + + fb->irq = platform_get_irq(pdev, 0); + if (fb->irq <= 0) { + ret = -ENODEV; + goto err_no_irq; + } + + width = readl(fb->reg_base + FB_GET_WIDTH); + height = readl(fb->reg_base + FB_GET_HEIGHT); + + fb->fb.fbops = &goldfish_fb_ops; + fb->fb.flags = FBINFO_FLAG_DEFAULT; + fb->fb.pseudo_palette = fb->cmap; + fb->fb.fix.type = FB_TYPE_PACKED_PIXELS; + fb->fb.fix.visual = FB_VISUAL_TRUECOLOR; + fb->fb.fix.line_length = width * 2; + fb->fb.fix.accel = FB_ACCEL_NONE; + fb->fb.fix.ypanstep = 1; + + fb->fb.var.xres = width; + fb->fb.var.yres = height; + fb->fb.var.xres_virtual = width; + fb->fb.var.yres_virtual = height * 2; + fb->fb.var.bits_per_pixel = 16; + fb->fb.var.activate = FB_ACTIVATE_NOW; + fb->fb.var.height = readl(fb->reg_base + FB_GET_PHYS_HEIGHT); + fb->fb.var.width = readl(fb->reg_base + FB_GET_PHYS_WIDTH); + fb->fb.var.pixclock = 10000; + + fb->fb.var.red.offset = 11; + fb->fb.var.red.length = 5; + fb->fb.var.green.offset = 5; + fb->fb.var.green.length = 6; + fb->fb.var.blue.offset = 0; + fb->fb.var.blue.length = 5; + + framesize = width * height * 2 * 2; + fb->fb.screen_base = (char __force __iomem *)dma_alloc_coherent( + &pdev->dev, framesize, + &fbpaddr, GFP_KERNEL); + pr_debug("allocating frame buffer %d * %d, got %p\n", + width, height, fb->fb.screen_base); + if (fb->fb.screen_base == NULL) { + ret = -ENOMEM; + goto err_alloc_screen_base_failed; + } + fb->fb.fix.smem_start = fbpaddr; + fb->fb.fix.smem_len = framesize; + + ret = fb_set_var(&fb->fb, &fb->fb.var); + if (ret) + goto err_fb_set_var_failed; + + ret = request_irq(fb->irq, goldfish_fb_interrupt, IRQF_SHARED, + pdev->name, fb); + if (ret) + goto err_request_irq_failed; + + writel(FB_INT_BASE_UPDATE_DONE, fb->reg_base + FB_INT_ENABLE); + goldfish_fb_pan_display(&fb->fb.var, &fb->fb); /* updates base */ + + ret = register_framebuffer(&fb->fb); + if (ret) + goto err_register_framebuffer_failed; + return 0; + +err_register_framebuffer_failed: + free_irq(fb->irq, fb); +err_request_irq_failed: +err_fb_set_var_failed: + dma_free_coherent(&pdev->dev, framesize, + (void *)fb->fb.screen_base, + fb->fb.fix.smem_start); +err_alloc_screen_base_failed: +err_no_irq: + iounmap(fb->reg_base); +err_no_io_base: + kfree(fb); +err_fb_alloc_failed: + return ret; +} + +static int goldfish_fb_remove(struct platform_device *pdev) +{ + size_t framesize; + struct goldfish_fb *fb = platform_get_drvdata(pdev); + + framesize = fb->fb.var.xres_virtual * fb->fb.var.yres_virtual * 2; + unregister_framebuffer(&fb->fb); + free_irq(fb->irq, fb); + + dma_free_coherent(&pdev->dev, framesize, (void *)fb->fb.screen_base, + fb->fb.fix.smem_start); + iounmap(fb->reg_base); + return 0; +} + + +static struct platform_driver goldfish_fb_driver = { + .probe = goldfish_fb_probe, + .remove = goldfish_fb_remove, + .driver = { + .name = "goldfish_fb" + } +}; + +module_platform_driver(goldfish_fb_driver); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c new file mode 100644 index 00000000000..ab23c9b7914 --- /dev/null +++ b/drivers/video/hdmi.c @@ -0,0 +1,308 @@ +/* + * Copyright (C) 2012 Avionic Design 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/bitops.h> +#include <linux/errno.h> +#include <linux/export.h> +#include <linux/hdmi.h> +#include <linux/string.h> + +static void hdmi_infoframe_checksum(void *buffer, size_t size) +{ + u8 *ptr = buffer; + u8 csum = 0; + size_t i; + + /* compute checksum */ + for (i = 0; i < size; i++) + csum += ptr[i]; + + ptr[3] = 256 - csum; +} + +/** + * hdmi_avi_infoframe_init() - initialize an HDMI AVI infoframe + * @frame: HDMI AVI infoframe + * + * Returns 0 on success or a negative error code on failure. + */ +int hdmi_avi_infoframe_init(struct hdmi_avi_infoframe *frame) +{ + memset(frame, 0, sizeof(*frame)); + + frame->type = HDMI_INFOFRAME_TYPE_AVI; + frame->version = 2; + frame->length = 13; + + return 0; +} +EXPORT_SYMBOL(hdmi_avi_infoframe_init); + +/** + * hdmi_avi_infoframe_pack() - write HDMI AVI infoframe to binary buffer + * @frame: HDMI AVI infoframe + * @buffer: destination buffer + * @size: size of buffer + * + * Packs the information contained in the @frame structure into a binary + * representation that can be written into the corresponding controller + * registers. Also computes the checksum as required by section 5.3.5 of + * the HDMI 1.4 specification. + * + * Returns the number of bytes packed into the binary buffer or a negative + * error code on failure. + */ +ssize_t hdmi_avi_infoframe_pack(struct hdmi_avi_infoframe *frame, void *buffer, + size_t size) +{ + u8 *ptr = buffer; + size_t length; + + length = HDMI_INFOFRAME_HEADER_SIZE + frame->length; + + if (size < length) + return -ENOSPC; + + memset(buffer, 0, length); + + ptr[0] = frame->type; + ptr[1] = frame->version; + ptr[2] = frame->length; + ptr[3] = 0; /* checksum */ + + /* start infoframe payload */ + ptr += HDMI_INFOFRAME_HEADER_SIZE; + + ptr[0] = ((frame->colorspace & 0x3) << 5) | (frame->scan_mode & 0x3); + + if (frame->active_info_valid) + ptr[0] |= BIT(4); + + if (frame->horizontal_bar_valid) + ptr[0] |= BIT(3); + + if (frame->vertical_bar_valid) + ptr[0] |= BIT(2); + + ptr[1] = ((frame->colorimetry & 0x3) << 6) | + ((frame->picture_aspect & 0x3) << 4) | + (frame->active_aspect & 0xf); + + ptr[2] = ((frame->extended_colorimetry & 0x7) << 4) | + ((frame->quantization_range & 0x3) << 2) | + (frame->nups & 0x3); + + if (frame->itc) + ptr[2] |= BIT(7); + + ptr[3] = frame->video_code & 0x7f; + + ptr[4] = ((frame->ycc_quantization_range & 0x3) << 6) | + ((frame->content_type & 0x3) << 4) | + (frame->pixel_repeat & 0xf); + + ptr[5] = frame->top_bar & 0xff; + ptr[6] = (frame->top_bar >> 8) & 0xff; + ptr[7] = frame->bottom_bar & 0xff; + ptr[8] = (frame->bottom_bar >> 8) & 0xff; + ptr[9] = frame->left_bar & 0xff; + ptr[10] = (frame->left_bar >> 8) & 0xff; + ptr[11] = frame->right_bar & 0xff; + ptr[12] = (frame->right_bar >> 8) & 0xff; + + hdmi_infoframe_checksum(buffer, length); + + return length; +} +EXPORT_SYMBOL(hdmi_avi_infoframe_pack); + +/** + * hdmi_spd_infoframe_init() - initialize an HDMI SPD infoframe + * @frame: HDMI SPD infoframe + * @vendor: vendor string + * @product: product string + * + * Returns 0 on success or a negative error code on failure. + */ +int hdmi_spd_infoframe_init(struct hdmi_spd_infoframe *frame, + const char *vendor, const char *product) +{ + memset(frame, 0, sizeof(*frame)); + + frame->type = HDMI_INFOFRAME_TYPE_SPD; + frame->version = 1; + frame->length = 25; + + strncpy(frame->vendor, vendor, sizeof(frame->vendor)); + strncpy(frame->product, product, sizeof(frame->product)); + + return 0; +} +EXPORT_SYMBOL(hdmi_spd_infoframe_init); + +/** + * hdmi_spd_infoframe_pack() - write HDMI SPD infoframe to binary buffer + * @frame: HDMI SPD infoframe + * @buffer: destination buffer + * @size: size of buffer + * + * Packs the information contained in the @frame structure into a binary + * representation that can be written into the corresponding controller + * registers. Also computes the checksum as required by section 5.3.5 of + * the HDMI 1.4 specification. + * + * Returns the number of bytes packed into the binary buffer or a negative + * error code on failure. + */ +ssize_t hdmi_spd_infoframe_pack(struct hdmi_spd_infoframe *frame, void *buffer, + size_t size) +{ + u8 *ptr = buffer; + size_t length; + + length = HDMI_INFOFRAME_HEADER_SIZE + frame->length; + + if (size < length) + return -ENOSPC; + + memset(buffer, 0, length); + + ptr[0] = frame->type; + ptr[1] = frame->version; + ptr[2] = frame->length; + ptr[3] = 0; /* checksum */ + + /* start infoframe payload */ + ptr += HDMI_INFOFRAME_HEADER_SIZE; + + memcpy(ptr, frame->vendor, sizeof(frame->vendor)); + memcpy(ptr + 8, frame->product, sizeof(frame->product)); + + ptr[24] = frame->sdi; + + hdmi_infoframe_checksum(buffer, length); + + return length; +} +EXPORT_SYMBOL(hdmi_spd_infoframe_pack); + +/** + * hdmi_audio_infoframe_init() - initialize an HDMI audio infoframe + * @frame: HDMI audio infoframe + * + * Returns 0 on success or a negative error code on failure. + */ +int hdmi_audio_infoframe_init(struct hdmi_audio_infoframe *frame) +{ + memset(frame, 0, sizeof(*frame)); + + frame->type = HDMI_INFOFRAME_TYPE_AUDIO; + frame->version = 1; + frame->length = 10; + + return 0; +} +EXPORT_SYMBOL(hdmi_audio_infoframe_init); + +/** + * hdmi_audio_infoframe_pack() - write HDMI audio infoframe to binary buffer + * @frame: HDMI audio infoframe + * @buffer: destination buffer + * @size: size of buffer + * + * Packs the information contained in the @frame structure into a binary + * representation that can be written into the corresponding controller + * registers. Also computes the checksum as required by section 5.3.5 of + * the HDMI 1.4 specification. + * + * Returns the number of bytes packed into the binary buffer or a negative + * error code on failure. + */ +ssize_t hdmi_audio_infoframe_pack(struct hdmi_audio_infoframe *frame, + void *buffer, size_t size) +{ + unsigned char channels; + u8 *ptr = buffer; + size_t length; + + length = HDMI_INFOFRAME_HEADER_SIZE + frame->length; + + if (size < length) + return -ENOSPC; + + memset(buffer, 0, length); + + if (frame->channels >= 2) + channels = frame->channels - 1; + else + channels = 0; + + ptr[0] = frame->type; + ptr[1] = frame->version; + ptr[2] = frame->length; + ptr[3] = 0; /* checksum */ + + /* start infoframe payload */ + ptr += HDMI_INFOFRAME_HEADER_SIZE; + + ptr[0] = ((frame->coding_type & 0xf) << 4) | (channels & 0x7); + ptr[1] = ((frame->sample_frequency & 0x7) << 2) | + (frame->sample_size & 0x3); + ptr[2] = frame->coding_type_ext & 0x1f; + ptr[3] = frame->channel_allocation; + ptr[4] = (frame->level_shift_value & 0xf) << 3; + + if (frame->downmix_inhibit) + ptr[4] |= BIT(7); + + hdmi_infoframe_checksum(buffer, length); + + return length; +} +EXPORT_SYMBOL(hdmi_audio_infoframe_pack); + +/** + * hdmi_vendor_infoframe_pack() - write a HDMI vendor infoframe to binary + * buffer + * @frame: HDMI vendor infoframe + * @buffer: destination buffer + * @size: size of buffer + * + * Packs the information contained in the @frame structure into a binary + * representation that can be written into the corresponding controller + * registers. Also computes the checksum as required by section 5.3.5 of + * the HDMI 1.4 specification. + * + * Returns the number of bytes packed into the binary buffer or a negative + * error code on failure. + */ +ssize_t hdmi_vendor_infoframe_pack(struct hdmi_vendor_infoframe *frame, + void *buffer, size_t size) +{ + u8 *ptr = buffer; + size_t length; + + length = HDMI_INFOFRAME_HEADER_SIZE + frame->length; + + if (size < length) + return -ENOSPC; + + memset(buffer, 0, length); + + ptr[0] = frame->type; + ptr[1] = frame->version; + ptr[2] = frame->length; + ptr[3] = 0; /* checksum */ + + memcpy(&ptr[HDMI_INFOFRAME_HEADER_SIZE], frame->data, frame->length); + + hdmi_infoframe_checksum(buffer, length); + + return length; +} +EXPORT_SYMBOL(hdmi_vendor_infoframe_pack); diff --git a/drivers/video/jz4740_fb.c b/drivers/video/jz4740_fb.c index d999bb5e048..36979b4131a 100644 --- a/drivers/video/jz4740_fb.c +++ b/drivers/video/jz4740_fb.c @@ -660,9 +660,9 @@ static int jzfb_probe(struct platform_device *pdev) } mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - jzfb->base = devm_request_and_ioremap(&pdev->dev, mem); - if (!jzfb->base) { - ret = -EBUSY; + jzfb->base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(jzfb->base)) { + ret = PTR_ERR(jzfb->base); goto err_framebuffer_release; } diff --git a/drivers/video/mmp/Kconfig b/drivers/video/mmp/Kconfig new file mode 100644 index 00000000000..e9ea39e1372 --- /dev/null +++ b/drivers/video/mmp/Kconfig @@ -0,0 +1,11 @@ +menuconfig MMP_DISP + tristate "Marvell MMP Display Subsystem support" + depends on CPU_PXA910 || CPU_MMP2 || CPU_MMP3 || CPU_PXA988 + help + Marvell Display Subsystem support. + +if MMP_DISP +source "drivers/video/mmp/hw/Kconfig" +source "drivers/video/mmp/panel/Kconfig" +source "drivers/video/mmp/fb/Kconfig" +endif diff --git a/drivers/video/mmp/Makefile b/drivers/video/mmp/Makefile new file mode 100644 index 00000000000..a014cb358bf --- /dev/null +++ b/drivers/video/mmp/Makefile @@ -0,0 +1 @@ +obj-y += core.o hw/ panel/ fb/ diff --git a/drivers/video/mmp/core.c b/drivers/video/mmp/core.c new file mode 100644 index 00000000000..9ed83419038 --- /dev/null +++ b/drivers/video/mmp/core.c @@ -0,0 +1,258 @@ +/* + * linux/drivers/video/mmp/common.c + * This driver is a common framework for Marvell Display Controller + * + * Copyright (C) 2012 Marvell Technology Group Ltd. + * Authors: Zhou Zhu <zzhu3@marvell.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, see <http://www.gnu.org/licenses/>. + * + */ + +#include <linux/slab.h> +#include <linux/dma-mapping.h> +#include <linux/export.h> +#include <video/mmp_disp.h> + +static struct mmp_overlay *path_get_overlay(struct mmp_path *path, + int overlay_id) +{ + if (path && overlay_id < path->overlay_num) + return &path->overlays[overlay_id]; + return 0; +} + +static int path_check_status(struct mmp_path *path) +{ + int i; + for (i = 0; i < path->overlay_num; i++) + if (path->overlays[i].status) + return 1; + + return 0; +} + +/* + * Get modelist write pointer of modelist. + * It also returns modelist number + * this function fetches modelist from phy/panel: + * for HDMI/parallel or dsi to hdmi cases, get from phy + * or get from panel + */ +static int path_get_modelist(struct mmp_path *path, + struct mmp_mode **modelist) +{ + BUG_ON(!path || !modelist); + + if (path->panel && path->panel->get_modelist) + return path->panel->get_modelist(path->panel, modelist); + + return 0; +} + +/* + * panel list is used to pair panel/path when path/panel registered + * path list is used for both buffer driver and platdriver + * plat driver do path register/unregister + * panel driver do panel register/unregister + * buffer driver get registered path + */ +static LIST_HEAD(panel_list); +static LIST_HEAD(path_list); +static DEFINE_MUTEX(disp_lock); + +/* + * mmp_register_panel - register panel to panel_list and connect to path + * @p: panel to be registered + * + * this function provides interface for panel drivers to register panel + * to panel_list and connect to path which matchs panel->plat_path_name. + * no error returns when no matching path is found as path register after + * panel register is permitted. + */ +void mmp_register_panel(struct mmp_panel *panel) +{ + struct mmp_path *path; + + mutex_lock(&disp_lock); + + /* add */ + list_add_tail(&panel->node, &panel_list); + + /* try to register to path */ + list_for_each_entry(path, &path_list, node) { + if (!strcmp(panel->plat_path_name, path->name)) { + dev_info(panel->dev, "connect to path %s\n", + path->name); + path->panel = panel; + break; + } + } + + mutex_unlock(&disp_lock); +} +EXPORT_SYMBOL_GPL(mmp_register_panel); + +/* + * mmp_unregister_panel - unregister panel from panel_list and disconnect + * @p: panel to be unregistered + * + * this function provides interface for panel drivers to unregister panel + * from panel_list and disconnect from path. + */ +void mmp_unregister_panel(struct mmp_panel *panel) +{ + struct mmp_path *path; + + mutex_lock(&disp_lock); + list_del(&panel->node); + + list_for_each_entry(path, &path_list, node) { + if (path->panel && path->panel == panel) { + dev_info(panel->dev, "disconnect from path %s\n", + path->name); + path->panel = NULL; + break; + } + } + mutex_unlock(&disp_lock); +} +EXPORT_SYMBOL_GPL(mmp_unregister_panel); + +/* + * mmp_get_path - get path by name + * @p: path name + * + * this function checks path name in path_list and return matching path + * return NULL if no matching path + */ +struct mmp_path *mmp_get_path(const char *name) +{ + struct mmp_path *path; + int found = 0; + + mutex_lock(&disp_lock); + list_for_each_entry(path, &path_list, node) { + if (!strcmp(name, path->name)) { + found = 1; + break; + } + } + mutex_unlock(&disp_lock); + + return found ? path : NULL; +} +EXPORT_SYMBOL_GPL(mmp_get_path); + +/* + * mmp_register_path - init and register path by path_info + * @p: path info provided by display controller + * + * this function init by path info and register path to path_list + * this function also try to connect path with panel by name + */ +struct mmp_path *mmp_register_path(struct mmp_path_info *info) +{ + int i; + size_t size; + struct mmp_path *path = NULL; + struct mmp_panel *panel; + + size = sizeof(struct mmp_path) + + sizeof(struct mmp_overlay) * info->overlay_num; + path = kzalloc(size, GFP_KERNEL); + if (!path) + goto failed; + + /* path set */ + mutex_init(&path->access_ok); + path->dev = info->dev; + path->id = info->id; + path->name = info->name; + path->output_type = info->output_type; + path->overlay_num = info->overlay_num; + path->plat_data = info->plat_data; + path->ops.set_mode = info->set_mode; + + mutex_lock(&disp_lock); + /* get panel */ + list_for_each_entry(panel, &panel_list, node) { + if (!strcmp(info->name, panel->plat_path_name)) { + dev_info(path->dev, "get panel %s\n", panel->name); + path->panel = panel; + break; + } + } + + dev_info(path->dev, "register %s, overlay_num %d\n", + path->name, path->overlay_num); + + /* default op set: if already set by driver, never cover it */ + if (!path->ops.check_status) + path->ops.check_status = path_check_status; + if (!path->ops.get_overlay) + path->ops.get_overlay = path_get_overlay; + if (!path->ops.get_modelist) + path->ops.get_modelist = path_get_modelist; + + /* step3: init overlays */ + for (i = 0; i < path->overlay_num; i++) { + path->overlays[i].path = path; + path->overlays[i].id = i; + mutex_init(&path->overlays[i].access_ok); + path->overlays[i].ops = info->overlay_ops; + } + + /* add to pathlist */ + list_add_tail(&path->node, &path_list); + + mutex_unlock(&disp_lock); + return path; + +failed: + kfree(path); + mutex_unlock(&disp_lock); + return NULL; +} +EXPORT_SYMBOL_GPL(mmp_register_path); + +/* + * mmp_unregister_path - unregister and destory path + * @p: path to be destoried. + * + * this function registers path and destorys it. + */ +void mmp_unregister_path(struct mmp_path *path) +{ + int i; + + if (!path) + return; + + mutex_lock(&disp_lock); + /* del from pathlist */ + list_del(&path->node); + + /* deinit overlays */ + for (i = 0; i < path->overlay_num; i++) + mutex_destroy(&path->overlays[i].access_ok); + + mutex_destroy(&path->access_ok); + + kfree(path); + mutex_unlock(&disp_lock); + + dev_info(path->dev, "de-register %s\n", path->name); +} +EXPORT_SYMBOL_GPL(mmp_unregister_path); diff --git a/drivers/video/mmp/fb/Kconfig b/drivers/video/mmp/fb/Kconfig new file mode 100644 index 00000000000..9b0141f105f --- /dev/null +++ b/drivers/video/mmp/fb/Kconfig @@ -0,0 +1,13 @@ +if MMP_DISP + +config MMP_FB + bool "fb driver for Marvell MMP Display Subsystem" + depends on FB + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + default y + help + fb driver for Marvell MMP Display Subsystem + +endif diff --git a/drivers/video/mmp/fb/Makefile b/drivers/video/mmp/fb/Makefile new file mode 100644 index 00000000000..709fd1f76ab --- /dev/null +++ b/drivers/video/mmp/fb/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_MMP_FB) += mmpfb.o diff --git a/drivers/video/mmp/fb/mmpfb.c b/drivers/video/mmp/fb/mmpfb.c new file mode 100644 index 00000000000..6d1fa96c5cc --- /dev/null +++ b/drivers/video/mmp/fb/mmpfb.c @@ -0,0 +1,685 @@ +/* + * linux/drivers/video/mmp/fb/mmpfb.c + * Framebuffer driver for Marvell Display controller. + * + * Copyright (C) 2012 Marvell Technology Group Ltd. + * Authors: Zhou Zhu <zzhu3@marvell.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, see <http://www.gnu.org/licenses/>. + * + */ +#include <linux/module.h> +#include <linux/dma-mapping.h> +#include <linux/platform_device.h> +#include "mmpfb.h" + +static int var_to_pixfmt(struct fb_var_screeninfo *var) +{ + /* + * Pseudocolor mode? + */ + if (var->bits_per_pixel == 8) + return PIXFMT_PSEUDOCOLOR; + + /* + * Check for YUV422PLANAR. + */ + if (var->bits_per_pixel == 16 && var->red.length == 8 && + var->green.length == 4 && var->blue.length == 4) { + if (var->green.offset >= var->blue.offset) + return PIXFMT_YUV422P; + else + return PIXFMT_YVU422P; + } + + /* + * Check for YUV420PLANAR. + */ + if (var->bits_per_pixel == 12 && var->red.length == 8 && + var->green.length == 2 && var->blue.length == 2) { + if (var->green.offset >= var->blue.offset) + return PIXFMT_YUV420P; + else + return PIXFMT_YVU420P; + } + + /* + * Check for YUV422PACK. + */ + if (var->bits_per_pixel == 16 && var->red.length == 16 && + var->green.length == 16 && var->blue.length == 16) { + if (var->red.offset == 0) + return PIXFMT_YUYV; + else if (var->green.offset >= var->blue.offset) + return PIXFMT_UYVY; + else + return PIXFMT_VYUY; + } + + /* + * Check for 565/1555. + */ + if (var->bits_per_pixel == 16 && var->red.length <= 5 && + var->green.length <= 6 && var->blue.length <= 5) { + if (var->transp.length == 0) { + if (var->red.offset >= var->blue.offset) + return PIXFMT_RGB565; + else + return PIXFMT_BGR565; + } + } + + /* + * Check for 888/A888. + */ + if (var->bits_per_pixel <= 32 && var->red.length <= 8 && + var->green.length <= 8 && var->blue.length <= 8) { + if (var->bits_per_pixel == 24 && var->transp.length == 0) { + if (var->red.offset >= var->blue.offset) + return PIXFMT_RGB888PACK; + else + return PIXFMT_BGR888PACK; + } + + if (var->bits_per_pixel == 32 && var->transp.offset == 24) { + if (var->red.offset >= var->blue.offset) + return PIXFMT_RGBA888; + else + return PIXFMT_BGRA888; + } else { + if (var->red.offset >= var->blue.offset) + return PIXFMT_RGB888UNPACK; + else + return PIXFMT_BGR888UNPACK; + } + + /* fall through */ + } + + return -EINVAL; +} + +static void pixfmt_to_var(struct fb_var_screeninfo *var, int pix_fmt) +{ + switch (pix_fmt) { + case PIXFMT_RGB565: + var->bits_per_pixel = 16; + var->red.offset = 11; var->red.length = 5; + var->green.offset = 5; var->green.length = 6; + var->blue.offset = 0; var->blue.length = 5; + var->transp.offset = 0; var->transp.length = 0; + break; + case PIXFMT_BGR565: + var->bits_per_pixel = 16; + var->red.offset = 0; var->red.length = 5; + var->green.offset = 5; var->green.length = 6; + var->blue.offset = 11; var->blue.length = 5; + var->transp.offset = 0; var->transp.length = 0; + break; + case PIXFMT_RGB888UNPACK: + var->bits_per_pixel = 32; + var->red.offset = 16; var->red.length = 8; + var->green.offset = 8; var->green.length = 8; + var->blue.offset = 0; var->blue.length = 8; + var->transp.offset = 0; var->transp.length = 0; + break; + case PIXFMT_BGR888UNPACK: + var->bits_per_pixel = 32; + var->red.offset = 0; var->red.length = 8; + var->green.offset = 8; var->green.length = 8; + var->blue.offset = 16; var->blue.length = 8; + var->transp.offset = 0; var->transp.length = 0; + break; + case PIXFMT_RGBA888: + var->bits_per_pixel = 32; + var->red.offset = 16; var->red.length = 8; + var->green.offset = 8; var->green.length = 8; + var->blue.offset = 0; var->blue.length = 8; + var->transp.offset = 24; var->transp.length = 8; + break; + case PIXFMT_BGRA888: + var->bits_per_pixel = 32; + var->red.offset = 0; var->red.length = 8; + var->green.offset = 8; var->green.length = 8; + var->blue.offset = 16; var->blue.length = 8; + var->transp.offset = 24; var->transp.length = 8; + break; + case PIXFMT_RGB888PACK: + var->bits_per_pixel = 24; + var->red.offset = 16; var->red.length = 8; + var->green.offset = 8; var->green.length = 8; + var->blue.offset = 0; var->blue.length = 8; + var->transp.offset = 0; var->transp.length = 0; + break; + case PIXFMT_BGR888PACK: + var->bits_per_pixel = 24; + var->red.offset = 0; var->red.length = 8; + var->green.offset = 8; var->green.length = 8; + var->blue.offset = 16; var->blue.length = 8; + var->transp.offset = 0; var->transp.length = 0; + break; + case PIXFMT_YUV420P: + var->bits_per_pixel = 12; + var->red.offset = 4; var->red.length = 8; + var->green.offset = 2; var->green.length = 2; + var->blue.offset = 0; var->blue.length = 2; + var->transp.offset = 0; var->transp.length = 0; + break; + case PIXFMT_YVU420P: + var->bits_per_pixel = 12; + var->red.offset = 4; var->red.length = 8; + var->green.offset = 0; var->green.length = 2; + var->blue.offset = 2; var->blue.length = 2; + var->transp.offset = 0; var->transp.length = 0; + break; + case PIXFMT_YUV422P: + var->bits_per_pixel = 16; + var->red.offset = 8; var->red.length = 8; + var->green.offset = 4; var->green.length = 4; + var->blue.offset = 0; var->blue.length = 4; + var->transp.offset = 0; var->transp.length = 0; + break; + case PIXFMT_YVU422P: + var->bits_per_pixel = 16; + var->red.offset = 8; var->red.length = 8; + var->green.offset = 0; var->green.length = 4; + var->blue.offset = 4; var->blue.length = 4; + var->transp.offset = 0; var->transp.length = 0; + break; + case PIXFMT_UYVY: + var->bits_per_pixel = 16; + var->red.offset = 8; var->red.length = 16; + var->green.offset = 4; var->green.length = 16; + var->blue.offset = 0; var->blue.length = 16; + var->transp.offset = 0; var->transp.length = 0; + break; + case PIXFMT_VYUY: + var->bits_per_pixel = 16; + var->red.offset = 8; var->red.length = 16; + var->green.offset = 0; var->green.length = 16; + var->blue.offset = 4; var->blue.length = 16; + var->transp.offset = 0; var->transp.length = 0; + break; + case PIXFMT_YUYV: + var->bits_per_pixel = 16; + var->red.offset = 0; var->red.length = 16; + var->green.offset = 4; var->green.length = 16; + var->blue.offset = 8; var->blue.length = 16; + var->transp.offset = 0; var->transp.length = 0; + break; + case PIXFMT_PSEUDOCOLOR: + var->bits_per_pixel = 8; + var->red.offset = 0; var->red.length = 8; + var->green.offset = 0; var->green.length = 8; + var->blue.offset = 0; var->blue.length = 8; + var->transp.offset = 0; var->transp.length = 0; + break; + } +} + +/* + * fb framework has its limitation: + * 1. input color/output color is not seprated + * 2. fb_videomode not include output color + * so for fb usage, we keep a output format which is not changed + * then it's added for mmpmode + */ +static void fbmode_to_mmpmode(struct mmp_mode *mode, + struct fb_videomode *videomode, int output_fmt) +{ + u64 div_result = 1000000000000ll; + mode->name = videomode->name; + mode->refresh = videomode->refresh; + mode->xres = videomode->xres; + mode->yres = videomode->yres; + + do_div(div_result, videomode->pixclock); + mode->pixclock_freq = (u32)div_result; + + mode->left_margin = videomode->left_margin; + mode->right_margin = videomode->right_margin; + mode->upper_margin = videomode->upper_margin; + mode->lower_margin = videomode->lower_margin; + mode->hsync_len = videomode->hsync_len; + mode->vsync_len = videomode->vsync_len; + mode->hsync_invert = !!(videomode->sync & FB_SYNC_HOR_HIGH_ACT); + mode->vsync_invert = !!(videomode->sync & FB_SYNC_VERT_HIGH_ACT); + /* no defined flag in fb, use vmode>>3*/ + mode->invert_pixclock = !!(videomode->vmode & 8); + mode->pix_fmt_out = output_fmt; +} + +static void mmpmode_to_fbmode(struct fb_videomode *videomode, + struct mmp_mode *mode) +{ + u64 div_result = 1000000000000ll; + + videomode->name = mode->name; + videomode->refresh = mode->refresh; + videomode->xres = mode->xres; + videomode->yres = mode->yres; + + do_div(div_result, mode->pixclock_freq); + videomode->pixclock = (u32)div_result; + + videomode->left_margin = mode->left_margin; + videomode->right_margin = mode->right_margin; + videomode->upper_margin = mode->upper_margin; + videomode->lower_margin = mode->lower_margin; + videomode->hsync_len = mode->hsync_len; + videomode->vsync_len = mode->vsync_len; + videomode->sync = (mode->hsync_invert ? FB_SYNC_HOR_HIGH_ACT : 0) + | (mode->vsync_invert ? FB_SYNC_VERT_HIGH_ACT : 0); + videomode->vmode = mode->invert_pixclock ? 8 : 0; +} + +static int mmpfb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct mmpfb_info *fbi = info->par; + + if (var->bits_per_pixel == 8) + return -EINVAL; + /* + * Basic geometry sanity checks. + */ + if (var->xoffset + var->xres > var->xres_virtual) + return -EINVAL; + if (var->yoffset + var->yres > var->yres_virtual) + return -EINVAL; + + /* + * Check size of framebuffer. + */ + if (var->xres_virtual * var->yres_virtual * + (var->bits_per_pixel >> 3) > fbi->fb_size) + return -EINVAL; + + return 0; +} + +static unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf) +{ + return ((chan & 0xffff) >> (16 - bf->length)) << bf->offset; +} + +static u32 to_rgb(u16 red, u16 green, u16 blue) +{ + red >>= 8; + green >>= 8; + blue >>= 8; + + return (red << 16) | (green << 8) | blue; +} + +static int mmpfb_setcolreg(unsigned int regno, unsigned int red, + unsigned int green, unsigned int blue, + unsigned int trans, struct fb_info *info) +{ + struct mmpfb_info *fbi = info->par; + u32 val; + + if (info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 16) { + val = chan_to_field(red, &info->var.red); + val |= chan_to_field(green, &info->var.green); + val |= chan_to_field(blue , &info->var.blue); + fbi->pseudo_palette[regno] = val; + } + + if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR && regno < 256) { + val = to_rgb(red, green, blue); + /* TODO */ + } + + return 0; +} + +static int mmpfb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct mmpfb_info *fbi = info->par; + struct mmp_addr addr; + + memset(&addr, 0, sizeof(addr)); + addr.phys[0] = (var->yoffset * var->xres_virtual + var->xoffset) + * var->bits_per_pixel / 8 + fbi->fb_start_dma; + mmp_overlay_set_addr(fbi->overlay, &addr); + + return 0; +} + +static int var_update(struct fb_info *info) +{ + struct mmpfb_info *fbi = info->par; + struct fb_var_screeninfo *var = &info->var; + struct fb_videomode *m; + int pix_fmt; + + /* set pix_fmt */ + pix_fmt = var_to_pixfmt(var); + if (pix_fmt < 0) + return -EINVAL; + pixfmt_to_var(var, pix_fmt); + fbi->pix_fmt = pix_fmt; + + /* set var according to best video mode*/ + m = (struct fb_videomode *)fb_match_mode(var, &info->modelist); + if (!m) { + dev_err(fbi->dev, "set par: no match mode, use best mode\n"); + m = (struct fb_videomode *)fb_find_best_mode(var, + &info->modelist); + fb_videomode_to_var(var, m); + } + memcpy(&fbi->mode, m, sizeof(struct fb_videomode)); + + /* fix to 2* yres */ + var->yres_virtual = var->yres * 2; + info->fix.visual = (pix_fmt == PIXFMT_PSEUDOCOLOR) ? + FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; + info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8; + info->fix.ypanstep = var->yres; + return 0; +} + +static int mmpfb_set_par(struct fb_info *info) +{ + struct mmpfb_info *fbi = info->par; + struct fb_var_screeninfo *var = &info->var; + struct mmp_addr addr; + struct mmp_win win; + struct mmp_mode mode; + int ret; + + ret = var_update(info); + if (ret != 0) + return ret; + + /* set window/path according to new videomode */ + fbmode_to_mmpmode(&mode, &fbi->mode, fbi->output_fmt); + mmp_path_set_mode(fbi->path, &mode); + + memset(&win, 0, sizeof(win)); + win.xsrc = win.xdst = fbi->mode.xres; + win.ysrc = win.ydst = fbi->mode.yres; + win.pix_fmt = fbi->pix_fmt; + mmp_overlay_set_win(fbi->overlay, &win); + + /* set address always */ + memset(&addr, 0, sizeof(addr)); + addr.phys[0] = (var->yoffset * var->xres_virtual + var->xoffset) + * var->bits_per_pixel / 8 + fbi->fb_start_dma; + mmp_overlay_set_addr(fbi->overlay, &addr); + + return 0; +} + +static void mmpfb_power(struct mmpfb_info *fbi, int power) +{ + struct mmp_addr addr; + struct mmp_win win; + struct fb_var_screeninfo *var = &fbi->fb_info->var; + + /* for power on, always set address/window again */ + if (power) { + memset(&win, 0, sizeof(win)); + win.xsrc = win.xdst = fbi->mode.xres; + win.ysrc = win.ydst = fbi->mode.yres; + win.pix_fmt = fbi->pix_fmt; + mmp_overlay_set_win(fbi->overlay, &win); + + /* set address always */ + memset(&addr, 0, sizeof(addr)); + addr.phys[0] = fbi->fb_start_dma + + (var->yoffset * var->xres_virtual + var->xoffset) + * var->bits_per_pixel / 8; + mmp_overlay_set_addr(fbi->overlay, &addr); + } + mmp_overlay_set_onoff(fbi->overlay, power); +} + +static int mmpfb_blank(int blank, struct fb_info *info) +{ + struct mmpfb_info *fbi = info->par; + + mmpfb_power(fbi, (blank == FB_BLANK_UNBLANK)); + + return 0; +} + +static struct fb_ops mmpfb_ops = { + .owner = THIS_MODULE, + .fb_blank = mmpfb_blank, + .fb_check_var = mmpfb_check_var, + .fb_set_par = mmpfb_set_par, + .fb_setcolreg = mmpfb_setcolreg, + .fb_pan_display = mmpfb_pan_display, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + +static int modes_setup(struct mmpfb_info *fbi) +{ + struct fb_videomode *videomodes; + struct mmp_mode *mmp_modes; + struct fb_info *info = fbi->fb_info; + int videomode_num, i; + + /* get videomodes from path */ + videomode_num = mmp_path_get_modelist(fbi->path, &mmp_modes); + if (!videomode_num) { + dev_warn(fbi->dev, "can't get videomode num\n"); + return 0; + } + /* put videomode list to info structure */ + videomodes = kzalloc(sizeof(struct fb_videomode) * videomode_num, + GFP_KERNEL); + if (!videomodes) { + dev_err(fbi->dev, "can't malloc video modes\n"); + return -ENOMEM; + } + for (i = 0; i < videomode_num; i++) + mmpmode_to_fbmode(&videomodes[i], &mmp_modes[i]); + fb_videomode_to_modelist(videomodes, videomode_num, &info->modelist); + + /* set videomode[0] as default mode */ + memcpy(&fbi->mode, &videomodes[0], sizeof(struct fb_videomode)); + fbi->output_fmt = mmp_modes[0].pix_fmt_out; + fb_videomode_to_var(&info->var, &fbi->mode); + mmp_path_set_mode(fbi->path, &mmp_modes[0]); + + kfree(videomodes); + return videomode_num; +} + +static int fb_info_setup(struct fb_info *info, + struct mmpfb_info *fbi) +{ + int ret = 0; + /* Initialise static fb parameters.*/ + info->flags = FBINFO_DEFAULT | FBINFO_PARTIAL_PAN_OK | + FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN; + info->node = -1; + strcpy(info->fix.id, fbi->name); + info->fix.type = FB_TYPE_PACKED_PIXELS; + info->fix.type_aux = 0; + info->fix.xpanstep = 0; + info->fix.ypanstep = info->var.yres; + info->fix.ywrapstep = 0; + info->fix.accel = FB_ACCEL_NONE; + info->fix.smem_start = fbi->fb_start_dma; + info->fix.smem_len = fbi->fb_size; + info->fix.visual = (fbi->pix_fmt == PIXFMT_PSEUDOCOLOR) ? + FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; + info->fix.line_length = info->var.xres_virtual * + info->var.bits_per_pixel / 8; + info->fbops = &mmpfb_ops; + info->pseudo_palette = fbi->pseudo_palette; + info->screen_base = fbi->fb_start; + info->screen_size = fbi->fb_size; + + /* For FB framework: Allocate color map and Register framebuffer*/ + if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) + ret = -ENOMEM; + + return ret; +} + +static void fb_info_clear(struct fb_info *info) +{ + fb_dealloc_cmap(&info->cmap); +} + +static int mmpfb_probe(struct platform_device *pdev) +{ + struct mmp_buffer_driver_mach_info *mi; + struct fb_info *info = 0; + struct mmpfb_info *fbi = 0; + int ret, modes_num; + + mi = pdev->dev.platform_data; + if (mi == NULL) { + dev_err(&pdev->dev, "no platform data defined\n"); + return -EINVAL; + } + + /* initialize fb */ + info = framebuffer_alloc(sizeof(struct mmpfb_info), &pdev->dev); + if (info == NULL) + return -ENOMEM; + fbi = info->par; + if (!fbi) { + ret = -EINVAL; + goto failed; + } + + /* init fb */ + fbi->fb_info = info; + platform_set_drvdata(pdev, fbi); + fbi->dev = &pdev->dev; + fbi->name = mi->name; + fbi->pix_fmt = mi->default_pixfmt; + pixfmt_to_var(&info->var, fbi->pix_fmt); + mutex_init(&fbi->access_ok); + + /* get display path by name */ + fbi->path = mmp_get_path(mi->path_name); + if (!fbi->path) { + dev_err(&pdev->dev, "can't get the path %s\n", mi->path_name); + ret = -EINVAL; + goto failed_destroy_mutex; + } + + dev_info(fbi->dev, "path %s get\n", fbi->path->name); + + /* get overlay */ + fbi->overlay = mmp_path_get_overlay(fbi->path, mi->overlay_id); + if (!fbi->overlay) { + ret = -EINVAL; + goto failed_destroy_mutex; + } + /* set fetch used */ + mmp_overlay_set_fetch(fbi->overlay, mi->dmafetch_id); + + modes_num = modes_setup(fbi); + if (modes_num < 0) { + ret = modes_num; + goto failed_destroy_mutex; + } + + /* + * if get modes success, means not hotplug panels, use caculated buffer + * or use default size + */ + if (modes_num > 0) { + /* fix to 2* yres */ + info->var.yres_virtual = info->var.yres * 2; + + /* Allocate framebuffer memory: size = modes xy *4 */ + fbi->fb_size = info->var.xres_virtual * info->var.yres_virtual + * info->var.bits_per_pixel / 8; + } else { + fbi->fb_size = MMPFB_DEFAULT_SIZE; + } + + fbi->fb_start = dma_alloc_coherent(&pdev->dev, PAGE_ALIGN(fbi->fb_size), + &fbi->fb_start_dma, GFP_KERNEL); + if (fbi->fb_start == NULL) { + dev_err(&pdev->dev, "can't alloc framebuffer\n"); + ret = -ENOMEM; + goto failed_destroy_mutex; + } + memset(fbi->fb_start, 0, fbi->fb_size); + dev_info(fbi->dev, "fb %dk allocated\n", fbi->fb_size/1024); + + /* fb power on */ + if (modes_num > 0) + mmpfb_power(fbi, 1); + + ret = fb_info_setup(info, fbi); + if (ret < 0) + goto failed_free_buff; + + ret = register_framebuffer(info); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register fb: %d\n", ret); + ret = -ENXIO; + goto failed_clear_info; + } + + dev_info(fbi->dev, "loaded to /dev/fb%d <%s>.\n", + info->node, info->fix.id); + +#ifdef CONFIG_LOGO + if (fbi->fb_start) { + fb_prepare_logo(info, 0); + fb_show_logo(info, 0); + } +#endif + + return 0; + +failed_clear_info: + fb_info_clear(info); +failed_free_buff: + dma_free_coherent(&pdev->dev, PAGE_ALIGN(fbi->fb_size), fbi->fb_start, + fbi->fb_start_dma); +failed_destroy_mutex: + mutex_destroy(&fbi->access_ok); +failed: + dev_err(fbi->dev, "mmp-fb: frame buffer device init failed\n"); + platform_set_drvdata(pdev, NULL); + + framebuffer_release(info); + + return ret; +} + +static struct platform_driver mmpfb_driver = { + .driver = { + .name = "mmp-fb", + .owner = THIS_MODULE, + }, + .probe = mmpfb_probe, +}; + +static int mmpfb_init(void) +{ + return platform_driver_register(&mmpfb_driver); +} +module_init(mmpfb_init); + +MODULE_AUTHOR("Zhou Zhu <zhou.zhu@marvell.com>"); +MODULE_DESCRIPTION("Framebuffer driver for Marvell displays"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/mmp/fb/mmpfb.h b/drivers/video/mmp/fb/mmpfb.h new file mode 100644 index 00000000000..88c23c10a9e --- /dev/null +++ b/drivers/video/mmp/fb/mmpfb.h @@ -0,0 +1,54 @@ +/* + * linux/drivers/video/mmp/fb/mmpfb.h + * Framebuffer driver for Marvell Display controller. + * + * Copyright (C) 2012 Marvell Technology Group Ltd. + * Authors: Zhou Zhu <zzhu3@marvell.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, see <http://www.gnu.org/licenses/>. + * + */ + +#ifndef _MMP_FB_H_ +#define _MMP_FB_H_ + +#include <video/mmp_disp.h> +#include <linux/fb.h> + +/* LCD controller private state. */ +struct mmpfb_info { + struct device *dev; + int id; + const char *name; + + struct fb_info *fb_info; + /* basicaly videomode is for output */ + struct fb_videomode mode; + int pix_fmt; + + void *fb_start; + int fb_size; + dma_addr_t fb_start_dma; + + struct mmp_overlay *overlay; + struct mmp_path *path; + + struct mutex access_ok; + + unsigned int pseudo_palette[16]; + int output_fmt; +}; + +#define MMPFB_DEFAULT_SIZE (PAGE_ALIGN(1920 * 1080 * 4 * 2)) +#endif /* _MMP_FB_H_ */ diff --git a/drivers/video/mmp/hw/Kconfig b/drivers/video/mmp/hw/Kconfig new file mode 100644 index 00000000000..02f109a20cd --- /dev/null +++ b/drivers/video/mmp/hw/Kconfig @@ -0,0 +1,20 @@ +if MMP_DISP + +config MMP_DISP_CONTROLLER + bool "mmp display controller hw support" + depends on CPU_PXA910 || CPU_MMP2 || CPU_MMP3 || CPU_PXA988 + default n + help + Marvell MMP display hw controller support + this controller is used on Marvell PXA910, + MMP2, MMP3, PXA988 chips + +config MMP_DISP_SPI + bool "mmp display controller spi port" + depends on MMP_DISP_CONTROLLER && SPI_MASTER + default y + help + Marvell MMP display hw controller spi port support + will register as a spi master for panel usage + +endif diff --git a/drivers/video/mmp/hw/Makefile b/drivers/video/mmp/hw/Makefile new file mode 100644 index 00000000000..0000a714fed --- /dev/null +++ b/drivers/video/mmp/hw/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_MMP_DISP_CONTROLLER) += mmp_ctrl.o +obj-$(CONFIG_MMP_DISP_SPI) += mmp_spi.o diff --git a/drivers/video/mmp/hw/mmp_ctrl.c b/drivers/video/mmp/hw/mmp_ctrl.c new file mode 100644 index 00000000000..4bd31b2af39 --- /dev/null +++ b/drivers/video/mmp/hw/mmp_ctrl.c @@ -0,0 +1,591 @@ +/* + * linux/drivers/video/mmp/hw/mmp_ctrl.c + * Marvell MMP series Display Controller support + * + * Copyright (C) 2012 Marvell Technology Group Ltd. + * Authors: Guoqing Li <ligq@marvell.com> + * Lisa Du <cldu@marvell.com> + * Zhou Zhu <zzhu3@marvell.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, see <http://www.gnu.org/licenses/>. + * + */ +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/vmalloc.h> +#include <linux/uaccess.h> +#include <linux/kthread.h> +#include <linux/io.h> + +#include "mmp_ctrl.h" + +static irqreturn_t ctrl_handle_irq(int irq, void *dev_id) +{ + struct mmphw_ctrl *ctrl = (struct mmphw_ctrl *)dev_id; + u32 isr, imask, tmp; + + isr = readl_relaxed(ctrl->reg_base + SPU_IRQ_ISR); + imask = readl_relaxed(ctrl->reg_base + SPU_IRQ_ENA); + + do { + /* clear clock only */ + tmp = readl_relaxed(ctrl->reg_base + SPU_IRQ_ISR); + if (tmp & isr) + writel_relaxed(~isr, ctrl->reg_base + SPU_IRQ_ISR); + } while ((isr = readl(ctrl->reg_base + SPU_IRQ_ISR)) & imask); + + return IRQ_HANDLED; +} + +static u32 fmt_to_reg(struct mmp_overlay *overlay, int pix_fmt) +{ + u32 link_config = path_to_path_plat(overlay->path)->link_config; + u32 rbswap, uvswap = 0, yuvswap = 0, + csc_en = 0, val = 0, + vid = overlay_is_vid(overlay); + + switch (pix_fmt) { + case PIXFMT_RGB565: + case PIXFMT_RGB1555: + case PIXFMT_RGB888PACK: + case PIXFMT_RGB888UNPACK: + case PIXFMT_RGBA888: + rbswap = !(link_config & 0x1); + break; + case PIXFMT_VYUY: + case PIXFMT_YVU422P: + case PIXFMT_YVU420P: + rbswap = link_config & 0x1; + uvswap = 1; + break; + case PIXFMT_YUYV: + rbswap = link_config & 0x1; + yuvswap = 1; + break; + default: + rbswap = link_config & 0x1; + break; + } + + switch (pix_fmt) { + case PIXFMT_RGB565: + case PIXFMT_BGR565: + val = 0; + break; + case PIXFMT_RGB1555: + case PIXFMT_BGR1555: + val = 0x1; + break; + case PIXFMT_RGB888PACK: + case PIXFMT_BGR888PACK: + val = 0x2; + break; + case PIXFMT_RGB888UNPACK: + case PIXFMT_BGR888UNPACK: + val = 0x3; + break; + case PIXFMT_RGBA888: + case PIXFMT_BGRA888: + val = 0x4; + break; + case PIXFMT_UYVY: + case PIXFMT_VYUY: + case PIXFMT_YUYV: + val = 0x5; + csc_en = 1; + break; + case PIXFMT_YUV422P: + case PIXFMT_YVU422P: + val = 0x6; + csc_en = 1; + break; + case PIXFMT_YUV420P: + case PIXFMT_YVU420P: + val = 0x7; + csc_en = 1; + break; + default: + break; + } + + return (dma_palette(0) | dma_fmt(vid, val) | + dma_swaprb(vid, rbswap) | dma_swapuv(vid, uvswap) | + dma_swapyuv(vid, yuvswap) | dma_csc(vid, csc_en)); +} + +static void dmafetch_set_fmt(struct mmp_overlay *overlay) +{ + u32 tmp; + struct mmp_path *path = overlay->path; + tmp = readl_relaxed(ctrl_regs(path) + dma_ctrl(0, path->id)); + tmp &= ~dma_mask(overlay_is_vid(overlay)); + tmp |= fmt_to_reg(overlay, overlay->win.pix_fmt); + writel_relaxed(tmp, ctrl_regs(path) + dma_ctrl(0, path->id)); +} + +static void overlay_set_win(struct mmp_overlay *overlay, struct mmp_win *win) +{ + struct lcd_regs *regs = path_regs(overlay->path); + u32 pitch; + + /* assert win supported */ + memcpy(&overlay->win, win, sizeof(struct mmp_win)); + + mutex_lock(&overlay->access_ok); + pitch = win->xsrc * pixfmt_to_stride(win->pix_fmt); + writel_relaxed(pitch, ®s->g_pitch); + writel_relaxed((win->ysrc << 16) | win->xsrc, ®s->g_size); + writel_relaxed((win->ydst << 16) | win->xdst, ®s->g_size_z); + writel_relaxed(0, ®s->g_start); + + dmafetch_set_fmt(overlay); + mutex_unlock(&overlay->access_ok); +} + +static void dmafetch_onoff(struct mmp_overlay *overlay, int on) +{ + u32 mask = overlay_is_vid(overlay) ? CFG_GRA_ENA_MASK : + CFG_DMA_ENA_MASK; + u32 enable = overlay_is_vid(overlay) ? CFG_GRA_ENA(1) : CFG_DMA_ENA(1); + u32 tmp; + struct mmp_path *path = overlay->path; + + mutex_lock(&overlay->access_ok); + tmp = readl_relaxed(ctrl_regs(path) + dma_ctrl(0, path->id)); + tmp &= ~mask; + tmp |= (on ? enable : 0); + writel(tmp, ctrl_regs(path) + dma_ctrl(0, path->id)); + mutex_unlock(&overlay->access_ok); +} + +static void path_enabledisable(struct mmp_path *path, int on) +{ + u32 tmp; + mutex_lock(&path->access_ok); + tmp = readl_relaxed(ctrl_regs(path) + LCD_SCLK(path)); + if (on) + tmp &= ~SCLK_DISABLE; + else + tmp |= SCLK_DISABLE; + writel_relaxed(tmp, ctrl_regs(path) + LCD_SCLK(path)); + mutex_unlock(&path->access_ok); +} + +static void path_onoff(struct mmp_path *path, int on) +{ + if (path->status == on) { + dev_info(path->dev, "path %s is already %s\n", + path->name, stat_name(path->status)); + return; + } + + if (on) { + path_enabledisable(path, 1); + + if (path->panel && path->panel->set_onoff) + path->panel->set_onoff(path->panel, 1); + } else { + if (path->panel && path->panel->set_onoff) + path->panel->set_onoff(path->panel, 0); + + path_enabledisable(path, 0); + } + path->status = on; +} + +static void overlay_set_onoff(struct mmp_overlay *overlay, int on) +{ + if (overlay->status == on) { + dev_info(overlay_to_ctrl(overlay)->dev, "overlay %s is already %s\n", + overlay->path->name, stat_name(overlay->status)); + return; + } + overlay->status = on; + dmafetch_onoff(overlay, on); + if (overlay->path->ops.check_status(overlay->path) + != overlay->path->status) + path_onoff(overlay->path, on); +} + +static void overlay_set_fetch(struct mmp_overlay *overlay, int fetch_id) +{ + overlay->dmafetch_id = fetch_id; +} + +static int overlay_set_addr(struct mmp_overlay *overlay, struct mmp_addr *addr) +{ + struct lcd_regs *regs = path_regs(overlay->path); + + /* FIXME: assert addr supported */ + memcpy(&overlay->addr, addr, sizeof(struct mmp_win)); + writel(addr->phys[0], ®s->g_0); + + return overlay->addr.phys[0]; +} + +static void path_set_mode(struct mmp_path *path, struct mmp_mode *mode) +{ + struct lcd_regs *regs = path_regs(path); + u32 total_x, total_y, vsync_ctrl, tmp, sclk_src, sclk_div, + link_config = path_to_path_plat(path)->link_config; + + /* FIXME: assert videomode supported */ + memcpy(&path->mode, mode, sizeof(struct mmp_mode)); + + mutex_lock(&path->access_ok); + + /* polarity of timing signals */ + tmp = readl_relaxed(ctrl_regs(path) + intf_ctrl(path->id)) & 0x1; + tmp |= mode->vsync_invert ? 0 : 0x8; + tmp |= mode->hsync_invert ? 0 : 0x4; + tmp |= link_config & CFG_DUMBMODE_MASK; + tmp |= CFG_DUMB_ENA(1); + writel_relaxed(tmp, ctrl_regs(path) + intf_ctrl(path->id)); + + writel_relaxed((mode->yres << 16) | mode->xres, ®s->screen_active); + writel_relaxed((mode->left_margin << 16) | mode->right_margin, + ®s->screen_h_porch); + writel_relaxed((mode->upper_margin << 16) | mode->lower_margin, + ®s->screen_v_porch); + total_x = mode->xres + mode->left_margin + mode->right_margin + + mode->hsync_len; + total_y = mode->yres + mode->upper_margin + mode->lower_margin + + mode->vsync_len; + writel_relaxed((total_y << 16) | total_x, ®s->screen_size); + + /* vsync ctrl */ + if (path->output_type == PATH_OUT_DSI) + vsync_ctrl = 0x01330133; + else + vsync_ctrl = ((mode->xres + mode->right_margin) << 16) + | (mode->xres + mode->right_margin); + writel_relaxed(vsync_ctrl, ®s->vsync_ctrl); + + /* set pixclock div */ + sclk_src = clk_get_rate(path_to_ctrl(path)->clk); + sclk_div = sclk_src / mode->pixclock_freq; + if (sclk_div * mode->pixclock_freq < sclk_src) + sclk_div++; + + dev_info(path->dev, "%s sclk_src %d sclk_div 0x%x pclk %d\n", + __func__, sclk_src, sclk_div, mode->pixclock_freq); + + tmp = readl_relaxed(ctrl_regs(path) + LCD_SCLK(path)); + tmp &= ~CLK_INT_DIV_MASK; + tmp |= sclk_div; + writel_relaxed(tmp, ctrl_regs(path) + LCD_SCLK(path)); + + mutex_unlock(&path->access_ok); +} + +static struct mmp_overlay_ops mmphw_overlay_ops = { + .set_fetch = overlay_set_fetch, + .set_onoff = overlay_set_onoff, + .set_win = overlay_set_win, + .set_addr = overlay_set_addr, +}; + +static void ctrl_set_default(struct mmphw_ctrl *ctrl) +{ + u32 tmp, irq_mask; + + /* + * LCD Global control(LCD_TOP_CTRL) should be configed before + * any other LCD registers read/write, or there maybe issues. + */ + tmp = readl_relaxed(ctrl->reg_base + LCD_TOP_CTRL); + tmp |= 0xfff0; + writel_relaxed(tmp, ctrl->reg_base + LCD_TOP_CTRL); + + + /* disable all interrupts */ + irq_mask = path_imasks(0) | err_imask(0) | + path_imasks(1) | err_imask(1); + tmp = readl_relaxed(ctrl->reg_base + SPU_IRQ_ENA); + tmp &= ~irq_mask; + tmp |= irq_mask; + writel_relaxed(tmp, ctrl->reg_base + SPU_IRQ_ENA); +} + +static void path_set_default(struct mmp_path *path) +{ + struct lcd_regs *regs = path_regs(path); + u32 dma_ctrl1, mask, tmp, path_config; + + path_config = path_to_path_plat(path)->path_config; + + /* Configure IOPAD: should be parallel only */ + if (PATH_OUT_PARALLEL == path->output_type) { + mask = CFG_IOPADMODE_MASK | CFG_BURST_MASK | CFG_BOUNDARY_MASK; + tmp = readl_relaxed(ctrl_regs(path) + SPU_IOPAD_CONTROL); + tmp &= ~mask; + tmp |= path_config; + writel_relaxed(tmp, ctrl_regs(path) + SPU_IOPAD_CONTROL); + } + + /* Select path clock source */ + tmp = readl_relaxed(ctrl_regs(path) + LCD_SCLK(path)); + tmp &= ~SCLK_SRC_SEL_MASK; + tmp |= path_config; + writel_relaxed(tmp, ctrl_regs(path) + LCD_SCLK(path)); + + /* + * Configure default bits: vsync triggers DMA, + * power save enable, configure alpha registers to + * display 100% graphics, and set pixel command. + */ + dma_ctrl1 = 0x2032ff81; + + dma_ctrl1 |= CFG_VSYNC_INV_MASK; + writel_relaxed(dma_ctrl1, ctrl_regs(path) + dma_ctrl(1, path->id)); + + /* Configure default register values */ + writel_relaxed(0x00000000, ®s->blank_color); + writel_relaxed(0x00000000, ®s->g_1); + writel_relaxed(0x00000000, ®s->g_start); + + /* + * 1.enable multiple burst request in DMA AXI + * bus arbiter for faster read if not tv path; + * 2.enable horizontal smooth filter; + */ + if (PATH_PN == path->id) { + mask = CFG_GRA_HSMOOTH_MASK | CFG_DMA_HSMOOTH_MASK + | CFG_ARBFAST_ENA(1); + tmp = readl_relaxed(ctrl_regs(path) + dma_ctrl(0, path->id)); + tmp |= mask; + writel_relaxed(tmp, ctrl_regs(path) + dma_ctrl(0, path->id)); + } else if (PATH_TV == path->id) { + mask = CFG_GRA_HSMOOTH_MASK | CFG_DMA_HSMOOTH_MASK + | CFG_ARBFAST_ENA(1); + tmp = readl_relaxed(ctrl_regs(path) + dma_ctrl(0, path->id)); + tmp &= ~mask; + tmp |= CFG_GRA_HSMOOTH_MASK | CFG_DMA_HSMOOTH_MASK; + writel_relaxed(tmp, ctrl_regs(path) + dma_ctrl(0, path->id)); + } +} + +static int path_init(struct mmphw_path_plat *path_plat, + struct mmp_mach_path_config *config) +{ + struct mmphw_ctrl *ctrl = path_plat->ctrl; + struct mmp_path_info *path_info; + struct mmp_path *path = NULL; + + dev_info(ctrl->dev, "%s: %s\n", __func__, config->name); + + /* init driver data */ + path_info = kzalloc(sizeof(struct mmp_path_info), GFP_KERNEL); + if (!path_info) { + dev_err(ctrl->dev, "%s: unable to alloc path_info for %s\n", + __func__, config->name); + return 0; + } + path_info->name = config->name; + path_info->id = path_plat->id; + path_info->dev = ctrl->dev; + path_info->overlay_num = config->overlay_num; + path_info->overlay_ops = &mmphw_overlay_ops; + path_info->set_mode = path_set_mode; + path_info->plat_data = path_plat; + + /* create/register platform device */ + path = mmp_register_path(path_info); + if (!path) { + kfree(path_info); + return 0; + } + path_plat->path = path; + path_plat->path_config = config->path_config; + path_plat->link_config = config->link_config; + path_set_default(path); + + kfree(path_info); + return 1; +} + +static void path_deinit(struct mmphw_path_plat *path_plat) +{ + if (!path_plat) + return; + + if (path_plat->path) + mmp_unregister_path(path_plat->path); +} + +static int mmphw_probe(struct platform_device *pdev) +{ + struct mmp_mach_plat_info *mi; + struct resource *res; + int ret, i, size, irq; + struct mmphw_path_plat *path_plat; + struct mmphw_ctrl *ctrl = NULL; + + /* get resources from platform data */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "%s: no IO memory defined\n", __func__); + ret = -ENOENT; + goto failed; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "%s: no IRQ defined\n", __func__); + ret = -ENOENT; + goto failed; + } + + /* get configs from platform data */ + mi = pdev->dev.platform_data; + if (mi == NULL || !mi->path_num || !mi->paths) { + dev_err(&pdev->dev, "%s: no platform data defined\n", __func__); + ret = -EINVAL; + goto failed; + } + + /* allocate */ + size = sizeof(struct mmphw_ctrl) + sizeof(struct mmphw_path_plat) * + mi->path_num; + ctrl = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (!ctrl) { + ret = -ENOMEM; + goto failed; + } + + ctrl->name = mi->name; + ctrl->path_num = mi->path_num; + ctrl->dev = &pdev->dev; + ctrl->irq = irq; + platform_set_drvdata(pdev, ctrl); + mutex_init(&ctrl->access_ok); + + /* map registers.*/ + if (!devm_request_mem_region(ctrl->dev, res->start, + resource_size(res), ctrl->name)) { + dev_err(ctrl->dev, + "can't request region for resource %pR\n", res); + ret = -EINVAL; + goto failed; + } + + ctrl->reg_base = devm_ioremap_nocache(ctrl->dev, + res->start, resource_size(res)); + if (ctrl->reg_base == NULL) { + dev_err(ctrl->dev, "%s: res %x - %x map failed\n", __func__, + res->start, res->end); + ret = -ENOMEM; + goto failed; + } + + /* request irq */ + ret = devm_request_irq(ctrl->dev, ctrl->irq, ctrl_handle_irq, + IRQF_SHARED, "lcd_controller", ctrl); + if (ret < 0) { + dev_err(ctrl->dev, "%s unable to request IRQ %d\n", + __func__, ctrl->irq); + ret = -ENXIO; + goto failed; + } + + /* get clock */ + ctrl->clk = devm_clk_get(ctrl->dev, mi->clk_name); + if (IS_ERR(ctrl->clk)) { + dev_err(ctrl->dev, "unable to get clk %s\n", mi->clk_name); + ret = -ENOENT; + goto failed_get_clk; + } + clk_prepare_enable(ctrl->clk); + + /* init global regs */ + ctrl_set_default(ctrl); + + /* init pathes from machine info and register them */ + for (i = 0; i < ctrl->path_num; i++) { + /* get from config and machine info */ + path_plat = &ctrl->path_plats[i]; + path_plat->id = i; + path_plat->ctrl = ctrl; + + /* path init */ + if (!path_init(path_plat, &mi->paths[i])) { + ret = -EINVAL; + goto failed_path_init; + } + } + +#ifdef CONFIG_MMP_DISP_SPI + ret = lcd_spi_register(ctrl); + if (ret < 0) + goto failed_path_init; +#endif + + dev_info(ctrl->dev, "device init done\n"); + + return 0; + +failed_path_init: + for (i = 0; i < ctrl->path_num; i++) { + path_plat = &ctrl->path_plats[i]; + path_deinit(path_plat); + } + + if (ctrl->clk) { + devm_clk_put(ctrl->dev, ctrl->clk); + clk_disable_unprepare(ctrl->clk); + } +failed_get_clk: + devm_free_irq(ctrl->dev, ctrl->irq, ctrl); +failed: + if (ctrl) { + if (ctrl->reg_base) + devm_iounmap(ctrl->dev, ctrl->reg_base); + devm_release_mem_region(ctrl->dev, res->start, + resource_size(res)); + devm_kfree(ctrl->dev, ctrl); + } + + platform_set_drvdata(pdev, NULL); + dev_err(&pdev->dev, "device init failed\n"); + + return ret; +} + +static struct platform_driver mmphw_driver = { + .driver = { + .name = "mmp-disp", + .owner = THIS_MODULE, + }, + .probe = mmphw_probe, +}; + +static int mmphw_init(void) +{ + return platform_driver_register(&mmphw_driver); +} +module_init(mmphw_init); + +MODULE_AUTHOR("Li Guoqing<ligq@marvell.com>"); +MODULE_DESCRIPTION("Framebuffer driver for mmp"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/mmp/hw/mmp_ctrl.h b/drivers/video/mmp/hw/mmp_ctrl.h new file mode 100644 index 00000000000..6408d8ef3ab --- /dev/null +++ b/drivers/video/mmp/hw/mmp_ctrl.h @@ -0,0 +1,1974 @@ +/* + * drivers/video/mmp/hw/mmp_ctrl.h + * + * + * Copyright (C) 2012 Marvell Technology Group Ltd. + * Authors: Guoqing Li <ligq@marvell.com> + * Lisa Du <cldu@marvell.com> + * Zhou Zhu <zzhu3@marvell.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, see <http://www.gnu.org/licenses/>. + * + */ + +#ifndef _MMP_CTRL_H_ +#define _MMP_CTRL_H_ + +#include <video/mmp_disp.h> + +/* ------------< LCD register >------------ */ +struct lcd_regs { +/* TV patch register for MMP2 */ +/* 32 bit TV Video Frame0 Y Starting Address */ +#define LCD_TVD_START_ADDR_Y0 (0x0000) +/* 32 bit TV Video Frame0 U Starting Address */ +#define LCD_TVD_START_ADDR_U0 (0x0004) +/* 32 bit TV Video Frame0 V Starting Address */ +#define LCD_TVD_START_ADDR_V0 (0x0008) +/* 32 bit TV Video Frame0 Command Starting Address */ +#define LCD_TVD_START_ADDR_C0 (0x000C) +/* 32 bit TV Video Frame1 Y Starting Address Register*/ +#define LCD_TVD_START_ADDR_Y1 (0x0010) +/* 32 bit TV Video Frame1 U Starting Address Register*/ +#define LCD_TVD_START_ADDR_U1 (0x0014) +/* 32 bit TV Video Frame1 V Starting Address Register*/ +#define LCD_TVD_START_ADDR_V1 (0x0018) +/* 32 bit TV Video Frame1 Command Starting Address Register*/ +#define LCD_TVD_START_ADDR_C1 (0x001C) +/* 32 bit TV Video Y andC Line Length(Pitch)Register*/ +#define LCD_TVD_PITCH_YC (0x0020) +/* 32 bit TV Video U andV Line Length(Pitch)Register*/ +#define LCD_TVD_PITCH_UV (0x0024) +/* 32 bit TV Video Starting Point on Screen Register*/ +#define LCD_TVD_OVSA_HPXL_VLN (0x0028) +/* 32 bit TV Video Source Size Register*/ +#define LCD_TVD_HPXL_VLN (0x002C) +/* 32 bit TV Video Destination Size (After Zooming)Register*/ +#define LCD_TVDZM_HPXL_VLN (0x0030) + u32 v_y0; + u32 v_u0; + u32 v_v0; + u32 v_c0; + u32 v_y1; + u32 v_u1; + u32 v_v1; + u32 v_c1; + u32 v_pitch_yc; /* Video Y and C Line Length (Pitch) */ + u32 v_pitch_uv; /* Video U and V Line Length (Pitch) */ + u32 v_start; /* Video Starting Point on Screen */ + u32 v_size; /* Video Source Size */ + u32 v_size_z; /* Video Destination Size (After Zooming) */ + +/* 32 bit TV Graphic Frame 0 Starting Address Register*/ +#define LCD_TVG_START_ADDR0 (0x0034) +/* 32 bit TV Graphic Frame 1 Starting Address Register*/ +#define LCD_TVG_START_ADDR1 (0x0038) +/* 32 bit TV Graphic Line Length(Pitch)Register*/ +#define LCD_TVG_PITCH (0x003C) +/* 32 bit TV Graphic Starting Point on Screen Register*/ +#define LCD_TVG_OVSA_HPXL_VLN (0x0040) +/* 32 bit TV Graphic Source Size Register*/ +#define LCD_TVG_HPXL_VLN (0x0044) +/* 32 bit TV Graphic Destination size (after Zooming)Register*/ +#define LCD_TVGZM_HPXL_VLN (0x0048) + u32 g_0; /* Graphic Frame 0/1 Starting Address */ + u32 g_1; + u32 g_pitch; /* Graphic Line Length (Pitch) */ + u32 g_start; /* Graphic Starting Point on Screen */ + u32 g_size; /* Graphic Source Size */ + u32 g_size_z; /* Graphic Destination Size (After Zooming) */ + +/* 32 bit TV Hardware Cursor Starting Point on screen Register*/ +#define LCD_TVC_OVSA_HPXL_VLN (0x004C) +/* 32 bit TV Hardware Cursor Size Register */ +#define LCD_TVC_HPXL_VLN (0x0050) + u32 hc_start; /* Hardware Cursor */ + u32 hc_size; /* Hardware Cursor */ + +/* 32 bit TV Total Screen Size Register*/ +#define LCD_TV_V_H_TOTAL (0x0054) +/* 32 bit TV Screen Active Size Register*/ +#define LCD_TV_V_H_ACTIVE (0x0058) +/* 32 bit TV Screen Horizontal Porch Register*/ +#define LCD_TV_H_PORCH (0x005C) +/* 32 bit TV Screen Vertical Porch Register*/ +#define LCD_TV_V_PORCH (0x0060) + u32 screen_size; /* Screen Total Size */ + u32 screen_active; /* Screen Active Size */ + u32 screen_h_porch; /* Screen Horizontal Porch */ + u32 screen_v_porch; /* Screen Vertical Porch */ + +/* 32 bit TV Screen Blank Color Register*/ +#define LCD_TV_BLANKCOLOR (0x0064) +/* 32 bit TV Hardware Cursor Color1 Register*/ +#define LCD_TV_ALPHA_COLOR1 (0x0068) +/* 32 bit TV Hardware Cursor Color2 Register*/ +#define LCD_TV_ALPHA_COLOR2 (0x006C) + u32 blank_color; /* Screen Blank Color */ + u32 hc_Alpha_color1; /* Hardware Cursor Color1 */ + u32 hc_Alpha_color2; /* Hardware Cursor Color2 */ + +/* 32 bit TV Video Y Color Key Control*/ +#define LCD_TV_COLORKEY_Y (0x0070) +/* 32 bit TV Video U Color Key Control*/ +#define LCD_TV_COLORKEY_U (0x0074) +/* 32 bit TV Video V Color Key Control*/ +#define LCD_TV_COLORKEY_V (0x0078) + u32 v_colorkey_y; /* Video Y Color Key Control */ + u32 v_colorkey_u; /* Video U Color Key Control */ + u32 v_colorkey_v; /* Video V Color Key Control */ + +/* 32 bit TV VSYNC PulsePixel Edge Control Register*/ +#define LCD_TV_SEPXLCNT (0x007C) + u32 vsync_ctrl; /* VSYNC PulsePixel Edge Control */ +}; + +#define intf_ctrl(id) ((id) ? (((id) & 1) ? LCD_TVIF_CTRL : \ + LCD_DUMB2_CTRL) : LCD_SPU_DUMB_CTRL) +#define dma_ctrl0(id) ((id) ? (((id) & 1) ? LCD_TV_CTRL0 : \ + LCD_PN2_CTRL0) : LCD_SPU_DMA_CTRL0) +#define dma_ctrl1(id) ((id) ? (((id) & 1) ? LCD_TV_CTRL1 : \ + LCD_PN2_CTRL1) : LCD_SPU_DMA_CTRL1) +#define dma_ctrl(ctrl1, id) (ctrl1 ? dma_ctrl1(id) : dma_ctrl0(id)) + +/* 32 bit TV Path DMA Control 0*/ +#define LCD_TV_CTRL0 (0x0080) +/* 32 bit TV Path DMA Control 1*/ +#define LCD_TV_CTRL1 (0x0084) +/* 32 bit TV Path Video Contrast*/ +#define LCD_TV_CONTRAST (0x0088) +/* 32 bit TV Path Video Saturation*/ +#define LCD_TV_SATURATION (0x008C) +/* 32 bit TV Path Video Hue Adjust*/ +#define LCD_TV_CBSH_HUE (0x0090) +/* 32 bit TV Path TVIF Control Register */ +#define LCD_TVIF_CTRL (0x0094) +#define TV_VBLNK_VALID_EN (1 << 12) + +/* 32 bit TV Path I/O Pad Control*/ +#define LCD_TVIOPAD_CTRL (0x0098) +/* 32 bit TV Path Cloc Divider */ +#define LCD_TCLK_DIV (0x009C) + +#define LCD_SCLK(path) ((PATH_PN == path->id) ? LCD_CFG_SCLK_DIV :\ + ((PATH_TV == path->id) ? LCD_TCLK_DIV : LCD_PN2_SCLK_DIV)) + +/* dither configure */ +#ifdef CONFIG_CPU_PXA988 +#define LCD_DITHER_CTRL (0x01EC) +#else +#define LCD_DITHER_CTRL (0x00A0) +#endif + +#define DITHER_TBL_INDEX_SEL(s) ((s) << 16) +#define DITHER_MODE2(m) ((m) << 12) +#define DITHER_MODE2_SHIFT (12) +#define DITHER_4X8_EN2 (1 << 9) +#define DITHER_4X8_EN2_SHIFT (9) +#define DITHER_EN2 (1 << 8) +#define DITHER_MODE1(m) ((m) << 4) +#define DITHER_MODE1_SHIFT (4) +#define DITHER_4X8_EN1 (1 << 1) +#define DITHER_4X8_EN1_SHIFT (1) +#define DITHER_EN1 (1) + +/* dither table data was fixed by video bpp of input and output*/ +#ifdef CONFIG_CPU_PXA988 +#define DITHER_TB_4X4_INDEX0 (0x6e4ca280) +#define DITHER_TB_4X4_INDEX1 (0x5d7f91b3) +#define DITHER_TB_4X8_INDEX0 (0xb391a280) +#define DITHER_TB_4X8_INDEX1 (0x7f5d6e4c) +#define DITHER_TB_4X8_INDEX2 (0x80a291b3) +#define DITHER_TB_4X8_INDEX3 (0x4c6e5d7f) +#define LCD_DITHER_TBL_DATA (0x01F0) +#else +#define DITHER_TB_4X4_INDEX0 (0x3b19f7d5) +#define DITHER_TB_4X4_INDEX1 (0x082ac4e6) +#define DITHER_TB_4X8_INDEX0 (0xf7d508e6) +#define DITHER_TB_4X8_INDEX1 (0x3b194c2a) +#define DITHER_TB_4X8_INDEX2 (0xc4e6d5f7) +#define DITHER_TB_4X8_INDEX3 (0x082a193b) +#define LCD_DITHER_TBL_DATA (0x00A4) +#endif + +/* Video Frame 0&1 start address registers */ +#define LCD_SPU_DMA_START_ADDR_Y0 0x00C0 +#define LCD_SPU_DMA_START_ADDR_U0 0x00C4 +#define LCD_SPU_DMA_START_ADDR_V0 0x00C8 +#define LCD_CFG_DMA_START_ADDR_0 0x00CC /* Cmd address */ +#define LCD_SPU_DMA_START_ADDR_Y1 0x00D0 +#define LCD_SPU_DMA_START_ADDR_U1 0x00D4 +#define LCD_SPU_DMA_START_ADDR_V1 0x00D8 +#define LCD_CFG_DMA_START_ADDR_1 0x00DC /* Cmd address */ + +/* YC & UV Pitch */ +#define LCD_SPU_DMA_PITCH_YC 0x00E0 +#define SPU_DMA_PITCH_C(c) ((c)<<16) +#define SPU_DMA_PITCH_Y(y) (y) +#define LCD_SPU_DMA_PITCH_UV 0x00E4 +#define SPU_DMA_PITCH_V(v) ((v)<<16) +#define SPU_DMA_PITCH_U(u) (u) + +/* Video Starting Point on Screen Register */ +#define LCD_SPUT_DMA_OVSA_HPXL_VLN 0x00E8 +#define CFG_DMA_OVSA_VLN(y) ((y)<<16) /* 0~0xfff */ +#define CFG_DMA_OVSA_HPXL(x) (x) /* 0~0xfff */ + +/* Video Size Register */ +#define LCD_SPU_DMA_HPXL_VLN 0x00EC +#define CFG_DMA_VLN(y) ((y)<<16) +#define CFG_DMA_HPXL(x) (x) + +/* Video Size After zooming Register */ +#define LCD_SPU_DZM_HPXL_VLN 0x00F0 +#define CFG_DZM_VLN(y) ((y)<<16) +#define CFG_DZM_HPXL(x) (x) + +/* Graphic Frame 0&1 Starting Address Register */ +#define LCD_CFG_GRA_START_ADDR0 0x00F4 +#define LCD_CFG_GRA_START_ADDR1 0x00F8 + +/* Graphic Frame Pitch */ +#define LCD_CFG_GRA_PITCH 0x00FC + +/* Graphic Starting Point on Screen Register */ +#define LCD_SPU_GRA_OVSA_HPXL_VLN 0x0100 +#define CFG_GRA_OVSA_VLN(y) ((y)<<16) +#define CFG_GRA_OVSA_HPXL(x) (x) + +/* Graphic Size Register */ +#define LCD_SPU_GRA_HPXL_VLN 0x0104 +#define CFG_GRA_VLN(y) ((y)<<16) +#define CFG_GRA_HPXL(x) (x) + +/* Graphic Size after Zooming Register */ +#define LCD_SPU_GZM_HPXL_VLN 0x0108 +#define CFG_GZM_VLN(y) ((y)<<16) +#define CFG_GZM_HPXL(x) (x) + +/* HW Cursor Starting Point on Screen Register */ +#define LCD_SPU_HWC_OVSA_HPXL_VLN 0x010C +#define CFG_HWC_OVSA_VLN(y) ((y)<<16) +#define CFG_HWC_OVSA_HPXL(x) (x) + +/* HW Cursor Size */ +#define LCD_SPU_HWC_HPXL_VLN 0x0110 +#define CFG_HWC_VLN(y) ((y)<<16) +#define CFG_HWC_HPXL(x) (x) + +/* Total Screen Size Register */ +#define LCD_SPUT_V_H_TOTAL 0x0114 +#define CFG_V_TOTAL(y) ((y)<<16) +#define CFG_H_TOTAL(x) (x) + +/* Total Screen Active Size Register */ +#define LCD_SPU_V_H_ACTIVE 0x0118 +#define CFG_V_ACTIVE(y) ((y)<<16) +#define CFG_H_ACTIVE(x) (x) + +/* Screen H&V Porch Register */ +#define LCD_SPU_H_PORCH 0x011C +#define CFG_H_BACK_PORCH(b) ((b)<<16) +#define CFG_H_FRONT_PORCH(f) (f) +#define LCD_SPU_V_PORCH 0x0120 +#define CFG_V_BACK_PORCH(b) ((b)<<16) +#define CFG_V_FRONT_PORCH(f) (f) + +/* Screen Blank Color Register */ +#define LCD_SPU_BLANKCOLOR 0x0124 +#define CFG_BLANKCOLOR_MASK 0x00FFFFFF +#define CFG_BLANKCOLOR_R_MASK 0x000000FF +#define CFG_BLANKCOLOR_G_MASK 0x0000FF00 +#define CFG_BLANKCOLOR_B_MASK 0x00FF0000 + +/* HW Cursor Color 1&2 Register */ +#define LCD_SPU_ALPHA_COLOR1 0x0128 +#define CFG_HWC_COLOR1 0x00FFFFFF +#define CFG_HWC_COLOR1_R(red) ((red)<<16) +#define CFG_HWC_COLOR1_G(green) ((green)<<8) +#define CFG_HWC_COLOR1_B(blue) (blue) +#define CFG_HWC_COLOR1_R_MASK 0x000000FF +#define CFG_HWC_COLOR1_G_MASK 0x0000FF00 +#define CFG_HWC_COLOR1_B_MASK 0x00FF0000 +#define LCD_SPU_ALPHA_COLOR2 0x012C +#define CFG_HWC_COLOR2 0x00FFFFFF +#define CFG_HWC_COLOR2_R_MASK 0x000000FF +#define CFG_HWC_COLOR2_G_MASK 0x0000FF00 +#define CFG_HWC_COLOR2_B_MASK 0x00FF0000 + +/* Video YUV Color Key Control */ +#define LCD_SPU_COLORKEY_Y 0x0130 +#define CFG_CKEY_Y2(y2) ((y2)<<24) +#define CFG_CKEY_Y2_MASK 0xFF000000 +#define CFG_CKEY_Y1(y1) ((y1)<<16) +#define CFG_CKEY_Y1_MASK 0x00FF0000 +#define CFG_CKEY_Y(y) ((y)<<8) +#define CFG_CKEY_Y_MASK 0x0000FF00 +#define CFG_ALPHA_Y(y) (y) +#define CFG_ALPHA_Y_MASK 0x000000FF +#define LCD_SPU_COLORKEY_U 0x0134 +#define CFG_CKEY_U2(u2) ((u2)<<24) +#define CFG_CKEY_U2_MASK 0xFF000000 +#define CFG_CKEY_U1(u1) ((u1)<<16) +#define CFG_CKEY_U1_MASK 0x00FF0000 +#define CFG_CKEY_U(u) ((u)<<8) +#define CFG_CKEY_U_MASK 0x0000FF00 +#define CFG_ALPHA_U(u) (u) +#define CFG_ALPHA_U_MASK 0x000000FF +#define LCD_SPU_COLORKEY_V 0x0138 +#define CFG_CKEY_V2(v2) ((v2)<<24) +#define CFG_CKEY_V2_MASK 0xFF000000 +#define CFG_CKEY_V1(v1) ((v1)<<16) +#define CFG_CKEY_V1_MASK 0x00FF0000 +#define CFG_CKEY_V(v) ((v)<<8) +#define CFG_CKEY_V_MASK 0x0000FF00 +#define CFG_ALPHA_V(v) (v) +#define CFG_ALPHA_V_MASK 0x000000FF + +/* Graphics/Video DMA color key enable bits in LCD_TV_CTRL1 */ +#define CFG_CKEY_GRA 0x2 +#define CFG_CKEY_DMA 0x1 + +/* Interlace mode enable bits in LCD_TV_CTRL1 */ +#define CFG_TV_INTERLACE_EN (1 << 22) +#define CFG_TV_NIB (1 << 0) + +#define LCD_PN_SEPXLCNT 0x013c /* MMP2 */ + +/* SPI Read Data Register */ +#define LCD_SPU_SPI_RXDATA 0x0140 + +/* Smart Panel Read Data Register */ +#define LCD_SPU_ISA_RSDATA 0x0144 +#define ISA_RXDATA_16BIT_1_DATA_MASK 0x000000FF +#define ISA_RXDATA_16BIT_2_DATA_MASK 0x0000FF00 +#define ISA_RXDATA_16BIT_3_DATA_MASK 0x00FF0000 +#define ISA_RXDATA_16BIT_4_DATA_MASK 0xFF000000 +#define ISA_RXDATA_32BIT_1_DATA_MASK 0x00FFFFFF + +#define LCD_SPU_DBG_ISA (0x0148) /* TTC */ +#define LCD_SPU_DMAVLD_YC (0x014C) +#define LCD_SPU_DMAVLD_UV (0x0150) +#define LCD_SPU_DMAVLD_UVSPU_GRAVLD (0x0154) + +#define LCD_READ_IOPAD (0x0148) /* MMP2*/ +#define LCD_DMAVLD_YC (0x014C) +#define LCD_DMAVLD_UV (0x0150) +#define LCD_TVGGRAVLD_HLEN (0x0154) + +/* HWC SRAM Read Data Register */ +#define LCD_SPU_HWC_RDDAT 0x0158 + +/* Gamma Table SRAM Read Data Register */ +#define LCD_SPU_GAMMA_RDDAT 0x015c +#define CFG_GAMMA_RDDAT_MASK 0x000000FF + +/* Palette Table SRAM Read Data Register */ +#define LCD_SPU_PALETTE_RDDAT 0x0160 +#define CFG_PALETTE_RDDAT_MASK 0x00FFFFFF + +#define LCD_SPU_DBG_DMATOP (0x0164) /* TTC */ +#define LCD_SPU_DBG_GRATOP (0x0168) +#define LCD_SPU_DBG_TXCTRL (0x016C) +#define LCD_SPU_DBG_SLVTOP (0x0170) +#define LCD_SPU_DBG_MUXTOP (0x0174) + +#define LCD_SLV_DBG (0x0164) /* MMP2 */ +#define LCD_TVDVLD_YC (0x0168) +#define LCD_TVDVLD_UV (0x016C) +#define LCD_TVC_RDDAT (0x0170) +#define LCD_TV_GAMMA_RDDAT (0x0174) + +/* I/O Pads Input Read Only Register */ +#define LCD_SPU_IOPAD_IN 0x0178 +#define CFG_IOPAD_IN_MASK 0x0FFFFFFF + +#define LCD_TV_PALETTE_RDDAT (0x0178) /* MMP2 */ + +/* Reserved Read Only Registers */ +#define LCD_CFG_RDREG5F 0x017C +#define IRE_FRAME_CNT_MASK 0x000000C0 +#define IPE_FRAME_CNT_MASK 0x00000030 +#define GRA_FRAME_CNT_MASK 0x0000000C /* Graphic */ +#define DMA_FRAME_CNT_MASK 0x00000003 /* Video */ + +#define LCD_FRAME_CNT (0x017C) /* MMP2 */ + +/* SPI Control Register. */ +#define LCD_SPU_SPI_CTRL 0x0180 +#define CFG_SCLKCNT(div) ((div)<<24) /* 0xFF~0x2 */ +#define CFG_SCLKCNT_MASK 0xFF000000 +#define CFG_RXBITS(rx) (((rx) - 1)<<16) /* 0x1F~0x1 */ +#define CFG_RXBITS_MASK 0x00FF0000 +#define CFG_TXBITS(tx) (((tx) - 1)<<8) /* 0x1F~0x1 */ +#define CFG_TXBITS_MASK 0x0000FF00 +#define CFG_CLKINV(clk) ((clk)<<7) +#define CFG_CLKINV_MASK 0x00000080 +#define CFG_KEEPXFER(transfer) ((transfer)<<6) +#define CFG_KEEPXFER_MASK 0x00000040 +#define CFG_RXBITSTO0(rx) ((rx)<<5) +#define CFG_RXBITSTO0_MASK 0x00000020 +#define CFG_TXBITSTO0(tx) ((tx)<<4) +#define CFG_TXBITSTO0_MASK 0x00000010 +#define CFG_SPI_ENA(spi) ((spi)<<3) +#define CFG_SPI_ENA_MASK 0x00000008 +#define CFG_SPI_SEL(spi) ((spi)<<2) +#define CFG_SPI_SEL_MASK 0x00000004 +#define CFG_SPI_3W4WB(wire) ((wire)<<1) +#define CFG_SPI_3W4WB_MASK 0x00000002 +#define CFG_SPI_START(start) (start) +#define CFG_SPI_START_MASK 0x00000001 + +/* SPI Tx Data Register */ +#define LCD_SPU_SPI_TXDATA 0x0184 + +/* + 1. Smart Pannel 8-bit Bus Control Register. + 2. AHB Slave Path Data Port Register +*/ +#define LCD_SPU_SMPN_CTRL 0x0188 + +/* DMA Control 0 Register */ +#define LCD_SPU_DMA_CTRL0 0x0190 +#define CFG_NOBLENDING(nb) ((nb)<<31) +#define CFG_NOBLENDING_MASK 0x80000000 +#define CFG_GAMMA_ENA(gn) ((gn)<<30) +#define CFG_GAMMA_ENA_MASK 0x40000000 +#define CFG_CBSH_ENA(cn) ((cn)<<29) +#define CFG_CBSH_ENA_MASK 0x20000000 +#define CFG_PALETTE_ENA(pn) ((pn)<<28) +#define CFG_PALETTE_ENA_MASK 0x10000000 +#define CFG_ARBFAST_ENA(an) ((an)<<27) +#define CFG_ARBFAST_ENA_MASK 0x08000000 +#define CFG_HWC_1BITMOD(mode) ((mode)<<26) +#define CFG_HWC_1BITMOD_MASK 0x04000000 +#define CFG_HWC_1BITENA(mn) ((mn)<<25) +#define CFG_HWC_1BITENA_MASK 0x02000000 +#define CFG_HWC_ENA(cn) ((cn)<<24) +#define CFG_HWC_ENA_MASK 0x01000000 +#define CFG_DMAFORMAT(dmaformat) ((dmaformat)<<20) +#define CFG_DMAFORMAT_MASK 0x00F00000 +#define CFG_GRAFORMAT(graformat) ((graformat)<<16) +#define CFG_GRAFORMAT_MASK 0x000F0000 +/* for graphic part */ +#define CFG_GRA_FTOGGLE(toggle) ((toggle)<<15) +#define CFG_GRA_FTOGGLE_MASK 0x00008000 +#define CFG_GRA_HSMOOTH(smooth) ((smooth)<<14) +#define CFG_GRA_HSMOOTH_MASK 0x00004000 +#define CFG_GRA_TSTMODE(test) ((test)<<13) +#define CFG_GRA_TSTMODE_MASK 0x00002000 +#define CFG_GRA_SWAPRB(swap) ((swap)<<12) +#define CFG_GRA_SWAPRB_MASK 0x00001000 +#define CFG_GRA_SWAPUV(swap) ((swap)<<11) +#define CFG_GRA_SWAPUV_MASK 0x00000800 +#define CFG_GRA_SWAPYU(swap) ((swap)<<10) +#define CFG_GRA_SWAPYU_MASK 0x00000400 +#define CFG_GRA_SWAP_MASK 0x00001C00 +#define CFG_YUV2RGB_GRA(cvrt) ((cvrt)<<9) +#define CFG_YUV2RGB_GRA_MASK 0x00000200 +#define CFG_GRA_ENA(gra) ((gra)<<8) +#define CFG_GRA_ENA_MASK 0x00000100 +#define dma0_gfx_masks (CFG_GRAFORMAT_MASK | CFG_GRA_FTOGGLE_MASK | \ + CFG_GRA_HSMOOTH_MASK | CFG_GRA_TSTMODE_MASK | CFG_GRA_SWAP_MASK | \ + CFG_YUV2RGB_GRA_MASK | CFG_GRA_ENA_MASK) +/* for video part */ +#define CFG_DMA_FTOGGLE(toggle) ((toggle)<<7) +#define CFG_DMA_FTOGGLE_MASK 0x00000080 +#define CFG_DMA_HSMOOTH(smooth) ((smooth)<<6) +#define CFG_DMA_HSMOOTH_MASK 0x00000040 +#define CFG_DMA_TSTMODE(test) ((test)<<5) +#define CFG_DMA_TSTMODE_MASK 0x00000020 +#define CFG_DMA_SWAPRB(swap) ((swap)<<4) +#define CFG_DMA_SWAPRB_MASK 0x00000010 +#define CFG_DMA_SWAPUV(swap) ((swap)<<3) +#define CFG_DMA_SWAPUV_MASK 0x00000008 +#define CFG_DMA_SWAPYU(swap) ((swap)<<2) +#define CFG_DMA_SWAPYU_MASK 0x00000004 +#define CFG_DMA_SWAP_MASK 0x0000001C +#define CFG_YUV2RGB_DMA(cvrt) ((cvrt)<<1) +#define CFG_YUV2RGB_DMA_MASK 0x00000002 +#define CFG_DMA_ENA(video) (video) +#define CFG_DMA_ENA_MASK 0x00000001 +#define dma0_vid_masks (CFG_DMAFORMAT_MASK | CFG_DMA_FTOGGLE_MASK | \ + CFG_DMA_HSMOOTH_MASK | CFG_DMA_TSTMODE_MASK | CFG_DMA_SWAP_MASK | \ + CFG_YUV2RGB_DMA_MASK | CFG_DMA_ENA_MASK) +#define dma_palette(val) ((val ? 1 : 0) << 28) +#define dma_fmt(vid, val) ((val & 0xf) << ((vid) ? 20 : 16)) +#define dma_swaprb(vid, val) ((val ? 1 : 0) << ((vid) ? 4 : 12)) +#define dma_swapuv(vid, val) ((val ? 1 : 0) << ((vid) ? 3 : 11)) +#define dma_swapyuv(vid, val) ((val ? 1 : 0) << ((vid) ? 2 : 10)) +#define dma_csc(vid, val) ((val ? 1 : 0) << ((vid) ? 1 : 9)) +#define dma_hsmooth(vid, val) ((val ? 1 : 0) << ((vid) ? 6 : 14)) +#define dma_mask(vid) (dma_palette(1) | dma_fmt(vid, 0xf) | dma_csc(vid, 1) \ + | dma_swaprb(vid, 1) | dma_swapuv(vid, 1) | dma_swapyuv(vid, 1)) + +/* DMA Control 1 Register */ +#define LCD_SPU_DMA_CTRL1 0x0194 +#define CFG_FRAME_TRIG(trig) ((trig)<<31) +#define CFG_FRAME_TRIG_MASK 0x80000000 +#define CFG_VSYNC_TRIG(trig) ((trig)<<28) +#define CFG_VSYNC_TRIG_MASK 0x70000000 +#define CFG_VSYNC_INV(inv) ((inv)<<27) +#define CFG_VSYNC_INV_MASK 0x08000000 +#define CFG_COLOR_KEY_MODE(cmode) ((cmode)<<24) +#define CFG_COLOR_KEY_MASK 0x07000000 +#define CFG_CARRY(carry) ((carry)<<23) +#define CFG_CARRY_MASK 0x00800000 +#define CFG_LNBUF_ENA(lnbuf) ((lnbuf)<<22) +#define CFG_LNBUF_ENA_MASK 0x00400000 +#define CFG_GATED_ENA(gated) ((gated)<<21) +#define CFG_GATED_ENA_MASK 0x00200000 +#define CFG_PWRDN_ENA(power) ((power)<<20) +#define CFG_PWRDN_ENA_MASK 0x00100000 +#define CFG_DSCALE(dscale) ((dscale)<<18) +#define CFG_DSCALE_MASK 0x000C0000 +#define CFG_ALPHA_MODE(amode) ((amode)<<16) +#define CFG_ALPHA_MODE_MASK 0x00030000 +#define CFG_ALPHA(alpha) ((alpha)<<8) +#define CFG_ALPHA_MASK 0x0000FF00 +#define CFG_PXLCMD(pxlcmd) (pxlcmd) +#define CFG_PXLCMD_MASK 0x000000FF + +/* SRAM Control Register */ +#define LCD_SPU_SRAM_CTRL 0x0198 +#define CFG_SRAM_INIT_WR_RD(mode) ((mode)<<14) +#define CFG_SRAM_INIT_WR_RD_MASK 0x0000C000 +#define CFG_SRAM_ADDR_LCDID(id) ((id)<<8) +#define CFG_SRAM_ADDR_LCDID_MASK 0x00000F00 +#define CFG_SRAM_ADDR(addr) (addr) +#define CFG_SRAM_ADDR_MASK 0x000000FF + +/* SRAM Write Data Register */ +#define LCD_SPU_SRAM_WRDAT 0x019C + +/* SRAM RTC/WTC Control Register */ +#define LCD_SPU_SRAM_PARA0 0x01A0 + +/* SRAM Power Down Control Register */ +#define LCD_SPU_SRAM_PARA1 0x01A4 +#define CFG_CSB_256x32(hwc) ((hwc)<<15) /* HWC */ +#define CFG_CSB_256x32_MASK 0x00008000 +#define CFG_CSB_256x24(palette) ((palette)<<14) /* Palette */ +#define CFG_CSB_256x24_MASK 0x00004000 +#define CFG_CSB_256x8(gamma) ((gamma)<<13) /* Gamma */ +#define CFG_CSB_256x8_MASK 0x00002000 +#define CFG_PDWN256x32(pdwn) ((pdwn)<<7) /* HWC */ +#define CFG_PDWN256x32_MASK 0x00000080 +#define CFG_PDWN256x24(pdwn) ((pdwn)<<6) /* Palette */ +#define CFG_PDWN256x24_MASK 0x00000040 +#define CFG_PDWN256x8(pdwn) ((pdwn)<<5) /* Gamma */ +#define CFG_PDWN256x8_MASK 0x00000020 +#define CFG_PDWN32x32(pdwn) ((pdwn)<<3) +#define CFG_PDWN32x32_MASK 0x00000008 +#define CFG_PDWN16x66(pdwn) ((pdwn)<<2) +#define CFG_PDWN16x66_MASK 0x00000004 +#define CFG_PDWN32x66(pdwn) ((pdwn)<<1) +#define CFG_PDWN32x66_MASK 0x00000002 +#define CFG_PDWN64x66(pdwn) (pdwn) +#define CFG_PDWN64x66_MASK 0x00000001 + +/* Smart or Dumb Panel Clock Divider */ +#define LCD_CFG_SCLK_DIV 0x01A8 +#define SCLK_SRC_SEL(src) ((src)<<31) +#define SCLK_SRC_SEL_MASK 0x80000000 +#define SCLK_DISABLE (1<<28) +#define CLK_FRACDIV(frac) ((frac)<<16) +#define CLK_FRACDIV_MASK 0x0FFF0000 +#define DSI1_BITCLK_DIV(div) (div<<8) +#define DSI1_BITCLK_DIV_MASK 0x00000F00 +#define CLK_INT_DIV(div) (div) +#define CLK_INT_DIV_MASK 0x000000FF + +/* Video Contrast Register */ +#define LCD_SPU_CONTRAST 0x01AC +#define CFG_BRIGHTNESS(bright) ((bright)<<16) +#define CFG_BRIGHTNESS_MASK 0xFFFF0000 +#define CFG_CONTRAST(contrast) (contrast) +#define CFG_CONTRAST_MASK 0x0000FFFF + +/* Video Saturation Register */ +#define LCD_SPU_SATURATION 0x01B0 +#define CFG_C_MULTS(mult) ((mult)<<16) +#define CFG_C_MULTS_MASK 0xFFFF0000 +#define CFG_SATURATION(sat) (sat) +#define CFG_SATURATION_MASK 0x0000FFFF + +/* Video Hue Adjust Register */ +#define LCD_SPU_CBSH_HUE 0x01B4 +#define CFG_SIN0(sin0) ((sin0)<<16) +#define CFG_SIN0_MASK 0xFFFF0000 +#define CFG_COS0(con0) (con0) +#define CFG_COS0_MASK 0x0000FFFF + +/* Dump LCD Panel Control Register */ +#define LCD_SPU_DUMB_CTRL 0x01B8 +#define CFG_DUMBMODE(mode) ((mode)<<28) +#define CFG_DUMBMODE_MASK 0xF0000000 +#define CFG_LCDGPIO_O(data) ((data)<<20) +#define CFG_LCDGPIO_O_MASK 0x0FF00000 +#define CFG_LCDGPIO_ENA(gpio) ((gpio)<<12) +#define CFG_LCDGPIO_ENA_MASK 0x000FF000 +#define CFG_BIAS_OUT(bias) ((bias)<<8) +#define CFG_BIAS_OUT_MASK 0x00000100 +#define CFG_REVERSE_RGB(RGB) ((RGB)<<7) +#define CFG_REVERSE_RGB_MASK 0x00000080 +#define CFG_INV_COMPBLANK(blank) ((blank)<<6) +#define CFG_INV_COMPBLANK_MASK 0x00000040 +#define CFG_INV_COMPSYNC(sync) ((sync)<<5) +#define CFG_INV_COMPSYNC_MASK 0x00000020 +#define CFG_INV_HENA(hena) ((hena)<<4) +#define CFG_INV_HENA_MASK 0x00000010 +#define CFG_INV_VSYNC(vsync) ((vsync)<<3) +#define CFG_INV_VSYNC_MASK 0x00000008 +#define CFG_INV_HSYNC(hsync) ((hsync)<<2) +#define CFG_INV_HSYNC_MASK 0x00000004 +#define CFG_INV_PCLK(pclk) ((pclk)<<1) +#define CFG_INV_PCLK_MASK 0x00000002 +#define CFG_DUMB_ENA(dumb) (dumb) +#define CFG_DUMB_ENA_MASK 0x00000001 + +/* LCD I/O Pads Control Register */ +#define SPU_IOPAD_CONTROL 0x01BC +#define CFG_GRA_VM_ENA(vm) ((vm)<<15) +#define CFG_GRA_VM_ENA_MASK 0x00008000 +#define CFG_DMA_VM_ENA(vm) ((vm)<<13) +#define CFG_DMA_VM_ENA_MASK 0x00002000 +#define CFG_CMD_VM_ENA(vm) ((vm)<<12) +#define CFG_CMD_VM_ENA_MASK 0x00001000 +#define CFG_CSC(csc) ((csc)<<8) +#define CFG_CSC_MASK 0x00000300 +#define CFG_BOUNDARY(size) ((size)<<5) +#define CFG_BOUNDARY_MASK 0x00000020 +#define CFG_BURST(len) ((len)<<4) +#define CFG_BURST_MASK 0x00000010 +#define CFG_IOPADMODE(iopad) (iopad) +#define CFG_IOPADMODE_MASK 0x0000000F + +/* LCD Interrupt Control Register */ +#define SPU_IRQ_ENA 0x01C0 +#define DMA_FRAME_IRQ0_ENA(irq) ((irq)<<31) +#define DMA_FRAME_IRQ0_ENA_MASK 0x80000000 +#define DMA_FRAME_IRQ1_ENA(irq) ((irq)<<30) +#define DMA_FRAME_IRQ1_ENA_MASK 0x40000000 +#define DMA_FF_UNDERFLOW_ENA(ff) ((ff)<<29) +#define DMA_FF_UNDERFLOW_ENA_MASK 0x20000000 +#define AXI_BUS_ERROR_IRQ_ENA(irq) ((irq)<<28) +#define AXI_BUS_ERROR_IRQ_ENA_MASK 0x10000000 +#define GRA_FRAME_IRQ0_ENA(irq) ((irq)<<27) +#define GRA_FRAME_IRQ0_ENA_MASK 0x08000000 +#define GRA_FRAME_IRQ1_ENA(irq) ((irq)<<26) +#define GRA_FRAME_IRQ1_ENA_MASK 0x04000000 +#define GRA_FF_UNDERFLOW_ENA(ff) ((ff)<<25) +#define GRA_FF_UNDERFLOW_ENA_MASK 0x02000000 +#define VSYNC_IRQ_ENA(vsync_irq) ((vsync_irq)<<23) +#define VSYNC_IRQ_ENA_MASK 0x00800000 +#define DUMB_FRAMEDONE_ENA(fdone) ((fdone)<<22) +#define DUMB_FRAMEDONE_ENA_MASK 0x00400000 +#define TWC_FRAMEDONE_ENA(fdone) ((fdone)<<21) +#define TWC_FRAMEDONE_ENA_MASK 0x00200000 +#define HWC_FRAMEDONE_ENA(fdone) ((fdone)<<20) +#define HWC_FRAMEDONE_ENA_MASK 0x00100000 +#define SLV_IRQ_ENA(irq) ((irq)<<19) +#define SLV_IRQ_ENA_MASK 0x00080000 +#define SPI_IRQ_ENA(irq) ((irq)<<18) +#define SPI_IRQ_ENA_MASK 0x00040000 +#define PWRDN_IRQ_ENA(irq) ((irq)<<17) +#define PWRDN_IRQ_ENA_MASK 0x00020000 +#define AXI_LATENCY_TOO_LONG_IRQ_ENA(irq) ((irq)<<16) +#define AXI_LATENCY_TOO_LONG_IRQ_ENA_MASK 0x00010000 +#define CLEAN_SPU_IRQ_ISR(irq) (irq) +#define CLEAN_SPU_IRQ_ISR_MASK 0x0000FFFF +#define TV_DMA_FRAME_IRQ0_ENA(irq) ((irq)<<15) +#define TV_DMA_FRAME_IRQ0_ENA_MASK 0x00008000 +#define TV_DMA_FRAME_IRQ1_ENA(irq) ((irq)<<14) +#define TV_DMA_FRAME_IRQ1_ENA_MASK 0x00004000 +#define TV_DMA_FF_UNDERFLOW_ENA(unerrun) ((unerrun)<<13) +#define TV_DMA_FF_UNDERFLOW_ENA_MASK 0x00002000 +#define TVSYNC_IRQ_ENA(irq) ((irq)<<12) +#define TVSYNC_IRQ_ENA_MASK 0x00001000 +#define TV_FRAME_IRQ0_ENA(irq) ((irq)<<11) +#define TV_FRAME_IRQ0_ENA_MASK 0x00000800 +#define TV_FRAME_IRQ1_ENA(irq) ((irq)<<10) +#define TV_FRAME_IRQ1_ENA_MASK 0x00000400 +#define TV_GRA_FF_UNDERFLOW_ENA(unerrun) ((unerrun)<<9) +#define TV_GRA_FF_UNDERFLOW_ENA_MASK 0x00000200 +#define TV_FRAMEDONE_ENA(irq) ((irq)<<8) +#define TV_FRAMEDONE_ENA_MASK 0x00000100 + +/* FIXME - JUST GUESS */ +#define PN2_DMA_FRAME_IRQ0_ENA(irq) ((irq)<<7) +#define PN2_DMA_FRAME_IRQ0_ENA_MASK 0x00000080 +#define PN2_DMA_FRAME_IRQ1_ENA(irq) ((irq)<<6) +#define PN2_DMA_FRAME_IRQ1_ENA_MASK 0x00000040 +#define PN2_DMA_FF_UNDERFLOW_ENA(ff) ((ff)<<5) +#define PN2_DMA_FF_UNDERFLOW_ENA_MASK 0x00000020 +#define PN2_GRA_FRAME_IRQ0_ENA(irq) ((irq)<<3) +#define PN2_GRA_FRAME_IRQ0_ENA_MASK 0x00000008 +#define PN2_GRA_FRAME_IRQ1_ENA(irq) ((irq)<<2) +#define PN2_GRA_FRAME_IRQ1_ENA_MASK 0x04000004 +#define PN2_GRA_FF_UNDERFLOW_ENA(ff) ((ff)<<1) +#define PN2_GRA_FF_UNDERFLOW_ENA_MASK 0x00000002 +#define PN2_VSYNC_IRQ_ENA(irq) ((irq)<<0) +#define PN2_SYNC_IRQ_ENA_MASK 0x00000001 + +#define gf0_imask(id) ((id) ? (((id) & 1) ? TV_FRAME_IRQ0_ENA_MASK \ + : PN2_GRA_FRAME_IRQ0_ENA_MASK) : GRA_FRAME_IRQ0_ENA_MASK) +#define gf1_imask(id) ((id) ? (((id) & 1) ? TV_FRAME_IRQ1_ENA_MASK \ + : PN2_GRA_FRAME_IRQ1_ENA_MASK) : GRA_FRAME_IRQ1_ENA_MASK) +#define vsync_imask(id) ((id) ? (((id) & 1) ? TVSYNC_IRQ_ENA_MASK \ + : PN2_SYNC_IRQ_ENA_MASK) : VSYNC_IRQ_ENA_MASK) +#define vsync_imasks (vsync_imask(0) | vsync_imask(1)) + +#define display_done_imask(id) ((id) ? (((id) & 1) ? TV_FRAMEDONE_ENA_MASK\ + : (PN2_DMA_FRAME_IRQ0_ENA_MASK | PN2_DMA_FRAME_IRQ1_ENA_MASK))\ + : DUMB_FRAMEDONE_ENA_MASK) + +#define display_done_imasks (display_done_imask(0) | display_done_imask(1)) + +#define vf0_imask(id) ((id) ? (((id) & 1) ? TV_DMA_FRAME_IRQ0_ENA_MASK \ + : PN2_DMA_FRAME_IRQ0_ENA_MASK) : DMA_FRAME_IRQ0_ENA_MASK) +#define vf1_imask(id) ((id) ? (((id) & 1) ? TV_DMA_FRAME_IRQ1_ENA_MASK \ + : PN2_DMA_FRAME_IRQ1_ENA_MASK) : DMA_FRAME_IRQ1_ENA_MASK) + +#define gfx_imasks (gf0_imask(0) | gf1_imask(0) | gf0_imask(1) | \ + gf1_imask(1)) +#define vid_imasks (vf0_imask(0) | vf1_imask(0) | vf0_imask(1) | \ + vf1_imask(1)) +#define vid_imask(id) (display_done_imask(id)) + +#define pn1_imasks (gf0_imask(0) | gf1_imask(0) | vsync_imask(0) | \ + display_done_imask(0) | vf0_imask(0) | vf1_imask(0)) +#define tv_imasks (gf0_imask(1) | gf1_imask(1) | vsync_imask(1) | \ + display_done_imask(1) | vf0_imask(1) | vf1_imask(1)) +#define path_imasks(id) ((id) ? (tv_imasks) : (pn1_imasks)) + +/* error indications */ +#define vid_udflow_imask(id) ((id) ? (((id) & 1) ? \ + (TV_DMA_FF_UNDERFLOW_ENA_MASK) : (PN2_DMA_FF_UNDERFLOW_ENA_MASK)) : \ + (DMA_FF_UNDERFLOW_ENA_MASK)) +#define gfx_udflow_imask(id) ((id) ? (((id) & 1) ? \ + (TV_GRA_FF_UNDERFLOW_ENA_MASK) : (PN2_GRA_FF_UNDERFLOW_ENA_MASK)) : \ + (GRA_FF_UNDERFLOW_ENA_MASK)) + +#define err_imask(id) (vid_udflow_imask(id) | gfx_udflow_imask(id) | \ + AXI_BUS_ERROR_IRQ_ENA_MASK | AXI_LATENCY_TOO_LONG_IRQ_ENA_MASK) +#define err_imasks (err_imask(0) | err_imask(1) | err_imask(2)) +/* LCD Interrupt Status Register */ +#define SPU_IRQ_ISR 0x01C4 +#define DMA_FRAME_IRQ0(irq) ((irq)<<31) +#define DMA_FRAME_IRQ0_MASK 0x80000000 +#define DMA_FRAME_IRQ1(irq) ((irq)<<30) +#define DMA_FRAME_IRQ1_MASK 0x40000000 +#define DMA_FF_UNDERFLOW(ff) ((ff)<<29) +#define DMA_FF_UNDERFLOW_MASK 0x20000000 +#define AXI_BUS_ERROR_IRQ(irq) ((irq)<<28) +#define AXI_BUS_ERROR_IRQ_MASK 0x10000000 +#define GRA_FRAME_IRQ0(irq) ((irq)<<27) +#define GRA_FRAME_IRQ0_MASK 0x08000000 +#define GRA_FRAME_IRQ1(irq) ((irq)<<26) +#define GRA_FRAME_IRQ1_MASK 0x04000000 +#define GRA_FF_UNDERFLOW(ff) ((ff)<<25) +#define GRA_FF_UNDERFLOW_MASK 0x02000000 +#define VSYNC_IRQ(vsync_irq) ((vsync_irq)<<23) +#define VSYNC_IRQ_MASK 0x00800000 +#define DUMB_FRAMEDONE(fdone) ((fdone)<<22) +#define DUMB_FRAMEDONE_MASK 0x00400000 +#define TWC_FRAMEDONE(fdone) ((fdone)<<21) +#define TWC_FRAMEDONE_MASK 0x00200000 +#define HWC_FRAMEDONE(fdone) ((fdone)<<20) +#define HWC_FRAMEDONE_MASK 0x00100000 +#define SLV_IRQ(irq) ((irq)<<19) +#define SLV_IRQ_MASK 0x00080000 +#define SPI_IRQ(irq) ((irq)<<18) +#define SPI_IRQ_MASK 0x00040000 +#define PWRDN_IRQ(irq) ((irq)<<17) +#define PWRDN_IRQ_MASK 0x00020000 +#define AXI_LATENCY_TOO_LONGR_IRQ(irq) ((irq)<<16) +#define AXI_LATENCY_TOO_LONGR_IRQ_MASK 0x00010000 +#define TV_DMA_FRAME_IRQ0(irq) ((irq)<<15) +#define TV_DMA_FRAME_IRQ0_MASK 0x00008000 +#define TV_DMA_FRAME_IRQ1(irq) ((irq)<<14) +#define TV_DMA_FRAME_IRQ1_MASK 0x00004000 +#define TV_DMA_FF_UNDERFLOW(unerrun) ((unerrun)<<13) +#define TV_DMA_FF_UNDERFLOW_MASK 0x00002000 +#define TVSYNC_IRQ(irq) ((irq)<<12) +#define TVSYNC_IRQ_MASK 0x00001000 +#define TV_FRAME_IRQ0(irq) ((irq)<<11) +#define TV_FRAME_IRQ0_MASK 0x00000800 +#define TV_FRAME_IRQ1(irq) ((irq)<<10) +#define TV_FRAME_IRQ1_MASK 0x00000400 +#define TV_GRA_FF_UNDERFLOW(unerrun) ((unerrun)<<9) +#define TV_GRA_FF_UNDERFLOW_MASK 0x00000200 +#define PN2_DMA_FRAME_IRQ0(irq) ((irq)<<7) +#define PN2_DMA_FRAME_IRQ0_MASK 0x00000080 +#define PN2_DMA_FRAME_IRQ1(irq) ((irq)<<6) +#define PN2_DMA_FRAME_IRQ1_MASK 0x00000040 +#define PN2_DMA_FF_UNDERFLOW(ff) ((ff)<<5) +#define PN2_DMA_FF_UNDERFLOW_MASK 0x00000020 +#define PN2_GRA_FRAME_IRQ0(irq) ((irq)<<3) +#define PN2_GRA_FRAME_IRQ0_MASK 0x00000008 +#define PN2_GRA_FRAME_IRQ1(irq) ((irq)<<2) +#define PN2_GRA_FRAME_IRQ1_MASK 0x04000004 +#define PN2_GRA_FF_UNDERFLOW(ff) ((ff)<<1) +#define PN2_GRA_FF_UNDERFLOW_MASK 0x00000002 +#define PN2_VSYNC_IRQ(irq) ((irq)<<0) +#define PN2_SYNC_IRQ_MASK 0x00000001 + +/* LCD FIFO Depth register */ +#define LCD_FIFO_DEPTH 0x01c8 +#define VIDEO_FIFO(fi) ((fi) << 0) +#define VIDEO_FIFO_MASK 0x00000003 +#define GRAPHIC_FIFO(fi) ((fi) << 2) +#define GRAPHIC_FIFO_MASK 0x0000000c + +/* read-only */ +#define DMA_FRAME_IRQ0_LEVEL_MASK 0x00008000 +#define DMA_FRAME_IRQ1_LEVEL_MASK 0x00004000 +#define DMA_FRAME_CNT_ISR_MASK 0x00003000 +#define GRA_FRAME_IRQ0_LEVEL_MASK 0x00000800 +#define GRA_FRAME_IRQ1_LEVEL_MASK 0x00000400 +#define GRA_FRAME_CNT_ISR_MASK 0x00000300 +#define VSYNC_IRQ_LEVEL_MASK 0x00000080 +#define DUMB_FRAMEDONE_LEVEL_MASK 0x00000040 +#define TWC_FRAMEDONE_LEVEL_MASK 0x00000020 +#define HWC_FRAMEDONE_LEVEL_MASK 0x00000010 +#define SLV_FF_EMPTY_MASK 0x00000008 +#define DMA_FF_ALLEMPTY_MASK 0x00000004 +#define GRA_FF_ALLEMPTY_MASK 0x00000002 +#define PWRDN_IRQ_LEVEL_MASK 0x00000001 + +/* 32 bit LCD Interrupt Reset Status*/ +#define SPU_IRQ_RSR (0x01C8) +/* 32 bit Panel Path Graphic Partial Display Horizontal Control Register*/ +#define LCD_GRA_CUTHPXL (0x01CC) +/* 32 bit Panel Path Graphic Partial Display Vertical Control Register*/ +#define LCD_GRA_CUTVLN (0x01D0) +/* 32 bit TV Path Graphic Partial Display Horizontal Control Register*/ +#define LCD_TVG_CUTHPXL (0x01D4) +/* 32 bit TV Path Graphic Partial Display Vertical Control Register*/ +#define LCD_TVG_CUTVLN (0x01D8) +/* 32 bit LCD Global Control Register*/ +#define LCD_TOP_CTRL (0x01DC) +/* 32 bit LCD SQU Line Buffer Control Register 1*/ +#define LCD_SQULN1_CTRL (0x01E0) +/* 32 bit LCD SQU Line Buffer Control Register 2*/ +#define LCD_SQULN2_CTRL (0x01E4) +#define squln_ctrl(id) ((id) ? (((id) & 1) ? LCD_SQULN2_CTRL : \ + LCD_PN2_SQULN1_CTRL) : LCD_SQULN1_CTRL) + +/* 32 bit LCD Mixed Overlay Control Register */ +#define LCD_AFA_ALL2ONE (0x01E8) + +#define LCD_PN2_SCLK_DIV (0x01EC) +#define LCD_PN2_TCLK_DIV (0x01F0) +#define LCD_LVDS_SCLK_DIV_WR (0x01F4) +#define LCD_LVDS_SCLK_DIV_RD (0x01FC) +#define PN2_LCD_DMA_START_ADDR_Y0 (0x0200) +#define PN2_LCD_DMA_START_ADDR_U0 (0x0204) +#define PN2_LCD_DMA_START_ADDR_V0 (0x0208) +#define PN2_LCD_DMA_START_ADDR_C0 (0x020C) +#define PN2_LCD_DMA_START_ADDR_Y1 (0x0210) +#define PN2_LCD_DMA_START_ADDR_U1 (0x0214) +#define PN2_LCD_DMA_START_ADDR_V1 (0x0218) +#define PN2_LCD_DMA_START_ADDR_C1 (0x021C) +#define PN2_LCD_DMA_PITCH_YC (0x0220) +#define PN2_LCD_DMA_PITCH_UV (0x0224) +#define PN2_LCD_DMA_OVSA_HPXL_VLN (0x0228) +#define PN2_LCD_DMA_HPXL_VLN (0x022C) +#define PN2_LCD_DMAZM_HPXL_VLN (0x0230) +#define PN2_LCD_GRA_START_ADDR0 (0x0234) +#define PN2_LCD_GRA_START_ADDR1 (0x0238) +#define PN2_LCD_GRA_PITCH (0x023C) +#define PN2_LCD_GRA_OVSA_HPXL_VLN (0x0240) +#define PN2_LCD_GRA_HPXL_VLN (0x0244) +#define PN2_LCD_GRAZM_HPXL_VLN (0x0248) +#define PN2_LCD_HWC_OVSA_HPXL_VLN (0x024C) +#define PN2_LCD_HWC_HPXL_VLN (0x0250) +#define LCD_PN2_V_H_TOTAL (0x0254) +#define LCD_PN2_V_H_ACTIVE (0x0258) +#define LCD_PN2_H_PORCH (0x025C) +#define LCD_PN2_V_PORCH (0x0260) +#define LCD_PN2_BLANKCOLOR (0x0264) +#define LCD_PN2_ALPHA_COLOR1 (0x0268) +#define LCD_PN2_ALPHA_COLOR2 (0x026C) +#define LCD_PN2_COLORKEY_Y (0x0270) +#define LCD_PN2_COLORKEY_U (0x0274) +#define LCD_PN2_COLORKEY_V (0x0278) +#define LCD_PN2_SEPXLCNT (0x027C) +#define LCD_TV_V_H_TOTAL_FLD (0x0280) +#define LCD_TV_V_PORCH_FLD (0x0284) +#define LCD_TV_SEPXLCNT_FLD (0x0288) + +#define LCD_2ND_ALPHA (0x0294) +#define LCD_PN2_CONTRAST (0x0298) +#define LCD_PN2_SATURATION (0x029c) +#define LCD_PN2_CBSH_HUE (0x02a0) +#define LCD_TIMING_EXT (0x02C0) +#define LCD_PN2_LAYER_ALPHA_SEL1 (0x02c4) +#define LCD_PN2_CTRL0 (0x02C8) +#define TV_LAYER_ALPHA_SEL1 (0x02cc) +#define LCD_SMPN2_CTRL (0x02D0) +#define LCD_IO_OVERL_MAP_CTRL (0x02D4) +#define LCD_DUMB2_CTRL (0x02d8) +#define LCD_PN2_CTRL1 (0x02DC) +#define PN2_IOPAD_CONTROL (0x02E0) +#define LCD_PN2_SQULN1_CTRL (0x02E4) +#define PN2_LCD_GRA_CUTHPXL (0x02e8) +#define PN2_LCD_GRA_CUTVLN (0x02ec) +#define LCD_PN2_SQULN2_CTRL (0x02F0) +#define ALL_LAYER_ALPHA_SEL (0x02F4) + +/* pxa988 has different MASTER_CTRL from MMP3/MMP2 */ +#ifdef CONFIG_CPU_PXA988 +#define TIMING_MASTER_CONTROL (0x01F4) +#define MASTER_ENH(id) (1 << ((id) + 5)) +#define MASTER_ENV(id) (1 << ((id) + 6)) +#else +#define TIMING_MASTER_CONTROL (0x02F8) +#define MASTER_ENH(id) (1 << (id)) +#define MASTER_ENV(id) (1 << ((id) + 4)) +#endif + +#define DSI_START_SEL_SHIFT(id) (((id) << 1) + 8) +#define timing_master_config(path, dsi_id, lcd_id) \ + (MASTER_ENH(path) | MASTER_ENV(path) | \ + (((lcd_id) + ((dsi_id) << 1)) << DSI_START_SEL_SHIFT(path))) + +#define LCD_2ND_BLD_CTL (0x02Fc) +#define LVDS_SRC_MASK (3 << 30) +#define LVDS_SRC_SHIFT (30) +#define LVDS_FMT_MASK (1 << 28) +#define LVDS_FMT_SHIFT (28) + +#define CLK_SCLK (1 << 0) +#define CLK_LVDS_RD (1 << 1) +#define CLK_LVDS_WR (1 << 2) + +#define gra_partdisp_ctrl_hor(id) ((id) ? (((id) & 1) ? \ + LCD_TVG_CUTHPXL : PN2_LCD_GRA_CUTHPXL) : LCD_GRA_CUTHPXL) +#define gra_partdisp_ctrl_ver(id) ((id) ? (((id) & 1) ? \ + LCD_TVG_CUTVLN : PN2_LCD_GRA_CUTVLN) : LCD_GRA_CUTVLN) + +/* + * defined Video Memory Color format for DMA control 0 register + * DMA0 bit[23:20] + */ +#define VMODE_RGB565 0x0 +#define VMODE_RGB1555 0x1 +#define VMODE_RGB888PACKED 0x2 +#define VMODE_RGB888UNPACKED 0x3 +#define VMODE_RGBA888 0x4 +#define VMODE_YUV422PACKED 0x5 +#define VMODE_YUV422PLANAR 0x6 +#define VMODE_YUV420PLANAR 0x7 +#define VMODE_SMPNCMD 0x8 +#define VMODE_PALETTE4BIT 0x9 +#define VMODE_PALETTE8BIT 0xa +#define VMODE_RESERVED 0xb + +/* + * defined Graphic Memory Color format for DMA control 0 register + * DMA0 bit[19:16] + */ +#define GMODE_RGB565 0x0 +#define GMODE_RGB1555 0x1 +#define GMODE_RGB888PACKED 0x2 +#define GMODE_RGB888UNPACKED 0x3 +#define GMODE_RGBA888 0x4 +#define GMODE_YUV422PACKED 0x5 +#define GMODE_YUV422PLANAR 0x6 +#define GMODE_YUV420PLANAR 0x7 +#define GMODE_SMPNCMD 0x8 +#define GMODE_PALETTE4BIT 0x9 +#define GMODE_PALETTE8BIT 0xa +#define GMODE_RESERVED 0xb + +/* + * define for DMA control 1 register + */ +#define DMA1_FRAME_TRIG 31 /* bit location */ +#define DMA1_VSYNC_MODE 28 +#define DMA1_VSYNC_INV 27 +#define DMA1_CKEY 24 +#define DMA1_CARRY 23 +#define DMA1_LNBUF_ENA 22 +#define DMA1_GATED_ENA 21 +#define DMA1_PWRDN_ENA 20 +#define DMA1_DSCALE 18 +#define DMA1_ALPHA_MODE 16 +#define DMA1_ALPHA 08 +#define DMA1_PXLCMD 00 + +/* + * defined for Configure Dumb Mode + * DUMB LCD Panel bit[31:28] + */ +#define DUMB16_RGB565_0 0x0 +#define DUMB16_RGB565_1 0x1 +#define DUMB18_RGB666_0 0x2 +#define DUMB18_RGB666_1 0x3 +#define DUMB12_RGB444_0 0x4 +#define DUMB12_RGB444_1 0x5 +#define DUMB24_RGB888_0 0x6 +#define DUMB_BLANK 0x7 + +/* + * defined for Configure I/O Pin Allocation Mode + * LCD LCD I/O Pads control register bit[3:0] + */ +#define IOPAD_DUMB24 0x0 +#define IOPAD_DUMB18SPI 0x1 +#define IOPAD_DUMB18GPIO 0x2 +#define IOPAD_DUMB16SPI 0x3 +#define IOPAD_DUMB16GPIO 0x4 +#define IOPAD_DUMB12 0x5 +#define IOPAD_SMART18SPI 0x6 +#define IOPAD_SMART16SPI 0x7 +#define IOPAD_SMART8BOTH 0x8 +#define IOPAD_DUMB18_SMART8 0x9 +#define IOPAD_DUMB16_SMART8SPI 0xa +#define IOPAD_DUMB16_SMART8GPIO 0xb +#define IOPAD_DUMB16_DUMB16 0xc +#define IOPAD_SMART8_SMART8 0xc + +/* + *defined for indicating boundary and cycle burst length + */ +#define CFG_BOUNDARY_1KB (1<<5) +#define CFG_BOUNDARY_4KB (0<<5) +#define CFG_CYC_BURST_LEN16 (1<<4) +#define CFG_CYC_BURST_LEN8 (0<<4) + +/* + * defined Dumb Panel Clock Divider register + * SCLK_Source bit[31] + */ + /* 0: PLL clock select*/ +#define AXI_BUS_SEL 0x80000000 +#define CCD_CLK_SEL 0x40000000 +#define DCON_CLK_SEL 0x20000000 +#define ENA_CLK_INT_DIV CONFIG_FB_DOVE_CLCD_SCLK_DIV +#define IDLE_CLK_INT_DIV 0x1 /* idle Integer Divider */ +#define DIS_CLK_INT_DIV 0x0 /* Disable Integer Divider */ + +/* SRAM ID */ +#define SRAMID_GAMMA_YR 0x0 +#define SRAMID_GAMMA_UG 0x1 +#define SRAMID_GAMMA_VB 0x2 +#define SRAMID_PALATTE 0x3 +#define SRAMID_HWC 0xf + +/* SRAM INIT Read/Write */ +#define SRAMID_INIT_READ 0x0 +#define SRAMID_INIT_WRITE 0x2 +#define SRAMID_INIT_DEFAULT 0x3 + +/* + * defined VSYNC selection mode for DMA control 1 register + * DMA1 bit[30:28] + */ +#define VMODE_SMPN 0x0 +#define VMODE_SMPNIRQ 0x1 +#define VMODE_DUMB 0x2 +#define VMODE_IPE 0x3 +#define VMODE_IRE 0x4 + +/* + * defined Configure Alpha and Alpha mode for DMA control 1 register + * DMA1 bit[15:08](alpha) / bit[17:16](alpha mode) + */ +/* ALPHA mode */ +#define MODE_ALPHA_DMA 0x0 +#define MODE_ALPHA_GRA 0x1 +#define MODE_ALPHA_CFG 0x2 + +/* alpha value */ +#define ALPHA_NOGRAPHIC 0xFF /* all video, no graphic */ +#define ALPHA_NOVIDEO 0x00 /* all graphic, no video */ +#define ALPHA_GRAPHNVIDEO 0x0F /* Selects graphic & video */ + +/* + * defined Pixel Command for DMA control 1 register + * DMA1 bit[07:00] + */ +#define PIXEL_CMD 0x81 + +/* DSI */ +/* DSI1 - 4 Lane Controller base */ +#define DSI1_REGS_PHYSICAL_BASE 0xD420B800 +/* DSI2 - 3 Lane Controller base */ +#define DSI2_REGS_PHYSICAL_BASE 0xD420BA00 + +/* DSI Controller Registers */ +struct dsi_lcd_regs { +#define DSI_LCD1_CTRL_0 0x100 /* DSI Active Panel 1 Control register 0 */ +#define DSI_LCD1_CTRL_1 0x104 /* DSI Active Panel 1 Control register 1 */ + u32 ctrl0; + u32 ctrl1; + u32 reserved1[2]; + +#define DSI_LCD1_TIMING_0 0x110 /* Timing register 0 */ +#define DSI_LCD1_TIMING_1 0x114 /* Timing register 1 */ +#define DSI_LCD1_TIMING_2 0x118 /* Timing register 2 */ +#define DSI_LCD1_TIMING_3 0x11C /* Timing register 3 */ +#define DSI_LCD1_WC_0 0x120 /* Word Count register 0 */ +#define DSI_LCD1_WC_1 0x124 /* Word Count register 1 */ +#define DSI_LCD1_WC_2 0x128 /* Word Count register 2 */ + u32 timing0; + u32 timing1; + u32 timing2; + u32 timing3; + u32 wc0; + u32 wc1; + u32 wc2; + u32 reserved2[1]; + u32 slot_cnt0; + u32 slot_cnt1; + u32 reserved3[2]; + u32 status_0; + u32 status_1; + u32 status_2; + u32 status_3; + u32 status_4; +}; + +struct dsi_regs { +#define DSI_CTRL_0 0x000 /* DSI control register 0 */ +#define DSI_CTRL_1 0x004 /* DSI control register 1 */ + u32 ctrl0; + u32 ctrl1; + u32 reserved1[2]; + u32 irq_status; + u32 irq_mask; + u32 reserved2[2]; + +#define DSI_CPU_CMD_0 0x020 /* DSI CPU packet command register 0 */ +#define DSI_CPU_CMD_1 0x024 /* DSU CPU Packet Command Register 1 */ +#define DSI_CPU_CMD_3 0x02C /* DSU CPU Packet Command Register 3 */ +#define DSI_CPU_WDAT_0 0x030 /* DSI CUP */ + u32 cmd0; + u32 cmd1; + u32 cmd2; + u32 cmd3; + u32 dat0; + u32 status0; + u32 status1; + u32 status2; + u32 status3; + u32 status4; + u32 reserved3[2]; + + u32 smt_cmd; + u32 smt_ctrl0; + u32 smt_ctrl1; + u32 reserved4[1]; + + u32 rx0_status; + +/* Rx Packet Header - data from slave device */ +#define DSI_RX_PKT_HDR_0 0x064 + u32 rx0_header; + u32 rx1_status; + u32 rx1_header; + u32 rx_ctrl; + u32 rx_ctrl1; + u32 rx2_status; + u32 rx2_header; + u32 reserved5[1]; + + u32 phy_ctrl1; +#define DSI_PHY_CTRL_2 0x088 /* DSI DPHI Control Register 2 */ +#define DSI_PHY_CTRL_3 0x08C /* DPHY Control Register 3 */ + u32 phy_ctrl2; + u32 phy_ctrl3; + u32 phy_status0; + u32 phy_status1; + u32 reserved6[5]; + u32 phy_status2; + +#define DSI_PHY_RCOMP_0 0x0B0 /* DPHY Rcomp Control Register */ + u32 phy_rcomp0; + u32 reserved7[3]; +#define DSI_PHY_TIME_0 0x0C0 /* DPHY Timing Control Register 0 */ +#define DSI_PHY_TIME_1 0x0C4 /* DPHY Timing Control Register 1 */ +#define DSI_PHY_TIME_2 0x0C8 /* DPHY Timing Control Register 2 */ +#define DSI_PHY_TIME_3 0x0CC /* DPHY Timing Control Register 3 */ +#define DSI_PHY_TIME_4 0x0D0 /* DPHY Timing Control Register 4 */ +#define DSI_PHY_TIME_5 0x0D4 /* DPHY Timing Control Register 5 */ + u32 phy_timing0; + u32 phy_timing1; + u32 phy_timing2; + u32 phy_timing3; + u32 phy_code_0; + u32 phy_code_1; + u32 reserved8[2]; + u32 mem_ctrl; + u32 tx_timer; + u32 rx_timer; + u32 turn_timer; + u32 reserved9[4]; + +#define DSI_LCD1_CTRL_0 0x100 /* DSI Active Panel 1 Control register 0 */ +#define DSI_LCD1_CTRL_1 0x104 /* DSI Active Panel 1 Control register 1 */ +#define DSI_LCD1_TIMING_0 0x110 /* Timing register 0 */ +#define DSI_LCD1_TIMING_1 0x114 /* Timing register 1 */ +#define DSI_LCD1_TIMING_2 0x118 /* Timing register 2 */ +#define DSI_LCD1_TIMING_3 0x11C /* Timing register 3 */ +#define DSI_LCD1_WC_0 0x120 /* Word Count register 0 */ +#define DSI_LCD1_WC_1 0x124 /* Word Count register 1 */ +#define DSI_LCD1_WC_2 0x128 /* Word Count register 2 */ + struct dsi_lcd_regs lcd1; + u32 reserved10[11]; + struct dsi_lcd_regs lcd2; +}; + +#define DSI_LCD2_CTRL_0 0x180 /* DSI Active Panel 2 Control register 0 */ +#define DSI_LCD2_CTRL_1 0x184 /* DSI Active Panel 2 Control register 1 */ +#define DSI_LCD2_TIMING_0 0x190 /* Timing register 0 */ +#define DSI_LCD2_TIMING_1 0x194 /* Timing register 1 */ +#define DSI_LCD2_TIMING_2 0x198 /* Timing register 2 */ +#define DSI_LCD2_TIMING_3 0x19C /* Timing register 3 */ +#define DSI_LCD2_WC_0 0x1A0 /* Word Count register 0 */ +#define DSI_LCD2_WC_1 0x1A4 /* Word Count register 1 */ +#define DSI_LCD2_WC_2 0x1A8 /* Word Count register 2 */ + +/* DSI_CTRL_0 0x0000 DSI Control Register 0 */ +#define DSI_CTRL_0_CFG_SOFT_RST (1<<31) +#define DSI_CTRL_0_CFG_SOFT_RST_REG (1<<30) +#define DSI_CTRL_0_CFG_LCD1_TX_EN (1<<8) +#define DSI_CTRL_0_CFG_LCD1_SLV (1<<4) +#define DSI_CTRL_0_CFG_LCD1_EN (1<<0) + +/* DSI_CTRL_1 0x0004 DSI Control Register 1 */ +#define DSI_CTRL_1_CFG_EOTP (1<<8) +#define DSI_CTRL_1_CFG_RSVD (2<<4) +#define DSI_CTRL_1_CFG_LCD2_VCH_NO_MASK (3<<2) +#define DSI_CTRL_1_CFG_LCD2_VCH_NO_SHIFT 2 +#define DSI_CTRL_1_CFG_LCD1_VCH_NO_MASK (3<<0) +#define DSI_CTRL_1_CFG_LCD1_VCH_NO_SHIFT 0 + +/* DSI_LCD1_CTRL_1 0x0104 DSI Active Panel 1 Control Register 1 */ +/* LCD 1 Vsync Reset Enable */ +#define DSI_LCD1_CTRL_1_CFG_L1_VSYNC_RST_EN (1<<31) +/* LCD 1 2K Pixel Buffer Mode Enable */ +#define DSI_LCD1_CTRL_1_CFG_L1_M2K_EN (1<<30) +/* Bit(s) DSI_LCD1_CTRL_1_RSRV_29_23 reserved */ +/* Long Blanking Packet Enable */ +#define DSI_LCD1_CTRL_1_CFG_L1_HLP_PKT_EN (1<<22) +/* Extra Long Blanking Packet Enable */ +#define DSI_LCD1_CTRL_1_CFG_L1_HEX_PKT_EN (1<<21) +/* Front Porch Packet Enable */ +#define DSI_LCD1_CTRL_1_CFG_L1_HFP_PKT_EN (1<<20) +/* hact Packet Enable */ +#define DSI_LCD1_CTRL_1_CFG_L1_HACT_PKT_EN (1<<19) +/* Back Porch Packet Enable */ +#define DSI_LCD1_CTRL_1_CFG_L1_HBP_PKT_EN (1<<18) +/* hse Packet Enable */ +#define DSI_LCD1_CTRL_1_CFG_L1_HSE_PKT_EN (1<<17) +/* hsa Packet Enable */ +#define DSI_LCD1_CTRL_1_CFG_L1_HSA_PKT_EN (1<<16) +/* All Item Enable after Pixel Data */ +#define DSI_LCD1_CTRL_1_CFG_L1_ALL_SLOT_EN (1<<15) +/* Extra Long Packet Enable after Pixel Data */ +#define DSI_LCD1_CTRL_1_CFG_L1_HEX_SLOT_EN (1<<14) +/* Bit(s) DSI_LCD1_CTRL_1_RSRV_13_11 reserved */ +/* Turn Around Bus at Last h Line */ +#define DSI_LCD1_CTRL_1_CFG_L1_LAST_LINE_TURN (1<<10) +/* Go to Low Power Every Frame */ +#define DSI_LCD1_CTRL_1_CFG_L1_LPM_FRAME_EN (1<<9) +/* Go to Low Power Every Line */ +#define DSI_LCD1_CTRL_1_CFG_L1_LPM_LINE_EN (1<<8) +/* Bit(s) DSI_LCD1_CTRL_1_RSRV_7_4 reserved */ +/* DSI Transmission Mode for LCD 1 */ +#define DSI_LCD1_CTRL_1_CFG_L1_BURST_MODE_SHIFT 2 +#define DSI_LCD1_CTRL_1_CFG_L1_BURST_MODE_MASK (3<<2) +/* LCD 1 Input Data RGB Mode for LCD 1 */ +#define DSI_LCD2_CTRL_1_CFG_L1_RGB_TYPE_SHIFT 0 +#define DSI_LCD2_CTRL_1_CFG_L1_RGB_TYPE_MASK (3<<2) + +/* DSI_PHY_CTRL_2 0x0088 DPHY Control Register 2 */ +/* Bit(s) DSI_PHY_CTRL_2_RSRV_31_12 reserved */ +/* DPHY LP Receiver Enable */ +#define DSI_PHY_CTRL_2_CFG_CSR_LANE_RESC_EN_MASK (0xf<<8) +#define DSI_PHY_CTRL_2_CFG_CSR_LANE_RESC_EN_SHIFT 8 +/* DPHY Data Lane Enable */ +#define DSI_PHY_CTRL_2_CFG_CSR_LANE_EN_MASK (0xf<<4) +#define DSI_PHY_CTRL_2_CFG_CSR_LANE_EN_SHIFT 4 +/* DPHY Bus Turn Around */ +#define DSI_PHY_CTRL_2_CFG_CSR_LANE_TURN_MASK (0xf) +#define DSI_PHY_CTRL_2_CFG_CSR_LANE_TURN_SHIFT 0 + +/* DSI_CPU_CMD_1 0x0024 DSI CPU Packet Command Register 1 */ +/* Bit(s) DSI_CPU_CMD_1_RSRV_31_24 reserved */ +/* LPDT TX Enable */ +#define DSI_CPU_CMD_1_CFG_TXLP_LPDT_MASK (0xf<<20) +#define DSI_CPU_CMD_1_CFG_TXLP_LPDT_SHIFT 20 +/* ULPS TX Enable */ +#define DSI_CPU_CMD_1_CFG_TXLP_ULPS_MASK (0xf<<16) +#define DSI_CPU_CMD_1_CFG_TXLP_ULPS_SHIFT 16 +/* Low Power TX Trigger Code */ +#define DSI_CPU_CMD_1_CFG_TXLP_TRIGGER_CODE_MASK (0xffff) +#define DSI_CPU_CMD_1_CFG_TXLP_TRIGGER_CODE_SHIFT 0 + +/* DSI_PHY_TIME_0 0x00c0 DPHY Timing Control Register 0 */ +/* Length of HS Exit Period in tx_clk_esc Cycles */ +#define DSI_PHY_TIME_0_CFG_CSR_TIME_HS_EXIT_MASK (0xff<<24) +#define DSI_PHY_TIME_0_CFG_CSR_TIME_HS_EXIT_SHIFT 24 +/* DPHY HS Trail Period Length */ +#define DSI_PHY_TIME_0_CFG_CSR_TIME_HS_TRAIL_MASK (0xff<<16) +#define DSI_PHY_TIME_0_CFG_CSR_TIME_HS_TRAIL_SHIFT 16 +/* DPHY HS Zero State Length */ +#define DSI_PHY_TIME_0_CDG_CSR_TIME_HS_ZERO_MASK (0xff<<8) +#define DSI_PHY_TIME_0_CDG_CSR_TIME_HS_ZERO_SHIFT 8 +/* DPHY HS Prepare State Length */ +#define DSI_PHY_TIME_0_CFG_CSR_TIME_HS_PREP_MASK (0xff) +#define DSI_PHY_TIME_0_CFG_CSR_TIME_HS_PREP_SHIFT 0 + +/* DSI_PHY_TIME_1 0x00c4 DPHY Timing Control Register 1 */ +/* Time to Drive LP-00 by New Transmitter */ +#define DSI_PHY_TIME_1_CFG_CSR_TIME_TA_GET_MASK (0xff<<24) +#define DSI_PHY_TIME_1_CFG_CSR_TIME_TA_GET_SHIFT 24 +/* Time to Drive LP-00 after Turn Request */ +#define DSI_PHY_TIME_1_CFG_CSR_TIME_TA_GO_MASK (0xff<<16) +#define DSI_PHY_TIME_1_CFG_CSR_TIME_TA_GO_SHIFT 16 +/* DPHY HS Wakeup Period Length */ +#define DSI_PHY_TIME_1_CFG_CSR_TIME_WAKEUP_MASK (0xffff) +#define DSI_PHY_TIME_1_CFG_CSR_TIME_WAKEUP_SHIFT 0 + +/* DSI_PHY_TIME_2 0x00c8 DPHY Timing Control Register 2 */ +/* DPHY CLK Exit Period Length */ +#define DSI_PHY_TIME_2_CFG_CSR_TIME_CK_EXIT_MASK (0xff<<24) +#define DSI_PHY_TIME_2_CFG_CSR_TIME_CK_EXIT_SHIFT 24 +/* DPHY CLK Trail Period Length */ +#define DSI_PHY_TIME_2_CFG_CSR_TIME_CK_TRAIL_MASK (0xff<<16) +#define DSI_PHY_TIME_2_CFG_CSR_TIME_CK_TRAIL_SHIFT 16 +/* DPHY CLK Zero State Length */ +#define DSI_PHY_TIME_2_CFG_CSR_TIME_CK_ZERO_MASK (0xff<<8) +#define DSI_PHY_TIME_2_CFG_CSR_TIME_CK_ZERO_SHIFT 8 +/* DPHY CLK LP Length */ +#define DSI_PHY_TIME_2_CFG_CSR_TIME_CK_LPX_MASK (0xff) +#define DSI_PHY_TIME_2_CFG_CSR_TIME_CK_LPX_SHIFT 0 + +/* DSI_PHY_TIME_3 0x00cc DPHY Timing Control Register 3 */ +/* Bit(s) DSI_PHY_TIME_3_RSRV_31_16 reserved */ +/* DPHY LP Length */ +#define DSI_PHY_TIME_3_CFG_CSR_TIME_LPX_MASK (0xff<<8) +#define DSI_PHY_TIME_3_CFG_CSR_TIME_LPX_SHIFT 8 +/* DPHY HS req to rdy Length */ +#define DSI_PHY_TIME_3_CFG_CSR_TIME_REQRDY_MASK (0xff) +#define DSI_PHY_TIME_3_CFG_CSR_TIME_REQRDY_SHIFT 0 + +/* + * DSI timings + * PXA988 has diffrent ESC CLK with MMP2/MMP3 + * it will be used in dsi_set_dphy() in pxa688_phy.c + * as low power mode clock. + */ +#ifdef CONFIG_CPU_PXA988 +#define DSI_ESC_CLK 52 /* Unit: Mhz */ +#define DSI_ESC_CLK_T 19 /* Unit: ns */ +#else +#define DSI_ESC_CLK 66 /* Unit: Mhz */ +#define DSI_ESC_CLK_T 15 /* Unit: ns */ +#endif + +/* LVDS */ +/* LVDS_PHY_CTRL */ +#define LVDS_PHY_CTL 0x2A4 +#define LVDS_PLL_LOCK (1 << 31) +#define LVDS_PHY_EXT_MASK (7 << 28) +#define LVDS_PHY_EXT_SHIFT (28) +#define LVDS_CLK_PHASE_MASK (0x7f << 16) +#define LVDS_CLK_PHASE_SHIFT (16) +#define LVDS_SSC_RESET_EXT (1 << 13) +#define LVDS_SSC_MODE_DOWN_SPREAD (1 << 12) +#define LVDS_SSC_EN (1 << 11) +#define LVDS_PU_PLL (1 << 10) +#define LVDS_PU_TX (1 << 9) +#define LVDS_PU_IVREF (1 << 8) +#define LVDS_CLK_SEL (1 << 7) +#define LVDS_CLK_SEL_LVDS_PCLK (1 << 7) +#define LVDS_PD_CH_MASK (0x3f << 1) +#define LVDS_PD_CH(ch) ((ch) << 1) +#define LVDS_RST (1 << 0) + +#define LVDS_PHY_CTL_EXT 0x2A8 + +/* LVDS_PHY_CTRL_EXT1 */ +#define LVDS_SSC_RNGE_MASK (0x7ff << 16) +#define LVDS_SSC_RNGE_SHIFT (16) +#define LVDS_RESERVE_IN_MASK (0xf << 12) +#define LVDS_RESERVE_IN_SHIFT (12) +#define LVDS_TEST_MON_MASK (0x7 << 8) +#define LVDS_TEST_MON_SHIFT (8) +#define LVDS_POL_SWAP_MASK (0x3f << 0) +#define LVDS_POL_SWAP_SHIFT (0) + +/* LVDS_PHY_CTRL_EXT2 */ +#define LVDS_TX_DIF_AMP_MASK (0xf << 24) +#define LVDS_TX_DIF_AMP_SHIFT (24) +#define LVDS_TX_DIF_CM_MASK (0x3 << 22) +#define LVDS_TX_DIF_CM_SHIFT (22) +#define LVDS_SELLV_TXCLK_MASK (0x1f << 16) +#define LVDS_SELLV_TXCLK_SHIFT (16) +#define LVDS_TX_CMFB_EN (0x1 << 15) +#define LVDS_TX_TERM_EN (0x1 << 14) +#define LVDS_SELLV_TXDATA_MASK (0x1f << 8) +#define LVDS_SELLV_TXDATA_SHIFT (8) +#define LVDS_SELLV_OP7_MASK (0x3 << 6) +#define LVDS_SELLV_OP7_SHIFT (6) +#define LVDS_SELLV_OP6_MASK (0x3 << 4) +#define LVDS_SELLV_OP6_SHIFT (4) +#define LVDS_SELLV_OP9_MASK (0x3 << 2) +#define LVDS_SELLV_OP9_SHIFT (2) +#define LVDS_STRESSTST_EN (0x1 << 0) + +/* LVDS_PHY_CTRL_EXT3 */ +#define LVDS_KVCO_MASK (0xf << 28) +#define LVDS_KVCO_SHIFT (28) +#define LVDS_CTUNE_MASK (0x3 << 26) +#define LVDS_CTUNE_SHIFT (26) +#define LVDS_VREG_IVREF_MASK (0x3 << 24) +#define LVDS_VREG_IVREF_SHIFT (24) +#define LVDS_VDDL_MASK (0xf << 20) +#define LVDS_VDDL_SHIFT (20) +#define LVDS_VDDM_MASK (0x3 << 18) +#define LVDS_VDDM_SHIFT (18) +#define LVDS_FBDIV_MASK (0xf << 8) +#define LVDS_FBDIV_SHIFT (8) +#define LVDS_REFDIV_MASK (0x7f << 0) +#define LVDS_REFDIV_SHIFT (0) + +/* LVDS_PHY_CTRL_EXT4 */ +#define LVDS_SSC_FREQ_DIV_MASK (0xffff << 16) +#define LVDS_SSC_FREQ_DIV_SHIFT (16) +#define LVDS_INTPI_MASK (0xf << 12) +#define LVDS_INTPI_SHIFT (12) +#define LVDS_VCODIV_SEL_SE_MASK (0xf << 8) +#define LVDS_VCODIV_SEL_SE_SHIFT (8) +#define LVDS_RESET_INTP_EXT (0x1 << 7) +#define LVDS_VCO_VRNG_MASK (0x7 << 4) +#define LVDS_VCO_VRNG_SHIFT (4) +#define LVDS_PI_EN (0x1 << 3) +#define LVDS_ICP_MASK (0x7 << 0) +#define LVDS_ICP_SHIFT (0) + +/* LVDS_PHY_CTRL_EXT5 */ +#define LVDS_FREQ_OFFSET_MASK (0x1ffff << 15) +#define LVDS_FREQ_OFFSET_SHIFT (15) +#define LVDS_FREQ_OFFSET_VALID (0x1 << 2) +#define LVDS_FREQ_OFFSET_MODE_CK_DIV4_OUT (0x1 << 1) +#define LVDS_FREQ_OFFSET_MODE_EN (0x1 << 0) + +/* VDMA */ +struct vdma_ch_regs { +#define VDMA_DC_SADDR_1 0x320 +#define VDMA_DC_SADDR_2 0x3A0 +#define VDMA_DC_SZ_1 0x324 +#define VDMA_DC_SZ_2 0x3A4 +#define VDMA_CTRL_1 0x328 +#define VDMA_CTRL_2 0x3A8 +#define VDMA_SRC_SZ_1 0x32C +#define VDMA_SRC_SZ_2 0x3AC +#define VDMA_SA_1 0x330 +#define VDMA_SA_2 0x3B0 +#define VDMA_DA_1 0x334 +#define VDMA_DA_2 0x3B4 +#define VDMA_SZ_1 0x338 +#define VDMA_SZ_2 0x3B8 + u32 dc_saddr; + u32 dc_size; + u32 ctrl; + u32 src_size; + u32 src_addr; + u32 dst_addr; + u32 dst_size; +#define VDMA_PITCH_1 0x33C +#define VDMA_PITCH_2 0x3BC +#define VDMA_ROT_CTRL_1 0x340 +#define VDMA_ROT_CTRL_2 0x3C0 +#define VDMA_RAM_CTRL0_1 0x344 +#define VDMA_RAM_CTRL0_2 0x3C4 +#define VDMA_RAM_CTRL1_1 0x348 +#define VDMA_RAM_CTRL1_2 0x3C8 + u32 pitch; + u32 rot_ctrl; + u32 ram_ctrl0; + u32 ram_ctrl1; + +}; +struct vdma_regs { +#define VDMA_ARBR_CTRL 0x300 +#define VDMA_IRQR 0x304 +#define VDMA_IRQM 0x308 +#define VDMA_IRQS 0x30C +#define VDMA_MDMA_ARBR_CTRL 0x310 + u32 arbr_ctr; + u32 irq_raw; + u32 irq_mask; + u32 irq_status; + u32 mdma_arbr_ctrl; + u32 reserved[3]; + + struct vdma_ch_regs ch1; + u32 reserved2[21]; + struct vdma_ch_regs ch2; +}; + +/* CMU */ +#define CMU_PIP_DE_H_CFG 0x0008 +#define CMU_PRI1_H_CFG 0x000C +#define CMU_PRI2_H_CFG 0x0010 +#define CMU_ACE_MAIN_DE1_H_CFG 0x0014 +#define CMU_ACE_MAIN_DE2_H_CFG 0x0018 +#define CMU_ACE_PIP_DE1_H_CFG 0x001C +#define CMU_ACE_PIP_DE2_H_CFG 0x0020 +#define CMU_PIP_DE_V_CFG 0x0024 +#define CMU_PRI_V_CFG 0x0028 +#define CMU_ACE_MAIN_DE_V_CFG 0x002C +#define CMU_ACE_PIP_DE_V_CFG 0x0030 +#define CMU_BAR_0_CFG 0x0034 +#define CMU_BAR_1_CFG 0x0038 +#define CMU_BAR_2_CFG 0x003C +#define CMU_BAR_3_CFG 0x0040 +#define CMU_BAR_4_CFG 0x0044 +#define CMU_BAR_5_CFG 0x0048 +#define CMU_BAR_6_CFG 0x004C +#define CMU_BAR_7_CFG 0x0050 +#define CMU_BAR_8_CFG 0x0054 +#define CMU_BAR_9_CFG 0x0058 +#define CMU_BAR_10_CFG 0x005C +#define CMU_BAR_11_CFG 0x0060 +#define CMU_BAR_12_CFG 0x0064 +#define CMU_BAR_13_CFG 0x0068 +#define CMU_BAR_14_CFG 0x006C +#define CMU_BAR_15_CFG 0x0070 +#define CMU_BAR_CTRL 0x0074 +#define PATTERN_TOTAL 0x0078 +#define PATTERN_ACTIVE 0x007C +#define PATTERN_FRONT_PORCH 0x0080 +#define PATTERN_BACK_PORCH 0x0084 +#define CMU_CLK_CTRL 0x0088 + +#define CMU_ICSC_M_C0_L 0x0900 +#define CMU_ICSC_M_C0_H 0x0901 +#define CMU_ICSC_M_C1_L 0x0902 +#define CMU_ICSC_M_C1_H 0x0903 +#define CMU_ICSC_M_C2_L 0x0904 +#define CMU_ICSC_M_C2_H 0x0905 +#define CMU_ICSC_M_C3_L 0x0906 +#define CMU_ICSC_M_C3_H 0x0907 +#define CMU_ICSC_M_C4_L 0x0908 +#define CMU_ICSC_M_C4_H 0x0909 +#define CMU_ICSC_M_C5_L 0x090A +#define CMU_ICSC_M_C5_H 0x090B +#define CMU_ICSC_M_C6_L 0x090C +#define CMU_ICSC_M_C6_H 0x090D +#define CMU_ICSC_M_C7_L 0x090E +#define CMU_ICSC_M_C7_H 0x090F +#define CMU_ICSC_M_C8_L 0x0910 +#define CMU_ICSC_M_C8_H 0x0911 +#define CMU_ICSC_M_O1_0 0x0914 +#define CMU_ICSC_M_O1_1 0x0915 +#define CMU_ICSC_M_O1_2 0x0916 +#define CMU_ICSC_M_O2_0 0x0918 +#define CMU_ICSC_M_O2_1 0x0919 +#define CMU_ICSC_M_O2_2 0x091A +#define CMU_ICSC_M_O3_0 0x091C +#define CMU_ICSC_M_O3_1 0x091D +#define CMU_ICSC_M_O3_2 0x091E +#define CMU_ICSC_P_C0_L 0x0920 +#define CMU_ICSC_P_C0_H 0x0921 +#define CMU_ICSC_P_C1_L 0x0922 +#define CMU_ICSC_P_C1_H 0x0923 +#define CMU_ICSC_P_C2_L 0x0924 +#define CMU_ICSC_P_C2_H 0x0925 +#define CMU_ICSC_P_C3_L 0x0926 +#define CMU_ICSC_P_C3_H 0x0927 +#define CMU_ICSC_P_C4_L 0x0928 +#define CMU_ICSC_P_C4_H 0x0929 +#define CMU_ICSC_P_C5_L 0x092A +#define CMU_ICSC_P_C5_H 0x092B +#define CMU_ICSC_P_C6_L 0x092C +#define CMU_ICSC_P_C6_H 0x092D +#define CMU_ICSC_P_C7_L 0x092E +#define CMU_ICSC_P_C7_H 0x092F +#define CMU_ICSC_P_C8_L 0x0930 +#define CMU_ICSC_P_C8_H 0x0931 +#define CMU_ICSC_P_O1_0 0x0934 +#define CMU_ICSC_P_O1_1 0x0935 +#define CMU_ICSC_P_O1_2 0x0936 +#define CMU_ICSC_P_O2_0 0x0938 +#define CMU_ICSC_P_O2_1 0x0939 +#define CMU_ICSC_P_O2_2 0x093A +#define CMU_ICSC_P_O3_0 0x093C +#define CMU_ICSC_P_O3_1 0x093D +#define CMU_ICSC_P_O3_2 0x093E +#define CMU_BR_M_EN 0x0940 +#define CMU_BR_M_TH1_L 0x0942 +#define CMU_BR_M_TH1_H 0x0943 +#define CMU_BR_M_TH2_L 0x0944 +#define CMU_BR_M_TH2_H 0x0945 +#define CMU_ACE_M_EN 0x0950 +#define CMU_ACE_M_WFG1 0x0951 +#define CMU_ACE_M_WFG2 0x0952 +#define CMU_ACE_M_WFG3 0x0953 +#define CMU_ACE_M_TH0 0x0954 +#define CMU_ACE_M_TH1 0x0955 +#define CMU_ACE_M_TH2 0x0956 +#define CMU_ACE_M_TH3 0x0957 +#define CMU_ACE_M_TH4 0x0958 +#define CMU_ACE_M_TH5 0x0959 +#define CMU_ACE_M_OP0_L 0x095A +#define CMU_ACE_M_OP0_H 0x095B +#define CMU_ACE_M_OP5_L 0x095C +#define CMU_ACE_M_OP5_H 0x095D +#define CMU_ACE_M_GB2 0x095E +#define CMU_ACE_M_GB3 0x095F +#define CMU_ACE_M_MS1 0x0960 +#define CMU_ACE_M_MS2 0x0961 +#define CMU_ACE_M_MS3 0x0962 +#define CMU_BR_P_EN 0x0970 +#define CMU_BR_P_TH1_L 0x0972 +#define CMU_BR_P_TH1_H 0x0973 +#define CMU_BR_P_TH2_L 0x0974 +#define CMU_BR_P_TH2_H 0x0975 +#define CMU_ACE_P_EN 0x0980 +#define CMU_ACE_P_WFG1 0x0981 +#define CMU_ACE_P_WFG2 0x0982 +#define CMU_ACE_P_WFG3 0x0983 +#define CMU_ACE_P_TH0 0x0984 +#define CMU_ACE_P_TH1 0x0985 +#define CMU_ACE_P_TH2 0x0986 +#define CMU_ACE_P_TH3 0x0987 +#define CMU_ACE_P_TH4 0x0988 +#define CMU_ACE_P_TH5 0x0989 +#define CMU_ACE_P_OP0_L 0x098A +#define CMU_ACE_P_OP0_H 0x098B +#define CMU_ACE_P_OP5_L 0x098C +#define CMU_ACE_P_OP5_H 0x098D +#define CMU_ACE_P_GB2 0x098E +#define CMU_ACE_P_GB3 0x098F +#define CMU_ACE_P_MS1 0x0990 +#define CMU_ACE_P_MS2 0x0991 +#define CMU_ACE_P_MS3 0x0992 +#define CMU_FTDC_M_EN 0x09A0 +#define CMU_FTDC_P_EN 0x09A1 +#define CMU_FTDC_INLOW_L 0x09A2 +#define CMU_FTDC_INLOW_H 0x09A3 +#define CMU_FTDC_INHIGH_L 0x09A4 +#define CMU_FTDC_INHIGH_H 0x09A5 +#define CMU_FTDC_OUTLOW_L 0x09A6 +#define CMU_FTDC_OUTLOW_H 0x09A7 +#define CMU_FTDC_OUTHIGH_L 0x09A8 +#define CMU_FTDC_OUTHIGH_H 0x09A9 +#define CMU_FTDC_YLOW 0x09AA +#define CMU_FTDC_YHIGH 0x09AB +#define CMU_FTDC_CH1 0x09AC +#define CMU_FTDC_CH2_L 0x09AE +#define CMU_FTDC_CH2_H 0x09AF +#define CMU_FTDC_CH3_L 0x09B0 +#define CMU_FTDC_CH3_H 0x09B1 +#define CMU_FTDC_1_C00_6 0x09B2 +#define CMU_FTDC_1_C01_6 0x09B8 +#define CMU_FTDC_1_C11_6 0x09BE +#define CMU_FTDC_1_C10_6 0x09C4 +#define CMU_FTDC_1_OFF00_6 0x09CA +#define CMU_FTDC_1_OFF10_6 0x09D0 +#define CMU_HS_M_EN 0x0A00 +#define CMU_HS_M_AX1_L 0x0A02 +#define CMU_HS_M_AX1_H 0x0A03 +#define CMU_HS_M_AX2_L 0x0A04 +#define CMU_HS_M_AX2_H 0x0A05 +#define CMU_HS_M_AX3_L 0x0A06 +#define CMU_HS_M_AX3_H 0x0A07 +#define CMU_HS_M_AX4_L 0x0A08 +#define CMU_HS_M_AX4_H 0x0A09 +#define CMU_HS_M_AX5_L 0x0A0A +#define CMU_HS_M_AX5_H 0x0A0B +#define CMU_HS_M_AX6_L 0x0A0C +#define CMU_HS_M_AX6_H 0x0A0D +#define CMU_HS_M_AX7_L 0x0A0E +#define CMU_HS_M_AX7_H 0x0A0F +#define CMU_HS_M_AX8_L 0x0A10 +#define CMU_HS_M_AX8_H 0x0A11 +#define CMU_HS_M_AX9_L 0x0A12 +#define CMU_HS_M_AX9_H 0x0A13 +#define CMU_HS_M_AX10_L 0x0A14 +#define CMU_HS_M_AX10_H 0x0A15 +#define CMU_HS_M_AX11_L 0x0A16 +#define CMU_HS_M_AX11_H 0x0A17 +#define CMU_HS_M_AX12_L 0x0A18 +#define CMU_HS_M_AX12_H 0x0A19 +#define CMU_HS_M_AX13_L 0x0A1A +#define CMU_HS_M_AX13_H 0x0A1B +#define CMU_HS_M_AX14_L 0x0A1C +#define CMU_HS_M_AX14_H 0x0A1D +#define CMU_HS_M_H1_H14 0x0A1E +#define CMU_HS_M_S1_S14 0x0A2C +#define CMU_HS_M_GL 0x0A3A +#define CMU_HS_M_MAXSAT_RGB_Y_L 0x0A3C +#define CMU_HS_M_MAXSAT_RGB_Y_H 0x0A3D +#define CMU_HS_M_MAXSAT_RCR_L 0x0A3E +#define CMU_HS_M_MAXSAT_RCR_H 0x0A3F +#define CMU_HS_M_MAXSAT_RCB_L 0x0A40 +#define CMU_HS_M_MAXSAT_RCB_H 0x0A41 +#define CMU_HS_M_MAXSAT_GCR_L 0x0A42 +#define CMU_HS_M_MAXSAT_GCR_H 0x0A43 +#define CMU_HS_M_MAXSAT_GCB_L 0x0A44 +#define CMU_HS_M_MAXSAT_GCB_H 0x0A45 +#define CMU_HS_M_MAXSAT_BCR_L 0x0A46 +#define CMU_HS_M_MAXSAT_BCR_H 0x0A47 +#define CMU_HS_M_MAXSAT_BCB_L 0x0A48 +#define CMU_HS_M_MAXSAT_BCB_H 0x0A49 +#define CMU_HS_M_ROFF_L 0x0A4A +#define CMU_HS_M_ROFF_H 0x0A4B +#define CMU_HS_M_GOFF_L 0x0A4C +#define CMU_HS_M_GOFF_H 0x0A4D +#define CMU_HS_M_BOFF_L 0x0A4E +#define CMU_HS_M_BOFF_H 0x0A4F +#define CMU_HS_P_EN 0x0A50 +#define CMU_HS_P_AX1_L 0x0A52 +#define CMU_HS_P_AX1_H 0x0A53 +#define CMU_HS_P_AX2_L 0x0A54 +#define CMU_HS_P_AX2_H 0x0A55 +#define CMU_HS_P_AX3_L 0x0A56 +#define CMU_HS_P_AX3_H 0x0A57 +#define CMU_HS_P_AX4_L 0x0A58 +#define CMU_HS_P_AX4_H 0x0A59 +#define CMU_HS_P_AX5_L 0x0A5A +#define CMU_HS_P_AX5_H 0x0A5B +#define CMU_HS_P_AX6_L 0x0A5C +#define CMU_HS_P_AX6_H 0x0A5D +#define CMU_HS_P_AX7_L 0x0A5E +#define CMU_HS_P_AX7_H 0x0A5F +#define CMU_HS_P_AX8_L 0x0A60 +#define CMU_HS_P_AX8_H 0x0A61 +#define CMU_HS_P_AX9_L 0x0A62 +#define CMU_HS_P_AX9_H 0x0A63 +#define CMU_HS_P_AX10_L 0x0A64 +#define CMU_HS_P_AX10_H 0x0A65 +#define CMU_HS_P_AX11_L 0x0A66 +#define CMU_HS_P_AX11_H 0x0A67 +#define CMU_HS_P_AX12_L 0x0A68 +#define CMU_HS_P_AX12_H 0x0A69 +#define CMU_HS_P_AX13_L 0x0A6A +#define CMU_HS_P_AX13_H 0x0A6B +#define CMU_HS_P_AX14_L 0x0A6C +#define CMU_HS_P_AX14_H 0x0A6D +#define CMU_HS_P_H1_H14 0x0A6E +#define CMU_HS_P_S1_S14 0x0A7C +#define CMU_HS_P_GL 0x0A8A +#define CMU_HS_P_MAXSAT_RGB_Y_L 0x0A8C +#define CMU_HS_P_MAXSAT_RGB_Y_H 0x0A8D +#define CMU_HS_P_MAXSAT_RCR_L 0x0A8E +#define CMU_HS_P_MAXSAT_RCR_H 0x0A8F +#define CMU_HS_P_MAXSAT_RCB_L 0x0A90 +#define CMU_HS_P_MAXSAT_RCB_H 0x0A91 +#define CMU_HS_P_MAXSAT_GCR_L 0x0A92 +#define CMU_HS_P_MAXSAT_GCR_H 0x0A93 +#define CMU_HS_P_MAXSAT_GCB_L 0x0A94 +#define CMU_HS_P_MAXSAT_GCB_H 0x0A95 +#define CMU_HS_P_MAXSAT_BCR_L 0x0A96 +#define CMU_HS_P_MAXSAT_BCR_H 0x0A97 +#define CMU_HS_P_MAXSAT_BCB_L 0x0A98 +#define CMU_HS_P_MAXSAT_BCB_H 0x0A99 +#define CMU_HS_P_ROFF_L 0x0A9A +#define CMU_HS_P_ROFF_H 0x0A9B +#define CMU_HS_P_GOFF_L 0x0A9C +#define CMU_HS_P_GOFF_H 0x0A9D +#define CMU_HS_P_BOFF_L 0x0A9E +#define CMU_HS_P_BOFF_H 0x0A9F +#define CMU_GLCSC_M_C0_L 0x0AA0 +#define CMU_GLCSC_M_C0_H 0x0AA1 +#define CMU_GLCSC_M_C1_L 0x0AA2 +#define CMU_GLCSC_M_C1_H 0x0AA3 +#define CMU_GLCSC_M_C2_L 0x0AA4 +#define CMU_GLCSC_M_C2_H 0x0AA5 +#define CMU_GLCSC_M_C3_L 0x0AA6 +#define CMU_GLCSC_M_C3_H 0x0AA7 +#define CMU_GLCSC_M_C4_L 0x0AA8 +#define CMU_GLCSC_M_C4_H 0x0AA9 +#define CMU_GLCSC_M_C5_L 0x0AAA +#define CMU_GLCSC_M_C5_H 0x0AAB +#define CMU_GLCSC_M_C6_L 0x0AAC +#define CMU_GLCSC_M_C6_H 0x0AAD +#define CMU_GLCSC_M_C7_L 0x0AAE +#define CMU_GLCSC_M_C7_H 0x0AAF +#define CMU_GLCSC_M_C8_L 0x0AB0 +#define CMU_GLCSC_M_C8_H 0x0AB1 +#define CMU_GLCSC_M_O1_1 0x0AB4 +#define CMU_GLCSC_M_O1_2 0x0AB5 +#define CMU_GLCSC_M_O1_3 0x0AB6 +#define CMU_GLCSC_M_O2_1 0x0AB8 +#define CMU_GLCSC_M_O2_2 0x0AB9 +#define CMU_GLCSC_M_O2_3 0x0ABA +#define CMU_GLCSC_M_O3_1 0x0ABC +#define CMU_GLCSC_M_O3_2 0x0ABD +#define CMU_GLCSC_M_O3_3 0x0ABE +#define CMU_GLCSC_P_C0_L 0x0AC0 +#define CMU_GLCSC_P_C0_H 0x0AC1 +#define CMU_GLCSC_P_C1_L 0x0AC2 +#define CMU_GLCSC_P_C1_H 0x0AC3 +#define CMU_GLCSC_P_C2_L 0x0AC4 +#define CMU_GLCSC_P_C2_H 0x0AC5 +#define CMU_GLCSC_P_C3_L 0x0AC6 +#define CMU_GLCSC_P_C3_H 0x0AC7 +#define CMU_GLCSC_P_C4_L 0x0AC8 +#define CMU_GLCSC_P_C4_H 0x0AC9 +#define CMU_GLCSC_P_C5_L 0x0ACA +#define CMU_GLCSC_P_C5_H 0x0ACB +#define CMU_GLCSC_P_C6_L 0x0ACC +#define CMU_GLCSC_P_C6_H 0x0ACD +#define CMU_GLCSC_P_C7_L 0x0ACE +#define CMU_GLCSC_P_C7_H 0x0ACF +#define CMU_GLCSC_P_C8_L 0x0AD0 +#define CMU_GLCSC_P_C8_H 0x0AD1 +#define CMU_GLCSC_P_O1_1 0x0AD4 +#define CMU_GLCSC_P_O1_2 0x0AD5 +#define CMU_GLCSC_P_O1_3 0x0AD6 +#define CMU_GLCSC_P_O2_1 0x0AD8 +#define CMU_GLCSC_P_O2_2 0x0AD9 +#define CMU_GLCSC_P_O2_3 0x0ADA +#define CMU_GLCSC_P_O3_1 0x0ADC +#define CMU_GLCSC_P_O3_2 0x0ADD +#define CMU_GLCSC_P_O3_3 0x0ADE +#define CMU_PIXVAL_M_EN 0x0AE0 +#define CMU_PIXVAL_P_EN 0x0AE1 + +#define CMU_CLK_CTRL_TCLK 0x0 +#define CMU_CLK_CTRL_SCLK 0x2 +#define CMU_CLK_CTRL_MSK 0x2 +#define CMU_CLK_CTRL_ENABLE 0x1 + +#define LCD_TOP_CTRL_TV 0x2 +#define LCD_TOP_CTRL_PN 0x0 +#define LCD_TOP_CTRL_SEL_MSK 0x2 +#define LCD_IO_CMU_IN_SEL_MSK (0x3 << 20) +#define LCD_IO_CMU_IN_SEL_TV 0 +#define LCD_IO_CMU_IN_SEL_PN 1 +#define LCD_IO_CMU_IN_SEL_PN2 2 +#define LCD_IO_TV_OUT_SEL_MSK (0x3 << 26) +#define LCD_IO_PN_OUT_SEL_MSK (0x3 << 24) +#define LCD_IO_PN2_OUT_SEL_MSK (0x3 << 28) +#define LCD_IO_TV_OUT_SEL_NON 3 +#define LCD_IO_PN_OUT_SEL_NON 3 +#define LCD_IO_PN2_OUT_SEL_NON 3 +#define LCD_TOP_CTRL_CMU_ENABLE 0x1 +#define LCD_IO_OVERL_MSK 0xC00000 +#define LCD_IO_OVERL_TV 0x0 +#define LCD_IO_OVERL_LCD1 0x400000 +#define LCD_IO_OVERL_LCD2 0xC00000 +#define HINVERT_MSK 0x4 +#define VINVERT_MSK 0x8 +#define HINVERT_LEN 0x2 +#define VINVERT_LEN 0x3 + +#define CMU_CTRL 0x88 +#define CMU_CTRL_A0_MSK 0x6 +#define CMU_CTRL_A0_TV 0x0 +#define CMU_CTRL_A0_LCD1 0x1 +#define CMU_CTRL_A0_LCD2 0x2 +#define CMU_CTRL_A0_HDMI 0x3 + +#define ICR_DRV_ROUTE_OFF 0x0 +#define ICR_DRV_ROUTE_TV 0x1 +#define ICR_DRV_ROUTE_LCD1 0x2 +#define ICR_DRV_ROUTE_LCD2 0x3 + +enum { + PATH_PN = 0, + PATH_TV, + PATH_P2, +}; + +/* + * mmp path describes part of mmp path related info: + * which is hiden in display driver and not exported to buffer driver + */ +struct mmphw_ctrl; +struct mmphw_path_plat { + int id; + struct mmphw_ctrl *ctrl; + struct mmp_path *path; + u32 path_config; + u32 link_config; +}; + +/* mmp ctrl describes mmp controller related info */ +struct mmphw_ctrl { + /* platform related, get from config */ + const char *name; + int irq; + void *reg_base; + struct clk *clk; + + /* sys info */ + struct device *dev; + + /* state */ + int open_count; + int status; + struct mutex access_ok; + + /*pathes*/ + int path_num; + struct mmphw_path_plat path_plats[0]; +}; + +static inline int overlay_is_vid(struct mmp_overlay *overlay) +{ + return overlay->dmafetch_id & 1; +} + +static inline struct mmphw_path_plat *path_to_path_plat(struct mmp_path *path) +{ + return (struct mmphw_path_plat *)path->plat_data; +} + +static inline struct mmphw_ctrl *path_to_ctrl(struct mmp_path *path) +{ + return path_to_path_plat(path)->ctrl; +} + +static inline struct mmphw_ctrl *overlay_to_ctrl(struct mmp_overlay *overlay) +{ + return path_to_ctrl(overlay->path); +} + +static inline void *ctrl_regs(struct mmp_path *path) +{ + return path_to_ctrl(path)->reg_base; +} + +/* path regs, for regs symmetrical for both pathes */ +static inline struct lcd_regs *path_regs(struct mmp_path *path) +{ + if (path->id == PATH_PN) + return (struct lcd_regs *)(ctrl_regs(path) + 0xc0); + else if (path->id == PATH_TV) + return (struct lcd_regs *)ctrl_regs(path); + else if (path->id == PATH_P2) + return (struct lcd_regs *)(ctrl_regs(path) + 0x200); + else { + dev_err(path->dev, "path id %d invalid\n", path->id); + BUG_ON(1); + return NULL; + } +} + +#ifdef CONFIG_MMP_DISP_SPI +extern int lcd_spi_register(struct mmphw_ctrl *ctrl); +#endif +#endif /* _MMP_CTRL_H_ */ diff --git a/drivers/video/mmp/hw/mmp_spi.c b/drivers/video/mmp/hw/mmp_spi.c new file mode 100644 index 00000000000..e62ca7bf0d5 --- /dev/null +++ b/drivers/video/mmp/hw/mmp_spi.c @@ -0,0 +1,180 @@ +/* + * linux/drivers/video/mmp/hw/mmp_spi.c + * using the spi in LCD controler for commands send + * + * Copyright (C) 2012 Marvell Technology Group Ltd. + * Authors: Guoqing Li <ligq@marvell.com> + * Lisa Du <cldu@marvell.com> + * Zhou Zhu <zzhu3@marvell.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, see <http://www.gnu.org/licenses/>. + * + */ +#include <linux/errno.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/spi/spi.h> +#include "mmp_ctrl.h" + +/** + * spi_write - write command to the SPI port + * @data: can be 8/16/32-bit, MSB justified data to write. + * @len: data length. + * + * Wait bus transfer complete IRQ. + * The caller is expected to perform the necessary locking. + * + * Returns: + * %-ETIMEDOUT timeout occurred + * 0 success + */ +static inline int lcd_spi_write(struct spi_device *spi, u32 data) +{ + int timeout = 100000, isr, ret = 0; + u32 tmp; + void *reg_base = + *(void **)spi_master_get_devdata(spi->master); + + /* clear ISR */ + writel_relaxed(~SPI_IRQ_MASK, reg_base + SPU_IRQ_ISR); + + switch (spi->bits_per_word) { + case 8: + writel_relaxed((u8)data, reg_base + LCD_SPU_SPI_TXDATA); + break; + case 16: + writel_relaxed((u16)data, reg_base + LCD_SPU_SPI_TXDATA); + break; + case 32: + writel_relaxed((u32)data, reg_base + LCD_SPU_SPI_TXDATA); + break; + default: + dev_err(&spi->dev, "Wrong spi bit length\n"); + } + + /* SPI start to send command */ + tmp = readl_relaxed(reg_base + LCD_SPU_SPI_CTRL); + tmp &= ~CFG_SPI_START_MASK; + tmp |= CFG_SPI_START(1); + writel(tmp, reg_base + LCD_SPU_SPI_CTRL); + + isr = readl_relaxed(reg_base + SPU_IRQ_ISR); + while (!(isr & SPI_IRQ_ENA_MASK)) { + udelay(100); + isr = readl_relaxed(reg_base + SPU_IRQ_ISR); + if (!--timeout) { + ret = -ETIMEDOUT; + dev_err(&spi->dev, "spi cmd send time out\n"); + break; + } + } + + tmp = readl_relaxed(reg_base + LCD_SPU_SPI_CTRL); + tmp &= ~CFG_SPI_START_MASK; + tmp |= CFG_SPI_START(0); + writel_relaxed(tmp, reg_base + LCD_SPU_SPI_CTRL); + + writel_relaxed(~SPI_IRQ_MASK, reg_base + SPU_IRQ_ISR); + + return ret; +} + +static int lcd_spi_setup(struct spi_device *spi) +{ + void *reg_base = + *(void **)spi_master_get_devdata(spi->master); + u32 tmp; + + tmp = CFG_SCLKCNT(16) | + CFG_TXBITS(spi->bits_per_word) | + CFG_SPI_SEL(1) | CFG_SPI_ENA(1) | + CFG_SPI_3W4WB(1); + writel(tmp, reg_base + LCD_SPU_SPI_CTRL); + + /* + * After set mode it need a time to pull up the spi singals, + * or it would cause the wrong waveform when send spi command, + * especially on pxa910h + */ + tmp = readl_relaxed(reg_base + SPU_IOPAD_CONTROL); + if ((tmp & CFG_IOPADMODE_MASK) != IOPAD_DUMB18SPI) + writel_relaxed(IOPAD_DUMB18SPI | + (tmp & ~CFG_IOPADMODE_MASK), + reg_base + SPU_IOPAD_CONTROL); + udelay(20); + return 0; +} + +static int lcd_spi_one_transfer(struct spi_device *spi, struct spi_message *m) +{ + struct spi_transfer *t; + int i; + + list_for_each_entry(t, &m->transfers, transfer_list) { + switch (spi->bits_per_word) { + case 8: + for (i = 0; i < t->len; i++) + lcd_spi_write(spi, ((u8 *)t->tx_buf)[i]); + break; + case 16: + for (i = 0; i < t->len/2; i++) + lcd_spi_write(spi, ((u16 *)t->tx_buf)[i]); + break; + case 32: + for (i = 0; i < t->len/4; i++) + lcd_spi_write(spi, ((u32 *)t->tx_buf)[i]); + break; + default: + dev_err(&spi->dev, "Wrong spi bit length\n"); + } + } + + m->status = 0; + if (m->complete) + m->complete(m->context); + return 0; +} + +int lcd_spi_register(struct mmphw_ctrl *ctrl) +{ + struct spi_master *master; + void **p_regbase; + int err; + + master = spi_alloc_master(ctrl->dev, sizeof(void *)); + if (!master) { + dev_err(ctrl->dev, "unable to allocate SPI master\n"); + return -ENOMEM; + } + p_regbase = spi_master_get_devdata(master); + *p_regbase = ctrl->reg_base; + + /* set bus num to 5 to avoid conflict with other spi hosts */ + master->bus_num = 5; + master->num_chipselect = 1; + master->setup = lcd_spi_setup; + master->transfer = lcd_spi_one_transfer; + + err = spi_register_master(master); + if (err < 0) { + dev_err(ctrl->dev, "unable to register SPI master\n"); + spi_master_put(master); + return err; + } + + dev_info(&master->dev, "registered\n"); + + return 0; +} diff --git a/drivers/video/mmp/panel/Kconfig b/drivers/video/mmp/panel/Kconfig new file mode 100644 index 00000000000..4b2c4f457b1 --- /dev/null +++ b/drivers/video/mmp/panel/Kconfig @@ -0,0 +1,6 @@ +config MMP_PANEL_TPOHVGA + bool "tpohvga panel TJ032MD01BW support" + depends on SPI_MASTER + default n + help + tpohvga panel support diff --git a/drivers/video/mmp/panel/Makefile b/drivers/video/mmp/panel/Makefile new file mode 100644 index 00000000000..2f91611c7e5 --- /dev/null +++ b/drivers/video/mmp/panel/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_MMP_PANEL_TPOHVGA) += tpo_tj032md01bw.o diff --git a/drivers/video/mmp/panel/tpo_tj032md01bw.c b/drivers/video/mmp/panel/tpo_tj032md01bw.c new file mode 100644 index 00000000000..998978b08f5 --- /dev/null +++ b/drivers/video/mmp/panel/tpo_tj032md01bw.c @@ -0,0 +1,186 @@ +/* + * linux/drivers/video/mmp/panel/tpo_tj032md01bw.c + * active panel using spi interface to do init + * + * Copyright (C) 2012 Marvell Technology Group Ltd. + * Authors: Guoqing Li <ligq@marvell.com> + * Lisa Du <cldu@marvell.com> + * Zhou Zhu <zzhu3@marvell.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, see <http://www.gnu.org/licenses/>. + * + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/spi/spi.h> +#include <video/mmp_disp.h> + +static u16 init[] = { + 0x0801, + 0x0800, + 0x0200, + 0x0304, + 0x040e, + 0x0903, + 0x0b18, + 0x0c53, + 0x0d01, + 0x0ee0, + 0x0f01, + 0x1058, + 0x201e, + 0x210a, + 0x220a, + 0x231e, + 0x2400, + 0x2532, + 0x2600, + 0x27ac, + 0x2904, + 0x2aa2, + 0x2b45, + 0x2c45, + 0x2d15, + 0x2e5a, + 0x2fff, + 0x306b, + 0x310d, + 0x3248, + 0x3382, + 0x34bd, + 0x35e7, + 0x3618, + 0x3794, + 0x3801, + 0x395d, + 0x3aae, + 0x3bff, + 0x07c9, +}; + +static u16 poweroff[] = { + 0x07d9, +}; + +struct tpohvga_plat_data { + void (*plat_onoff)(int status); + struct spi_device *spi; +}; + +static void tpohvga_onoff(struct mmp_panel *panel, int status) +{ + struct tpohvga_plat_data *plat = panel->plat_data; + int ret; + + if (status) { + plat->plat_onoff(1); + + ret = spi_write(plat->spi, init, sizeof(init)); + if (ret < 0) + dev_warn(panel->dev, "init cmd failed(%d)\n", ret); + } else { + ret = spi_write(plat->spi, poweroff, sizeof(poweroff)); + if (ret < 0) + dev_warn(panel->dev, "poweroff cmd failed(%d)\n", ret); + + plat->plat_onoff(0); + } +} + +static struct mmp_mode mmp_modes_tpohvga[] = { + [0] = { + .pixclock_freq = 10394400, + .refresh = 60, + .xres = 320, + .yres = 480, + .hsync_len = 10, + .left_margin = 15, + .right_margin = 10, + .vsync_len = 2, + .upper_margin = 4, + .lower_margin = 2, + .invert_pixclock = 1, + .pix_fmt_out = PIXFMT_RGB565, + }, +}; + +static int tpohvga_get_modelist(struct mmp_panel *panel, + struct mmp_mode **modelist) +{ + *modelist = mmp_modes_tpohvga; + return 1; +} + +static struct mmp_panel panel_tpohvga = { + .name = "tpohvga", + .panel_type = PANELTYPE_ACTIVE, + .get_modelist = tpohvga_get_modelist, + .set_onoff = tpohvga_onoff, +}; + +static int tpohvga_probe(struct spi_device *spi) +{ + struct mmp_mach_panel_info *mi; + int ret; + struct tpohvga_plat_data *plat_data; + + /* get configs from platform data */ + mi = spi->dev.platform_data; + if (mi == NULL) { + dev_err(&spi->dev, "%s: no platform data defined\n", __func__); + return -EINVAL; + } + + /* setup spi related info */ + spi->bits_per_word = 16; + ret = spi_setup(spi); + if (ret < 0) { + dev_err(&spi->dev, "spi setup failed %d", ret); + return ret; + } + + plat_data = kzalloc(sizeof(*plat_data), GFP_KERNEL); + if (plat_data == NULL) + return -ENOMEM; + + plat_data->spi = spi; + plat_data->plat_onoff = mi->plat_set_onoff; + panel_tpohvga.plat_data = plat_data; + panel_tpohvga.plat_path_name = mi->plat_path_name; + panel_tpohvga.dev = &spi->dev; + + mmp_register_panel(&panel_tpohvga); + + return 0; +} + +static struct spi_driver panel_tpohvga_driver = { + .driver = { + .name = "tpo-hvga", + .owner = THIS_MODULE, + }, + .probe = tpohvga_probe, +}; +module_spi_driver(panel_tpohvga_driver); + +MODULE_AUTHOR("Lisa Du<cldu@marvell.com>"); +MODULE_DESCRIPTION("Panel driver for tpohvga"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c index f2566c19e71..113c7876c85 100644 --- a/drivers/video/msm/mdp.c +++ b/drivers/video/msm/mdp.c @@ -261,7 +261,7 @@ int get_img(struct mdp_img *img, struct fb_info *info, if (f.file == NULL) return -1; - if (MAJOR(f.file->f_dentry->d_inode->i_rdev) == FB_MAJOR) { + if (MAJOR(file_inode(f.file)->i_rdev) == FB_MAJOR) { *start = info->fix.smem_start; *len = info->fix.smem_len; } else diff --git a/drivers/video/mx3fb.c b/drivers/video/mx3fb.c index 73688720857..cfdb380ec81 100644 --- a/drivers/video/mx3fb.c +++ b/drivers/video/mx3fb.c @@ -1306,7 +1306,7 @@ static int mx3fb_unmap_video_memory(struct fb_info *fbi) dma_free_writecombine(fbi->device, fbi->fix.smem_len, fbi->screen_base, fbi->fix.smem_start); - fbi->screen_base = 0; + fbi->screen_base = NULL; mutex_lock(&fbi->mm_lock); fbi->fix.smem_start = 0; fbi->fix.smem_len = 0; diff --git a/drivers/video/of_display_timing.c b/drivers/video/of_display_timing.c new file mode 100644 index 00000000000..13ecd989701 --- /dev/null +++ b/drivers/video/of_display_timing.c @@ -0,0 +1,239 @@ +/* + * OF helpers for parsing display timings + * + * Copyright (c) 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>, Pengutronix + * + * based on of_videomode.c by Sascha Hauer <s.hauer@pengutronix.de> + * + * This file is released under the GPLv2 + */ +#include <linux/export.h> +#include <linux/of.h> +#include <linux/slab.h> +#include <video/display_timing.h> +#include <video/of_display_timing.h> + +/** + * parse_timing_property - parse timing_entry from device_node + * @np: device_node with the property + * @name: name of the property + * @result: will be set to the return value + * + * DESCRIPTION: + * Every display_timing can be specified with either just the typical value or + * a range consisting of min/typ/max. This function helps handling this + **/ +static int parse_timing_property(struct device_node *np, const char *name, + struct timing_entry *result) +{ + struct property *prop; + int length, cells, ret; + + prop = of_find_property(np, name, &length); + if (!prop) { + pr_err("%s: could not find property %s\n", + of_node_full_name(np), name); + return -EINVAL; + } + + cells = length / sizeof(u32); + if (cells == 1) { + ret = of_property_read_u32(np, name, &result->typ); + result->min = result->typ; + result->max = result->typ; + } else if (cells == 3) { + ret = of_property_read_u32_array(np, name, &result->min, cells); + } else { + pr_err("%s: illegal timing specification in %s\n", + of_node_full_name(np), name); + return -EINVAL; + } + + return ret; +} + +/** + * of_get_display_timing - parse display_timing entry from device_node + * @np: device_node with the properties + **/ +static struct display_timing *of_get_display_timing(struct device_node *np) +{ + struct display_timing *dt; + u32 val = 0; + int ret = 0; + + dt = kzalloc(sizeof(*dt), GFP_KERNEL); + if (!dt) { + pr_err("%s: could not allocate display_timing struct\n", + of_node_full_name(np)); + return NULL; + } + + ret |= parse_timing_property(np, "hback-porch", &dt->hback_porch); + ret |= parse_timing_property(np, "hfront-porch", &dt->hfront_porch); + ret |= parse_timing_property(np, "hactive", &dt->hactive); + ret |= parse_timing_property(np, "hsync-len", &dt->hsync_len); + ret |= parse_timing_property(np, "vback-porch", &dt->vback_porch); + ret |= parse_timing_property(np, "vfront-porch", &dt->vfront_porch); + ret |= parse_timing_property(np, "vactive", &dt->vactive); + ret |= parse_timing_property(np, "vsync-len", &dt->vsync_len); + ret |= parse_timing_property(np, "clock-frequency", &dt->pixelclock); + + dt->dmt_flags = 0; + dt->data_flags = 0; + if (!of_property_read_u32(np, "vsync-active", &val)) + dt->dmt_flags |= val ? VESA_DMT_VSYNC_HIGH : + VESA_DMT_VSYNC_LOW; + if (!of_property_read_u32(np, "hsync-active", &val)) + dt->dmt_flags |= val ? VESA_DMT_HSYNC_HIGH : + VESA_DMT_HSYNC_LOW; + if (!of_property_read_u32(np, "de-active", &val)) + dt->data_flags |= val ? DISPLAY_FLAGS_DE_HIGH : + DISPLAY_FLAGS_DE_LOW; + if (!of_property_read_u32(np, "pixelclk-active", &val)) + dt->data_flags |= val ? DISPLAY_FLAGS_PIXDATA_POSEDGE : + DISPLAY_FLAGS_PIXDATA_NEGEDGE; + + if (of_property_read_bool(np, "interlaced")) + dt->data_flags |= DISPLAY_FLAGS_INTERLACED; + if (of_property_read_bool(np, "doublescan")) + dt->data_flags |= DISPLAY_FLAGS_DOUBLESCAN; + + if (ret) { + pr_err("%s: error reading timing properties\n", + of_node_full_name(np)); + kfree(dt); + return NULL; + } + + return dt; +} + +/** + * of_get_display_timings - parse all display_timing entries from a device_node + * @np: device_node with the subnodes + **/ +struct display_timings *of_get_display_timings(struct device_node *np) +{ + struct device_node *timings_np; + struct device_node *entry; + struct device_node *native_mode; + struct display_timings *disp; + + if (!np) { + pr_err("%s: no devicenode given\n", of_node_full_name(np)); + return NULL; + } + + timings_np = of_find_node_by_name(np, "display-timings"); + if (!timings_np) { + pr_err("%s: could not find display-timings node\n", + of_node_full_name(np)); + return NULL; + } + + disp = kzalloc(sizeof(*disp), GFP_KERNEL); + if (!disp) { + pr_err("%s: could not allocate struct disp'\n", + of_node_full_name(np)); + goto dispfail; + } + + entry = of_parse_phandle(timings_np, "native-mode", 0); + /* assume first child as native mode if none provided */ + if (!entry) + entry = of_get_next_child(np, NULL); + /* if there is no child, it is useless to go on */ + if (!entry) { + pr_err("%s: no timing specifications given\n", + of_node_full_name(np)); + goto entryfail; + } + + pr_debug("%s: using %s as default timing\n", + of_node_full_name(np), entry->name); + + native_mode = entry; + + disp->num_timings = of_get_child_count(timings_np); + if (disp->num_timings == 0) { + /* should never happen, as entry was already found above */ + pr_err("%s: no timings specified\n", of_node_full_name(np)); + goto entryfail; + } + + disp->timings = kzalloc(sizeof(struct display_timing *) * + disp->num_timings, GFP_KERNEL); + if (!disp->timings) { + pr_err("%s: could not allocate timings array\n", + of_node_full_name(np)); + goto entryfail; + } + + disp->num_timings = 0; + disp->native_mode = 0; + + for_each_child_of_node(timings_np, entry) { + struct display_timing *dt; + + dt = of_get_display_timing(entry); + if (!dt) { + /* + * to not encourage wrong devicetrees, fail in case of + * an error + */ + pr_err("%s: error in timing %d\n", + of_node_full_name(np), disp->num_timings + 1); + goto timingfail; + } + + if (native_mode == entry) + disp->native_mode = disp->num_timings; + + disp->timings[disp->num_timings] = dt; + disp->num_timings++; + } + of_node_put(timings_np); + /* + * native_mode points to the device_node returned by of_parse_phandle + * therefore call of_node_put on it + */ + of_node_put(native_mode); + + pr_debug("%s: got %d timings. Using timing #%d as default\n", + of_node_full_name(np), disp->num_timings, + disp->native_mode + 1); + + return disp; + +timingfail: + if (native_mode) + of_node_put(native_mode); + display_timings_release(disp); +entryfail: + kfree(disp); +dispfail: + of_node_put(timings_np); + return NULL; +} +EXPORT_SYMBOL_GPL(of_get_display_timings); + +/** + * of_display_timings_exist - check if a display-timings node is provided + * @np: device_node with the timing + **/ +int of_display_timings_exist(struct device_node *np) +{ + struct device_node *timings_np; + + if (!np) + return -EINVAL; + + timings_np = of_parse_phandle(np, "display-timings", 0); + if (!timings_np) + return -EINVAL; + + of_node_put(timings_np); + return 1; +} +EXPORT_SYMBOL_GPL(of_display_timings_exist); diff --git a/drivers/video/of_videomode.c b/drivers/video/of_videomode.c new file mode 100644 index 00000000000..5b8066cd397 --- /dev/null +++ b/drivers/video/of_videomode.c @@ -0,0 +1,54 @@ +/* + * generic videomode helper + * + * Copyright (c) 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>, Pengutronix + * + * This file is released under the GPLv2 + */ +#include <linux/errno.h> +#include <linux/export.h> +#include <linux/of.h> +#include <video/display_timing.h> +#include <video/of_display_timing.h> +#include <video/of_videomode.h> +#include <video/videomode.h> + +/** + * of_get_videomode - get the videomode #<index> from devicetree + * @np - devicenode with the display_timings + * @vm - set to return value + * @index - index into list of display_timings + * (Set this to OF_USE_NATIVE_MODE to use whatever mode is + * specified as native mode in the DT.) + * + * DESCRIPTION: + * Get a list of all display timings and put the one + * specified by index into *vm. This function should only be used, if + * only one videomode is to be retrieved. A driver that needs to work + * with multiple/all videomodes should work with + * of_get_display_timings instead. + **/ +int of_get_videomode(struct device_node *np, struct videomode *vm, + int index) +{ + struct display_timings *disp; + int ret; + + disp = of_get_display_timings(np); + if (!disp) { + pr_err("%s: no timings specified\n", of_node_full_name(np)); + return -EINVAL; + } + + if (index == OF_USE_NATIVE_MODE) + index = disp->native_mode; + + ret = videomode_from_timing(disp, vm, index); + if (ret) + return ret; + + display_timings_release(disp); + + return 0; +} +EXPORT_SYMBOL_GPL(of_get_videomode); diff --git a/drivers/video/omap/Kconfig b/drivers/video/omap/Kconfig index b48f95f0dfe..e512581300f 100644 --- a/drivers/video/omap/Kconfig +++ b/drivers/video/omap/Kconfig @@ -1,5 +1,5 @@ config FB_OMAP - tristate "OMAP frame buffer support (EXPERIMENTAL)" + tristate "OMAP frame buffer support" depends on FB depends on ARCH_OMAP1 select FB_CFB_FILLRECT diff --git a/drivers/video/omap2/displays/panel-generic-dpi.c b/drivers/video/omap2/displays/panel-generic-dpi.c index 54ca8ae2107..c904f42d81c 100644 --- a/drivers/video/omap2/displays/panel-generic-dpi.c +++ b/drivers/video/omap2/displays/panel-generic-dpi.c @@ -291,30 +291,6 @@ static struct panel_config generic_dpi_panels[] = { .name = "h4", }, - /* Unknown panel used in Samsung OMAP2 Apollon */ - { - { - .x_res = 480, - .y_res = 272, - - .pixel_clock = 6250, - - .hsw = 41, - .hfp = 2, - .hbp = 2, - - .vsw = 10, - .vfp = 2, - .vbp = 2, - - .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, - .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, - .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, - .de_level = OMAPDSS_SIG_ACTIVE_HIGH, - .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, - }, - .name = "apollon", - }, /* FocalTech ETM070003DH6 */ { { diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c index 769d0828581..72923645dcc 100644 --- a/drivers/video/omap2/dss/hdmi.c +++ b/drivers/video/omap2/dss/hdmi.c @@ -1080,11 +1080,9 @@ static int __init omapdss_hdmihw_probe(struct platform_device *pdev) } /* Base address taken from platform */ - hdmi.ip_data.base_wp = devm_request_and_ioremap(&pdev->dev, res); - if (!hdmi.ip_data.base_wp) { - DSSERR("can't ioremap WP\n"); - return -ENOMEM; - } + hdmi.ip_data.base_wp = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(hdmi.ip_data.base_wp)) + return PTR_ERR(hdmi.ip_data.base_wp); r = hdmi_get_clocks(pdev); if (r) { diff --git a/drivers/video/omap2/vrfb.c b/drivers/video/omap2/vrfb.c index 5d8fdac3b80..10560efeb35 100644 --- a/drivers/video/omap2/vrfb.c +++ b/drivers/video/omap2/vrfb.c @@ -20,6 +20,7 @@ /*#define DEBUG*/ +#include <linux/err.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/ioport.h> @@ -357,11 +358,9 @@ static int __init vrfb_probe(struct platform_device *pdev) return -EINVAL; } - vrfb_base = devm_request_and_ioremap(&pdev->dev, mem); - if (!vrfb_base) { - dev_err(&pdev->dev, "can't ioremap vrfb memory\n"); - return -ENOMEM; - } + vrfb_base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(vrfb_base)) + return PTR_ERR(vrfb_base); num_ctxs = pdev->num_resources - 1; diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c index 9b57a235c9b..968a62571df 100644 --- a/drivers/video/s3c-fb.c +++ b/drivers/video/s3c-fb.c @@ -1421,10 +1421,9 @@ static int s3c_fb_probe(struct platform_device *pdev) pm_runtime_enable(sfb->dev); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - sfb->regs = devm_request_and_ioremap(dev, res); - if (!sfb->regs) { - dev_err(dev, "failed to map registers\n"); - ret = -ENXIO; + sfb->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(sfb->regs)) { + ret = PTR_ERR(sfb->regs); goto err_lcd_clk; } diff --git a/drivers/video/via/hw.c b/drivers/video/via/hw.c index 80233dae358..22450908306 100644 --- a/drivers/video/via/hw.c +++ b/drivers/video/via/hw.c @@ -1467,10 +1467,10 @@ void viafb_set_vclock(u32 clk, int set_iga) via_write_misc_reg_mask(0x0C, 0x0C); /* select external clock */ } -struct display_timing var_to_timing(const struct fb_var_screeninfo *var, +struct via_display_timing var_to_timing(const struct fb_var_screeninfo *var, u16 cxres, u16 cyres) { - struct display_timing timing; + struct via_display_timing timing; u16 dx = (var->xres - cxres) / 2, dy = (var->yres - cyres) / 2; timing.hor_addr = cxres; @@ -1491,7 +1491,7 @@ struct display_timing var_to_timing(const struct fb_var_screeninfo *var, 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 via_display_timing crt_reg = var_to_timing(var, cxres ? cxres : var->xres, cyres ? cyres : var->yres); if (iga == IGA1) diff --git a/drivers/video/via/hw.h b/drivers/video/via/hw.h index a8205754c73..3be073c58b0 100644 --- a/drivers/video/via/hw.h +++ b/drivers/video/via/hw.h @@ -637,7 +637,7 @@ extern int viafb_LCD_ON; extern int viafb_DVI_ON; extern int viafb_hotplug; -struct display_timing var_to_timing(const struct fb_var_screeninfo *var, +struct via_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); diff --git a/drivers/video/via/lcd.c b/drivers/video/via/lcd.c index 980ee1b1dcf..5d21ff436ec 100644 --- a/drivers/video/via/lcd.c +++ b/drivers/video/via/lcd.c @@ -549,7 +549,7 @@ void viafb_lcd_set_mode(const struct fb_var_screeninfo *var, u16 cxres, int panel_hres = plvds_setting_info->lcd_panel_hres; int panel_vres = plvds_setting_info->lcd_panel_vres; u32 clock; - struct display_timing timing; + struct via_display_timing timing; struct fb_var_screeninfo panel_var; const struct fb_videomode *mode_crt_table, *panel_crt_table; diff --git a/drivers/video/via/share.h b/drivers/video/via/share.h index 3158dfc90be..65c65c611e0 100644 --- a/drivers/video/via/share.h +++ b/drivers/video/via/share.h @@ -319,7 +319,7 @@ struct crt_mode_table { int refresh_rate; int h_sync_polarity; int v_sync_polarity; - struct display_timing crtc; + struct via_display_timing crtc; }; struct io_reg { diff --git a/drivers/video/via/via_modesetting.c b/drivers/video/via/via_modesetting.c index 0e431aee17b..0b414b09b9b 100644 --- a/drivers/video/via/via_modesetting.c +++ b/drivers/video/via/via_modesetting.c @@ -30,9 +30,9 @@ #include "debug.h" -void via_set_primary_timing(const struct display_timing *timing) +void via_set_primary_timing(const struct via_display_timing *timing) { - struct display_timing raw; + struct via_display_timing raw; raw.hor_total = timing->hor_total / 8 - 5; raw.hor_addr = timing->hor_addr / 8 - 1; @@ -88,9 +88,9 @@ void via_set_primary_timing(const struct display_timing *timing) via_write_reg_mask(VIACR, 0x17, 0x80, 0x80); } -void via_set_secondary_timing(const struct display_timing *timing) +void via_set_secondary_timing(const struct via_display_timing *timing) { - struct display_timing raw; + struct via_display_timing raw; raw.hor_total = timing->hor_total - 1; raw.hor_addr = timing->hor_addr - 1; diff --git a/drivers/video/via/via_modesetting.h b/drivers/video/via/via_modesetting.h index 06e09fe351a..f6a6503da3b 100644 --- a/drivers/video/via/via_modesetting.h +++ b/drivers/video/via/via_modesetting.h @@ -33,7 +33,7 @@ #define VIA_PITCH_MAX 0x3FF8 -struct display_timing { +struct via_display_timing { u16 hor_total; u16 hor_addr; u16 hor_blank_start; @@ -49,8 +49,8 @@ struct display_timing { }; -void via_set_primary_timing(const struct display_timing *timing); -void via_set_secondary_timing(const struct display_timing *timing); +void via_set_primary_timing(const struct via_display_timing *timing); +void via_set_secondary_timing(const struct via_display_timing *timing); void via_set_primary_address(u32 addr); void via_set_secondary_address(u32 addr); void via_set_primary_pitch(u32 pitch); diff --git a/drivers/video/videomode.c b/drivers/video/videomode.c new file mode 100644 index 00000000000..21c47a202af --- /dev/null +++ b/drivers/video/videomode.c @@ -0,0 +1,39 @@ +/* + * generic display timing functions + * + * Copyright (c) 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>, Pengutronix + * + * This file is released under the GPLv2 + */ + +#include <linux/errno.h> +#include <linux/export.h> +#include <video/display_timing.h> +#include <video/videomode.h> + +int videomode_from_timing(const struct display_timings *disp, + struct videomode *vm, unsigned int index) +{ + struct display_timing *dt; + + dt = display_timings_get(disp, index); + if (!dt) + return -EINVAL; + + vm->pixelclock = display_timing_get_value(&dt->pixelclock, TE_TYP); + vm->hactive = display_timing_get_value(&dt->hactive, TE_TYP); + vm->hfront_porch = display_timing_get_value(&dt->hfront_porch, TE_TYP); + vm->hback_porch = display_timing_get_value(&dt->hback_porch, TE_TYP); + vm->hsync_len = display_timing_get_value(&dt->hsync_len, TE_TYP); + + vm->vactive = display_timing_get_value(&dt->vactive, TE_TYP); + vm->vfront_porch = display_timing_get_value(&dt->vfront_porch, TE_TYP); + vm->vback_porch = display_timing_get_value(&dt->vback_porch, TE_TYP); + vm->vsync_len = display_timing_get_value(&dt->vsync_len, TE_TYP); + + vm->dmt_flags = dt->dmt_flags; + vm->data_flags = dt->data_flags; + + return 0; +} +EXPORT_SYMBOL_GPL(videomode_from_timing); |