video: move fbdev to drivers/video/fbdev
The drivers/video directory is a mess. It contains generic video related files, directories for backlight, console, linux logo, lots of fbdev device drivers, fbdev framework files. Make some order into the chaos by creating drivers/video/fbdev directory, and move all fbdev related files there. No functionality is changed, although I guess it is possible that some subtle Makefile build order related issue could be created by this patch. Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com> Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Acked-by: Geert Uytterhoeven <geert@linux-m68k.org> Acked-by: Rob Clark <robdclark@gmail.com> Acked-by: Jingoo Han <jg1.han@samsung.com> Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
This commit is contained in:
52
drivers/video/fbdev/omap/Kconfig
Normal file
52
drivers/video/fbdev/omap/Kconfig
Normal file
@@ -0,0 +1,52 @@
|
||||
config FB_OMAP
|
||||
tristate "OMAP frame buffer support"
|
||||
depends on FB
|
||||
depends on ARCH_OMAP1
|
||||
select FB_CFB_FILLRECT
|
||||
select FB_CFB_COPYAREA
|
||||
select FB_CFB_IMAGEBLIT
|
||||
help
|
||||
Frame buffer driver for OMAP based boards.
|
||||
|
||||
config FB_OMAP_LCDC_EXTERNAL
|
||||
bool "External LCD controller support"
|
||||
depends on FB_OMAP
|
||||
help
|
||||
Say Y here, if you want to have support for boards with an
|
||||
external LCD controller connected to the SoSSI/RFBI interface.
|
||||
|
||||
config FB_OMAP_LCDC_HWA742
|
||||
bool "Epson HWA742 LCD controller support"
|
||||
depends on FB_OMAP && FB_OMAP_LCDC_EXTERNAL
|
||||
help
|
||||
Say Y here if you want to have support for the external
|
||||
Epson HWA742 LCD controller.
|
||||
|
||||
config FB_OMAP_MANUAL_UPDATE
|
||||
bool "Default to manual update mode"
|
||||
depends on FB_OMAP && FB_OMAP_LCDC_EXTERNAL
|
||||
help
|
||||
Say Y here, if your user-space applications are capable of
|
||||
notifying the frame buffer driver when a change has occurred in
|
||||
the frame buffer content and thus a reload of the image data to
|
||||
the external frame buffer is required. If unsure, say N.
|
||||
|
||||
config FB_OMAP_LCD_MIPID
|
||||
bool "MIPI DBI-C/DCS compatible LCD support"
|
||||
depends on FB_OMAP && SPI_MASTER
|
||||
help
|
||||
Say Y here if you want to have support for LCDs compatible with
|
||||
the Mobile Industry Processor Interface DBI-C/DCS
|
||||
specification. (Supported LCDs: Philips LPH8923, Sharp LS041Y3)
|
||||
|
||||
config FB_OMAP_DMA_TUNE
|
||||
bool "Set DMA SDRAM access priority high"
|
||||
depends on FB_OMAP
|
||||
help
|
||||
On systems in which video memory is in system memory
|
||||
(SDRAM) this will speed up graphics DMA operations.
|
||||
If you have such a system and want to use rotation
|
||||
answer yes. Answer no if you have a dedicated video
|
||||
memory, or don't use any of the accelerated features.
|
||||
|
||||
|
26
drivers/video/fbdev/omap/Makefile
Normal file
26
drivers/video/fbdev/omap/Makefile
Normal file
@@ -0,0 +1,26 @@
|
||||
#
|
||||
# Makefile for the OMAP1 framebuffer device driver
|
||||
#
|
||||
|
||||
obj-$(CONFIG_FB_OMAP) += omapfb.o
|
||||
|
||||
objs-yy := omapfb_main.o lcdc.o
|
||||
|
||||
objs-y$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += sossi.o
|
||||
|
||||
objs-y$(CONFIG_FB_OMAP_LCDC_HWA742) += hwa742.o
|
||||
|
||||
objs-y$(CONFIG_MACH_AMS_DELTA) += lcd_ams_delta.o
|
||||
objs-y$(CONFIG_MACH_OMAP_H3) += lcd_h3.o
|
||||
objs-y$(CONFIG_MACH_OMAP_PALMTE) += lcd_palmte.o
|
||||
objs-y$(CONFIG_MACH_OMAP_PALMTT) += lcd_palmtt.o
|
||||
objs-y$(CONFIG_MACH_OMAP_PALMZ71) += lcd_palmz71.o
|
||||
objs-$(CONFIG_ARCH_OMAP16XX)$(CONFIG_MACH_OMAP_INNOVATOR) += lcd_inn1610.o
|
||||
objs-$(CONFIG_ARCH_OMAP15XX)$(CONFIG_MACH_OMAP_INNOVATOR) += lcd_inn1510.o
|
||||
objs-y$(CONFIG_MACH_OMAP_OSK) += lcd_osk.o
|
||||
|
||||
objs-y$(CONFIG_FB_OMAP_LCD_MIPID) += lcd_mipid.o
|
||||
objs-y$(CONFIG_MACH_HERALD) += lcd_htcherald.o
|
||||
|
||||
omapfb-objs := $(objs-yy)
|
||||
|
1059
drivers/video/fbdev/omap/hwa742.c
Normal file
1059
drivers/video/fbdev/omap/hwa742.c
Normal file
File diff suppressed because it is too large
Load Diff
225
drivers/video/fbdev/omap/lcd_ams_delta.c
Normal file
225
drivers/video/fbdev/omap/lcd_ams_delta.c
Normal file
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
* Based on drivers/video/omap/lcd_inn1510.c
|
||||
*
|
||||
* LCD panel support for the Amstrad E3 (Delta) videophone.
|
||||
*
|
||||
* Copyright (C) 2006 Jonathan McDowell <noodles@earth.li>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/lcd.h>
|
||||
#include <linux/gpio.h>
|
||||
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/board-ams-delta.h>
|
||||
|
||||
#include "omapfb.h"
|
||||
|
||||
#define AMS_DELTA_DEFAULT_CONTRAST 112
|
||||
|
||||
#define AMS_DELTA_MAX_CONTRAST 0x00FF
|
||||
#define AMS_DELTA_LCD_POWER 0x0100
|
||||
|
||||
|
||||
/* LCD class device section */
|
||||
|
||||
static int ams_delta_lcd;
|
||||
|
||||
static int ams_delta_lcd_set_power(struct lcd_device *dev, int power)
|
||||
{
|
||||
if (power == FB_BLANK_UNBLANK) {
|
||||
if (!(ams_delta_lcd & AMS_DELTA_LCD_POWER)) {
|
||||
omap_writeb(ams_delta_lcd & AMS_DELTA_MAX_CONTRAST,
|
||||
OMAP_PWL_ENABLE);
|
||||
omap_writeb(1, OMAP_PWL_CLK_ENABLE);
|
||||
ams_delta_lcd |= AMS_DELTA_LCD_POWER;
|
||||
}
|
||||
} else {
|
||||
if (ams_delta_lcd & AMS_DELTA_LCD_POWER) {
|
||||
omap_writeb(0, OMAP_PWL_ENABLE);
|
||||
omap_writeb(0, OMAP_PWL_CLK_ENABLE);
|
||||
ams_delta_lcd &= ~AMS_DELTA_LCD_POWER;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ams_delta_lcd_set_contrast(struct lcd_device *dev, int value)
|
||||
{
|
||||
if ((value >= 0) && (value <= AMS_DELTA_MAX_CONTRAST)) {
|
||||
omap_writeb(value, OMAP_PWL_ENABLE);
|
||||
ams_delta_lcd &= ~AMS_DELTA_MAX_CONTRAST;
|
||||
ams_delta_lcd |= value;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LCD_CLASS_DEVICE
|
||||
static int ams_delta_lcd_get_power(struct lcd_device *dev)
|
||||
{
|
||||
if (ams_delta_lcd & AMS_DELTA_LCD_POWER)
|
||||
return FB_BLANK_UNBLANK;
|
||||
else
|
||||
return FB_BLANK_POWERDOWN;
|
||||
}
|
||||
|
||||
static int ams_delta_lcd_get_contrast(struct lcd_device *dev)
|
||||
{
|
||||
if (!(ams_delta_lcd & AMS_DELTA_LCD_POWER))
|
||||
return 0;
|
||||
|
||||
return ams_delta_lcd & AMS_DELTA_MAX_CONTRAST;
|
||||
}
|
||||
|
||||
static struct lcd_ops ams_delta_lcd_ops = {
|
||||
.get_power = ams_delta_lcd_get_power,
|
||||
.set_power = ams_delta_lcd_set_power,
|
||||
.get_contrast = ams_delta_lcd_get_contrast,
|
||||
.set_contrast = ams_delta_lcd_set_contrast,
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
/* omapfb panel section */
|
||||
|
||||
static const struct gpio _gpios[] = {
|
||||
{
|
||||
.gpio = AMS_DELTA_GPIO_PIN_LCD_VBLEN,
|
||||
.flags = GPIOF_OUT_INIT_LOW,
|
||||
.label = "lcd_vblen",
|
||||
},
|
||||
{
|
||||
.gpio = AMS_DELTA_GPIO_PIN_LCD_NDISP,
|
||||
.flags = GPIOF_OUT_INIT_LOW,
|
||||
.label = "lcd_ndisp",
|
||||
},
|
||||
};
|
||||
|
||||
static int ams_delta_panel_init(struct lcd_panel *panel,
|
||||
struct omapfb_device *fbdev)
|
||||
{
|
||||
return gpio_request_array(_gpios, ARRAY_SIZE(_gpios));
|
||||
}
|
||||
|
||||
static void ams_delta_panel_cleanup(struct lcd_panel *panel)
|
||||
{
|
||||
gpio_free_array(_gpios, ARRAY_SIZE(_gpios));
|
||||
}
|
||||
|
||||
static int ams_delta_panel_enable(struct lcd_panel *panel)
|
||||
{
|
||||
gpio_set_value(AMS_DELTA_GPIO_PIN_LCD_NDISP, 1);
|
||||
gpio_set_value(AMS_DELTA_GPIO_PIN_LCD_VBLEN, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ams_delta_panel_disable(struct lcd_panel *panel)
|
||||
{
|
||||
gpio_set_value(AMS_DELTA_GPIO_PIN_LCD_VBLEN, 0);
|
||||
gpio_set_value(AMS_DELTA_GPIO_PIN_LCD_NDISP, 0);
|
||||
}
|
||||
|
||||
static unsigned long ams_delta_panel_get_caps(struct lcd_panel *panel)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct lcd_panel ams_delta_panel = {
|
||||
.name = "ams-delta",
|
||||
.config = 0,
|
||||
|
||||
.bpp = 12,
|
||||
.data_lines = 16,
|
||||
.x_res = 480,
|
||||
.y_res = 320,
|
||||
.pixel_clock = 4687,
|
||||
.hsw = 3,
|
||||
.hfp = 1,
|
||||
.hbp = 1,
|
||||
.vsw = 1,
|
||||
.vfp = 0,
|
||||
.vbp = 0,
|
||||
.pcd = 0,
|
||||
.acb = 37,
|
||||
|
||||
.init = ams_delta_panel_init,
|
||||
.cleanup = ams_delta_panel_cleanup,
|
||||
.enable = ams_delta_panel_enable,
|
||||
.disable = ams_delta_panel_disable,
|
||||
.get_caps = ams_delta_panel_get_caps,
|
||||
};
|
||||
|
||||
|
||||
/* platform driver section */
|
||||
|
||||
static int ams_delta_panel_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct lcd_device *lcd_device = NULL;
|
||||
#ifdef CONFIG_LCD_CLASS_DEVICE
|
||||
int ret;
|
||||
|
||||
lcd_device = lcd_device_register("omapfb", &pdev->dev, NULL,
|
||||
&ams_delta_lcd_ops);
|
||||
|
||||
if (IS_ERR(lcd_device)) {
|
||||
ret = PTR_ERR(lcd_device);
|
||||
dev_err(&pdev->dev, "failed to register device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, lcd_device);
|
||||
lcd_device->props.max_contrast = AMS_DELTA_MAX_CONTRAST;
|
||||
#endif
|
||||
|
||||
ams_delta_lcd_set_contrast(lcd_device, AMS_DELTA_DEFAULT_CONTRAST);
|
||||
ams_delta_lcd_set_power(lcd_device, FB_BLANK_UNBLANK);
|
||||
|
||||
omapfb_register_panel(&ams_delta_panel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ams_delta_panel_remove(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ams_delta_panel_suspend(struct platform_device *pdev,
|
||||
pm_message_t mesg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ams_delta_panel_resume(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver ams_delta_panel_driver = {
|
||||
.probe = ams_delta_panel_probe,
|
||||
.remove = ams_delta_panel_remove,
|
||||
.suspend = ams_delta_panel_suspend,
|
||||
.resume = ams_delta_panel_resume,
|
||||
.driver = {
|
||||
.name = "lcd_ams_delta",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(ams_delta_panel_driver);
|
127
drivers/video/fbdev/omap/lcd_h3.c
Normal file
127
drivers/video/fbdev/omap/lcd_h3.c
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* LCD panel support for the TI OMAP H3 board
|
||||
*
|
||||
* Copyright (C) 2004 Nokia Corporation
|
||||
* Author: Imre Deak <imre.deak@nokia.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/i2c/tps65010.h>
|
||||
|
||||
#include <asm/gpio.h>
|
||||
#include "omapfb.h"
|
||||
|
||||
#define MODULE_NAME "omapfb-lcd_h3"
|
||||
|
||||
static int h3_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void h3_panel_cleanup(struct lcd_panel *panel)
|
||||
{
|
||||
}
|
||||
|
||||
static int h3_panel_enable(struct lcd_panel *panel)
|
||||
{
|
||||
int r = 0;
|
||||
|
||||
/* GPIO1 and GPIO2 of TPS65010 send LCD_ENBKL and LCD_ENVDD signals */
|
||||
r = tps65010_set_gpio_out_value(GPIO1, HIGH);
|
||||
if (!r)
|
||||
r = tps65010_set_gpio_out_value(GPIO2, HIGH);
|
||||
if (r)
|
||||
pr_err(MODULE_NAME ": Unable to turn on LCD panel\n");
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void h3_panel_disable(struct lcd_panel *panel)
|
||||
{
|
||||
int r = 0;
|
||||
|
||||
/* GPIO1 and GPIO2 of TPS65010 send LCD_ENBKL and LCD_ENVDD signals */
|
||||
r = tps65010_set_gpio_out_value(GPIO1, LOW);
|
||||
if (!r)
|
||||
tps65010_set_gpio_out_value(GPIO2, LOW);
|
||||
if (r)
|
||||
pr_err(MODULE_NAME ": Unable to turn off LCD panel\n");
|
||||
}
|
||||
|
||||
static unsigned long h3_panel_get_caps(struct lcd_panel *panel)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct lcd_panel h3_panel = {
|
||||
.name = "h3",
|
||||
.config = OMAP_LCDC_PANEL_TFT,
|
||||
|
||||
.data_lines = 16,
|
||||
.bpp = 16,
|
||||
.x_res = 240,
|
||||
.y_res = 320,
|
||||
.pixel_clock = 12000,
|
||||
.hsw = 12,
|
||||
.hfp = 14,
|
||||
.hbp = 72 - 12,
|
||||
.vsw = 1,
|
||||
.vfp = 1,
|
||||
.vbp = 0,
|
||||
.pcd = 0,
|
||||
|
||||
.init = h3_panel_init,
|
||||
.cleanup = h3_panel_cleanup,
|
||||
.enable = h3_panel_enable,
|
||||
.disable = h3_panel_disable,
|
||||
.get_caps = h3_panel_get_caps,
|
||||
};
|
||||
|
||||
static int h3_panel_probe(struct platform_device *pdev)
|
||||
{
|
||||
omapfb_register_panel(&h3_panel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int h3_panel_remove(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int h3_panel_suspend(struct platform_device *pdev, pm_message_t mesg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int h3_panel_resume(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver h3_panel_driver = {
|
||||
.probe = h3_panel_probe,
|
||||
.remove = h3_panel_remove,
|
||||
.suspend = h3_panel_suspend,
|
||||
.resume = h3_panel_resume,
|
||||
.driver = {
|
||||
.name = "lcd_h3",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(h3_panel_driver);
|
118
drivers/video/fbdev/omap/lcd_htcherald.c
Normal file
118
drivers/video/fbdev/omap/lcd_htcherald.c
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* File: drivers/video/omap/lcd-htcherald.c
|
||||
*
|
||||
* LCD panel support for the HTC Herald
|
||||
*
|
||||
* Copyright (C) 2009 Cory Maccarrone <darkstar6262@gmail.com>
|
||||
* Copyright (C) 2009 Wing Linux
|
||||
*
|
||||
* Based on the lcd_htcwizard.c file from the linwizard project:
|
||||
* Copyright (C) linwizard.sourceforge.net
|
||||
* Author: Angelo Arrifano <miknix@gmail.com>
|
||||
* Based on lcd_h4 by Imre Deak <imre.deak@nokia.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "omapfb.h"
|
||||
|
||||
static int htcherald_panel_init(struct lcd_panel *panel,
|
||||
struct omapfb_device *fbdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void htcherald_panel_cleanup(struct lcd_panel *panel)
|
||||
{
|
||||
}
|
||||
|
||||
static int htcherald_panel_enable(struct lcd_panel *panel)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void htcherald_panel_disable(struct lcd_panel *panel)
|
||||
{
|
||||
}
|
||||
|
||||
static unsigned long htcherald_panel_get_caps(struct lcd_panel *panel)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Found on WIZ200 (miknix) and some HERA110 models (darkstar62) */
|
||||
struct lcd_panel htcherald_panel_1 = {
|
||||
.name = "lcd_herald",
|
||||
.config = OMAP_LCDC_PANEL_TFT |
|
||||
OMAP_LCDC_INV_HSYNC |
|
||||
OMAP_LCDC_INV_VSYNC |
|
||||
OMAP_LCDC_INV_PIX_CLOCK,
|
||||
.bpp = 16,
|
||||
.data_lines = 16,
|
||||
.x_res = 240,
|
||||
.y_res = 320,
|
||||
.pixel_clock = 6093,
|
||||
.pcd = 0, /* 15 */
|
||||
.hsw = 10,
|
||||
.hfp = 10,
|
||||
.hbp = 20,
|
||||
.vsw = 3,
|
||||
.vfp = 2,
|
||||
.vbp = 2,
|
||||
|
||||
.init = htcherald_panel_init,
|
||||
.cleanup = htcherald_panel_cleanup,
|
||||
.enable = htcherald_panel_enable,
|
||||
.disable = htcherald_panel_disable,
|
||||
.get_caps = htcherald_panel_get_caps,
|
||||
};
|
||||
|
||||
static int htcherald_panel_probe(struct platform_device *pdev)
|
||||
{
|
||||
omapfb_register_panel(&htcherald_panel_1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int htcherald_panel_remove(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int htcherald_panel_suspend(struct platform_device *pdev,
|
||||
pm_message_t mesg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int htcherald_panel_resume(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver htcherald_panel_driver = {
|
||||
.probe = htcherald_panel_probe,
|
||||
.remove = htcherald_panel_remove,
|
||||
.suspend = htcherald_panel_suspend,
|
||||
.resume = htcherald_panel_resume,
|
||||
.driver = {
|
||||
.name = "lcd_htcherald",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(htcherald_panel_driver);
|
113
drivers/video/fbdev/omap/lcd_inn1510.c
Normal file
113
drivers/video/fbdev/omap/lcd_inn1510.c
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* LCD panel support for the TI OMAP1510 Innovator board
|
||||
*
|
||||
* Copyright (C) 2004 Nokia Corporation
|
||||
* Author: Imre Deak <imre.deak@nokia.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <mach/hardware.h>
|
||||
|
||||
#include "omapfb.h"
|
||||
|
||||
static int innovator1510_panel_init(struct lcd_panel *panel,
|
||||
struct omapfb_device *fbdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void innovator1510_panel_cleanup(struct lcd_panel *panel)
|
||||
{
|
||||
}
|
||||
|
||||
static int innovator1510_panel_enable(struct lcd_panel *panel)
|
||||
{
|
||||
__raw_writeb(0x7, OMAP1510_FPGA_LCD_PANEL_CONTROL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void innovator1510_panel_disable(struct lcd_panel *panel)
|
||||
{
|
||||
__raw_writeb(0x0, OMAP1510_FPGA_LCD_PANEL_CONTROL);
|
||||
}
|
||||
|
||||
static unsigned long innovator1510_panel_get_caps(struct lcd_panel *panel)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct lcd_panel innovator1510_panel = {
|
||||
.name = "inn1510",
|
||||
.config = OMAP_LCDC_PANEL_TFT,
|
||||
|
||||
.bpp = 16,
|
||||
.data_lines = 16,
|
||||
.x_res = 240,
|
||||
.y_res = 320,
|
||||
.pixel_clock = 12500,
|
||||
.hsw = 40,
|
||||
.hfp = 40,
|
||||
.hbp = 72,
|
||||
.vsw = 1,
|
||||
.vfp = 1,
|
||||
.vbp = 0,
|
||||
.pcd = 12,
|
||||
|
||||
.init = innovator1510_panel_init,
|
||||
.cleanup = innovator1510_panel_cleanup,
|
||||
.enable = innovator1510_panel_enable,
|
||||
.disable = innovator1510_panel_disable,
|
||||
.get_caps = innovator1510_panel_get_caps,
|
||||
};
|
||||
|
||||
static int innovator1510_panel_probe(struct platform_device *pdev)
|
||||
{
|
||||
omapfb_register_panel(&innovator1510_panel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int innovator1510_panel_remove(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int innovator1510_panel_suspend(struct platform_device *pdev,
|
||||
pm_message_t mesg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int innovator1510_panel_resume(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver innovator1510_panel_driver = {
|
||||
.probe = innovator1510_panel_probe,
|
||||
.remove = innovator1510_panel_remove,
|
||||
.suspend = innovator1510_panel_suspend,
|
||||
.resume = innovator1510_panel_resume,
|
||||
.driver = {
|
||||
.name = "lcd_inn1510",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(innovator1510_panel_driver);
|
134
drivers/video/fbdev/omap/lcd_inn1610.c
Normal file
134
drivers/video/fbdev/omap/lcd_inn1610.c
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* LCD panel support for the TI OMAP1610 Innovator board
|
||||
*
|
||||
* Copyright (C) 2004 Nokia Corporation
|
||||
* Author: Imre Deak <imre.deak@nokia.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include "omapfb.h"
|
||||
|
||||
#define MODULE_NAME "omapfb-lcd_h3"
|
||||
|
||||
static int innovator1610_panel_init(struct lcd_panel *panel,
|
||||
struct omapfb_device *fbdev)
|
||||
{
|
||||
int r = 0;
|
||||
|
||||
/* configure GPIO(14, 15) as outputs */
|
||||
if (gpio_request_one(14, GPIOF_OUT_INIT_LOW, "lcd_en0")) {
|
||||
pr_err(MODULE_NAME ": can't request GPIO 14\n");
|
||||
r = -1;
|
||||
goto exit;
|
||||
}
|
||||
if (gpio_request_one(15, GPIOF_OUT_INIT_LOW, "lcd_en1")) {
|
||||
pr_err(MODULE_NAME ": can't request GPIO 15\n");
|
||||
gpio_free(14);
|
||||
r = -1;
|
||||
goto exit;
|
||||
}
|
||||
exit:
|
||||
return r;
|
||||
}
|
||||
|
||||
static void innovator1610_panel_cleanup(struct lcd_panel *panel)
|
||||
{
|
||||
gpio_free(15);
|
||||
gpio_free(14);
|
||||
}
|
||||
|
||||
static int innovator1610_panel_enable(struct lcd_panel *panel)
|
||||
{
|
||||
/* set GPIO14 and GPIO15 high */
|
||||
gpio_set_value(14, 1);
|
||||
gpio_set_value(15, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void innovator1610_panel_disable(struct lcd_panel *panel)
|
||||
{
|
||||
/* set GPIO13, GPIO14 and GPIO15 low */
|
||||
gpio_set_value(14, 0);
|
||||
gpio_set_value(15, 0);
|
||||
}
|
||||
|
||||
static unsigned long innovator1610_panel_get_caps(struct lcd_panel *panel)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct lcd_panel innovator1610_panel = {
|
||||
.name = "inn1610",
|
||||
.config = OMAP_LCDC_PANEL_TFT,
|
||||
|
||||
.bpp = 16,
|
||||
.data_lines = 16,
|
||||
.x_res = 320,
|
||||
.y_res = 240,
|
||||
.pixel_clock = 12500,
|
||||
.hsw = 40,
|
||||
.hfp = 40,
|
||||
.hbp = 72,
|
||||
.vsw = 1,
|
||||
.vfp = 1,
|
||||
.vbp = 0,
|
||||
.pcd = 12,
|
||||
|
||||
.init = innovator1610_panel_init,
|
||||
.cleanup = innovator1610_panel_cleanup,
|
||||
.enable = innovator1610_panel_enable,
|
||||
.disable = innovator1610_panel_disable,
|
||||
.get_caps = innovator1610_panel_get_caps,
|
||||
};
|
||||
|
||||
static int innovator1610_panel_probe(struct platform_device *pdev)
|
||||
{
|
||||
omapfb_register_panel(&innovator1610_panel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int innovator1610_panel_remove(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int innovator1610_panel_suspend(struct platform_device *pdev,
|
||||
pm_message_t mesg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int innovator1610_panel_resume(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver innovator1610_panel_driver = {
|
||||
.probe = innovator1610_panel_probe,
|
||||
.remove = innovator1610_panel_remove,
|
||||
.suspend = innovator1610_panel_suspend,
|
||||
.resume = innovator1610_panel_resume,
|
||||
.driver = {
|
||||
.name = "lcd_inn1610",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(innovator1610_panel_driver);
|
615
drivers/video/fbdev/omap/lcd_mipid.c
Normal file
615
drivers/video/fbdev/omap/lcd_mipid.c
Normal file
@@ -0,0 +1,615 @@
|
||||
/*
|
||||
* LCD driver for MIPI DBI-C / DCS compatible LCDs
|
||||
*
|
||||
* Copyright (C) 2006 Nokia Corporation
|
||||
* Author: Imre Deak <imre.deak@nokia.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#include <linux/device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/platform_data/lcd-mipid.h>
|
||||
|
||||
#include "omapfb.h"
|
||||
|
||||
#define MIPID_MODULE_NAME "lcd_mipid"
|
||||
|
||||
#define MIPID_CMD_READ_DISP_ID 0x04
|
||||
#define MIPID_CMD_READ_RED 0x06
|
||||
#define MIPID_CMD_READ_GREEN 0x07
|
||||
#define MIPID_CMD_READ_BLUE 0x08
|
||||
#define MIPID_CMD_READ_DISP_STATUS 0x09
|
||||
#define MIPID_CMD_RDDSDR 0x0F
|
||||
#define MIPID_CMD_SLEEP_IN 0x10
|
||||
#define MIPID_CMD_SLEEP_OUT 0x11
|
||||
#define MIPID_CMD_DISP_OFF 0x28
|
||||
#define MIPID_CMD_DISP_ON 0x29
|
||||
|
||||
#define MIPID_ESD_CHECK_PERIOD msecs_to_jiffies(5000)
|
||||
|
||||
#define to_mipid_device(p) container_of(p, struct mipid_device, \
|
||||
panel)
|
||||
struct mipid_device {
|
||||
int enabled;
|
||||
int revision;
|
||||
unsigned int saved_bklight_level;
|
||||
unsigned long hw_guard_end; /* next value of jiffies
|
||||
when we can issue the
|
||||
next sleep in/out command */
|
||||
unsigned long hw_guard_wait; /* max guard time in jiffies */
|
||||
|
||||
struct omapfb_device *fbdev;
|
||||
struct spi_device *spi;
|
||||
struct mutex mutex;
|
||||
struct lcd_panel panel;
|
||||
|
||||
struct workqueue_struct *esd_wq;
|
||||
struct delayed_work esd_work;
|
||||
void (*esd_check)(struct mipid_device *m);
|
||||
};
|
||||
|
||||
static void mipid_transfer(struct mipid_device *md, int cmd, const u8 *wbuf,
|
||||
int wlen, u8 *rbuf, int rlen)
|
||||
{
|
||||
struct spi_message m;
|
||||
struct spi_transfer *x, xfer[4];
|
||||
u16 w;
|
||||
int r;
|
||||
|
||||
BUG_ON(md->spi == NULL);
|
||||
|
||||
spi_message_init(&m);
|
||||
|
||||
memset(xfer, 0, sizeof(xfer));
|
||||
x = &xfer[0];
|
||||
|
||||
cmd &= 0xff;
|
||||
x->tx_buf = &cmd;
|
||||
x->bits_per_word = 9;
|
||||
x->len = 2;
|
||||
spi_message_add_tail(x, &m);
|
||||
|
||||
if (wlen) {
|
||||
x++;
|
||||
x->tx_buf = wbuf;
|
||||
x->len = wlen;
|
||||
x->bits_per_word = 9;
|
||||
spi_message_add_tail(x, &m);
|
||||
}
|
||||
|
||||
if (rlen) {
|
||||
x++;
|
||||
x->rx_buf = &w;
|
||||
x->len = 1;
|
||||
spi_message_add_tail(x, &m);
|
||||
|
||||
if (rlen > 1) {
|
||||
/* Arrange for the extra clock before the first
|
||||
* data bit.
|
||||
*/
|
||||
x->bits_per_word = 9;
|
||||
x->len = 2;
|
||||
|
||||
x++;
|
||||
x->rx_buf = &rbuf[1];
|
||||
x->len = rlen - 1;
|
||||
spi_message_add_tail(x, &m);
|
||||
}
|
||||
}
|
||||
|
||||
r = spi_sync(md->spi, &m);
|
||||
if (r < 0)
|
||||
dev_dbg(&md->spi->dev, "spi_sync %d\n", r);
|
||||
|
||||
if (rlen)
|
||||
rbuf[0] = w & 0xff;
|
||||
}
|
||||
|
||||
static inline void mipid_cmd(struct mipid_device *md, int cmd)
|
||||
{
|
||||
mipid_transfer(md, cmd, NULL, 0, NULL, 0);
|
||||
}
|
||||
|
||||
static inline void mipid_write(struct mipid_device *md,
|
||||
int reg, const u8 *buf, int len)
|
||||
{
|
||||
mipid_transfer(md, reg, buf, len, NULL, 0);
|
||||
}
|
||||
|
||||
static inline void mipid_read(struct mipid_device *md,
|
||||
int reg, u8 *buf, int len)
|
||||
{
|
||||
mipid_transfer(md, reg, NULL, 0, buf, len);
|
||||
}
|
||||
|
||||
static void set_data_lines(struct mipid_device *md, int data_lines)
|
||||
{
|
||||
u16 par;
|
||||
|
||||
switch (data_lines) {
|
||||
case 16:
|
||||
par = 0x150;
|
||||
break;
|
||||
case 18:
|
||||
par = 0x160;
|
||||
break;
|
||||
case 24:
|
||||
par = 0x170;
|
||||
break;
|
||||
}
|
||||
mipid_write(md, 0x3a, (u8 *)&par, 2);
|
||||
}
|
||||
|
||||
static void send_init_string(struct mipid_device *md)
|
||||
{
|
||||
u16 initpar[] = { 0x0102, 0x0100, 0x0100 };
|
||||
|
||||
mipid_write(md, 0xc2, (u8 *)initpar, sizeof(initpar));
|
||||
set_data_lines(md, md->panel.data_lines);
|
||||
}
|
||||
|
||||
static void hw_guard_start(struct mipid_device *md, int guard_msec)
|
||||
{
|
||||
md->hw_guard_wait = msecs_to_jiffies(guard_msec);
|
||||
md->hw_guard_end = jiffies + md->hw_guard_wait;
|
||||
}
|
||||
|
||||
static void hw_guard_wait(struct mipid_device *md)
|
||||
{
|
||||
unsigned long wait = md->hw_guard_end - jiffies;
|
||||
|
||||
if ((long)wait > 0 && wait <= md->hw_guard_wait) {
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
schedule_timeout(wait);
|
||||
}
|
||||
}
|
||||
|
||||
static void set_sleep_mode(struct mipid_device *md, int on)
|
||||
{
|
||||
int cmd, sleep_time = 50;
|
||||
|
||||
if (on)
|
||||
cmd = MIPID_CMD_SLEEP_IN;
|
||||
else
|
||||
cmd = MIPID_CMD_SLEEP_OUT;
|
||||
hw_guard_wait(md);
|
||||
mipid_cmd(md, cmd);
|
||||
hw_guard_start(md, 120);
|
||||
/*
|
||||
* When we enable the panel, it seems we _have_ to sleep
|
||||
* 120 ms before sending the init string. When disabling the
|
||||
* panel we'll sleep for the duration of 2 frames, so that the
|
||||
* controller can still provide the PCLK,HS,VS signals.
|
||||
*/
|
||||
if (!on)
|
||||
sleep_time = 120;
|
||||
msleep(sleep_time);
|
||||
}
|
||||
|
||||
static void set_display_state(struct mipid_device *md, int enabled)
|
||||
{
|
||||
int cmd = enabled ? MIPID_CMD_DISP_ON : MIPID_CMD_DISP_OFF;
|
||||
|
||||
mipid_cmd(md, cmd);
|
||||
}
|
||||
|
||||
static int mipid_set_bklight_level(struct lcd_panel *panel, unsigned int level)
|
||||
{
|
||||
struct mipid_device *md = to_mipid_device(panel);
|
||||
struct mipid_platform_data *pd = md->spi->dev.platform_data;
|
||||
|
||||
if (pd->get_bklight_max == NULL || pd->set_bklight_level == NULL)
|
||||
return -ENODEV;
|
||||
if (level > pd->get_bklight_max(pd))
|
||||
return -EINVAL;
|
||||
if (!md->enabled) {
|
||||
md->saved_bklight_level = level;
|
||||
return 0;
|
||||
}
|
||||
pd->set_bklight_level(pd, level);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int mipid_get_bklight_level(struct lcd_panel *panel)
|
||||
{
|
||||
struct mipid_device *md = to_mipid_device(panel);
|
||||
struct mipid_platform_data *pd = md->spi->dev.platform_data;
|
||||
|
||||
if (pd->get_bklight_level == NULL)
|
||||
return -ENODEV;
|
||||
return pd->get_bklight_level(pd);
|
||||
}
|
||||
|
||||
static unsigned int mipid_get_bklight_max(struct lcd_panel *panel)
|
||||
{
|
||||
struct mipid_device *md = to_mipid_device(panel);
|
||||
struct mipid_platform_data *pd = md->spi->dev.platform_data;
|
||||
|
||||
if (pd->get_bklight_max == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
return pd->get_bklight_max(pd);
|
||||
}
|
||||
|
||||
static unsigned long mipid_get_caps(struct lcd_panel *panel)
|
||||
{
|
||||
return OMAPFB_CAPS_SET_BACKLIGHT;
|
||||
}
|
||||
|
||||
static u16 read_first_pixel(struct mipid_device *md)
|
||||
{
|
||||
u16 pixel;
|
||||
u8 red, green, blue;
|
||||
|
||||
mutex_lock(&md->mutex);
|
||||
mipid_read(md, MIPID_CMD_READ_RED, &red, 1);
|
||||
mipid_read(md, MIPID_CMD_READ_GREEN, &green, 1);
|
||||
mipid_read(md, MIPID_CMD_READ_BLUE, &blue, 1);
|
||||
mutex_unlock(&md->mutex);
|
||||
|
||||
switch (md->panel.data_lines) {
|
||||
case 16:
|
||||
pixel = ((red >> 1) << 11) | (green << 5) | (blue >> 1);
|
||||
break;
|
||||
case 24:
|
||||
/* 24 bit -> 16 bit */
|
||||
pixel = ((red >> 3) << 11) | ((green >> 2) << 5) |
|
||||
(blue >> 3);
|
||||
break;
|
||||
default:
|
||||
pixel = 0;
|
||||
BUG();
|
||||
}
|
||||
|
||||
return pixel;
|
||||
}
|
||||
|
||||
static int mipid_run_test(struct lcd_panel *panel, int test_num)
|
||||
{
|
||||
struct mipid_device *md = to_mipid_device(panel);
|
||||
static const u16 test_values[4] = {
|
||||
0x0000, 0xffff, 0xaaaa, 0x5555,
|
||||
};
|
||||
int i;
|
||||
|
||||
if (test_num != MIPID_TEST_RGB_LINES)
|
||||
return MIPID_TEST_INVALID;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(test_values); i++) {
|
||||
int delay;
|
||||
unsigned long tmo;
|
||||
|
||||
omapfb_write_first_pixel(md->fbdev, test_values[i]);
|
||||
tmo = jiffies + msecs_to_jiffies(100);
|
||||
delay = 25;
|
||||
while (1) {
|
||||
u16 pixel;
|
||||
|
||||
msleep(delay);
|
||||
pixel = read_first_pixel(md);
|
||||
if (pixel == test_values[i])
|
||||
break;
|
||||
if (time_after(jiffies, tmo)) {
|
||||
dev_err(&md->spi->dev,
|
||||
"MIPI LCD RGB I/F test failed: "
|
||||
"expecting %04x, got %04x\n",
|
||||
test_values[i], pixel);
|
||||
return MIPID_TEST_FAILED;
|
||||
}
|
||||
delay = 10;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ls041y3_esd_recover(struct mipid_device *md)
|
||||
{
|
||||
dev_err(&md->spi->dev, "performing LCD ESD recovery\n");
|
||||
set_sleep_mode(md, 1);
|
||||
set_sleep_mode(md, 0);
|
||||
}
|
||||
|
||||
static void ls041y3_esd_check_mode1(struct mipid_device *md)
|
||||
{
|
||||
u8 state1, state2;
|
||||
|
||||
mipid_read(md, MIPID_CMD_RDDSDR, &state1, 1);
|
||||
set_sleep_mode(md, 0);
|
||||
mipid_read(md, MIPID_CMD_RDDSDR, &state2, 1);
|
||||
dev_dbg(&md->spi->dev, "ESD mode 1 state1 %02x state2 %02x\n",
|
||||
state1, state2);
|
||||
/* Each sleep out command will trigger a self diagnostic and flip
|
||||
* Bit6 if the test passes.
|
||||
*/
|
||||
if (!((state1 ^ state2) & (1 << 6)))
|
||||
ls041y3_esd_recover(md);
|
||||
}
|
||||
|
||||
static void ls041y3_esd_check_mode2(struct mipid_device *md)
|
||||
{
|
||||
int i;
|
||||
u8 rbuf[2];
|
||||
static const struct {
|
||||
int cmd;
|
||||
int wlen;
|
||||
u16 wbuf[3];
|
||||
} *rd, rd_ctrl[7] = {
|
||||
{ 0xb0, 4, { 0x0101, 0x01fe, } },
|
||||
{ 0xb1, 4, { 0x01de, 0x0121, } },
|
||||
{ 0xc2, 4, { 0x0100, 0x0100, } },
|
||||
{ 0xbd, 2, { 0x0100, } },
|
||||
{ 0xc2, 4, { 0x01fc, 0x0103, } },
|
||||
{ 0xb4, 0, },
|
||||
{ 0x00, 0, },
|
||||
};
|
||||
|
||||
rd = rd_ctrl;
|
||||
for (i = 0; i < 3; i++, rd++)
|
||||
mipid_write(md, rd->cmd, (u8 *)rd->wbuf, rd->wlen);
|
||||
|
||||
udelay(10);
|
||||
mipid_read(md, rd->cmd, rbuf, 2);
|
||||
rd++;
|
||||
|
||||
for (i = 0; i < 3; i++, rd++) {
|
||||
udelay(10);
|
||||
mipid_write(md, rd->cmd, (u8 *)rd->wbuf, rd->wlen);
|
||||
}
|
||||
|
||||
dev_dbg(&md->spi->dev, "ESD mode 2 state %02x\n", rbuf[1]);
|
||||
if (rbuf[1] == 0x00)
|
||||
ls041y3_esd_recover(md);
|
||||
}
|
||||
|
||||
static void ls041y3_esd_check(struct mipid_device *md)
|
||||
{
|
||||
ls041y3_esd_check_mode1(md);
|
||||
if (md->revision >= 0x88)
|
||||
ls041y3_esd_check_mode2(md);
|
||||
}
|
||||
|
||||
static void mipid_esd_start_check(struct mipid_device *md)
|
||||
{
|
||||
if (md->esd_check != NULL)
|
||||
queue_delayed_work(md->esd_wq, &md->esd_work,
|
||||
MIPID_ESD_CHECK_PERIOD);
|
||||
}
|
||||
|
||||
static void mipid_esd_stop_check(struct mipid_device *md)
|
||||
{
|
||||
if (md->esd_check != NULL)
|
||||
cancel_delayed_work_sync(&md->esd_work);
|
||||
}
|
||||
|
||||
static void mipid_esd_work(struct work_struct *work)
|
||||
{
|
||||
struct mipid_device *md = container_of(work, struct mipid_device,
|
||||
esd_work.work);
|
||||
|
||||
mutex_lock(&md->mutex);
|
||||
md->esd_check(md);
|
||||
mutex_unlock(&md->mutex);
|
||||
mipid_esd_start_check(md);
|
||||
}
|
||||
|
||||
static int mipid_enable(struct lcd_panel *panel)
|
||||
{
|
||||
struct mipid_device *md = to_mipid_device(panel);
|
||||
|
||||
mutex_lock(&md->mutex);
|
||||
|
||||
if (md->enabled) {
|
||||
mutex_unlock(&md->mutex);
|
||||
return 0;
|
||||
}
|
||||
set_sleep_mode(md, 0);
|
||||
md->enabled = 1;
|
||||
send_init_string(md);
|
||||
set_display_state(md, 1);
|
||||
mipid_set_bklight_level(panel, md->saved_bklight_level);
|
||||
mipid_esd_start_check(md);
|
||||
|
||||
mutex_unlock(&md->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mipid_disable(struct lcd_panel *panel)
|
||||
{
|
||||
struct mipid_device *md = to_mipid_device(panel);
|
||||
|
||||
/*
|
||||
* A final ESD work might be called before returning,
|
||||
* so do this without holding the lock.
|
||||
*/
|
||||
mipid_esd_stop_check(md);
|
||||
mutex_lock(&md->mutex);
|
||||
|
||||
if (!md->enabled) {
|
||||
mutex_unlock(&md->mutex);
|
||||
return;
|
||||
}
|
||||
md->saved_bklight_level = mipid_get_bklight_level(panel);
|
||||
mipid_set_bklight_level(panel, 0);
|
||||
set_display_state(md, 0);
|
||||
set_sleep_mode(md, 1);
|
||||
md->enabled = 0;
|
||||
|
||||
mutex_unlock(&md->mutex);
|
||||
}
|
||||
|
||||
static int panel_enabled(struct mipid_device *md)
|
||||
{
|
||||
u32 disp_status;
|
||||
int enabled;
|
||||
|
||||
mipid_read(md, MIPID_CMD_READ_DISP_STATUS, (u8 *)&disp_status, 4);
|
||||
disp_status = __be32_to_cpu(disp_status);
|
||||
enabled = (disp_status & (1 << 17)) && (disp_status & (1 << 10));
|
||||
dev_dbg(&md->spi->dev,
|
||||
"LCD panel %senabled by bootloader (status 0x%04x)\n",
|
||||
enabled ? "" : "not ", disp_status);
|
||||
return enabled;
|
||||
}
|
||||
|
||||
static int mipid_init(struct lcd_panel *panel,
|
||||
struct omapfb_device *fbdev)
|
||||
{
|
||||
struct mipid_device *md = to_mipid_device(panel);
|
||||
|
||||
md->fbdev = fbdev;
|
||||
md->esd_wq = create_singlethread_workqueue("mipid_esd");
|
||||
if (md->esd_wq == NULL) {
|
||||
dev_err(&md->spi->dev, "can't create ESD workqueue\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
INIT_DELAYED_WORK(&md->esd_work, mipid_esd_work);
|
||||
mutex_init(&md->mutex);
|
||||
|
||||
md->enabled = panel_enabled(md);
|
||||
|
||||
if (md->enabled)
|
||||
mipid_esd_start_check(md);
|
||||
else
|
||||
md->saved_bklight_level = mipid_get_bklight_level(panel);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mipid_cleanup(struct lcd_panel *panel)
|
||||
{
|
||||
struct mipid_device *md = to_mipid_device(panel);
|
||||
|
||||
if (md->enabled)
|
||||
mipid_esd_stop_check(md);
|
||||
destroy_workqueue(md->esd_wq);
|
||||
}
|
||||
|
||||
static struct lcd_panel mipid_panel = {
|
||||
.config = OMAP_LCDC_PANEL_TFT,
|
||||
|
||||
.bpp = 16,
|
||||
.x_res = 800,
|
||||
.y_res = 480,
|
||||
.pixel_clock = 21940,
|
||||
.hsw = 50,
|
||||
.hfp = 20,
|
||||
.hbp = 15,
|
||||
.vsw = 2,
|
||||
.vfp = 1,
|
||||
.vbp = 3,
|
||||
|
||||
.init = mipid_init,
|
||||
.cleanup = mipid_cleanup,
|
||||
.enable = mipid_enable,
|
||||
.disable = mipid_disable,
|
||||
.get_caps = mipid_get_caps,
|
||||
.set_bklight_level = mipid_set_bklight_level,
|
||||
.get_bklight_level = mipid_get_bklight_level,
|
||||
.get_bklight_max = mipid_get_bklight_max,
|
||||
.run_test = mipid_run_test,
|
||||
};
|
||||
|
||||
static int mipid_detect(struct mipid_device *md)
|
||||
{
|
||||
struct mipid_platform_data *pdata;
|
||||
u8 display_id[3];
|
||||
|
||||
pdata = md->spi->dev.platform_data;
|
||||
if (pdata == NULL) {
|
||||
dev_err(&md->spi->dev, "missing platform data\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
mipid_read(md, MIPID_CMD_READ_DISP_ID, display_id, 3);
|
||||
dev_dbg(&md->spi->dev, "MIPI display ID: %02x%02x%02x\n",
|
||||
display_id[0], display_id[1], display_id[2]);
|
||||
|
||||
switch (display_id[0]) {
|
||||
case 0x45:
|
||||
md->panel.name = "lph8923";
|
||||
break;
|
||||
case 0x83:
|
||||
md->panel.name = "ls041y3";
|
||||
md->esd_check = ls041y3_esd_check;
|
||||
break;
|
||||
default:
|
||||
md->panel.name = "unknown";
|
||||
dev_err(&md->spi->dev, "invalid display ID\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
md->revision = display_id[1];
|
||||
md->panel.data_lines = pdata->data_lines;
|
||||
pr_info("omapfb: %s rev %02x LCD detected, %d data lines\n",
|
||||
md->panel.name, md->revision, md->panel.data_lines);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mipid_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct mipid_device *md;
|
||||
int r;
|
||||
|
||||
md = kzalloc(sizeof(*md), GFP_KERNEL);
|
||||
if (md == NULL) {
|
||||
dev_err(&spi->dev, "out of memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
spi->mode = SPI_MODE_0;
|
||||
md->spi = spi;
|
||||
dev_set_drvdata(&spi->dev, md);
|
||||
md->panel = mipid_panel;
|
||||
|
||||
r = mipid_detect(md);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
omapfb_register_panel(&md->panel);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mipid_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
struct mipid_device *md = dev_get_drvdata(&spi->dev);
|
||||
|
||||
mipid_disable(&md->panel);
|
||||
kfree(md);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct spi_driver mipid_spi_driver = {
|
||||
.driver = {
|
||||
.name = MIPID_MODULE_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = mipid_spi_probe,
|
||||
.remove = mipid_spi_remove,
|
||||
};
|
||||
|
||||
module_spi_driver(mipid_spi_driver);
|
||||
|
||||
MODULE_DESCRIPTION("MIPI display driver");
|
||||
MODULE_LICENSE("GPL");
|
133
drivers/video/fbdev/omap/lcd_osk.c
Normal file
133
drivers/video/fbdev/omap/lcd_osk.c
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* LCD panel support for the TI OMAP OSK board
|
||||
*
|
||||
* Copyright (C) 2004 Nokia Corporation
|
||||
* Author: Imre Deak <imre.deak@nokia.com>
|
||||
* Adapted for OSK by <dirk.behme@de.bosch.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <asm/gpio.h>
|
||||
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/mux.h>
|
||||
|
||||
#include "omapfb.h"
|
||||
|
||||
static int osk_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev)
|
||||
{
|
||||
/* gpio2 was allocated in board init */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void osk_panel_cleanup(struct lcd_panel *panel)
|
||||
{
|
||||
}
|
||||
|
||||
static int osk_panel_enable(struct lcd_panel *panel)
|
||||
{
|
||||
/* configure PWL pin */
|
||||
omap_cfg_reg(PWL);
|
||||
|
||||
/* Enable PWL unit */
|
||||
omap_writeb(0x01, OMAP_PWL_CLK_ENABLE);
|
||||
|
||||
/* Set PWL level */
|
||||
omap_writeb(0xFF, OMAP_PWL_ENABLE);
|
||||
|
||||
/* set GPIO2 high (lcd power enabled) */
|
||||
gpio_set_value(2, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void osk_panel_disable(struct lcd_panel *panel)
|
||||
{
|
||||
/* Set PWL level to zero */
|
||||
omap_writeb(0x00, OMAP_PWL_ENABLE);
|
||||
|
||||
/* Disable PWL unit */
|
||||
omap_writeb(0x00, OMAP_PWL_CLK_ENABLE);
|
||||
|
||||
/* set GPIO2 low */
|
||||
gpio_set_value(2, 0);
|
||||
}
|
||||
|
||||
static unsigned long osk_panel_get_caps(struct lcd_panel *panel)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct lcd_panel osk_panel = {
|
||||
.name = "osk",
|
||||
.config = OMAP_LCDC_PANEL_TFT,
|
||||
|
||||
.bpp = 16,
|
||||
.data_lines = 16,
|
||||
.x_res = 240,
|
||||
.y_res = 320,
|
||||
.pixel_clock = 12500,
|
||||
.hsw = 40,
|
||||
.hfp = 40,
|
||||
.hbp = 72,
|
||||
.vsw = 1,
|
||||
.vfp = 1,
|
||||
.vbp = 0,
|
||||
.pcd = 12,
|
||||
|
||||
.init = osk_panel_init,
|
||||
.cleanup = osk_panel_cleanup,
|
||||
.enable = osk_panel_enable,
|
||||
.disable = osk_panel_disable,
|
||||
.get_caps = osk_panel_get_caps,
|
||||
};
|
||||
|
||||
static int osk_panel_probe(struct platform_device *pdev)
|
||||
{
|
||||
omapfb_register_panel(&osk_panel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int osk_panel_remove(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int osk_panel_suspend(struct platform_device *pdev, pm_message_t mesg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int osk_panel_resume(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver osk_panel_driver = {
|
||||
.probe = osk_panel_probe,
|
||||
.remove = osk_panel_remove,
|
||||
.suspend = osk_panel_suspend,
|
||||
.resume = osk_panel_resume,
|
||||
.driver = {
|
||||
.name = "lcd_osk",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(osk_panel_driver);
|
110
drivers/video/fbdev/omap/lcd_palmte.c
Normal file
110
drivers/video/fbdev/omap/lcd_palmte.c
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* LCD panel support for the Palm Tungsten E
|
||||
*
|
||||
* Original version : Romain Goyet <r.goyet@gmail.com>
|
||||
* Current version : Laurent Gonzalez <palmte.linux@free.fr>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include "omapfb.h"
|
||||
|
||||
static int palmte_panel_init(struct lcd_panel *panel,
|
||||
struct omapfb_device *fbdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void palmte_panel_cleanup(struct lcd_panel *panel)
|
||||
{
|
||||
}
|
||||
|
||||
static int palmte_panel_enable(struct lcd_panel *panel)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void palmte_panel_disable(struct lcd_panel *panel)
|
||||
{
|
||||
}
|
||||
|
||||
static unsigned long palmte_panel_get_caps(struct lcd_panel *panel)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct lcd_panel palmte_panel = {
|
||||
.name = "palmte",
|
||||
.config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC |
|
||||
OMAP_LCDC_INV_HSYNC | OMAP_LCDC_HSVS_RISING_EDGE |
|
||||
OMAP_LCDC_HSVS_OPPOSITE,
|
||||
|
||||
.data_lines = 16,
|
||||
.bpp = 8,
|
||||
.pixel_clock = 12000,
|
||||
.x_res = 320,
|
||||
.y_res = 320,
|
||||
.hsw = 4,
|
||||
.hfp = 8,
|
||||
.hbp = 28,
|
||||
.vsw = 1,
|
||||
.vfp = 8,
|
||||
.vbp = 7,
|
||||
.pcd = 0,
|
||||
|
||||
.init = palmte_panel_init,
|
||||
.cleanup = palmte_panel_cleanup,
|
||||
.enable = palmte_panel_enable,
|
||||
.disable = palmte_panel_disable,
|
||||
.get_caps = palmte_panel_get_caps,
|
||||
};
|
||||
|
||||
static int palmte_panel_probe(struct platform_device *pdev)
|
||||
{
|
||||
omapfb_register_panel(&palmte_panel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int palmte_panel_remove(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int palmte_panel_suspend(struct platform_device *pdev, pm_message_t mesg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int palmte_panel_resume(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver palmte_panel_driver = {
|
||||
.probe = palmte_panel_probe,
|
||||
.remove = palmte_panel_remove,
|
||||
.suspend = palmte_panel_suspend,
|
||||
.resume = palmte_panel_resume,
|
||||
.driver = {
|
||||
.name = "lcd_palmte",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(palmte_panel_driver);
|
116
drivers/video/fbdev/omap/lcd_palmtt.c
Normal file
116
drivers/video/fbdev/omap/lcd_palmtt.c
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* LCD panel support for Palm Tungsten|T
|
||||
* Current version : Marek Vasut <marek.vasut@gmail.com>
|
||||
*
|
||||
* Modified from lcd_inn1510.c
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
GPIO11 - backlight
|
||||
GPIO12 - screen blanking
|
||||
GPIO13 - screen blanking
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <asm/gpio.h>
|
||||
#include "omapfb.h"
|
||||
|
||||
static int palmtt_panel_init(struct lcd_panel *panel,
|
||||
struct omapfb_device *fbdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void palmtt_panel_cleanup(struct lcd_panel *panel)
|
||||
{
|
||||
}
|
||||
|
||||
static int palmtt_panel_enable(struct lcd_panel *panel)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void palmtt_panel_disable(struct lcd_panel *panel)
|
||||
{
|
||||
}
|
||||
|
||||
static unsigned long palmtt_panel_get_caps(struct lcd_panel *panel)
|
||||
{
|
||||
return OMAPFB_CAPS_SET_BACKLIGHT;
|
||||
}
|
||||
|
||||
struct lcd_panel palmtt_panel = {
|
||||
.name = "palmtt",
|
||||
.config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC |
|
||||
OMAP_LCDC_INV_HSYNC | OMAP_LCDC_HSVS_RISING_EDGE |
|
||||
OMAP_LCDC_HSVS_OPPOSITE,
|
||||
.bpp = 16,
|
||||
.data_lines = 16,
|
||||
.x_res = 320,
|
||||
.y_res = 320,
|
||||
.pixel_clock = 10000,
|
||||
.hsw = 4,
|
||||
.hfp = 8,
|
||||
.hbp = 28,
|
||||
.vsw = 1,
|
||||
.vfp = 8,
|
||||
.vbp = 7,
|
||||
.pcd = 0,
|
||||
|
||||
.init = palmtt_panel_init,
|
||||
.cleanup = palmtt_panel_cleanup,
|
||||
.enable = palmtt_panel_enable,
|
||||
.disable = palmtt_panel_disable,
|
||||
.get_caps = palmtt_panel_get_caps,
|
||||
};
|
||||
|
||||
static int palmtt_panel_probe(struct platform_device *pdev)
|
||||
{
|
||||
omapfb_register_panel(&palmtt_panel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int palmtt_panel_remove(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int palmtt_panel_suspend(struct platform_device *pdev, pm_message_t mesg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int palmtt_panel_resume(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver palmtt_panel_driver = {
|
||||
.probe = palmtt_panel_probe,
|
||||
.remove = palmtt_panel_remove,
|
||||
.suspend = palmtt_panel_suspend,
|
||||
.resume = palmtt_panel_resume,
|
||||
.driver = {
|
||||
.name = "lcd_palmtt",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(palmtt_panel_driver);
|
112
drivers/video/fbdev/omap/lcd_palmz71.c
Normal file
112
drivers/video/fbdev/omap/lcd_palmz71.c
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* LCD panel support for the Palm Zire71
|
||||
*
|
||||
* Original version : Romain Goyet
|
||||
* Current version : Laurent Gonzalez
|
||||
* Modified for zire71 : Marek Vasut
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include "omapfb.h"
|
||||
|
||||
static int palmz71_panel_init(struct lcd_panel *panel,
|
||||
struct omapfb_device *fbdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void palmz71_panel_cleanup(struct lcd_panel *panel)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static int palmz71_panel_enable(struct lcd_panel *panel)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void palmz71_panel_disable(struct lcd_panel *panel)
|
||||
{
|
||||
}
|
||||
|
||||
static unsigned long palmz71_panel_get_caps(struct lcd_panel *panel)
|
||||
{
|
||||
return OMAPFB_CAPS_SET_BACKLIGHT;
|
||||
}
|
||||
|
||||
struct lcd_panel palmz71_panel = {
|
||||
.name = "palmz71",
|
||||
.config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC |
|
||||
OMAP_LCDC_INV_HSYNC | OMAP_LCDC_HSVS_RISING_EDGE |
|
||||
OMAP_LCDC_HSVS_OPPOSITE,
|
||||
.data_lines = 16,
|
||||
.bpp = 16,
|
||||
.pixel_clock = 24000,
|
||||
.x_res = 320,
|
||||
.y_res = 320,
|
||||
.hsw = 4,
|
||||
.hfp = 8,
|
||||
.hbp = 28,
|
||||
.vsw = 1,
|
||||
.vfp = 8,
|
||||
.vbp = 7,
|
||||
.pcd = 0,
|
||||
|
||||
.init = palmz71_panel_init,
|
||||
.cleanup = palmz71_panel_cleanup,
|
||||
.enable = palmz71_panel_enable,
|
||||
.disable = palmz71_panel_disable,
|
||||
.get_caps = palmz71_panel_get_caps,
|
||||
};
|
||||
|
||||
static int palmz71_panel_probe(struct platform_device *pdev)
|
||||
{
|
||||
omapfb_register_panel(&palmz71_panel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int palmz71_panel_remove(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int palmz71_panel_suspend(struct platform_device *pdev,
|
||||
pm_message_t mesg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int palmz71_panel_resume(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver palmz71_panel_driver = {
|
||||
.probe = palmz71_panel_probe,
|
||||
.remove = palmz71_panel_remove,
|
||||
.suspend = palmz71_panel_suspend,
|
||||
.resume = palmz71_panel_resume,
|
||||
.driver = {
|
||||
.name = "lcd_palmz71",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(palmz71_panel_driver);
|
856
drivers/video/fbdev/omap/lcdc.c
Normal file
856
drivers/video/fbdev/omap/lcdc.c
Normal file
@@ -0,0 +1,856 @@
|
||||
/*
|
||||
* OMAP1 internal LCD controller
|
||||
*
|
||||
* Copyright (C) 2004 Nokia Corporation
|
||||
* Author: Imre Deak <imre.deak@nokia.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/gfp.h>
|
||||
|
||||
#include <mach/lcdc.h>
|
||||
#include <linux/omap-dma.h>
|
||||
|
||||
#include <asm/mach-types.h>
|
||||
|
||||
#include "omapfb.h"
|
||||
|
||||
#include "lcdc.h"
|
||||
|
||||
#define MODULE_NAME "lcdc"
|
||||
|
||||
#define MAX_PALETTE_SIZE PAGE_SIZE
|
||||
|
||||
enum lcdc_load_mode {
|
||||
OMAP_LCDC_LOAD_PALETTE,
|
||||
OMAP_LCDC_LOAD_FRAME,
|
||||
OMAP_LCDC_LOAD_PALETTE_AND_FRAME
|
||||
};
|
||||
|
||||
static struct omap_lcd_controller {
|
||||
enum omapfb_update_mode update_mode;
|
||||
int ext_mode;
|
||||
|
||||
unsigned long frame_offset;
|
||||
int screen_width;
|
||||
int xres;
|
||||
int yres;
|
||||
|
||||
enum omapfb_color_format color_mode;
|
||||
int bpp;
|
||||
void *palette_virt;
|
||||
dma_addr_t palette_phys;
|
||||
int palette_code;
|
||||
int palette_size;
|
||||
|
||||
unsigned int irq_mask;
|
||||
struct completion last_frame_complete;
|
||||
struct completion palette_load_complete;
|
||||
struct clk *lcd_ck;
|
||||
struct omapfb_device *fbdev;
|
||||
|
||||
void (*dma_callback)(void *data);
|
||||
void *dma_callback_data;
|
||||
|
||||
int fbmem_allocated;
|
||||
dma_addr_t vram_phys;
|
||||
void *vram_virt;
|
||||
unsigned long vram_size;
|
||||
} lcdc;
|
||||
|
||||
static void inline enable_irqs(int mask)
|
||||
{
|
||||
lcdc.irq_mask |= mask;
|
||||
}
|
||||
|
||||
static void inline disable_irqs(int mask)
|
||||
{
|
||||
lcdc.irq_mask &= ~mask;
|
||||
}
|
||||
|
||||
static void set_load_mode(enum lcdc_load_mode mode)
|
||||
{
|
||||
u32 l;
|
||||
|
||||
l = omap_readl(OMAP_LCDC_CONTROL);
|
||||
l &= ~(3 << 20);
|
||||
switch (mode) {
|
||||
case OMAP_LCDC_LOAD_PALETTE:
|
||||
l |= 1 << 20;
|
||||
break;
|
||||
case OMAP_LCDC_LOAD_FRAME:
|
||||
l |= 2 << 20;
|
||||
break;
|
||||
case OMAP_LCDC_LOAD_PALETTE_AND_FRAME:
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
omap_writel(l, OMAP_LCDC_CONTROL);
|
||||
}
|
||||
|
||||
static void enable_controller(void)
|
||||
{
|
||||
u32 l;
|
||||
|
||||
l = omap_readl(OMAP_LCDC_CONTROL);
|
||||
l |= OMAP_LCDC_CTRL_LCD_EN;
|
||||
l &= ~OMAP_LCDC_IRQ_MASK;
|
||||
l |= lcdc.irq_mask | OMAP_LCDC_IRQ_DONE; /* enabled IRQs */
|
||||
omap_writel(l, OMAP_LCDC_CONTROL);
|
||||
}
|
||||
|
||||
static void disable_controller_async(void)
|
||||
{
|
||||
u32 l;
|
||||
u32 mask;
|
||||
|
||||
l = omap_readl(OMAP_LCDC_CONTROL);
|
||||
mask = OMAP_LCDC_CTRL_LCD_EN | OMAP_LCDC_IRQ_MASK;
|
||||
/*
|
||||
* Preserve the DONE mask, since we still want to get the
|
||||
* final DONE irq. It will be disabled in the IRQ handler.
|
||||
*/
|
||||
mask &= ~OMAP_LCDC_IRQ_DONE;
|
||||
l &= ~mask;
|
||||
omap_writel(l, OMAP_LCDC_CONTROL);
|
||||
}
|
||||
|
||||
static void disable_controller(void)
|
||||
{
|
||||
init_completion(&lcdc.last_frame_complete);
|
||||
disable_controller_async();
|
||||
if (!wait_for_completion_timeout(&lcdc.last_frame_complete,
|
||||
msecs_to_jiffies(500)))
|
||||
dev_err(lcdc.fbdev->dev, "timeout waiting for FRAME DONE\n");
|
||||
}
|
||||
|
||||
static void reset_controller(u32 status)
|
||||
{
|
||||
static unsigned long reset_count;
|
||||
static unsigned long last_jiffies;
|
||||
|
||||
disable_controller_async();
|
||||
reset_count++;
|
||||
if (reset_count == 1 || time_after(jiffies, last_jiffies + HZ)) {
|
||||
dev_err(lcdc.fbdev->dev,
|
||||
"resetting (status %#010x,reset count %lu)\n",
|
||||
status, reset_count);
|
||||
last_jiffies = jiffies;
|
||||
}
|
||||
if (reset_count < 100) {
|
||||
enable_controller();
|
||||
} else {
|
||||
reset_count = 0;
|
||||
dev_err(lcdc.fbdev->dev,
|
||||
"too many reset attempts, giving up.\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure the LCD DMA according to the current mode specified by parameters
|
||||
* in lcdc.fbdev and fbdev->var.
|
||||
*/
|
||||
static void setup_lcd_dma(void)
|
||||
{
|
||||
static const int dma_elem_type[] = {
|
||||
0,
|
||||
OMAP_DMA_DATA_TYPE_S8,
|
||||
OMAP_DMA_DATA_TYPE_S16,
|
||||
0,
|
||||
OMAP_DMA_DATA_TYPE_S32,
|
||||
};
|
||||
struct omapfb_plane_struct *plane = lcdc.fbdev->fb_info[0]->par;
|
||||
struct fb_var_screeninfo *var = &lcdc.fbdev->fb_info[0]->var;
|
||||
unsigned long src;
|
||||
int esize, xelem, yelem;
|
||||
|
||||
src = lcdc.vram_phys + lcdc.frame_offset;
|
||||
|
||||
switch (var->rotate) {
|
||||
case 0:
|
||||
if (plane->info.mirror || (src & 3) ||
|
||||
lcdc.color_mode == OMAPFB_COLOR_YUV420 ||
|
||||
(lcdc.xres & 1))
|
||||
esize = 2;
|
||||
else
|
||||
esize = 4;
|
||||
xelem = lcdc.xres * lcdc.bpp / 8 / esize;
|
||||
yelem = lcdc.yres;
|
||||
break;
|
||||
case 90:
|
||||
case 180:
|
||||
case 270:
|
||||
if (cpu_is_omap15xx()) {
|
||||
BUG();
|
||||
}
|
||||
esize = 2;
|
||||
xelem = lcdc.yres * lcdc.bpp / 16;
|
||||
yelem = lcdc.xres;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
return;
|
||||
}
|
||||
#ifdef VERBOSE
|
||||
dev_dbg(lcdc.fbdev->dev,
|
||||
"setup_dma: src %#010lx esize %d xelem %d yelem %d\n",
|
||||
src, esize, xelem, yelem);
|
||||
#endif
|
||||
omap_set_lcd_dma_b1(src, xelem, yelem, dma_elem_type[esize]);
|
||||
if (!cpu_is_omap15xx()) {
|
||||
int bpp = lcdc.bpp;
|
||||
|
||||
/*
|
||||
* YUV support is only for external mode when we have the
|
||||
* YUV window embedded in a 16bpp frame buffer.
|
||||
*/
|
||||
if (lcdc.color_mode == OMAPFB_COLOR_YUV420)
|
||||
bpp = 16;
|
||||
/* Set virtual xres elem size */
|
||||
omap_set_lcd_dma_b1_vxres(
|
||||
lcdc.screen_width * bpp / 8 / esize);
|
||||
/* Setup transformations */
|
||||
omap_set_lcd_dma_b1_rotation(var->rotate);
|
||||
omap_set_lcd_dma_b1_mirror(plane->info.mirror);
|
||||
}
|
||||
omap_setup_lcd_dma();
|
||||
}
|
||||
|
||||
static irqreturn_t lcdc_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
u32 status;
|
||||
|
||||
status = omap_readl(OMAP_LCDC_STATUS);
|
||||
|
||||
if (status & (OMAP_LCDC_STAT_FUF | OMAP_LCDC_STAT_SYNC_LOST))
|
||||
reset_controller(status);
|
||||
else {
|
||||
if (status & OMAP_LCDC_STAT_DONE) {
|
||||
u32 l;
|
||||
|
||||
/*
|
||||
* Disable IRQ_DONE. The status bit will be cleared
|
||||
* only when the controller is reenabled and we don't
|
||||
* want to get more interrupts.
|
||||
*/
|
||||
l = omap_readl(OMAP_LCDC_CONTROL);
|
||||
l &= ~OMAP_LCDC_IRQ_DONE;
|
||||
omap_writel(l, OMAP_LCDC_CONTROL);
|
||||
complete(&lcdc.last_frame_complete);
|
||||
}
|
||||
if (status & OMAP_LCDC_STAT_LOADED_PALETTE) {
|
||||
disable_controller_async();
|
||||
complete(&lcdc.palette_load_complete);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear these interrupt status bits.
|
||||
* Sync_lost, FUF bits were cleared by disabling the LCD controller
|
||||
* LOADED_PALETTE can be cleared this way only in palette only
|
||||
* load mode. In other load modes it's cleared by disabling the
|
||||
* controller.
|
||||
*/
|
||||
status &= ~(OMAP_LCDC_STAT_VSYNC |
|
||||
OMAP_LCDC_STAT_LOADED_PALETTE |
|
||||
OMAP_LCDC_STAT_ABC |
|
||||
OMAP_LCDC_STAT_LINE_INT);
|
||||
omap_writel(status, OMAP_LCDC_STATUS);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Change to a new video mode. We defer this to a later time to avoid any
|
||||
* flicker and not to mess up the current LCD DMA context. For this we disable
|
||||
* the LCD controller, which will generate a DONE irq after the last frame has
|
||||
* been transferred. Then it'll be safe to reconfigure both the LCD controller
|
||||
* as well as the LCD DMA.
|
||||
*/
|
||||
static int omap_lcdc_setup_plane(int plane, int channel_out,
|
||||
unsigned long offset, int screen_width,
|
||||
int pos_x, int pos_y, int width, int height,
|
||||
int color_mode)
|
||||
{
|
||||
struct fb_var_screeninfo *var = &lcdc.fbdev->fb_info[0]->var;
|
||||
struct lcd_panel *panel = lcdc.fbdev->panel;
|
||||
int rot_x, rot_y;
|
||||
|
||||
if (var->rotate == 0) {
|
||||
rot_x = panel->x_res;
|
||||
rot_y = panel->y_res;
|
||||
} else {
|
||||
rot_x = panel->y_res;
|
||||
rot_y = panel->x_res;
|
||||
}
|
||||
if (plane != 0 || channel_out != 0 || pos_x != 0 || pos_y != 0 ||
|
||||
width > rot_x || height > rot_y) {
|
||||
#ifdef VERBOSE
|
||||
dev_dbg(lcdc.fbdev->dev,
|
||||
"invalid plane params plane %d pos_x %d pos_y %d "
|
||||
"w %d h %d\n", plane, pos_x, pos_y, width, height);
|
||||
#endif
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
lcdc.frame_offset = offset;
|
||||
lcdc.xres = width;
|
||||
lcdc.yres = height;
|
||||
lcdc.screen_width = screen_width;
|
||||
lcdc.color_mode = color_mode;
|
||||
|
||||
switch (color_mode) {
|
||||
case OMAPFB_COLOR_CLUT_8BPP:
|
||||
lcdc.bpp = 8;
|
||||
lcdc.palette_code = 0x3000;
|
||||
lcdc.palette_size = 512;
|
||||
break;
|
||||
case OMAPFB_COLOR_RGB565:
|
||||
lcdc.bpp = 16;
|
||||
lcdc.palette_code = 0x4000;
|
||||
lcdc.palette_size = 32;
|
||||
break;
|
||||
case OMAPFB_COLOR_RGB444:
|
||||
lcdc.bpp = 16;
|
||||
lcdc.palette_code = 0x4000;
|
||||
lcdc.palette_size = 32;
|
||||
break;
|
||||
case OMAPFB_COLOR_YUV420:
|
||||
if (lcdc.ext_mode) {
|
||||
lcdc.bpp = 12;
|
||||
break;
|
||||
}
|
||||
/* fallthrough */
|
||||
case OMAPFB_COLOR_YUV422:
|
||||
if (lcdc.ext_mode) {
|
||||
lcdc.bpp = 16;
|
||||
break;
|
||||
}
|
||||
/* fallthrough */
|
||||
default:
|
||||
/* FIXME: other BPPs.
|
||||
* bpp1: code 0, size 256
|
||||
* bpp2: code 0x1000 size 256
|
||||
* bpp4: code 0x2000 size 256
|
||||
* bpp12: code 0x4000 size 32
|
||||
*/
|
||||
dev_dbg(lcdc.fbdev->dev, "invalid color mode %d\n", color_mode);
|
||||
BUG();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (lcdc.ext_mode) {
|
||||
setup_lcd_dma();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lcdc.update_mode == OMAPFB_AUTO_UPDATE) {
|
||||
disable_controller();
|
||||
omap_stop_lcd_dma();
|
||||
setup_lcd_dma();
|
||||
enable_controller();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int omap_lcdc_enable_plane(int plane, int enable)
|
||||
{
|
||||
dev_dbg(lcdc.fbdev->dev,
|
||||
"plane %d enable %d update_mode %d ext_mode %d\n",
|
||||
plane, enable, lcdc.update_mode, lcdc.ext_mode);
|
||||
if (plane != OMAPFB_PLANE_GFX)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure the LCD DMA for a palette load operation and do the palette
|
||||
* downloading synchronously. We don't use the frame+palette load mode of
|
||||
* the controller, since the palette can always be downloaded separately.
|
||||
*/
|
||||
static void load_palette(void)
|
||||
{
|
||||
u16 *palette;
|
||||
|
||||
palette = (u16 *)lcdc.palette_virt;
|
||||
|
||||
*(u16 *)palette &= 0x0fff;
|
||||
*(u16 *)palette |= lcdc.palette_code;
|
||||
|
||||
omap_set_lcd_dma_b1(lcdc.palette_phys,
|
||||
lcdc.palette_size / 4 + 1, 1, OMAP_DMA_DATA_TYPE_S32);
|
||||
|
||||
omap_set_lcd_dma_single_transfer(1);
|
||||
omap_setup_lcd_dma();
|
||||
|
||||
init_completion(&lcdc.palette_load_complete);
|
||||
enable_irqs(OMAP_LCDC_IRQ_LOADED_PALETTE);
|
||||
set_load_mode(OMAP_LCDC_LOAD_PALETTE);
|
||||
enable_controller();
|
||||
if (!wait_for_completion_timeout(&lcdc.palette_load_complete,
|
||||
msecs_to_jiffies(500)))
|
||||
dev_err(lcdc.fbdev->dev, "timeout waiting for FRAME DONE\n");
|
||||
/* The controller gets disabled in the irq handler */
|
||||
disable_irqs(OMAP_LCDC_IRQ_LOADED_PALETTE);
|
||||
omap_stop_lcd_dma();
|
||||
|
||||
omap_set_lcd_dma_single_transfer(lcdc.ext_mode);
|
||||
}
|
||||
|
||||
/* Used only in internal controller mode */
|
||||
static int omap_lcdc_setcolreg(u_int regno, u16 red, u16 green, u16 blue,
|
||||
u16 transp, int update_hw_pal)
|
||||
{
|
||||
u16 *palette;
|
||||
|
||||
if (lcdc.color_mode != OMAPFB_COLOR_CLUT_8BPP || regno > 255)
|
||||
return -EINVAL;
|
||||
|
||||
palette = (u16 *)lcdc.palette_virt;
|
||||
|
||||
palette[regno] &= ~0x0fff;
|
||||
palette[regno] |= ((red >> 12) << 8) | ((green >> 12) << 4 ) |
|
||||
(blue >> 12);
|
||||
|
||||
if (update_hw_pal) {
|
||||
disable_controller();
|
||||
omap_stop_lcd_dma();
|
||||
load_palette();
|
||||
setup_lcd_dma();
|
||||
set_load_mode(OMAP_LCDC_LOAD_FRAME);
|
||||
enable_controller();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void calc_ck_div(int is_tft, int pck, int *pck_div)
|
||||
{
|
||||
unsigned long lck;
|
||||
|
||||
pck = max(1, pck);
|
||||
lck = clk_get_rate(lcdc.lcd_ck);
|
||||
*pck_div = (lck + pck - 1) / pck;
|
||||
if (is_tft)
|
||||
*pck_div = max(2, *pck_div);
|
||||
else
|
||||
*pck_div = max(3, *pck_div);
|
||||
if (*pck_div > 255) {
|
||||
/* FIXME: try to adjust logic clock divider as well */
|
||||
*pck_div = 255;
|
||||
dev_warn(lcdc.fbdev->dev, "pixclock %d kHz too low.\n",
|
||||
pck / 1000);
|
||||
}
|
||||
}
|
||||
|
||||
static void inline setup_regs(void)
|
||||
{
|
||||
u32 l;
|
||||
struct lcd_panel *panel = lcdc.fbdev->panel;
|
||||
int is_tft = panel->config & OMAP_LCDC_PANEL_TFT;
|
||||
unsigned long lck;
|
||||
int pcd;
|
||||
|
||||
l = omap_readl(OMAP_LCDC_CONTROL);
|
||||
l &= ~OMAP_LCDC_CTRL_LCD_TFT;
|
||||
l |= is_tft ? OMAP_LCDC_CTRL_LCD_TFT : 0;
|
||||
#ifdef CONFIG_MACH_OMAP_PALMTE
|
||||
/* FIXME:if (machine_is_omap_palmte()) { */
|
||||
/* PalmTE uses alternate TFT setting in 8BPP mode */
|
||||
l |= (is_tft && panel->bpp == 8) ? 0x810000 : 0;
|
||||
/* } */
|
||||
#endif
|
||||
omap_writel(l, OMAP_LCDC_CONTROL);
|
||||
|
||||
l = omap_readl(OMAP_LCDC_TIMING2);
|
||||
l &= ~(((1 << 6) - 1) << 20);
|
||||
l |= (panel->config & OMAP_LCDC_SIGNAL_MASK) << 20;
|
||||
omap_writel(l, OMAP_LCDC_TIMING2);
|
||||
|
||||
l = panel->x_res - 1;
|
||||
l |= (panel->hsw - 1) << 10;
|
||||
l |= (panel->hfp - 1) << 16;
|
||||
l |= (panel->hbp - 1) << 24;
|
||||
omap_writel(l, OMAP_LCDC_TIMING0);
|
||||
|
||||
l = panel->y_res - 1;
|
||||
l |= (panel->vsw - 1) << 10;
|
||||
l |= panel->vfp << 16;
|
||||
l |= panel->vbp << 24;
|
||||
omap_writel(l, OMAP_LCDC_TIMING1);
|
||||
|
||||
l = omap_readl(OMAP_LCDC_TIMING2);
|
||||
l &= ~0xff;
|
||||
|
||||
lck = clk_get_rate(lcdc.lcd_ck);
|
||||
|
||||
if (!panel->pcd)
|
||||
calc_ck_div(is_tft, panel->pixel_clock * 1000, &pcd);
|
||||
else {
|
||||
dev_warn(lcdc.fbdev->dev,
|
||||
"Pixel clock divider value is obsolete.\n"
|
||||
"Try to set pixel_clock to %lu and pcd to 0 "
|
||||
"in drivers/video/omap/lcd_%s.c and submit a patch.\n",
|
||||
lck / panel->pcd / 1000, panel->name);
|
||||
|
||||
pcd = panel->pcd;
|
||||
}
|
||||
l |= pcd & 0xff;
|
||||
l |= panel->acb << 8;
|
||||
omap_writel(l, OMAP_LCDC_TIMING2);
|
||||
|
||||
/* update panel info with the exact clock */
|
||||
panel->pixel_clock = lck / pcd / 1000;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure the LCD controller, download the color palette and start a looped
|
||||
* DMA transfer of the frame image data. Called only in internal
|
||||
* controller mode.
|
||||
*/
|
||||
static int omap_lcdc_set_update_mode(enum omapfb_update_mode mode)
|
||||
{
|
||||
int r = 0;
|
||||
|
||||
if (mode != lcdc.update_mode) {
|
||||
switch (mode) {
|
||||
case OMAPFB_AUTO_UPDATE:
|
||||
setup_regs();
|
||||
load_palette();
|
||||
|
||||
/* Setup and start LCD DMA */
|
||||
setup_lcd_dma();
|
||||
|
||||
set_load_mode(OMAP_LCDC_LOAD_FRAME);
|
||||
enable_irqs(OMAP_LCDC_IRQ_DONE);
|
||||
/* This will start the actual DMA transfer */
|
||||
enable_controller();
|
||||
lcdc.update_mode = mode;
|
||||
break;
|
||||
case OMAPFB_UPDATE_DISABLED:
|
||||
disable_controller();
|
||||
omap_stop_lcd_dma();
|
||||
lcdc.update_mode = mode;
|
||||
break;
|
||||
default:
|
||||
r = -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static enum omapfb_update_mode omap_lcdc_get_update_mode(void)
|
||||
{
|
||||
return lcdc.update_mode;
|
||||
}
|
||||
|
||||
/* PM code called only in internal controller mode */
|
||||
static void omap_lcdc_suspend(void)
|
||||
{
|
||||
omap_lcdc_set_update_mode(OMAPFB_UPDATE_DISABLED);
|
||||
}
|
||||
|
||||
static void omap_lcdc_resume(void)
|
||||
{
|
||||
omap_lcdc_set_update_mode(OMAPFB_AUTO_UPDATE);
|
||||
}
|
||||
|
||||
static void omap_lcdc_get_caps(int plane, struct omapfb_caps *caps)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int omap_lcdc_set_dma_callback(void (*callback)(void *data), void *data)
|
||||
{
|
||||
BUG_ON(callback == NULL);
|
||||
|
||||
if (lcdc.dma_callback)
|
||||
return -EBUSY;
|
||||
else {
|
||||
lcdc.dma_callback = callback;
|
||||
lcdc.dma_callback_data = data;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(omap_lcdc_set_dma_callback);
|
||||
|
||||
void omap_lcdc_free_dma_callback(void)
|
||||
{
|
||||
lcdc.dma_callback = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(omap_lcdc_free_dma_callback);
|
||||
|
||||
static void lcdc_dma_handler(u16 status, void *data)
|
||||
{
|
||||
if (lcdc.dma_callback)
|
||||
lcdc.dma_callback(lcdc.dma_callback_data);
|
||||
}
|
||||
|
||||
static int mmap_kern(void)
|
||||
{
|
||||
struct vm_struct *kvma;
|
||||
struct vm_area_struct vma;
|
||||
pgprot_t pgprot;
|
||||
unsigned long vaddr;
|
||||
|
||||
kvma = get_vm_area(lcdc.vram_size, VM_IOREMAP);
|
||||
if (kvma == NULL) {
|
||||
dev_err(lcdc.fbdev->dev, "can't get kernel vm area\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
vma.vm_mm = &init_mm;
|
||||
|
||||
vaddr = (unsigned long)kvma->addr;
|
||||
vma.vm_start = vaddr;
|
||||
vma.vm_end = vaddr + lcdc.vram_size;
|
||||
|
||||
pgprot = pgprot_writecombine(pgprot_kernel);
|
||||
if (io_remap_pfn_range(&vma, vaddr,
|
||||
lcdc.vram_phys >> PAGE_SHIFT,
|
||||
lcdc.vram_size, pgprot) < 0) {
|
||||
dev_err(lcdc.fbdev->dev, "kernel mmap for FB memory failed\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
lcdc.vram_virt = (void *)vaddr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void unmap_kern(void)
|
||||
{
|
||||
vunmap(lcdc.vram_virt);
|
||||
}
|
||||
|
||||
static int alloc_palette_ram(void)
|
||||
{
|
||||
lcdc.palette_virt = dma_alloc_writecombine(lcdc.fbdev->dev,
|
||||
MAX_PALETTE_SIZE, &lcdc.palette_phys, GFP_KERNEL);
|
||||
if (lcdc.palette_virt == NULL) {
|
||||
dev_err(lcdc.fbdev->dev, "failed to alloc palette memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset(lcdc.palette_virt, 0, MAX_PALETTE_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void free_palette_ram(void)
|
||||
{
|
||||
dma_free_writecombine(lcdc.fbdev->dev, MAX_PALETTE_SIZE,
|
||||
lcdc.palette_virt, lcdc.palette_phys);
|
||||
}
|
||||
|
||||
static int alloc_fbmem(struct omapfb_mem_region *region)
|
||||
{
|
||||
int bpp;
|
||||
int frame_size;
|
||||
struct lcd_panel *panel = lcdc.fbdev->panel;
|
||||
|
||||
bpp = panel->bpp;
|
||||
if (bpp == 12)
|
||||
bpp = 16;
|
||||
frame_size = PAGE_ALIGN(panel->x_res * bpp / 8 * panel->y_res);
|
||||
if (region->size > frame_size)
|
||||
frame_size = region->size;
|
||||
lcdc.vram_size = frame_size;
|
||||
lcdc.vram_virt = dma_alloc_writecombine(lcdc.fbdev->dev,
|
||||
lcdc.vram_size, &lcdc.vram_phys, GFP_KERNEL);
|
||||
if (lcdc.vram_virt == NULL) {
|
||||
dev_err(lcdc.fbdev->dev, "unable to allocate FB DMA memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
region->size = frame_size;
|
||||
region->paddr = lcdc.vram_phys;
|
||||
region->vaddr = lcdc.vram_virt;
|
||||
region->alloc = 1;
|
||||
|
||||
memset(lcdc.vram_virt, 0, lcdc.vram_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void free_fbmem(void)
|
||||
{
|
||||
dma_free_writecombine(lcdc.fbdev->dev, lcdc.vram_size,
|
||||
lcdc.vram_virt, lcdc.vram_phys);
|
||||
}
|
||||
|
||||
static int setup_fbmem(struct omapfb_mem_desc *req_md)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (!req_md->region_cnt) {
|
||||
dev_err(lcdc.fbdev->dev, "no memory regions defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (req_md->region_cnt > 1) {
|
||||
dev_err(lcdc.fbdev->dev, "only one plane is supported\n");
|
||||
req_md->region_cnt = 1;
|
||||
}
|
||||
|
||||
if (req_md->region[0].paddr == 0) {
|
||||
lcdc.fbmem_allocated = 1;
|
||||
if ((r = alloc_fbmem(&req_md->region[0])) < 0)
|
||||
return r;
|
||||
return 0;
|
||||
}
|
||||
|
||||
lcdc.vram_phys = req_md->region[0].paddr;
|
||||
lcdc.vram_size = req_md->region[0].size;
|
||||
|
||||
if ((r = mmap_kern()) < 0)
|
||||
return r;
|
||||
|
||||
dev_dbg(lcdc.fbdev->dev, "vram at %08x size %08lx mapped to 0x%p\n",
|
||||
lcdc.vram_phys, lcdc.vram_size, lcdc.vram_virt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cleanup_fbmem(void)
|
||||
{
|
||||
if (lcdc.fbmem_allocated)
|
||||
free_fbmem();
|
||||
else
|
||||
unmap_kern();
|
||||
}
|
||||
|
||||
static int omap_lcdc_init(struct omapfb_device *fbdev, int ext_mode,
|
||||
struct omapfb_mem_desc *req_vram)
|
||||
{
|
||||
int r;
|
||||
u32 l;
|
||||
int rate;
|
||||
struct clk *tc_ck;
|
||||
|
||||
lcdc.irq_mask = 0;
|
||||
|
||||
lcdc.fbdev = fbdev;
|
||||
lcdc.ext_mode = ext_mode;
|
||||
|
||||
l = 0;
|
||||
omap_writel(l, OMAP_LCDC_CONTROL);
|
||||
|
||||
/* FIXME:
|
||||
* According to errata some platforms have a clock rate limitiation
|
||||
*/
|
||||
lcdc.lcd_ck = clk_get(fbdev->dev, "lcd_ck");
|
||||
if (IS_ERR(lcdc.lcd_ck)) {
|
||||
dev_err(fbdev->dev, "unable to access LCD clock\n");
|
||||
r = PTR_ERR(lcdc.lcd_ck);
|
||||
goto fail0;
|
||||
}
|
||||
|
||||
tc_ck = clk_get(fbdev->dev, "tc_ck");
|
||||
if (IS_ERR(tc_ck)) {
|
||||
dev_err(fbdev->dev, "unable to access TC clock\n");
|
||||
r = PTR_ERR(tc_ck);
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
rate = clk_get_rate(tc_ck);
|
||||
clk_put(tc_ck);
|
||||
|
||||
if (machine_is_ams_delta())
|
||||
rate /= 4;
|
||||
if (machine_is_omap_h3())
|
||||
rate /= 3;
|
||||
r = clk_set_rate(lcdc.lcd_ck, rate);
|
||||
if (r) {
|
||||
dev_err(fbdev->dev, "failed to adjust LCD rate\n");
|
||||
goto fail1;
|
||||
}
|
||||
clk_enable(lcdc.lcd_ck);
|
||||
|
||||
r = request_irq(OMAP_LCDC_IRQ, lcdc_irq_handler, 0, MODULE_NAME, fbdev);
|
||||
if (r) {
|
||||
dev_err(fbdev->dev, "unable to get IRQ\n");
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
r = omap_request_lcd_dma(lcdc_dma_handler, NULL);
|
||||
if (r) {
|
||||
dev_err(fbdev->dev, "unable to get LCD DMA\n");
|
||||
goto fail3;
|
||||
}
|
||||
|
||||
omap_set_lcd_dma_single_transfer(ext_mode);
|
||||
omap_set_lcd_dma_ext_controller(ext_mode);
|
||||
|
||||
if (!ext_mode)
|
||||
if ((r = alloc_palette_ram()) < 0)
|
||||
goto fail4;
|
||||
|
||||
if ((r = setup_fbmem(req_vram)) < 0)
|
||||
goto fail5;
|
||||
|
||||
pr_info("omapfb: LCDC initialized\n");
|
||||
|
||||
return 0;
|
||||
fail5:
|
||||
if (!ext_mode)
|
||||
free_palette_ram();
|
||||
fail4:
|
||||
omap_free_lcd_dma();
|
||||
fail3:
|
||||
free_irq(OMAP_LCDC_IRQ, lcdc.fbdev);
|
||||
fail2:
|
||||
clk_disable(lcdc.lcd_ck);
|
||||
fail1:
|
||||
clk_put(lcdc.lcd_ck);
|
||||
fail0:
|
||||
return r;
|
||||
}
|
||||
|
||||
static void omap_lcdc_cleanup(void)
|
||||
{
|
||||
if (!lcdc.ext_mode)
|
||||
free_palette_ram();
|
||||
cleanup_fbmem();
|
||||
omap_free_lcd_dma();
|
||||
free_irq(OMAP_LCDC_IRQ, lcdc.fbdev);
|
||||
clk_disable(lcdc.lcd_ck);
|
||||
clk_put(lcdc.lcd_ck);
|
||||
}
|
||||
|
||||
const struct lcd_ctrl omap1_int_ctrl = {
|
||||
.name = "internal",
|
||||
.init = omap_lcdc_init,
|
||||
.cleanup = omap_lcdc_cleanup,
|
||||
.get_caps = omap_lcdc_get_caps,
|
||||
.set_update_mode = omap_lcdc_set_update_mode,
|
||||
.get_update_mode = omap_lcdc_get_update_mode,
|
||||
.update_window = NULL,
|
||||
.suspend = omap_lcdc_suspend,
|
||||
.resume = omap_lcdc_resume,
|
||||
.setup_plane = omap_lcdc_setup_plane,
|
||||
.enable_plane = omap_lcdc_enable_plane,
|
||||
.setcolreg = omap_lcdc_setcolreg,
|
||||
};
|
9
drivers/video/fbdev/omap/lcdc.h
Normal file
9
drivers/video/fbdev/omap/lcdc.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef LCDC_H
|
||||
#define LCDC_H
|
||||
|
||||
int omap_lcdc_set_dma_callback(void (*callback)(void *data), void *data);
|
||||
void omap_lcdc_free_dma_callback(void);
|
||||
|
||||
extern const struct lcd_ctrl omap1_int_ctrl;
|
||||
|
||||
#endif
|
246
drivers/video/fbdev/omap/omapfb.h
Normal file
246
drivers/video/fbdev/omap/omapfb.h
Normal file
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* File: drivers/video/omap/omapfb.h
|
||||
*
|
||||
* Framebuffer driver for TI OMAP boards
|
||||
*
|
||||
* Copyright (C) 2004 Nokia Corporation
|
||||
* Author: Imre Deak <imre.deak@nokia.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __OMAPFB_H
|
||||
#define __OMAPFB_H
|
||||
|
||||
#include <linux/fb.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/omapfb.h>
|
||||
|
||||
#define OMAPFB_EVENT_READY 1
|
||||
#define OMAPFB_EVENT_DISABLED 2
|
||||
|
||||
#define OMAP_LCDC_INV_VSYNC 0x0001
|
||||
#define OMAP_LCDC_INV_HSYNC 0x0002
|
||||
#define OMAP_LCDC_INV_PIX_CLOCK 0x0004
|
||||
#define OMAP_LCDC_INV_OUTPUT_EN 0x0008
|
||||
#define OMAP_LCDC_HSVS_RISING_EDGE 0x0010
|
||||
#define OMAP_LCDC_HSVS_OPPOSITE 0x0020
|
||||
|
||||
#define OMAP_LCDC_SIGNAL_MASK 0x003f
|
||||
|
||||
#define OMAP_LCDC_PANEL_TFT 0x0100
|
||||
|
||||
#define OMAPFB_PLANE_XRES_MIN 8
|
||||
#define OMAPFB_PLANE_YRES_MIN 8
|
||||
|
||||
struct omapfb_device;
|
||||
|
||||
#define OMAPFB_PLANE_NUM 1
|
||||
|
||||
struct omapfb_mem_region {
|
||||
u32 paddr;
|
||||
void __iomem *vaddr;
|
||||
unsigned long size;
|
||||
u8 type; /* OMAPFB_PLANE_MEM_* */
|
||||
enum omapfb_color_format format;/* OMAPFB_COLOR_* */
|
||||
unsigned format_used:1; /* Must be set when format is set.
|
||||
* Needed b/c of the badly chosen 0
|
||||
* base for OMAPFB_COLOR_* values
|
||||
*/
|
||||
unsigned alloc:1; /* allocated by the driver */
|
||||
unsigned map:1; /* kernel mapped by the driver */
|
||||
};
|
||||
|
||||
struct omapfb_mem_desc {
|
||||
int region_cnt;
|
||||
struct omapfb_mem_region region[OMAPFB_PLANE_NUM];
|
||||
};
|
||||
|
||||
struct lcd_panel {
|
||||
const char *name;
|
||||
int config; /* TFT/STN, signal inversion */
|
||||
int bpp; /* Pixel format in fb mem */
|
||||
int data_lines; /* Lines on LCD HW interface */
|
||||
|
||||
int x_res, y_res;
|
||||
int pixel_clock; /* In kHz */
|
||||
int hsw; /* Horizontal synchronization
|
||||
pulse width */
|
||||
int hfp; /* Horizontal front porch */
|
||||
int hbp; /* Horizontal back porch */
|
||||
int vsw; /* Vertical synchronization
|
||||
pulse width */
|
||||
int vfp; /* Vertical front porch */
|
||||
int vbp; /* Vertical back porch */
|
||||
int acb; /* ac-bias pin frequency */
|
||||
int pcd; /* pixel clock divider.
|
||||
Obsolete use pixel_clock instead */
|
||||
|
||||
int (*init) (struct lcd_panel *panel,
|
||||
struct omapfb_device *fbdev);
|
||||
void (*cleanup) (struct lcd_panel *panel);
|
||||
int (*enable) (struct lcd_panel *panel);
|
||||
void (*disable) (struct lcd_panel *panel);
|
||||
unsigned long (*get_caps) (struct lcd_panel *panel);
|
||||
int (*set_bklight_level)(struct lcd_panel *panel,
|
||||
unsigned int level);
|
||||
unsigned int (*get_bklight_level)(struct lcd_panel *panel);
|
||||
unsigned int (*get_bklight_max) (struct lcd_panel *panel);
|
||||
int (*run_test) (struct lcd_panel *panel, int test_num);
|
||||
};
|
||||
|
||||
struct extif_timings {
|
||||
int cs_on_time;
|
||||
int cs_off_time;
|
||||
int we_on_time;
|
||||
int we_off_time;
|
||||
int re_on_time;
|
||||
int re_off_time;
|
||||
int we_cycle_time;
|
||||
int re_cycle_time;
|
||||
int cs_pulse_width;
|
||||
int access_time;
|
||||
|
||||
int clk_div;
|
||||
|
||||
u32 tim[5]; /* set by extif->convert_timings */
|
||||
|
||||
int converted;
|
||||
};
|
||||
|
||||
struct lcd_ctrl_extif {
|
||||
int (*init) (struct omapfb_device *fbdev);
|
||||
void (*cleanup) (void);
|
||||
void (*get_clk_info) (u32 *clk_period, u32 *max_clk_div);
|
||||
unsigned long (*get_max_tx_rate)(void);
|
||||
int (*convert_timings) (struct extif_timings *timings);
|
||||
void (*set_timings) (const struct extif_timings *timings);
|
||||
void (*set_bits_per_cycle)(int bpc);
|
||||
void (*write_command) (const void *buf, unsigned int len);
|
||||
void (*read_data) (void *buf, unsigned int len);
|
||||
void (*write_data) (const void *buf, unsigned int len);
|
||||
void (*transfer_area) (int width, int height,
|
||||
void (callback)(void *data), void *data);
|
||||
int (*setup_tearsync) (unsigned pin_cnt,
|
||||
unsigned hs_pulse_time, unsigned vs_pulse_time,
|
||||
int hs_pol_inv, int vs_pol_inv, int div);
|
||||
int (*enable_tearsync) (int enable, unsigned line);
|
||||
|
||||
unsigned long max_transmit_size;
|
||||
};
|
||||
|
||||
struct omapfb_notifier_block {
|
||||
struct notifier_block nb;
|
||||
void *data;
|
||||
int plane_idx;
|
||||
};
|
||||
|
||||
typedef int (*omapfb_notifier_callback_t)(struct notifier_block *,
|
||||
unsigned long event,
|
||||
void *fbi);
|
||||
|
||||
struct lcd_ctrl {
|
||||
const char *name;
|
||||
void *data;
|
||||
|
||||
int (*init) (struct omapfb_device *fbdev,
|
||||
int ext_mode,
|
||||
struct omapfb_mem_desc *req_md);
|
||||
void (*cleanup) (void);
|
||||
void (*bind_client) (struct omapfb_notifier_block *nb);
|
||||
void (*get_caps) (int plane, struct omapfb_caps *caps);
|
||||
int (*set_update_mode)(enum omapfb_update_mode mode);
|
||||
enum omapfb_update_mode (*get_update_mode)(void);
|
||||
int (*setup_plane) (int plane, int channel_out,
|
||||
unsigned long offset,
|
||||
int screen_width,
|
||||
int pos_x, int pos_y, int width,
|
||||
int height, int color_mode);
|
||||
int (*set_rotate) (int angle);
|
||||
int (*setup_mem) (int plane, size_t size,
|
||||
int mem_type, unsigned long *paddr);
|
||||
int (*mmap) (struct fb_info *info,
|
||||
struct vm_area_struct *vma);
|
||||
int (*set_scale) (int plane,
|
||||
int orig_width, int orig_height,
|
||||
int out_width, int out_height);
|
||||
int (*enable_plane) (int plane, int enable);
|
||||
int (*update_window) (struct fb_info *fbi,
|
||||
struct omapfb_update_window *win,
|
||||
void (*callback)(void *),
|
||||
void *callback_data);
|
||||
void (*sync) (void);
|
||||
void (*suspend) (void);
|
||||
void (*resume) (void);
|
||||
int (*run_test) (int test_num);
|
||||
int (*setcolreg) (u_int regno, u16 red, u16 green,
|
||||
u16 blue, u16 transp,
|
||||
int update_hw_mem);
|
||||
int (*set_color_key) (struct omapfb_color_key *ck);
|
||||
int (*get_color_key) (struct omapfb_color_key *ck);
|
||||
};
|
||||
|
||||
enum omapfb_state {
|
||||
OMAPFB_DISABLED = 0,
|
||||
OMAPFB_SUSPENDED = 99,
|
||||
OMAPFB_ACTIVE = 100
|
||||
};
|
||||
|
||||
struct omapfb_plane_struct {
|
||||
int idx;
|
||||
struct omapfb_plane_info info;
|
||||
enum omapfb_color_format color_mode;
|
||||
struct omapfb_device *fbdev;
|
||||
};
|
||||
|
||||
struct omapfb_device {
|
||||
int state;
|
||||
int ext_lcdc; /* Using external
|
||||
LCD controller */
|
||||
struct mutex rqueue_mutex;
|
||||
|
||||
int palette_size;
|
||||
u32 pseudo_palette[17];
|
||||
|
||||
struct lcd_panel *panel; /* LCD panel */
|
||||
const struct lcd_ctrl *ctrl; /* LCD controller */
|
||||
const struct lcd_ctrl *int_ctrl; /* internal LCD ctrl */
|
||||
struct lcd_ctrl_extif *ext_if; /* LCD ctrl external
|
||||
interface */
|
||||
struct device *dev;
|
||||
struct fb_var_screeninfo new_var; /* for mode changes */
|
||||
|
||||
struct omapfb_mem_desc mem_desc;
|
||||
struct fb_info *fb_info[OMAPFB_PLANE_NUM];
|
||||
|
||||
struct platform_device *dssdev; /* dummy dev for clocks */
|
||||
};
|
||||
|
||||
extern struct lcd_ctrl omap1_lcd_ctrl;
|
||||
|
||||
extern void omapfb_register_panel(struct lcd_panel *panel);
|
||||
extern void omapfb_write_first_pixel(struct omapfb_device *fbdev, u16 pixval);
|
||||
extern void omapfb_notify_clients(struct omapfb_device *fbdev,
|
||||
unsigned long event);
|
||||
extern int omapfb_register_client(struct omapfb_notifier_block *nb,
|
||||
omapfb_notifier_callback_t callback,
|
||||
void *callback_data);
|
||||
extern int omapfb_unregister_client(struct omapfb_notifier_block *nb);
|
||||
extern int omapfb_update_window_async(struct fb_info *fbi,
|
||||
struct omapfb_update_window *win,
|
||||
void (*callback)(void *),
|
||||
void *callback_data);
|
||||
|
||||
#endif /* __OMAPFB_H */
|
1971
drivers/video/fbdev/omap/omapfb_main.c
Normal file
1971
drivers/video/fbdev/omap/omapfb_main.c
Normal file
File diff suppressed because it is too large
Load Diff
693
drivers/video/fbdev/omap/sossi.c
Normal file
693
drivers/video/fbdev/omap/sossi.c
Normal file
@@ -0,0 +1,693 @@
|
||||
/*
|
||||
* OMAP1 Special OptimiSed Screen Interface support
|
||||
*
|
||||
* Copyright (C) 2004-2005 Nokia Corporation
|
||||
* Author: Juha Yrjölä <juha.yrjola@nokia.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include <linux/omap-dma.h>
|
||||
|
||||
#include "omapfb.h"
|
||||
#include "lcdc.h"
|
||||
|
||||
#define MODULE_NAME "omapfb-sossi"
|
||||
|
||||
#define OMAP_SOSSI_BASE 0xfffbac00
|
||||
#define SOSSI_ID_REG 0x00
|
||||
#define SOSSI_INIT1_REG 0x04
|
||||
#define SOSSI_INIT2_REG 0x08
|
||||
#define SOSSI_INIT3_REG 0x0c
|
||||
#define SOSSI_FIFO_REG 0x10
|
||||
#define SOSSI_REOTABLE_REG 0x14
|
||||
#define SOSSI_TEARING_REG 0x18
|
||||
#define SOSSI_INIT1B_REG 0x1c
|
||||
#define SOSSI_FIFOB_REG 0x20
|
||||
|
||||
#define DMA_GSCR 0xfffedc04
|
||||
#define DMA_LCD_CCR 0xfffee3c2
|
||||
#define DMA_LCD_CTRL 0xfffee3c4
|
||||
#define DMA_LCD_LCH_CTRL 0xfffee3ea
|
||||
|
||||
#define CONF_SOSSI_RESET_R (1 << 23)
|
||||
|
||||
#define RD_ACCESS 0
|
||||
#define WR_ACCESS 1
|
||||
|
||||
#define SOSSI_MAX_XMIT_BYTES (512 * 1024)
|
||||
|
||||
static struct {
|
||||
void __iomem *base;
|
||||
struct clk *fck;
|
||||
unsigned long fck_hz;
|
||||
spinlock_t lock;
|
||||
int bus_pick_count;
|
||||
int bus_pick_width;
|
||||
int tearsync_mode;
|
||||
int tearsync_line;
|
||||
void (*lcdc_callback)(void *data);
|
||||
void *lcdc_callback_data;
|
||||
int vsync_dma_pending;
|
||||
/* timing for read and write access */
|
||||
int clk_div;
|
||||
u8 clk_tw0[2];
|
||||
u8 clk_tw1[2];
|
||||
/*
|
||||
* if last_access is the same as current we don't have to change
|
||||
* the timings
|
||||
*/
|
||||
int last_access;
|
||||
|
||||
struct omapfb_device *fbdev;
|
||||
} sossi;
|
||||
|
||||
static inline u32 sossi_read_reg(int reg)
|
||||
{
|
||||
return readl(sossi.base + reg);
|
||||
}
|
||||
|
||||
static inline u16 sossi_read_reg16(int reg)
|
||||
{
|
||||
return readw(sossi.base + reg);
|
||||
}
|
||||
|
||||
static inline u8 sossi_read_reg8(int reg)
|
||||
{
|
||||
return readb(sossi.base + reg);
|
||||
}
|
||||
|
||||
static inline void sossi_write_reg(int reg, u32 value)
|
||||
{
|
||||
writel(value, sossi.base + reg);
|
||||
}
|
||||
|
||||
static inline void sossi_write_reg16(int reg, u16 value)
|
||||
{
|
||||
writew(value, sossi.base + reg);
|
||||
}
|
||||
|
||||
static inline void sossi_write_reg8(int reg, u8 value)
|
||||
{
|
||||
writeb(value, sossi.base + reg);
|
||||
}
|
||||
|
||||
static void sossi_set_bits(int reg, u32 bits)
|
||||
{
|
||||
sossi_write_reg(reg, sossi_read_reg(reg) | bits);
|
||||
}
|
||||
|
||||
static void sossi_clear_bits(int reg, u32 bits)
|
||||
{
|
||||
sossi_write_reg(reg, sossi_read_reg(reg) & ~bits);
|
||||
}
|
||||
|
||||
#define HZ_TO_PS(x) (1000000000 / (x / 1000))
|
||||
|
||||
static u32 ps_to_sossi_ticks(u32 ps, int div)
|
||||
{
|
||||
u32 clk_period = HZ_TO_PS(sossi.fck_hz) * div;
|
||||
return (clk_period + ps - 1) / clk_period;
|
||||
}
|
||||
|
||||
static int calc_rd_timings(struct extif_timings *t)
|
||||
{
|
||||
u32 tw0, tw1;
|
||||
int reon, reoff, recyc, actim;
|
||||
int div = t->clk_div;
|
||||
|
||||
/*
|
||||
* Make sure that after conversion it still holds that:
|
||||
* reoff > reon, recyc >= reoff, actim > reon
|
||||
*/
|
||||
reon = ps_to_sossi_ticks(t->re_on_time, div);
|
||||
/* reon will be exactly one sossi tick */
|
||||
if (reon > 1)
|
||||
return -1;
|
||||
|
||||
reoff = ps_to_sossi_ticks(t->re_off_time, div);
|
||||
|
||||
if (reoff <= reon)
|
||||
reoff = reon + 1;
|
||||
|
||||
tw0 = reoff - reon;
|
||||
if (tw0 > 0x10)
|
||||
return -1;
|
||||
|
||||
recyc = ps_to_sossi_ticks(t->re_cycle_time, div);
|
||||
if (recyc <= reoff)
|
||||
recyc = reoff + 1;
|
||||
|
||||
tw1 = recyc - tw0;
|
||||
/* values less then 3 result in the SOSSI block resetting itself */
|
||||
if (tw1 < 3)
|
||||
tw1 = 3;
|
||||
if (tw1 > 0x40)
|
||||
return -1;
|
||||
|
||||
actim = ps_to_sossi_ticks(t->access_time, div);
|
||||
if (actim < reoff)
|
||||
actim++;
|
||||
/*
|
||||
* access time (data hold time) will be exactly one sossi
|
||||
* tick
|
||||
*/
|
||||
if (actim - reoff > 1)
|
||||
return -1;
|
||||
|
||||
t->tim[0] = tw0 - 1;
|
||||
t->tim[1] = tw1 - 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int calc_wr_timings(struct extif_timings *t)
|
||||
{
|
||||
u32 tw0, tw1;
|
||||
int weon, weoff, wecyc;
|
||||
int div = t->clk_div;
|
||||
|
||||
/*
|
||||
* Make sure that after conversion it still holds that:
|
||||
* weoff > weon, wecyc >= weoff
|
||||
*/
|
||||
weon = ps_to_sossi_ticks(t->we_on_time, div);
|
||||
/* weon will be exactly one sossi tick */
|
||||
if (weon > 1)
|
||||
return -1;
|
||||
|
||||
weoff = ps_to_sossi_ticks(t->we_off_time, div);
|
||||
if (weoff <= weon)
|
||||
weoff = weon + 1;
|
||||
tw0 = weoff - weon;
|
||||
if (tw0 > 0x10)
|
||||
return -1;
|
||||
|
||||
wecyc = ps_to_sossi_ticks(t->we_cycle_time, div);
|
||||
if (wecyc <= weoff)
|
||||
wecyc = weoff + 1;
|
||||
|
||||
tw1 = wecyc - tw0;
|
||||
/* values less then 3 result in the SOSSI block resetting itself */
|
||||
if (tw1 < 3)
|
||||
tw1 = 3;
|
||||
if (tw1 > 0x40)
|
||||
return -1;
|
||||
|
||||
t->tim[2] = tw0 - 1;
|
||||
t->tim[3] = tw1 - 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _set_timing(int div, int tw0, int tw1)
|
||||
{
|
||||
u32 l;
|
||||
|
||||
#ifdef VERBOSE
|
||||
dev_dbg(sossi.fbdev->dev, "Using TW0 = %d, TW1 = %d, div = %d\n",
|
||||
tw0 + 1, tw1 + 1, div);
|
||||
#endif
|
||||
|
||||
clk_set_rate(sossi.fck, sossi.fck_hz / div);
|
||||
clk_enable(sossi.fck);
|
||||
l = sossi_read_reg(SOSSI_INIT1_REG);
|
||||
l &= ~((0x0f << 20) | (0x3f << 24));
|
||||
l |= (tw0 << 20) | (tw1 << 24);
|
||||
sossi_write_reg(SOSSI_INIT1_REG, l);
|
||||
clk_disable(sossi.fck);
|
||||
}
|
||||
|
||||
static void _set_bits_per_cycle(int bus_pick_count, int bus_pick_width)
|
||||
{
|
||||
u32 l;
|
||||
|
||||
l = sossi_read_reg(SOSSI_INIT3_REG);
|
||||
l &= ~0x3ff;
|
||||
l |= ((bus_pick_count - 1) << 5) | ((bus_pick_width - 1) & 0x1f);
|
||||
sossi_write_reg(SOSSI_INIT3_REG, l);
|
||||
}
|
||||
|
||||
static void _set_tearsync_mode(int mode, unsigned line)
|
||||
{
|
||||
u32 l;
|
||||
|
||||
l = sossi_read_reg(SOSSI_TEARING_REG);
|
||||
l &= ~(((1 << 11) - 1) << 15);
|
||||
l |= line << 15;
|
||||
l &= ~(0x3 << 26);
|
||||
l |= mode << 26;
|
||||
sossi_write_reg(SOSSI_TEARING_REG, l);
|
||||
if (mode)
|
||||
sossi_set_bits(SOSSI_INIT2_REG, 1 << 6); /* TE logic */
|
||||
else
|
||||
sossi_clear_bits(SOSSI_INIT2_REG, 1 << 6);
|
||||
}
|
||||
|
||||
static inline void set_timing(int access)
|
||||
{
|
||||
if (access != sossi.last_access) {
|
||||
sossi.last_access = access;
|
||||
_set_timing(sossi.clk_div,
|
||||
sossi.clk_tw0[access], sossi.clk_tw1[access]);
|
||||
}
|
||||
}
|
||||
|
||||
static void sossi_start_transfer(void)
|
||||
{
|
||||
/* WE */
|
||||
sossi_clear_bits(SOSSI_INIT2_REG, 1 << 4);
|
||||
/* CS active low */
|
||||
sossi_clear_bits(SOSSI_INIT1_REG, 1 << 30);
|
||||
}
|
||||
|
||||
static void sossi_stop_transfer(void)
|
||||
{
|
||||
/* WE */
|
||||
sossi_set_bits(SOSSI_INIT2_REG, 1 << 4);
|
||||
/* CS active low */
|
||||
sossi_set_bits(SOSSI_INIT1_REG, 1 << 30);
|
||||
}
|
||||
|
||||
static void wait_end_of_write(void)
|
||||
{
|
||||
/* Before reading we must check if some writings are going on */
|
||||
while (!(sossi_read_reg(SOSSI_INIT2_REG) & (1 << 3)));
|
||||
}
|
||||
|
||||
static void send_data(const void *data, unsigned int len)
|
||||
{
|
||||
while (len >= 4) {
|
||||
sossi_write_reg(SOSSI_FIFO_REG, *(const u32 *) data);
|
||||
len -= 4;
|
||||
data += 4;
|
||||
}
|
||||
while (len >= 2) {
|
||||
sossi_write_reg16(SOSSI_FIFO_REG, *(const u16 *) data);
|
||||
len -= 2;
|
||||
data += 2;
|
||||
}
|
||||
while (len) {
|
||||
sossi_write_reg8(SOSSI_FIFO_REG, *(const u8 *) data);
|
||||
len--;
|
||||
data++;
|
||||
}
|
||||
}
|
||||
|
||||
static void set_cycles(unsigned int len)
|
||||
{
|
||||
unsigned long nr_cycles = len / (sossi.bus_pick_width / 8);
|
||||
|
||||
BUG_ON((nr_cycles - 1) & ~0x3ffff);
|
||||
|
||||
sossi_clear_bits(SOSSI_INIT1_REG, 0x3ffff);
|
||||
sossi_set_bits(SOSSI_INIT1_REG, (nr_cycles - 1) & 0x3ffff);
|
||||
}
|
||||
|
||||
static int sossi_convert_timings(struct extif_timings *t)
|
||||
{
|
||||
int r = 0;
|
||||
int div = t->clk_div;
|
||||
|
||||
t->converted = 0;
|
||||
|
||||
if (div <= 0 || div > 8)
|
||||
return -1;
|
||||
|
||||
/* no CS on SOSSI, so ignore cson, csoff, cs_pulsewidth */
|
||||
if ((r = calc_rd_timings(t)) < 0)
|
||||
return r;
|
||||
|
||||
if ((r = calc_wr_timings(t)) < 0)
|
||||
return r;
|
||||
|
||||
t->tim[4] = div;
|
||||
|
||||
t->converted = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sossi_set_timings(const struct extif_timings *t)
|
||||
{
|
||||
BUG_ON(!t->converted);
|
||||
|
||||
sossi.clk_tw0[RD_ACCESS] = t->tim[0];
|
||||
sossi.clk_tw1[RD_ACCESS] = t->tim[1];
|
||||
|
||||
sossi.clk_tw0[WR_ACCESS] = t->tim[2];
|
||||
sossi.clk_tw1[WR_ACCESS] = t->tim[3];
|
||||
|
||||
sossi.clk_div = t->tim[4];
|
||||
}
|
||||
|
||||
static void sossi_get_clk_info(u32 *clk_period, u32 *max_clk_div)
|
||||
{
|
||||
*clk_period = HZ_TO_PS(sossi.fck_hz);
|
||||
*max_clk_div = 8;
|
||||
}
|
||||
|
||||
static void sossi_set_bits_per_cycle(int bpc)
|
||||
{
|
||||
int bus_pick_count, bus_pick_width;
|
||||
|
||||
/*
|
||||
* We set explicitly the the bus_pick_count as well, although
|
||||
* with remapping/reordering disabled it will be calculated by HW
|
||||
* as (32 / bus_pick_width).
|
||||
*/
|
||||
switch (bpc) {
|
||||
case 8:
|
||||
bus_pick_count = 4;
|
||||
bus_pick_width = 8;
|
||||
break;
|
||||
case 16:
|
||||
bus_pick_count = 2;
|
||||
bus_pick_width = 16;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
return;
|
||||
}
|
||||
sossi.bus_pick_width = bus_pick_width;
|
||||
sossi.bus_pick_count = bus_pick_count;
|
||||
}
|
||||
|
||||
static int sossi_setup_tearsync(unsigned pin_cnt,
|
||||
unsigned hs_pulse_time, unsigned vs_pulse_time,
|
||||
int hs_pol_inv, int vs_pol_inv, int div)
|
||||
{
|
||||
int hs, vs;
|
||||
u32 l;
|
||||
|
||||
if (pin_cnt != 1 || div < 1 || div > 8)
|
||||
return -EINVAL;
|
||||
|
||||
hs = ps_to_sossi_ticks(hs_pulse_time, div);
|
||||
vs = ps_to_sossi_ticks(vs_pulse_time, div);
|
||||
if (vs < 8 || vs <= hs || vs >= (1 << 12))
|
||||
return -EDOM;
|
||||
vs /= 8;
|
||||
vs--;
|
||||
if (hs > 8)
|
||||
hs = 8;
|
||||
if (hs)
|
||||
hs--;
|
||||
|
||||
dev_dbg(sossi.fbdev->dev,
|
||||
"setup_tearsync: hs %d vs %d hs_inv %d vs_inv %d\n",
|
||||
hs, vs, hs_pol_inv, vs_pol_inv);
|
||||
|
||||
clk_enable(sossi.fck);
|
||||
l = sossi_read_reg(SOSSI_TEARING_REG);
|
||||
l &= ~((1 << 15) - 1);
|
||||
l |= vs << 3;
|
||||
l |= hs;
|
||||
if (hs_pol_inv)
|
||||
l |= 1 << 29;
|
||||
else
|
||||
l &= ~(1 << 29);
|
||||
if (vs_pol_inv)
|
||||
l |= 1 << 28;
|
||||
else
|
||||
l &= ~(1 << 28);
|
||||
sossi_write_reg(SOSSI_TEARING_REG, l);
|
||||
clk_disable(sossi.fck);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sossi_enable_tearsync(int enable, unsigned line)
|
||||
{
|
||||
int mode;
|
||||
|
||||
dev_dbg(sossi.fbdev->dev, "tearsync %d line %d\n", enable, line);
|
||||
if (line >= 1 << 11)
|
||||
return -EINVAL;
|
||||
if (enable) {
|
||||
if (line)
|
||||
mode = 2; /* HS or VS */
|
||||
else
|
||||
mode = 3; /* VS only */
|
||||
} else
|
||||
mode = 0;
|
||||
sossi.tearsync_line = line;
|
||||
sossi.tearsync_mode = mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sossi_write_command(const void *data, unsigned int len)
|
||||
{
|
||||
clk_enable(sossi.fck);
|
||||
set_timing(WR_ACCESS);
|
||||
_set_bits_per_cycle(sossi.bus_pick_count, sossi.bus_pick_width);
|
||||
/* CMD#/DATA */
|
||||
sossi_clear_bits(SOSSI_INIT1_REG, 1 << 18);
|
||||
set_cycles(len);
|
||||
sossi_start_transfer();
|
||||
send_data(data, len);
|
||||
sossi_stop_transfer();
|
||||
wait_end_of_write();
|
||||
clk_disable(sossi.fck);
|
||||
}
|
||||
|
||||
static void sossi_write_data(const void *data, unsigned int len)
|
||||
{
|
||||
clk_enable(sossi.fck);
|
||||
set_timing(WR_ACCESS);
|
||||
_set_bits_per_cycle(sossi.bus_pick_count, sossi.bus_pick_width);
|
||||
/* CMD#/DATA */
|
||||
sossi_set_bits(SOSSI_INIT1_REG, 1 << 18);
|
||||
set_cycles(len);
|
||||
sossi_start_transfer();
|
||||
send_data(data, len);
|
||||
sossi_stop_transfer();
|
||||
wait_end_of_write();
|
||||
clk_disable(sossi.fck);
|
||||
}
|
||||
|
||||
static void sossi_transfer_area(int width, int height,
|
||||
void (callback)(void *data), void *data)
|
||||
{
|
||||
BUG_ON(callback == NULL);
|
||||
|
||||
sossi.lcdc_callback = callback;
|
||||
sossi.lcdc_callback_data = data;
|
||||
|
||||
clk_enable(sossi.fck);
|
||||
set_timing(WR_ACCESS);
|
||||
_set_bits_per_cycle(sossi.bus_pick_count, sossi.bus_pick_width);
|
||||
_set_tearsync_mode(sossi.tearsync_mode, sossi.tearsync_line);
|
||||
/* CMD#/DATA */
|
||||
sossi_set_bits(SOSSI_INIT1_REG, 1 << 18);
|
||||
set_cycles(width * height * sossi.bus_pick_width / 8);
|
||||
|
||||
sossi_start_transfer();
|
||||
if (sossi.tearsync_mode) {
|
||||
/*
|
||||
* Wait for the sync signal and start the transfer only
|
||||
* then. We can't seem to be able to use HW sync DMA for
|
||||
* this since LCD DMA shows huge latencies, as if it
|
||||
* would ignore some of the DMA requests from SoSSI.
|
||||
*/
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&sossi.lock, flags);
|
||||
sossi.vsync_dma_pending++;
|
||||
spin_unlock_irqrestore(&sossi.lock, flags);
|
||||
} else
|
||||
/* Just start the transfer right away. */
|
||||
omap_enable_lcd_dma();
|
||||
}
|
||||
|
||||
static void sossi_dma_callback(void *data)
|
||||
{
|
||||
omap_stop_lcd_dma();
|
||||
sossi_stop_transfer();
|
||||
clk_disable(sossi.fck);
|
||||
sossi.lcdc_callback(sossi.lcdc_callback_data);
|
||||
}
|
||||
|
||||
static void sossi_read_data(void *data, unsigned int len)
|
||||
{
|
||||
clk_enable(sossi.fck);
|
||||
set_timing(RD_ACCESS);
|
||||
_set_bits_per_cycle(sossi.bus_pick_count, sossi.bus_pick_width);
|
||||
/* CMD#/DATA */
|
||||
sossi_set_bits(SOSSI_INIT1_REG, 1 << 18);
|
||||
set_cycles(len);
|
||||
sossi_start_transfer();
|
||||
while (len >= 4) {
|
||||
*(u32 *) data = sossi_read_reg(SOSSI_FIFO_REG);
|
||||
len -= 4;
|
||||
data += 4;
|
||||
}
|
||||
while (len >= 2) {
|
||||
*(u16 *) data = sossi_read_reg16(SOSSI_FIFO_REG);
|
||||
len -= 2;
|
||||
data += 2;
|
||||
}
|
||||
while (len) {
|
||||
*(u8 *) data = sossi_read_reg8(SOSSI_FIFO_REG);
|
||||
len--;
|
||||
data++;
|
||||
}
|
||||
sossi_stop_transfer();
|
||||
clk_disable(sossi.fck);
|
||||
}
|
||||
|
||||
static irqreturn_t sossi_match_irq(int irq, void *data)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&sossi.lock, flags);
|
||||
if (sossi.vsync_dma_pending) {
|
||||
sossi.vsync_dma_pending--;
|
||||
omap_enable_lcd_dma();
|
||||
}
|
||||
spin_unlock_irqrestore(&sossi.lock, flags);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int sossi_init(struct omapfb_device *fbdev)
|
||||
{
|
||||
u32 l, k;
|
||||
struct clk *fck;
|
||||
struct clk *dpll1out_ck;
|
||||
int r;
|
||||
|
||||
sossi.base = ioremap(OMAP_SOSSI_BASE, SZ_1K);
|
||||
if (!sossi.base) {
|
||||
dev_err(fbdev->dev, "can't ioremap SoSSI\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
sossi.fbdev = fbdev;
|
||||
spin_lock_init(&sossi.lock);
|
||||
|
||||
dpll1out_ck = clk_get(fbdev->dev, "ck_dpll1out");
|
||||
if (IS_ERR(dpll1out_ck)) {
|
||||
dev_err(fbdev->dev, "can't get DPLL1OUT clock\n");
|
||||
return PTR_ERR(dpll1out_ck);
|
||||
}
|
||||
/*
|
||||
* We need the parent clock rate, which we might divide further
|
||||
* depending on the timing requirements of the controller. See
|
||||
* _set_timings.
|
||||
*/
|
||||
sossi.fck_hz = clk_get_rate(dpll1out_ck);
|
||||
clk_put(dpll1out_ck);
|
||||
|
||||
fck = clk_get(fbdev->dev, "ck_sossi");
|
||||
if (IS_ERR(fck)) {
|
||||
dev_err(fbdev->dev, "can't get SoSSI functional clock\n");
|
||||
return PTR_ERR(fck);
|
||||
}
|
||||
sossi.fck = fck;
|
||||
|
||||
/* Reset and enable the SoSSI module */
|
||||
l = omap_readl(MOD_CONF_CTRL_1);
|
||||
l |= CONF_SOSSI_RESET_R;
|
||||
omap_writel(l, MOD_CONF_CTRL_1);
|
||||
l &= ~CONF_SOSSI_RESET_R;
|
||||
omap_writel(l, MOD_CONF_CTRL_1);
|
||||
|
||||
clk_enable(sossi.fck);
|
||||
l = omap_readl(ARM_IDLECT2);
|
||||
l &= ~(1 << 8); /* DMACK_REQ */
|
||||
omap_writel(l, ARM_IDLECT2);
|
||||
|
||||
l = sossi_read_reg(SOSSI_INIT2_REG);
|
||||
/* Enable and reset the SoSSI block */
|
||||
l |= (1 << 0) | (1 << 1);
|
||||
sossi_write_reg(SOSSI_INIT2_REG, l);
|
||||
/* Take SoSSI out of reset */
|
||||
l &= ~(1 << 1);
|
||||
sossi_write_reg(SOSSI_INIT2_REG, l);
|
||||
|
||||
sossi_write_reg(SOSSI_ID_REG, 0);
|
||||
l = sossi_read_reg(SOSSI_ID_REG);
|
||||
k = sossi_read_reg(SOSSI_ID_REG);
|
||||
|
||||
if (l != 0x55555555 || k != 0xaaaaaaaa) {
|
||||
dev_err(fbdev->dev,
|
||||
"invalid SoSSI sync pattern: %08x, %08x\n", l, k);
|
||||
r = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if ((r = omap_lcdc_set_dma_callback(sossi_dma_callback, NULL)) < 0) {
|
||||
dev_err(fbdev->dev, "can't get LCDC IRQ\n");
|
||||
r = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
l = sossi_read_reg(SOSSI_ID_REG); /* Component code */
|
||||
l = sossi_read_reg(SOSSI_ID_REG);
|
||||
dev_info(fbdev->dev, "SoSSI version %d.%d initialized\n",
|
||||
l >> 16, l & 0xffff);
|
||||
|
||||
l = sossi_read_reg(SOSSI_INIT1_REG);
|
||||
l |= (1 << 19); /* DMA_MODE */
|
||||
l &= ~(1 << 31); /* REORDERING */
|
||||
sossi_write_reg(SOSSI_INIT1_REG, l);
|
||||
|
||||
if ((r = request_irq(INT_1610_SoSSI_MATCH, sossi_match_irq,
|
||||
IRQ_TYPE_EDGE_FALLING,
|
||||
"sossi_match", sossi.fbdev->dev)) < 0) {
|
||||
dev_err(sossi.fbdev->dev, "can't get SoSSI match IRQ\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
clk_disable(sossi.fck);
|
||||
return 0;
|
||||
|
||||
err:
|
||||
clk_disable(sossi.fck);
|
||||
clk_put(sossi.fck);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void sossi_cleanup(void)
|
||||
{
|
||||
omap_lcdc_free_dma_callback();
|
||||
clk_put(sossi.fck);
|
||||
iounmap(sossi.base);
|
||||
}
|
||||
|
||||
struct lcd_ctrl_extif omap1_ext_if = {
|
||||
.init = sossi_init,
|
||||
.cleanup = sossi_cleanup,
|
||||
.get_clk_info = sossi_get_clk_info,
|
||||
.convert_timings = sossi_convert_timings,
|
||||
.set_timings = sossi_set_timings,
|
||||
.set_bits_per_cycle = sossi_set_bits_per_cycle,
|
||||
.setup_tearsync = sossi_setup_tearsync,
|
||||
.enable_tearsync = sossi_enable_tearsync,
|
||||
.write_command = sossi_write_command,
|
||||
.read_data = sossi_read_data,
|
||||
.write_data = sossi_write_data,
|
||||
.transfer_area = sossi_transfer_area,
|
||||
|
||||
.max_transmit_size = SOSSI_MAX_XMIT_BYTES,
|
||||
};
|
||||
|
Reference in New Issue
Block a user