diff options
author | David Woodhouse <dwmw2@infradead.org> | 2007-10-13 14:58:23 +0100 |
---|---|---|
committer | David Woodhouse <dwmw2@infradead.org> | 2007-10-13 14:58:23 +0100 |
commit | ebf8889bd1fe3615991ff4494635d237280652a2 (patch) | |
tree | 10fb735717122bbb86474339eac07f26e7ccdf40 /drivers/video | |
parent | b160292cc216a50fd0cd386b0bda2cd48352c73b (diff) | |
parent | 752097cec53eea111d087c545179b421e2bde98a (diff) |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
Diffstat (limited to 'drivers/video')
-rw-r--r-- | drivers/video/backlight/Kconfig | 23 | ||||
-rw-r--r-- | drivers/video/backlight/Makefile | 2 | ||||
-rw-r--r-- | drivers/video/backlight/backlight.c | 2 | ||||
-rw-r--r-- | drivers/video/backlight/corgi_bl.c | 22 | ||||
-rw-r--r-- | drivers/video/backlight/cr_bllcd.c | 35 | ||||
-rw-r--r-- | drivers/video/backlight/lcd.c | 2 | ||||
-rw-r--r-- | drivers/video/backlight/ltv350qv.c | 330 | ||||
-rw-r--r-- | drivers/video/backlight/ltv350qv.h | 95 | ||||
-rw-r--r-- | drivers/video/imacfb.c | 2 | ||||
-rw-r--r-- | drivers/video/output.c | 29 | ||||
-rw-r--r-- | drivers/video/platinumfb.c | 48 | ||||
-rw-r--r-- | drivers/video/xilinxfb.c | 353 |
12 files changed, 772 insertions, 171 deletions
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 2580f5fa248..9609a6c676b 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -24,6 +24,18 @@ config LCD_CLASS_DEVICE To have support for your specific LCD panel you will have to select the proper drivers which depend on this option. +config LCD_LTV350QV + tristate "Samsung LTV350QV LCD Panel" + depends on LCD_CLASS_DEVICE && SPI_MASTER + default n + help + If you have a Samsung LTV350QV LCD panel, say y to include a + power control driver for it. The panel starts up in power + off state, so you need this driver in order to see any + output. + + The LTV350QV panel is present on all ATSTK1000 boards. + # # Backlight # @@ -39,12 +51,13 @@ config BACKLIGHT_CLASS_DEVICE select the proper drivers which depend on this option. config BACKLIGHT_CORGI - tristate "Sharp Corgi Backlight Driver (SL Series)" - depends on BACKLIGHT_CLASS_DEVICE && PXA_SHARPSL - default y + tristate "Generic (aka Sharp Corgi) Backlight Driver" + depends on BACKLIGHT_CLASS_DEVICE + default n help - If you have a Sharp Zaurus SL-C7xx, SL-Cxx00 or SL-6000x say y to enable the - backlight driver. + Say y to enable the generic platform backlight driver previously + known as the Corgi backlight driver. If you have a Sharp Zaurus + SL-C7xx, SL-Cxx00 or SL-6000x say y. Most users can say n. config BACKLIGHT_LOCOMO tristate "Sharp LOCOMO LCD/Backlight Driver" diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index c6e2266f63e..965a78b1811 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -1,6 +1,8 @@ # Backlight & LCD drivers obj-$(CONFIG_LCD_CLASS_DEVICE) += lcd.o +obj-$(CONFIG_LCD_LTV350QV) += ltv350qv.o + obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o obj-$(CONFIG_BACKLIGHT_CORGI) += corgi_bl.o obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c index b26de8cf311..4840fe217e4 100644 --- a/drivers/video/backlight/backlight.c +++ b/drivers/video/backlight/backlight.c @@ -164,7 +164,7 @@ static ssize_t backlight_show_actual_brightness(struct device *dev, return rc; } -struct class *backlight_class; +static struct class *backlight_class; static void bl_device_release(struct device *dev) { diff --git a/drivers/video/backlight/corgi_bl.c b/drivers/video/backlight/corgi_bl.c index ce00e18a4e5..4d4d037e3ec 100644 --- a/drivers/video/backlight/corgi_bl.c +++ b/drivers/video/backlight/corgi_bl.c @@ -18,13 +18,11 @@ #include <linux/mutex.h> #include <linux/fb.h> #include <linux/backlight.h> -#include <asm/arch/sharpsl.h> -#include <asm/hardware/sharpsl_pm.h> static int corgibl_intensity; static struct backlight_properties corgibl_data; static struct backlight_device *corgi_backlight_device; -static struct corgibl_machinfo *bl_machinfo; +static struct generic_bl_info *bl_machinfo; static unsigned long corgibl_flags; #define CORGIBL_SUSPENDED 0x01 @@ -32,7 +30,6 @@ static unsigned long corgibl_flags; static int corgibl_send_intensity(struct backlight_device *bd) { - void (*corgi_kick_batt)(void); int intensity = bd->props.brightness; if (bd->props.power != FB_BLANK_UNBLANK) @@ -48,11 +45,8 @@ static int corgibl_send_intensity(struct backlight_device *bd) corgibl_intensity = intensity; - corgi_kick_batt = symbol_get(sharpsl_battery_kick); - if (corgi_kick_batt) { - corgi_kick_batt(); - symbol_put(sharpsl_battery_kick); - } + if (bl_machinfo->kick_battery) + bl_machinfo->kick_battery(); return 0; } @@ -107,13 +101,17 @@ static struct backlight_ops corgibl_ops = { static int corgibl_probe(struct platform_device *pdev) { - struct corgibl_machinfo *machinfo = pdev->dev.platform_data; + struct generic_bl_info *machinfo = pdev->dev.platform_data; + const char *name = "generic-bl"; bl_machinfo = machinfo; if (!machinfo->limit_mask) machinfo->limit_mask = -1; - corgi_backlight_device = backlight_device_register ("corgi-bl", + if (machinfo->name) + name = machinfo->name; + + corgi_backlight_device = backlight_device_register (name, &pdev->dev, NULL, &corgibl_ops); if (IS_ERR (corgi_backlight_device)) return PTR_ERR (corgi_backlight_device); @@ -149,7 +147,7 @@ static struct platform_driver corgibl_driver = { .suspend = corgibl_suspend, .resume = corgibl_resume, .driver = { - .name = "corgi-bl", + .name = "generic-bl", }, }; diff --git a/drivers/video/backlight/cr_bllcd.c b/drivers/video/backlight/cr_bllcd.c index b7904da51b2..92e201e81fb 100644 --- a/drivers/video/backlight/cr_bllcd.c +++ b/drivers/video/backlight/cr_bllcd.c @@ -171,13 +171,11 @@ static struct lcd_ops cr_lcd_ops = { static int cr_backlight_probe(struct platform_device *pdev) { + struct backlight_device *bdp; + struct lcd_device *ldp; struct cr_panel *crp; u8 dev_en; - crp = kzalloc(sizeof(*crp), GFP_KERNEL); - if (crp == NULL) - return -ENOMEM; - lpc_dev = pci_get_device(PCI_VENDOR_ID_INTEL, CRVML_DEVICE_LPC, NULL); if (!lpc_dev) { @@ -193,27 +191,34 @@ static int cr_backlight_probe(struct platform_device *pdev) return -ENODEV; } - crp->cr_backlight_device = backlight_device_register("cr-backlight", - &pdev->dev, NULL, - &cr_backlight_ops); - if (IS_ERR(crp->cr_backlight_device)) { + bdp = backlight_device_register("cr-backlight", + &pdev->dev, NULL, &cr_backlight_ops); + if (IS_ERR(bdp)) { pci_dev_put(lpc_dev); - return PTR_ERR(crp->cr_backlight_device); + return PTR_ERR(bdp); } - crp->cr_lcd_device = lcd_device_register("cr-lcd", - &pdev->dev, NULL, - &cr_lcd_ops); - - if (IS_ERR(crp->cr_lcd_device)) { + ldp = lcd_device_register("cr-lcd", &pdev->dev, NULL, &cr_lcd_ops); + if (IS_ERR(ldp)) { + backlight_device_unregister(bdp); pci_dev_put(lpc_dev); - return PTR_ERR(crp->cr_backlight_device); + return PTR_ERR(bdp); } pci_read_config_dword(lpc_dev, CRVML_REG_GPIOBAR, &gpio_bar); gpio_bar &= ~0x3F; + crp = kzalloc(sizeof(*crp), GFP_KERNEL); + if (!crp) { + lcd_device_unregister(ldp); + backlight_device_unregister(bdp); + pci_dev_put(lpc_dev); + return -ENOMEM; + } + + crp->cr_backlight_device = bdp; + crp->cr_lcd_device = ldp; crp->cr_backlight_device->props.power = FB_BLANK_UNBLANK; crp->cr_backlight_device->props.brightness = 0; crp->cr_backlight_device->props.max_brightness = 0; diff --git a/drivers/video/backlight/lcd.c b/drivers/video/backlight/lcd.c index 6f652c65fae..299fd318dd4 100644 --- a/drivers/video/backlight/lcd.c +++ b/drivers/video/backlight/lcd.c @@ -149,7 +149,7 @@ static ssize_t lcd_show_max_contrast(struct device *dev, return sprintf(buf, "%d\n", ld->props.max_contrast); } -struct class *lcd_class; +static struct class *lcd_class; static void lcd_device_release(struct device *dev) { diff --git a/drivers/video/backlight/ltv350qv.c b/drivers/video/backlight/ltv350qv.c new file mode 100644 index 00000000000..2eb206bf73e --- /dev/null +++ b/drivers/video/backlight/ltv350qv.c @@ -0,0 +1,330 @@ +/* + * Power control for Samsung LTV350QV Quarter VGA LCD Panel + * + * Copyright (C) 2006, 2007 Atmel Corporation + * + * 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/delay.h> +#include <linux/err.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <linux/lcd.h> +#include <linux/module.h> +#include <linux/spi/spi.h> + +#include "ltv350qv.h" + +#define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL) + +struct ltv350qv { + struct spi_device *spi; + u8 *buffer; + int power; + struct lcd_device *ld; +}; + +/* + * The power-on and power-off sequences are taken from the + * LTV350QV-F04 data sheet from Samsung. The register definitions are + * taken from the S6F2002 command list also from Samsung. Both + * documents are distributed with the AVR32 Linux BSP CD from Atmel. + * + * There's still some voodoo going on here, but it's a lot better than + * in the first incarnation of the driver where all we had was the raw + * numbers from the initialization sequence. + */ +static int ltv350qv_write_reg(struct ltv350qv *lcd, u8 reg, u16 val) +{ + struct spi_message msg; + struct spi_transfer index_xfer = { + .len = 3, + .cs_change = 1, + }; + struct spi_transfer value_xfer = { + .len = 3, + }; + + spi_message_init(&msg); + + /* register index */ + lcd->buffer[0] = LTV_OPC_INDEX; + lcd->buffer[1] = 0x00; + lcd->buffer[2] = reg & 0x7f; + index_xfer.tx_buf = lcd->buffer; + spi_message_add_tail(&index_xfer, &msg); + + /* register value */ + lcd->buffer[4] = LTV_OPC_DATA; + lcd->buffer[5] = val >> 8; + lcd->buffer[6] = val; + value_xfer.tx_buf = lcd->buffer + 4; + spi_message_add_tail(&value_xfer, &msg); + + return spi_sync(lcd->spi, &msg); +} + +/* The comments are taken straight from the data sheet */ +static int ltv350qv_power_on(struct ltv350qv *lcd) +{ + int ret; + + /* Power On Reset Display off State */ + if (ltv350qv_write_reg(lcd, LTV_PWRCTL1, 0x0000)) + goto err; + msleep(15); + + /* Power Setting Function 1 */ + if (ltv350qv_write_reg(lcd, LTV_PWRCTL1, LTV_VCOM_DISABLE)) + goto err; + if (ltv350qv_write_reg(lcd, LTV_PWRCTL2, LTV_VCOML_ENABLE)) + goto err_power1; + + /* Power Setting Function 2 */ + if (ltv350qv_write_reg(lcd, LTV_PWRCTL1, + LTV_VCOM_DISABLE | LTV_DRIVE_CURRENT(5) + | LTV_SUPPLY_CURRENT(5))) + goto err_power2; + + msleep(55); + + /* Instruction Setting */ + ret = ltv350qv_write_reg(lcd, LTV_IFCTL, + LTV_NMD | LTV_REV | LTV_NL(0x1d)); + ret |= ltv350qv_write_reg(lcd, LTV_DATACTL, + LTV_DS_SAME | LTV_CHS_480 + | LTV_DF_RGB | LTV_RGB_BGR); + ret |= ltv350qv_write_reg(lcd, LTV_ENTRY_MODE, + LTV_VSPL_ACTIVE_LOW + | LTV_HSPL_ACTIVE_LOW + | LTV_DPL_SAMPLE_RISING + | LTV_EPL_ACTIVE_LOW + | LTV_SS_RIGHT_TO_LEFT); + ret |= ltv350qv_write_reg(lcd, LTV_GATECTL1, LTV_CLW(3)); + ret |= ltv350qv_write_reg(lcd, LTV_GATECTL2, + LTV_NW_INV_1LINE | LTV_FWI(3)); + ret |= ltv350qv_write_reg(lcd, LTV_VBP, 0x000a); + ret |= ltv350qv_write_reg(lcd, LTV_HBP, 0x0021); + ret |= ltv350qv_write_reg(lcd, LTV_SOTCTL, LTV_SDT(3) | LTV_EQ(0)); + ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(0), 0x0103); + ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(1), 0x0301); + ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(2), 0x1f0f); + ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(3), 0x1f0f); + ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(4), 0x0707); + ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(5), 0x0307); + ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(6), 0x0707); + ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(7), 0x0000); + ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(8), 0x0004); + ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(9), 0x0000); + if (ret) + goto err_settings; + + /* Wait more than 2 frames */ + msleep(20); + + /* Display On Sequence */ + ret = ltv350qv_write_reg(lcd, LTV_PWRCTL1, + LTV_VCOM_DISABLE | LTV_VCOMOUT_ENABLE + | LTV_POWER_ON | LTV_DRIVE_CURRENT(5) + | LTV_SUPPLY_CURRENT(5)); + ret |= ltv350qv_write_reg(lcd, LTV_GATECTL2, + LTV_NW_INV_1LINE | LTV_DSC | LTV_FWI(3)); + if (ret) + goto err_disp_on; + + /* Display should now be ON. Phew. */ + return 0; + +err_disp_on: + /* + * Try to recover. Error handling probably isn't very useful + * at this point, just make a best effort to switch the panel + * off. + */ + ltv350qv_write_reg(lcd, LTV_PWRCTL1, + LTV_VCOM_DISABLE | LTV_DRIVE_CURRENT(5) + | LTV_SUPPLY_CURRENT(5)); + ltv350qv_write_reg(lcd, LTV_GATECTL2, + LTV_NW_INV_1LINE | LTV_FWI(3)); +err_settings: +err_power2: +err_power1: + ltv350qv_write_reg(lcd, LTV_PWRCTL2, 0x0000); + msleep(1); +err: + ltv350qv_write_reg(lcd, LTV_PWRCTL1, LTV_VCOM_DISABLE); + return -EIO; +} + +static int ltv350qv_power_off(struct ltv350qv *lcd) +{ + int ret; + + /* Display Off Sequence */ + ret = ltv350qv_write_reg(lcd, LTV_PWRCTL1, + LTV_VCOM_DISABLE + | LTV_DRIVE_CURRENT(5) + | LTV_SUPPLY_CURRENT(5)); + ret |= ltv350qv_write_reg(lcd, LTV_GATECTL2, + LTV_NW_INV_1LINE | LTV_FWI(3)); + + /* Power down setting 1 */ + ret |= ltv350qv_write_reg(lcd, LTV_PWRCTL2, 0x0000); + + /* Wait at least 1 ms */ + msleep(1); + + /* Power down setting 2 */ + ret |= ltv350qv_write_reg(lcd, LTV_PWRCTL1, LTV_VCOM_DISABLE); + + /* + * No point in trying to recover here. If we can't switch the + * panel off, what are we supposed to do other than inform the + * user about the failure? + */ + if (ret) + return -EIO; + + /* Display power should now be OFF */ + return 0; +} + +static int ltv350qv_power(struct ltv350qv *lcd, int power) +{ + int ret = 0; + + if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power)) + ret = ltv350qv_power_on(lcd); + else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power)) + ret = ltv350qv_power_off(lcd); + + if (!ret) + lcd->power = power; + + return ret; +} + +static int ltv350qv_set_power(struct lcd_device *ld, int power) +{ + struct ltv350qv *lcd = lcd_get_data(ld); + + return ltv350qv_power(lcd, power); +} + +static int ltv350qv_get_power(struct lcd_device *ld) +{ + struct ltv350qv *lcd = lcd_get_data(ld); + + return lcd->power; +} + +static struct lcd_ops ltv_ops = { + .get_power = ltv350qv_get_power, + .set_power = ltv350qv_set_power, +}; + +static int __devinit ltv350qv_probe(struct spi_device *spi) +{ + struct ltv350qv *lcd; + struct lcd_device *ld; + int ret; + + lcd = kzalloc(sizeof(struct ltv350qv), GFP_KERNEL); + if (!lcd) + return -ENOMEM; + + lcd->spi = spi; + lcd->power = FB_BLANK_POWERDOWN; + lcd->buffer = kzalloc(8, GFP_KERNEL); + + ld = lcd_device_register("ltv350qv", &spi->dev, lcd, <v_ops); + if (IS_ERR(ld)) { + ret = PTR_ERR(ld); + goto out_free_lcd; + } + lcd->ld = ld; + + ret = ltv350qv_power(lcd, FB_BLANK_UNBLANK); + if (ret) + goto out_unregister; + + dev_set_drvdata(&spi->dev, lcd); + + return 0; + +out_unregister: + lcd_device_unregister(ld); +out_free_lcd: + kfree(lcd); + return ret; +} + +static int __devexit ltv350qv_remove(struct spi_device *spi) +{ + struct ltv350qv *lcd = dev_get_drvdata(&spi->dev); + + ltv350qv_power(lcd, FB_BLANK_POWERDOWN); + lcd_device_unregister(lcd->ld); + kfree(lcd); + + return 0; +} + +#ifdef CONFIG_PM +static int ltv350qv_suspend(struct spi_device *spi, pm_message_t state) +{ + struct ltv350qv *lcd = dev_get_drvdata(&spi->dev); + + return ltv350qv_power(lcd, FB_BLANK_POWERDOWN); +} + +static int ltv350qv_resume(struct spi_device *spi) +{ + struct ltv350qv *lcd = dev_get_drvdata(&spi->dev); + + return ltv350qv_power(lcd, FB_BLANK_UNBLANK); +} +#else +#define ltv350qv_suspend NULL +#define ltv350qv_resume NULL +#endif + +/* 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); + + ltv350qv_power(lcd, FB_BLANK_POWERDOWN); +} + +static struct spi_driver ltv350qv_driver = { + .driver = { + .name = "ltv350qv", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + + .probe = ltv350qv_probe, + .remove = __devexit_p(ltv350qv_remove), + .shutdown = ltv350qv_shutdown, + .suspend = ltv350qv_suspend, + .resume = ltv350qv_resume, +}; + +static int __init ltv350qv_init(void) +{ + return spi_register_driver(<v350qv_driver); +} + +static void __exit ltv350qv_exit(void) +{ + spi_unregister_driver(<v350qv_driver); +} +module_init(ltv350qv_init); +module_exit(ltv350qv_exit); + +MODULE_AUTHOR("Haavard Skinnemoen <hskinnemoen@atmel.com>"); +MODULE_DESCRIPTION("Samsung LTV350QV LCD Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/backlight/ltv350qv.h b/drivers/video/backlight/ltv350qv.h new file mode 100644 index 00000000000..189112e3fc7 --- /dev/null +++ b/drivers/video/backlight/ltv350qv.h @@ -0,0 +1,95 @@ +/* + * Register definitions for Samsung LTV350QV Quarter VGA LCD Panel + * + * Copyright (C) 2006, 2007 Atmel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __LTV350QV_H +#define __LTV350QV_H + +#define LTV_OPC_INDEX 0x74 +#define LTV_OPC_DATA 0x76 + +#define LTV_ID 0x00 /* ID Read */ +#define LTV_IFCTL 0x01 /* Display Interface Control */ +#define LTV_DATACTL 0x02 /* Display Data Control */ +#define LTV_ENTRY_MODE 0x03 /* Entry Mode */ +#define LTV_GATECTL1 0x04 /* Gate Control 1 */ +#define LTV_GATECTL2 0x05 /* Gate Control 2 */ +#define LTV_VBP 0x06 /* Vertical Back Porch */ +#define LTV_HBP 0x07 /* Horizontal Back Porch */ +#define LTV_SOTCTL 0x08 /* Source Output Timing Control */ +#define LTV_PWRCTL1 0x09 /* Power Control 1 */ +#define LTV_PWRCTL2 0x0a /* Power Control 2 */ +#define LTV_GAMMA(x) (0x10 + (x)) /* Gamma control */ + +/* Bit definitions for LTV_IFCTL */ +#define LTV_IM (1 << 15) +#define LTV_NMD (1 << 14) +#define LTV_SSMD (1 << 13) +#define LTV_REV (1 << 7) +#define LTV_NL(x) (((x) & 0x001f) << 0) + +/* Bit definitions for LTV_DATACTL */ +#define LTV_DS_SAME (0 << 12) +#define LTV_DS_D_TO_S (1 << 12) +#define LTV_DS_S_TO_D (2 << 12) +#define LTV_CHS_384 (0 << 9) +#define LTV_CHS_480 (1 << 9) +#define LTV_CHS_492 (2 << 9) +#define LTV_DF_RGB (0 << 6) +#define LTV_DF_RGBX (1 << 6) +#define LTV_DF_XRGB (2 << 6) +#define LTV_RGB_RGB (0 << 2) +#define LTV_RGB_BGR (1 << 2) +#define LTV_RGB_GRB (2 << 2) +#define LTV_RGB_RBG (3 << 2) + +/* Bit definitions for LTV_ENTRY_MODE */ +#define LTV_VSPL_ACTIVE_LOW (0 << 15) +#define LTV_VSPL_ACTIVE_HIGH (1 << 15) +#define LTV_HSPL_ACTIVE_LOW (0 << 14) +#define LTV_HSPL_ACTIVE_HIGH (1 << 14) +#define LTV_DPL_SAMPLE_RISING (0 << 13) +#define LTV_DPL_SAMPLE_FALLING (1 << 13) +#define LTV_EPL_ACTIVE_LOW (0 << 12) +#define LTV_EPL_ACTIVE_HIGH (1 << 12) +#define LTV_SS_LEFT_TO_RIGHT (0 << 8) +#define LTV_SS_RIGHT_TO_LEFT (1 << 8) +#define LTV_STB (1 << 1) + +/* Bit definitions for LTV_GATECTL1 */ +#define LTV_CLW(x) (((x) & 0x0007) << 12) +#define LTV_GAON (1 << 5) +#define LTV_SDR (1 << 3) + +/* Bit definitions for LTV_GATECTL2 */ +#define LTV_NW_INV_FRAME (0 << 14) +#define LTV_NW_INV_1LINE (1 << 14) +#define LTV_NW_INV_2LINE (2 << 14) +#define LTV_DSC (1 << 12) +#define LTV_GIF (1 << 8) +#define LTV_FHN (1 << 7) +#define LTV_FTI(x) (((x) & 0x0003) << 4) +#define LTV_FWI(x) (((x) & 0x0003) << 0) + +/* Bit definitions for LTV_SOTCTL */ +#define LTV_SDT(x) (((x) & 0x0007) << 10) +#define LTV_EQ(x) (((x) & 0x0007) << 2) + +/* Bit definitions for LTV_PWRCTL1 */ +#define LTV_VCOM_DISABLE (1 << 14) +#define LTV_VCOMOUT_ENABLE (1 << 11) +#define LTV_POWER_ON (1 << 9) +#define LTV_DRIVE_CURRENT(x) (((x) & 0x0007) << 4) /* 0=off, 5=max */ +#define LTV_SUPPLY_CURRENT(x) (((x) & 0x0007) << 0) /* 0=off, 5=max */ + +/* Bit definitions for LTV_PWRCTL2 */ +#define LTV_VCOML_ENABLE (1 << 13) +#define LTV_VCOML_VOLTAGE(x) (((x) & 0x001f) << 8) /* 0=1V, 31=-1V */ +#define LTV_VCOMH_VOLTAGE(x) (((x) & 0x001f) << 0) /* 0=3V, 31=4.5V */ + +#endif /* __LTV350QV_H */ diff --git a/drivers/video/imacfb.c b/drivers/video/imacfb.c index 18ea4a54910..6455fd2a39f 100644 --- a/drivers/video/imacfb.c +++ b/drivers/video/imacfb.c @@ -58,7 +58,7 @@ static int model = M_UNKNOWN; static int manual_height; static int manual_width; -static int set_system(struct dmi_system_id *id) +static int set_system(const struct dmi_system_id *id) { printk(KERN_INFO "imacfb: %s detected - set system to %ld\n", id->ident, (long)id->driver_data); diff --git a/drivers/video/output.c b/drivers/video/output.c index 1473f2c892d..f2df5519c9c 100644 --- a/drivers/video/output.c +++ b/drivers/video/output.c @@ -31,7 +31,8 @@ MODULE_DESCRIPTION("Display Output Switcher Lowlevel Control Abstraction"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Luming Yu <luming.yu@intel.com>"); -static ssize_t video_output_show_state(struct class_device *dev,char *buf) +static ssize_t video_output_show_state(struct device *dev, + struct device_attribute *attr, char *buf) { ssize_t ret_size = 0; struct output_device *od = to_output_device(dev); @@ -40,8 +41,9 @@ static ssize_t video_output_show_state(struct class_device *dev,char *buf) return ret_size; } -static ssize_t video_output_store_state(struct class_device *dev, - const char *buf,size_t count) +static ssize_t video_output_store_state(struct device *dev, + struct device_attribute *attr, + const char *buf,size_t count) { char *endp; struct output_device *od = to_output_device(dev); @@ -60,21 +62,22 @@ static ssize_t video_output_store_state(struct class_device *dev, return count; } -static void video_output_class_release(struct class_device *dev) +static void video_output_release(struct device *dev) { struct output_device *od = to_output_device(dev); kfree(od); } -static struct class_device_attribute video_output_attributes[] = { +static struct device_attribute video_output_attributes[] = { __ATTR(state, 0644, video_output_show_state, video_output_store_state), __ATTR_NULL, }; + static struct class video_output_class = { .name = "video_output", - .release = video_output_class_release, - .class_dev_attrs = video_output_attributes, + .dev_release = video_output_release, + .dev_attrs = video_output_attributes, }; struct output_device *video_output_register(const char *name, @@ -91,11 +94,11 @@ struct output_device *video_output_register(const char *name, goto error_return; } new_dev->props = op; - new_dev->class_dev.class = &video_output_class; - new_dev->class_dev.dev = dev; - strlcpy(new_dev->class_dev.class_id,name,KOBJ_NAME_LEN); - class_set_devdata(&new_dev->class_dev,devdata); - ret_code = class_device_register(&new_dev->class_dev); + new_dev->dev.class = &video_output_class; + new_dev->dev.parent = dev; + strlcpy(new_dev->dev.bus_id,name, BUS_ID_SIZE); + dev_set_drvdata(&new_dev->dev, devdata); + ret_code = device_register(&new_dev->dev); if (ret_code) { kfree(new_dev); goto error_return; @@ -111,7 +114,7 @@ void video_output_unregister(struct output_device *dev) { if (!dev) return; - class_device_unregister(&dev->class_dev); + device_unregister(&dev->dev); } EXPORT_SYMBOL(video_output_unregister); diff --git a/drivers/video/platinumfb.c b/drivers/video/platinumfb.c index 8503e733a17..cbe71a5338d 100644 --- a/drivers/video/platinumfb.c +++ b/drivers/video/platinumfb.c @@ -17,6 +17,8 @@ * more details. */ +#undef DEBUG + #include <linux/module.h> #include <linux/kernel.h> #include <linux/errno.h> @@ -535,33 +537,35 @@ static int __devinit platinumfb_probe(struct of_device* odev, volatile __u8 *fbuffer; int bank0, bank1, bank2, bank3, rc; - printk(KERN_INFO "platinumfb: Found Apple Platinum video hardware\n"); + dev_info(&odev->dev, "Found Apple Platinum video hardware\n"); info = framebuffer_alloc(sizeof(*pinfo), &odev->dev); - if (info == NULL) + if (info == NULL) { + dev_err(&odev->dev, "Failed to allocate fbdev !\n"); return -ENOMEM; + } pinfo = info->par; if (of_address_to_resource(dp, 0, &pinfo->rsrc_reg) || of_address_to_resource(dp, 1, &pinfo->rsrc_fb)) { - printk(KERN_ERR "platinumfb: Can't get resources\n"); - framebuffer_release(info); - return -ENXIO; - } - if (!request_mem_region(pinfo->rsrc_reg.start, - pinfo->rsrc_reg.start - - pinfo->rsrc_reg.end + 1, - "platinumfb registers")) { + dev_err(&odev->dev, "Can't get resources\n"); framebuffer_release(info); return -ENXIO; } + dev_dbg(&odev->dev, " registers : 0x%llx...0x%llx\n", + (unsigned long long)pinfo->rsrc_reg.start, + (unsigned long long)pinfo->rsrc_reg.end); + dev_dbg(&odev->dev, " framebuffer: 0x%llx...0x%llx\n", + (unsigned long long)pinfo->rsrc_fb.start, + (unsigned long long)pinfo->rsrc_fb.end); + + /* Do not try to request register space, they overlap with the + * northbridge and that can fail. Only request framebuffer + */ if (!request_mem_region(pinfo->rsrc_fb.start, - pinfo->rsrc_fb.start - - pinfo->rsrc_fb.end + 1, + pinfo->rsrc_fb.end - pinfo->rsrc_fb.start + 1, "platinumfb framebuffer")) { - release_mem_region(pinfo->rsrc_reg.start, - pinfo->rsrc_reg.end - - pinfo->rsrc_reg.start + 1); + printk(KERN_ERR "platinumfb: Can't request framebuffer !\n"); framebuffer_release(info); return -ENXIO; } @@ -600,7 +604,8 @@ static int __devinit platinumfb_probe(struct of_device* odev, bank2 = fbuffer[0x200000] == 0x56; bank3 = fbuffer[0x300000] == 0x78; pinfo->total_vram = (bank0 + bank1 + bank2 + bank3) * 0x100000; - printk(KERN_INFO "platinumfb: Total VRAM = %dMB (%d%d%d%d)\n", (int) (pinfo->total_vram / 1024 / 1024), + printk(KERN_INFO "platinumfb: Total VRAM = %dMB (%d%d%d%d)\n", + (unsigned int) (pinfo->total_vram / 1024 / 1024), bank3, bank2, bank1, bank0); /* @@ -644,16 +649,15 @@ static int __devexit platinumfb_remove(struct of_device* odev) unregister_framebuffer (info); /* Unmap frame buffer and registers */ + iounmap(pinfo->frame_buffer); + iounmap(pinfo->platinum_regs); + iounmap(pinfo->cmap_regs); + release_mem_region(pinfo->rsrc_fb.start, pinfo->rsrc_fb.end - pinfo->rsrc_fb.start + 1); - release_mem_region(pinfo->rsrc_reg.start, - pinfo->rsrc_reg.end - - pinfo->rsrc_reg.start + 1); - iounmap(pinfo->frame_buffer); - iounmap(pinfo->platinum_regs); + release_mem_region(pinfo->cmap_regs_phys, 0x1000); - iounmap(pinfo->cmap_regs); framebuffer_release(info); diff --git a/drivers/video/xilinxfb.c b/drivers/video/xilinxfb.c index 6ef9733a18d..6ef99b2d13c 100644 --- a/drivers/video/xilinxfb.c +++ b/drivers/video/xilinxfb.c @@ -6,9 +6,12 @@ * Author: MontaVista Software, Inc. * source@mvista.com * - * 2002-2007 (c) MontaVista Software, Inc. This file is licensed under the - * terms of the GNU General Public License version 2. This program is licensed - * "as is" without any warranty of any kind, whether express or implied. + * 2002-2007 (c) MontaVista Software, Inc. + * 2007 (c) Secret Lab Technologies, Ltd. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. */ /* @@ -18,6 +21,7 @@ * Geert Uytterhoeven. */ +#include <linux/device.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/version.h> @@ -28,9 +32,12 @@ #include <linux/init.h> #include <linux/dma-mapping.h> #include <linux/platform_device.h> - +#if defined(CONFIG_OF) +#include <linux/of_device.h> +#include <linux/of_platform.h> +#endif #include <asm/io.h> -#include <syslib/virtex_devices.h> +#include <linux/xilinxfb.h> #define DRIVER_NAME "xilinxfb" #define DRIVER_DESCRIPTION "Xilinx TFT LCD frame buffer driver" @@ -63,12 +70,6 @@ */ #define BYTES_PER_PIXEL 4 #define BITS_PER_PIXEL (BYTES_PER_PIXEL * 8) -#define XRES 640 -#define YRES 480 -#define XRES_VIRTUAL 1024 -#define YRES_VIRTUAL YRES -#define LINE_LENGTH (XRES_VIRTUAL * BYTES_PER_PIXEL) -#define FB_SIZE (YRES_VIRTUAL * LINE_LENGTH) #define RED_SHIFT 16 #define GREEN_SHIFT 8 @@ -77,23 +78,26 @@ #define PALETTE_ENTRIES_NO 16 /* passed to fb_alloc_cmap() */ /* + * Default xilinxfb configuration + */ +static struct xilinxfb_platform_data xilinx_fb_default_pdata = { + .xres = 640, + .yres = 480, + .xvirt = 1024, + .yvirt = 480; +}; + +/* * Here are the default fb_fix_screeninfo and fb_var_screeninfo structures */ static struct fb_fix_screeninfo xilinx_fb_fix = { .id = "Xilinx", .type = FB_TYPE_PACKED_PIXELS, .visual = FB_VISUAL_TRUECOLOR, - .smem_len = FB_SIZE, - .line_length = LINE_LENGTH, .accel = FB_ACCEL_NONE }; static struct fb_var_screeninfo xilinx_fb_var = { - .xres = XRES, - .yres = YRES, - .xres_virtual = XRES_VIRTUAL, - .yres_virtual = YRES_VIRTUAL, - .bits_per_pixel = BITS_PER_PIXEL, .red = { RED_SHIFT, 8, 0 }, @@ -111,8 +115,9 @@ struct xilinxfb_drvdata { u32 regs_phys; /* phys. address of the control registers */ u32 __iomem *regs; /* virt. address of the control registers */ - unsigned char __iomem *fb_virt; /* virt. address of the frame buffer */ + void *fb_virt; /* virt. address of the frame buffer */ dma_addr_t fb_phys; /* phys. address of the frame buffer */ + int fb_alloced; /* Flag, was the fb memory alloced? */ u32 reg_ctrl_default; @@ -195,130 +200,136 @@ static struct fb_ops xilinxfb_ops = .fb_imageblit = cfb_imageblit, }; -/* === The device driver === */ +/* --------------------------------------------------------------------- + * Bus independent setup/teardown + */ -static int -xilinxfb_drv_probe(struct device *dev) +static int xilinxfb_assign(struct device *dev, unsigned long physaddr, + struct xilinxfb_platform_data *pdata) { - struct platform_device *pdev; - struct xilinxfb_platform_data *pdata; struct xilinxfb_drvdata *drvdata; - struct resource *regs_res; - int retval; - - if (!dev) - return -EINVAL; - - pdev = to_platform_device(dev); - pdata = pdev->dev.platform_data; + int rc; + int fbsize = pdata->xvirt * pdata->yvirt * BYTES_PER_PIXEL; + /* Allocate the driver data region */ drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL); if (!drvdata) { - printk(KERN_ERR "Couldn't allocate device private record\n"); + dev_err(dev, "Couldn't allocate device private record\n"); return -ENOMEM; } dev_set_drvdata(dev, drvdata); /* Map the control registers in */ - regs_res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!regs_res || (regs_res->end - regs_res->start + 1 < 8)) { - printk(KERN_ERR "Couldn't get registers resource\n"); - retval = -EFAULT; - goto failed1; + if (!request_mem_region(physaddr, 8, DRIVER_NAME)) { + dev_err(dev, "Couldn't lock memory region at 0x%08lX\n", + physaddr); + rc = -ENODEV; + goto err_region; } - - if (!request_mem_region(regs_res->start, 8, DRIVER_NAME)) { - printk(KERN_ERR - "Couldn't lock memory region at 0x%08X\n", - regs_res->start); - retval = -EBUSY; - goto failed1; + drvdata->regs_phys = physaddr; + drvdata->regs = ioremap(physaddr, 8); + if (!drvdata->regs) { + dev_err(dev, "Couldn't lock memory region at 0x%08lX\n", + physaddr); + rc = -ENODEV; + goto err_map; } - drvdata->regs = (u32 __iomem*) ioremap(regs_res->start, 8); - drvdata->regs_phys = regs_res->start; /* Allocate the framebuffer memory */ - drvdata->fb_virt = dma_alloc_coherent(dev, PAGE_ALIGN(FB_SIZE), - &drvdata->fb_phys, GFP_KERNEL); + if (pdata->fb_phys) { + drvdata->fb_phys = pdata->fb_phys; + drvdata->fb_virt = ioremap(pdata->fb_phys, fbsize); + } else { + drvdata->fb_alloced = 1; + drvdata->fb_virt = dma_alloc_coherent(dev, PAGE_ALIGN(fbsize), + &drvdata->fb_phys, GFP_KERNEL); + } + if (!drvdata->fb_virt) { - printk(KERN_ERR "Could not allocate frame buffer memory\n"); - retval = -ENOMEM; - goto failed2; + dev_err(dev, "Could not allocate frame buffer memory\n"); + rc = -ENOMEM; + goto err_fbmem; } /* Clear (turn to black) the framebuffer */ - memset_io((void *) drvdata->fb_virt, 0, FB_SIZE); + memset_io((void __iomem *)drvdata->fb_virt, 0, fbsize); /* Tell the hardware where the frame buffer is */ xilinx_fb_out_be32(drvdata, REG_FB_ADDR, drvdata->fb_phys); /* Turn on the display */ drvdata->reg_ctrl_default = REG_CTRL_ENABLE; - if (pdata && pdata->rotate_screen) + if (pdata->rotate_screen) drvdata->reg_ctrl_default |= REG_CTRL_ROTATE; xilinx_fb_out_be32(drvdata, REG_CTRL, drvdata->reg_ctrl_default); /* Fill struct fb_info */ drvdata->info.device = dev; - drvdata->info.screen_base = drvdata->fb_virt; + drvdata->info.screen_base = (void __iomem *)drvdata->fb_virt; drvdata->info.fbops = &xilinxfb_ops; drvdata->info.fix = xilinx_fb_fix; drvdata->info.fix.smem_start = drvdata->fb_phys; - drvdata->info.pseudo_palette = drvdata->pseudo_palette; - - if (fb_alloc_cmap(&drvdata->info.cmap, PALETTE_ENTRIES_NO, 0) < 0) { - printk(KERN_ERR "Fail to allocate colormap (%d entries)\n", - PALETTE_ENTRIES_NO); - retval = -EFAULT; - goto failed3; - } + drvdata->info.fix.smem_len = fbsize; + drvdata->info.fix.line_length = pdata->xvirt * BYTES_PER_PIXEL; + drvdata->info.pseudo_palette = drvdata->pseudo_palette; drvdata->info.flags = FBINFO_DEFAULT; - if (pdata) { - xilinx_fb_var.height = pdata->screen_height_mm; - xilinx_fb_var.width = pdata->screen_width_mm; - } drvdata->info.var = xilinx_fb_var; + drvdata->info.var.height = pdata->screen_height_mm; + drvdata->info.var.width = pdata->screen_width_mm; + drvdata->info.var.xres = pdata->xres; + drvdata->info.var.yres = pdata->yres; + drvdata->info.var.xres_virtual = pdata->xvirt; + drvdata->info.var.yres_virtual = pdata->yvirt; + + /* Allocate a colour map */ + rc = fb_alloc_cmap(&drvdata->info.cmap, PALETTE_ENTRIES_NO, 0); + if (rc) { + dev_err(dev, "Fail to allocate colormap (%d entries)\n", + PALETTE_ENTRIES_NO); + goto err_cmap; + } /* Register new frame buffer */ - if (register_framebuffer(&drvdata->info) < 0) { - printk(KERN_ERR "Could not register frame buffer\n"); - retval = -EINVAL; - goto failed4; + rc = register_framebuffer(&drvdata->info); + if (rc) { + dev_err(dev, "Could not register frame buffer\n"); + goto err_regfb; } + /* Put a banner in the log (for DEBUG) */ + dev_dbg(dev, "regs: phys=%lx, virt=%p\n", physaddr, drvdata->regs); + dev_dbg(dev, "fb: phys=%p, virt=%p, size=%x\n", + (void*)drvdata->fb_phys, drvdata->fb_virt, fbsize); + return 0; /* success */ -failed4: +err_regfb: fb_dealloc_cmap(&drvdata->info.cmap); -failed3: - dma_free_coherent(dev, PAGE_ALIGN(FB_SIZE), drvdata->fb_virt, - drvdata->fb_phys); - +err_cmap: + if (drvdata->fb_alloced) + dma_free_coherent(dev, PAGE_ALIGN(fbsize), drvdata->fb_virt, + drvdata->fb_phys); /* Turn off the display */ xilinx_fb_out_be32(drvdata, REG_CTRL, 0); + +err_fbmem: iounmap(drvdata->regs); -failed2: - release_mem_region(regs_res->start, 8); +err_map: + release_mem_region(physaddr, 8); -failed1: +err_region: kfree(drvdata); dev_set_drvdata(dev, NULL); - return retval; + return rc; } -static int -xilinxfb_drv_remove(struct device *dev) +static int xilinxfb_release(struct device *dev) { - struct xilinxfb_drvdata *drvdata; - - if (!dev) - return -ENODEV; - - drvdata = (struct xilinxfb_drvdata *) dev_get_drvdata(dev); + struct xilinxfb_drvdata *drvdata = dev_get_drvdata(dev); #if !defined(CONFIG_FRAMEBUFFER_CONSOLE) && defined(CONFIG_LOGO) xilinx_fb_blank(VESA_POWERDOWN, &drvdata->info); @@ -328,8 +339,9 @@ xilinxfb_drv_remove(struct device *dev) fb_dealloc_cmap(&drvdata->info.cmap); - dma_free_coherent(dev, PAGE_ALIGN(FB_SIZE), drvdata->fb_virt, - drvdata->fb_phys); + if (drvdata->fb_alloced) + dma_free_coherent(dev, PAGE_ALIGN(drvdata->info.fix.smem_len), + drvdata->fb_virt, drvdata->fb_phys); /* Turn off the display */ xilinx_fb_out_be32(drvdata, REG_CTRL, 0); @@ -343,29 +355,168 @@ xilinxfb_drv_remove(struct device *dev) return 0; } +/* --------------------------------------------------------------------- + * Platform bus binding + */ + +static int +xilinxfb_platform_probe(struct platform_device *pdev) +{ + struct xilinxfb_platform_data *pdata; + struct resource *res; -static struct device_driver xilinxfb_driver = { - .name = DRIVER_NAME, - .bus = &platform_bus_type, + /* Find the registers address */ + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!res) { + dev_err(&pdev->dev, "Couldn't get registers resource\n"); + return -ENODEV; + } - .probe = xilinxfb_drv_probe, - .remove = xilinxfb_drv_remove + /* If a pdata structure is provided, then extract the parameters */ + pdata = &xilinx_fb_default_pdata; + if (pdev->dev.platform_data) { + pdata = pdev->dev.platform_data; + if (!pdata->xres) + pdata->xres = xilinx_fb_default_pdata.xres; + if (!pdata->yres) + pdata->yres = xilinx_fb_default_pdata.yres; + if (!pdata->xvirt) + pdata->xvirt = xilinx_fb_default_pdata.xvirt; + if (!pdata->yvirt) + pdata->yvirt = xilinx_fb_default_pdata.yvirt; + } + + return xilinxfb_assign(&pdev->dev, res->start, pdata); +} + +static int +xilinxfb_platform_remove(struct platform_device *pdev) +{ + return xilinxfb_release(&pdev->dev); +} + + +static struct platform_driver xilinxfb_platform_driver = { + .probe = xilinxfb_platform_probe, + .remove = xilinxfb_platform_remove, + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + }, +}; + +/* --------------------------------------------------------------------- + * OF bus binding + */ + +#if defined(CONFIG_OF) +static int __devinit +xilinxfb_of_probe(struct of_device *op, const struct of_device_id *match) +{ + struct resource res; + const u32 *prop; + struct xilinxfb_platform_data pdata; + int size, rc; + + /* Copy with the default pdata (not a ptr reference!) */ + pdata = xilinx_fb_default_pdata; + + dev_dbg(&op->dev, "xilinxfb_of_probe(%p, %p)\n", op, match); + + rc = of_address_to_resource(op->node, 0, &res); + if (rc) { + dev_err(&op->dev, "invalid address\n"); + return rc; + } + + prop = of_get_property(op->node, "phys-size", &size); + if ((prop) && (size >= sizeof(u32)*2)) { + pdata.screen_width_mm = prop[0]; + pdata.screen_height_mm = prop[1]; + } + + prop = of_get_property(op->node, "resolution", &size); + if ((prop) && (size >= sizeof(u32)*2)) { + pdata.xres = prop[0]; + pdata.yres = prop[1]; + } + + prop = of_get_property(op->node, "virtual-resolution", &size); + if ((prop) && (size >= sizeof(u32)*2)) { + pdata.xvirt = prop[0]; + pdata.yvirt = prop[1]; + } + + if (of_find_property(op->node, "rotate-display", NULL)) + pdata.rotate_screen = 1; + + return xilinxfb_assign(&op->dev, res.start, &pdata); +} + +static int __devexit xilinxfb_of_remove(struct of_device *op) +{ + return xilinxfb_release(&op->dev); +} + +/* Match table for of_platform binding */ +static struct of_device_id __devinit xilinxfb_of_match[] = { + { .compatible = "xilinx,ml300-fb", }, + {}, }; +MODULE_DEVICE_TABLE(of, xilinxfb_of_match); + +static struct of_platform_driver xilinxfb_of_driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .match_table = xilinxfb_of_match, + .probe = xilinxfb_of_probe, + .remove = __devexit_p(xilinxfb_of_remove), + .driver = { + .name = DRIVER_NAME, + }, +}; + +/* Registration helpers to keep the number of #ifdefs to a minimum */ +static inline int __init xilinxfb_of_register(void) +{ + pr_debug("xilinxfb: calling of_register_platform_driver()\n"); + return of_register_platform_driver(&xilinxfb_of_driver); +} + +static inline void __exit xilinxfb_of_unregister(void) +{ + of_unregister_platform_driver(&xilinxfb_of_driver); +} +#else /* CONFIG_OF */ +/* CONFIG_OF not enabled; do nothing helpers */ +static inline int __init xilinxfb_of_register(void) { return 0; } +static inline void __exit xilinxfb_of_unregister(void) { } +#endif /* CONFIG_OF */ + +/* --------------------------------------------------------------------- + * Module setup and teardown + */ static int __init xilinxfb_init(void) { - /* - * No kernel boot options used, - * so we just need to register the driver - */ - return driver_register(&xilinxfb_driver); + int rc; + rc = xilinxfb_of_register(); + if (rc) + return rc; + + rc = platform_driver_register(&xilinxfb_platform_driver); + if (rc) + xilinxfb_of_unregister(); + + return rc; } static void __exit xilinxfb_cleanup(void) { - driver_unregister(&xilinxfb_driver); + platform_driver_unregister(&xilinxfb_platform_driver); + xilinxfb_of_unregister(); } module_init(xilinxfb_init); |