Commit 3a5b27bf authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://gitorious.org/linux-omap-dss2/linux

* 'for-linus' of git://gitorious.org/linux-omap-dss2/linux: (49 commits)
  OMAP: DSS2: Taal: Fix TE when resuming
  OMAP: DSS2: Taal: Fix ESD check
  OMAP: DSS2: OMAPFB: Constify some function parameters
  OMAP: DSS2: OMAPFB: install omapfb.h
  OMAP: DSS2: DSI: add error prints
  OMAP: DSS2: TPO-TD03MTEA1: fix function names
  OMAP: DSS2: DSI: add dsi_vc_dcs_read_2() helper
  OMAP: DSS2: OMAPFB: Remove FB_OMAP2_FORCE_AUTO_UPDATE
  OMAP: DSS2: DSI: remove external TE support
  OMAP: DSS2: move timing functions
  OMAP: DSS2: move set/get_wss()
  OMAP: DSS2: move enable/disable/suspend/resume
  OMAP: DSS2: move update() and sync()
  OMAP: DSS2: move set/get_update_mode()
  OMAP: DSS2: move enable/get_te()
  OMAP: DSS2: move get_recommended_bpp()
  OMAP: DSS2: move get_resolution()
  OMAP: DSS2: move enable/disable_channel to overlay manager
  OMAP: DSS2: move wait_vsync()
  OMAP: DSS2: move get/set_rotate()
  ...
parents 5057bfaf 21df20fc
......@@ -541,10 +541,6 @@ static struct regulator_init_data sdp3430_vdac = {
/* VPLL2 for digital video outputs */
static struct regulator_consumer_supply sdp3430_vpll2_supplies[] = {
{
.supply = "vdvi",
.dev = &sdp3430_lcd_device.dev,
},
{
.supply = "vdds_dsi",
.dev = &sdp3430_dss_device.dev,
......
......@@ -233,8 +233,12 @@ int omap_rfbi_setup_te(enum omap_rfbi_te_mode mode,
void dsi_bus_lock(void);
void dsi_bus_unlock(void);
int dsi_vc_dcs_write(int channel, u8 *data, int len);
int dsi_vc_dcs_write_0(int channel, u8 dcs_cmd);
int dsi_vc_dcs_write_1(int channel, u8 dcs_cmd, u8 param);
int dsi_vc_dcs_write_nosync(int channel, u8 *data, int len);
int dsi_vc_dcs_read(int channel, u8 dcs_cmd, u8 *buf, int buflen);
int dsi_vc_dcs_read_1(int channel, u8 dcs_cmd, u8 *data);
int dsi_vc_dcs_read_2(int channel, u8 dcs_cmd, u16 *data);
int dsi_vc_set_max_rx_packet_size(int channel, u16 len);
int dsi_vc_send_null(int channel);
int dsi_vc_send_bta_sync(int channel);
......@@ -367,6 +371,10 @@ struct omap_overlay_manager {
int (*apply)(struct omap_overlay_manager *mgr);
int (*wait_for_go)(struct omap_overlay_manager *mgr);
int (*wait_for_vsync)(struct omap_overlay_manager *mgr);
int (*enable)(struct omap_overlay_manager *mgr);
int (*disable)(struct omap_overlay_manager *mgr);
};
struct omap_dss_device {
......@@ -426,16 +434,11 @@ struct omap_dss_device {
int acb; /* ac-bias pin frequency */
enum omap_panel_config config;
u8 recommended_bpp;
struct omap_dss_device *ctrl;
} panel;
struct {
u8 pixel_size;
struct rfbi_timings rfbi_timings;
struct omap_dss_device *panel;
} ctrl;
int reset_gpio;
......@@ -460,49 +463,6 @@ struct omap_dss_device {
enum omap_dss_display_state state;
int (*enable)(struct omap_dss_device *dssdev);
void (*disable)(struct omap_dss_device *dssdev);
int (*suspend)(struct omap_dss_device *dssdev);
int (*resume)(struct omap_dss_device *dssdev);
void (*get_resolution)(struct omap_dss_device *dssdev,
u16 *xres, u16 *yres);
int (*get_recommended_bpp)(struct omap_dss_device *dssdev);
int (*check_timings)(struct omap_dss_device *dssdev,
struct omap_video_timings *timings);
void (*set_timings)(struct omap_dss_device *dssdev,
struct omap_video_timings *timings);
void (*get_timings)(struct omap_dss_device *dssdev,
struct omap_video_timings *timings);
int (*update)(struct omap_dss_device *dssdev,
u16 x, u16 y, u16 w, u16 h);
int (*sync)(struct omap_dss_device *dssdev);
int (*wait_vsync)(struct omap_dss_device *dssdev);
int (*set_update_mode)(struct omap_dss_device *dssdev,
enum omap_dss_update_mode);
enum omap_dss_update_mode (*get_update_mode)
(struct omap_dss_device *dssdev);
int (*enable_te)(struct omap_dss_device *dssdev, bool enable);
int (*get_te)(struct omap_dss_device *dssdev);
u8 (*get_rotate)(struct omap_dss_device *dssdev);
int (*set_rotate)(struct omap_dss_device *dssdev, u8 rotate);
bool (*get_mirror)(struct omap_dss_device *dssdev);
int (*set_mirror)(struct omap_dss_device *dssdev, bool enable);
int (*run_test)(struct omap_dss_device *dssdev, int test);
int (*memory_read)(struct omap_dss_device *dssdev,
void *buf, size_t size,
u16 x, u16 y, u16 w, u16 h);
int (*set_wss)(struct omap_dss_device *dssdev, u32 wss);
u32 (*get_wss)(struct omap_dss_device *dssdev);
/* platform specific */
int (*platform_enable)(struct omap_dss_device *dssdev);
void (*platform_disable)(struct omap_dss_device *dssdev);
......@@ -522,11 +482,17 @@ struct omap_dss_driver {
int (*resume)(struct omap_dss_device *display);
int (*run_test)(struct omap_dss_device *display, int test);
void (*setup_update)(struct omap_dss_device *dssdev,
u16 x, u16 y, u16 w, u16 h);
int (*set_update_mode)(struct omap_dss_device *dssdev,
enum omap_dss_update_mode);
enum omap_dss_update_mode (*get_update_mode)(
struct omap_dss_device *dssdev);
int (*update)(struct omap_dss_device *dssdev,
u16 x, u16 y, u16 w, u16 h);
int (*sync)(struct omap_dss_device *dssdev);
int (*enable_te)(struct omap_dss_device *dssdev, bool enable);
int (*wait_for_te)(struct omap_dss_device *dssdev);
int (*get_te)(struct omap_dss_device *dssdev);
u8 (*get_rotate)(struct omap_dss_device *dssdev);
int (*set_rotate)(struct omap_dss_device *dssdev, u8 rotate);
......@@ -537,6 +503,20 @@ struct omap_dss_driver {
int (*memory_read)(struct omap_dss_device *dssdev,
void *buf, size_t size,
u16 x, u16 y, u16 w, u16 h);
void (*get_resolution)(struct omap_dss_device *dssdev,
u16 *xres, u16 *yres);
int (*get_recommended_bpp)(struct omap_dss_device *dssdev);
int (*check_timings)(struct omap_dss_device *dssdev,
struct omap_video_timings *timings);
void (*set_timings)(struct omap_dss_device *dssdev,
struct omap_video_timings *timings);
void (*get_timings)(struct omap_dss_device *dssdev,
struct omap_video_timings *timings);
int (*set_wss)(struct omap_dss_device *dssdev, u32 wss);
u32 (*get_wss)(struct omap_dss_device *dssdev);
};
int omap_dss_register_driver(struct omap_dss_driver *);
......@@ -561,6 +541,10 @@ struct omap_overlay_manager *omap_dss_get_overlay_manager(int num);
int omap_dss_get_num_overlays(void);
struct omap_overlay *omap_dss_get_overlay(int num);
void omapdss_default_get_resolution(struct omap_dss_device *dssdev,
u16 *xres, u16 *yres);
int omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev);
typedef void (*omap_dispc_isr_t) (void *arg, u32 mask);
int omap_dispc_register_isr(omap_dispc_isr_t isr, void *arg, u32 mask);
int omap_dispc_unregister_isr(omap_dispc_isr_t isr, void *arg, u32 mask);
......@@ -572,4 +556,35 @@ int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask,
#define to_dss_driver(x) container_of((x), struct omap_dss_driver, driver)
#define to_dss_device(x) container_of((x), struct omap_dss_device, dev)
void omapdss_dsi_vc_enable_hs(int channel, bool enable);
int omapdss_dsi_enable_te(struct omap_dss_device *dssdev, bool enable);
int omap_dsi_prepare_update(struct omap_dss_device *dssdev,
u16 *x, u16 *y, u16 *w, u16 *h);
int omap_dsi_update(struct omap_dss_device *dssdev,
int channel,
u16 x, u16 y, u16 w, u16 h,
void (*callback)(int, void *), void *data);
int omapdss_dsi_display_enable(struct omap_dss_device *dssdev);
void omapdss_dsi_display_disable(struct omap_dss_device *dssdev);
int omapdss_dpi_display_enable(struct omap_dss_device *dssdev);
void omapdss_dpi_display_disable(struct omap_dss_device *dssdev);
void dpi_set_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings);
int dpi_check_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings);
int omapdss_sdi_display_enable(struct omap_dss_device *dssdev);
void omapdss_sdi_display_disable(struct omap_dss_device *dssdev);
int omapdss_rfbi_display_enable(struct omap_dss_device *dssdev);
void omapdss_rfbi_display_disable(struct omap_dss_device *dssdev);
int omap_rfbi_prepare_update(struct omap_dss_device *dssdev,
u16 *x, u16 *y, u16 *w, u16 *h);
int omap_rfbi_update(struct omap_dss_device *dssdev,
u16 x, u16 y, u16 w, u16 h,
void (*callback)(void *), void *data);
#endif
......@@ -24,6 +24,7 @@
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/lcd.h>
#include <plat/board-ams-delta.h>
#include <mach/hardware.h>
......@@ -32,6 +33,71 @@
#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 int ams_delta_panel_init(struct lcd_panel *panel,
struct omapfb_device *fbdev)
{
......@@ -48,10 +114,6 @@ static int ams_delta_panel_enable(struct lcd_panel *panel)
AMS_DELTA_LATCH2_LCD_NDISP);
ams_delta_latch2_write(AMS_DELTA_LATCH2_LCD_VBLEN,
AMS_DELTA_LATCH2_LCD_VBLEN);
omap_writeb(1, OMAP_PWL_CLK_ENABLE);
omap_writeb(AMS_DELTA_DEFAULT_CONTRAST, OMAP_PWL_ENABLE);
return 0;
}
......@@ -91,8 +153,31 @@ static struct lcd_panel ams_delta_panel = {
.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;
}
......
......@@ -486,10 +486,11 @@ static int set_color_mode(struct omapfb_plane_struct *plane,
return 0;
case 12:
var->bits_per_pixel = 16;
plane->color_mode = OMAPFB_COLOR_RGB444;
return 0;
case 16:
plane->color_mode = OMAPFB_COLOR_RGB565;
if (plane->fbdev->panel->bpp == 12)
plane->color_mode = OMAPFB_COLOR_RGB444;
else
plane->color_mode = OMAPFB_COLOR_RGB565;
return 0;
default:
return -EINVAL;
......
......@@ -13,10 +13,28 @@ config PANEL_SHARP_LS037V7DW01
help
LCD Panel used in TI's SDP3430 and EVM boards
config PANEL_SHARP_LQ043T1DG01
tristate "Sharp LQ043T1DG01 LCD Panel"
depends on OMAP2_DSS
help
LCD Panel used in TI's OMAP3517 EVM boards
config PANEL_TAAL
tristate "Taal DSI Panel"
depends on OMAP2_DSS_DSI
help
Taal DSI command mode panel from TPO.
config PANEL_TOPPOLY_TDO35S
tristate "Toppoly TDO35S LCD Panel support"
depends on OMAP2_DSS
help
LCD Panel used in CM-T35
config PANEL_TPO_TD043MTEA1
tristate "TPO TD043MTEA1 LCD Panel"
depends on OMAP2_DSS && I2C
help
LCD Panel used in OMAP3 Pandora
endmenu
obj-$(CONFIG_PANEL_GENERIC) += panel-generic.o
obj-$(CONFIG_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o
obj-$(CONFIG_PANEL_SHARP_LQ043T1DG01) += panel-sharp-lq043t1dg01.o
obj-$(CONFIG_PANEL_TAAL) += panel-taal.o
obj-$(CONFIG_PANEL_TOPPOLY_TDO35S) += panel-toppoly-tdo35s.o
obj-$(CONFIG_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o
......@@ -35,6 +35,35 @@ static struct omap_video_timings generic_panel_timings = {
.vbp = 7,
};
static int generic_panel_power_on(struct omap_dss_device *dssdev)
{
int r;
r = omapdss_dpi_display_enable(dssdev);
if (r)
goto err0;
if (dssdev->platform_enable) {
r = dssdev->platform_enable(dssdev);
if (r)
goto err1;
}
return 0;
err1:
omapdss_dpi_display_disable(dssdev);
err0:
return r;
}
static void generic_panel_power_off(struct omap_dss_device *dssdev)
{
if (dssdev->platform_disable)
dssdev->platform_disable(dssdev);
omapdss_dpi_display_disable(dssdev);
}
static int generic_panel_probe(struct omap_dss_device *dssdev)
{
dssdev->panel.config = OMAP_DSS_LCD_TFT;
......@@ -51,27 +80,40 @@ static int generic_panel_enable(struct omap_dss_device *dssdev)
{
int r = 0;
if (dssdev->platform_enable)
r = dssdev->platform_enable(dssdev);
r = generic_panel_power_on(dssdev);
if (r)
return r;
return r;
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return 0;
}
static void generic_panel_disable(struct omap_dss_device *dssdev)
{
if (dssdev->platform_disable)
dssdev->platform_disable(dssdev);
generic_panel_power_off(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
}
static int generic_panel_suspend(struct omap_dss_device *dssdev)
{
generic_panel_disable(dssdev);
generic_panel_power_off(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
return 0;
}
static int generic_panel_resume(struct omap_dss_device *dssdev)
{
return generic_panel_enable(dssdev);
int r = 0;
r = generic_panel_power_on(dssdev);
if (r)
return r;
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return 0;
}
static struct omap_dss_driver generic_driver = {
......
/*
* LCD panel driver for Sharp LQ043T1DG01
*
* Copyright (C) 2009 Texas Instruments Inc
* Author: Vaibhav Hiremath <hvaibhav@ti.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <plat/display.h>
static struct omap_video_timings sharp_lq_timings = {
.x_res = 480,
.y_res = 272,
.pixel_clock = 9000,
.hsw = 42,
.hfp = 3,
.hbp = 2,
.vsw = 11,
.vfp = 3,
.vbp = 2,
};
static int sharp_lq_panel_power_on(struct omap_dss_device *dssdev)
{
int r;
r = omapdss_dpi_display_enable(dssdev);
if (r)
goto err0;
/* wait couple of vsyncs until enabling the LCD */
msleep(50);
if (dssdev->platform_enable) {
r = dssdev->platform_enable(dssdev);
if (r)
goto err1;
}
return 0;
err1:
omapdss_dpi_display_disable(dssdev);
err0:
return r;
}
static void sharp_lq_panel_power_off(struct omap_dss_device *dssdev)
{
if (dssdev->platform_disable)
dssdev->platform_disable(dssdev);
/* wait at least 5 vsyncs after disabling the LCD */
msleep(100);
omapdss_dpi_display_disable(dssdev);
}
static int sharp_lq_panel_probe(struct omap_dss_device *dssdev)
{
dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IEO;
dssdev->panel.acb = 0x0;
dssdev->panel.timings = sharp_lq_timings;
return 0;
}
static void sharp_lq_panel_remove(struct omap_dss_device *dssdev)
{
}
static int sharp_lq_panel_enable(struct omap_dss_device *dssdev)
{
int r = 0;
r = sharp_lq_panel_power_on(dssdev);
if (r)
return r;
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return 0;
}
static void sharp_lq_panel_disable(struct omap_dss_device *dssdev)
{
sharp_lq_panel_power_off(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
}
static int sharp_lq_panel_suspend(struct omap_dss_device *dssdev)
{
sharp_lq_panel_power_off(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
return 0;
}
static int sharp_lq_panel_resume(struct omap_dss_device *dssdev)
{
int r = 0;
r = sharp_lq_panel_power_on(dssdev);
if (r)
return r;
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return 0;
}
static struct omap_dss_driver sharp_lq_driver = {
.probe = sharp_lq_panel_probe,
.remove = sharp_lq_panel_remove,
.enable = sharp_lq_panel_enable,
.disable = sharp_lq_panel_disable,
.suspend = sharp_lq_panel_suspend,
.resume = sharp_lq_panel_resume,
.driver = {
.name = "sharp_lq_panel",
.owner = THIS_MODULE,
},
};
static int __init sharp_lq_panel_drv_init(void)
{
return omap_dss_register_driver(&sharp_lq_driver);
}
static void __exit sharp_lq_panel_drv_exit(void)
{
omap_dss_unregister_driver(&sharp_lq_driver);
}
module_init(sharp_lq_panel_drv_init);
module_exit(sharp_lq_panel_drv_exit);
MODULE_LICENSE("GPL");
......@@ -20,19 +20,10 @@
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/regulator/consumer.h>
#include <linux/err.h>
#include <plat/display.h>
struct sharp_data {
/* XXX This regulator should actually be in SDP board file, not here,
* as it doesn't actually power the LCD, but something else that
* affects the output to LCD (I think. Somebody clarify). It doesn't do
* harm here, as SDP is the only board using this currently */
struct regulator *vdvi_reg;
};
static struct omap_video_timings sharp_ls_timings = {
.x_res = 480,
.y_res = 640,
......@@ -50,77 +41,81 @@ static struct omap_video_timings sharp_ls_timings = {
static int sharp_ls_panel_probe(struct omap_dss_device *dssdev)
{
struct sharp_data *sd;
dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
OMAP_DSS_LCD_IHS;
dssdev->panel.acb = 0x28;
dssdev->panel.timings = sharp_ls_timings;
sd = kzalloc(sizeof(*sd), GFP_KERNEL);
if (!sd)
return -ENOMEM;
dev_set_drvdata(&dssdev->dev, sd);
sd->vdvi_reg = regulator_get(&dssdev->dev, "vdvi");
if (IS_ERR(sd->vdvi_reg)) {
kfree(sd);
pr_err("failed to get VDVI regulator\n");
return PTR_ERR(sd->vdvi_reg);
}
return 0;
}
static void sharp_ls_panel_remove(struct omap_dss_device *dssdev)
{
struct sharp_data *sd = dev_get_drvdata(&dssdev->dev);
regulator_put(sd->vdvi_reg);
kfree(sd);
}
static int sharp_ls_panel_enable(struct omap_dss_device *dssdev)
static int sharp_ls_power_on(struct omap_dss_device *dssdev)
{
struct sharp_data *sd = dev_get_drvdata(&dssdev->dev);
int r = 0;
r = omapdss_dpi_display_enable(dssdev);
if (r)
goto err0;
/* wait couple of vsyncs until enabling the LCD */
msleep(50);
regulator_enable(sd->vdvi_reg);
if (dssdev->platform_enable)
if (dssdev->platform_enable) {
r = dssdev->platform_enable(dssdev);
if (r)
goto err1;
}
return 0;
err1:
omapdss_dpi_display_disable(dssdev);
err0:
return r;
}
static void sharp_ls_panel_disable(struct omap_dss_device *dssdev)
static void sharp_ls_power_off(struct omap_dss_device *dssdev)
{
struct sharp_data *sd = dev_get_drvdata(&dssdev->dev);
if (dssdev->platform_disable)
dssdev->platform_disable(dssdev);
regulator_disable(sd->vdvi_reg);
/* wait at least 5 vsyncs after disabling the LCD */
msleep(100);
omapdss_dpi_display_disable(dssdev);
}
static int sharp_ls_panel_enable(struct omap_dss_device *dssdev)
{
int r;
r = sharp_ls_power_on(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return r;
}
static void sharp_ls_panel_disable(struct omap_dss_device *dssdev)
{
sharp_ls_power_off(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
}
static int sharp_ls_panel_suspend(struct omap_dss_device *dssdev)
{
sharp_ls_panel_disable(dssdev);
sharp_ls_power_off(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
return 0;
}
static int sharp_ls_panel_resume(struct omap_dss_device *dssdev)
{
return sharp_ls_panel_enable(dssdev);
int r;
r = sharp_ls_power_on(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return r;
}
static struct omap_dss_driver sharp_ls_driver = {
......
......@@ -63,6 +63,8 @@
/* #define TAAL_USE_ESD_CHECK */
#define TAAL_ESD_CHECK_PERIOD msecs_to_jiffies(5000)
static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable);
struct taal_data {
struct backlight_device *bldev;
......@@ -510,15 +512,12 @@ static int taal_probe(struct omap_dss_device *dssdev)
if (td->esd_wq == NULL) {
dev_err(&dssdev->dev, "can't create ESD workqueue\n");
r = -ENOMEM;
goto err2;
goto err1;
}
INIT_DELAYED_WORK_DEFERRABLE(&td->esd_work, taal_esd_work);
dev_set_drvdata(&dssdev->dev, td);
dssdev->get_timings = taal_get_timings;
dssdev->get_resolution = taal_get_resolution;
/* if no platform set_backlight() defined, presume DSI backlight
* control */
if (!dssdev->set_backlight)
......@@ -528,7 +527,7 @@ static int taal_probe(struct omap_dss_device *dssdev)
&taal_bl_ops);
if (IS_ERR(bldev)) {
r = PTR_ERR(bldev);
goto err1;
goto err2;
}
td->bldev = bldev;
......@@ -621,14 +620,12 @@ static void taal_remove(struct omap_dss_device *dssdev)
kfree(td);
}
static int taal_enable(struct omap_dss_device *dssdev)
static int taal_power_on(struct omap_dss_device *dssdev)
{
struct taal_data *td = dev_get_drvdata(&dssdev->dev);
u8 id1, id2, id3;
int r;
dev_dbg(&dssdev->dev, "enable\n");
if (dssdev->platform_enable) {
r = dssdev->platform_enable(dssdev);
if (r)
......@@ -638,6 +635,16 @@ static int taal_enable(struct omap_dss_device *dssdev)
/* it seems we have to wait a bit until taal is ready */
msleep(5);
dsi_bus_lock();
r = omapdss_dsi_display_enable(dssdev);
if (r) {
dev_err(&dssdev->dev, "failed to enable DSI\n");
goto err0;
}
omapdss_dsi_vc_enable_hs(TCH, false);
r = taal_sleep_out(td);
if (r)
goto err;
......@@ -661,6 +668,10 @@ static int taal_enable(struct omap_dss_device *dssdev)
taal_dcs_write_0(DCS_DISPLAY_ON);
r = _taal_enable_te(dssdev, td->te_enabled);
if (r)
goto err;
#ifdef TAAL_USE_ESD_CHECK
queue_delayed_work(td->esd_wq, &td->esd_work, TAAL_ESD_CHECK_PERIOD);
#endif
......@@ -676,19 +687,27 @@ static int taal_enable(struct omap_dss_device *dssdev)
td->intro_printed = true;
}
omapdss_dsi_vc_enable_hs(TCH, true);
dsi_bus_unlock();
return 0;
err:
dsi_bus_unlock();
omapdss_dsi_display_disable(dssdev);
err0:
if (dssdev->platform_disable)
dssdev->platform_disable(dssdev);
return r;
}
static void taal_disable(struct omap_dss_device *dssdev)
static void taal_power_off(struct omap_dss_device *dssdev)
{
struct taal_data *td = dev_get_drvdata(&dssdev->dev);
dev_dbg(&dssdev->dev, "disable\n");
dsi_bus_lock();
cancel_delayed_work(&td->esd_work);
......@@ -698,41 +717,124 @@ static void taal_disable(struct omap_dss_device *dssdev)
/* wait a bit so that the message goes through */
msleep(10);
omapdss_dsi_display_disable(dssdev);
if (dssdev->platform_disable)
dssdev->platform_disable(dssdev);
td->enabled = 0;
dsi_bus_unlock();
}
static int taal_enable(struct omap_dss_device *dssdev)
{
int r;
dev_dbg(&dssdev->dev, "enable\n");
if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED)
return -EINVAL;
r = taal_power_on(dssdev);
if (r)
return r;
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return r;
}
static void taal_disable(struct omap_dss_device *dssdev)
{
dev_dbg(&dssdev->dev, "disable\n");
if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
taal_power_off(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
}
static int taal_suspend(struct omap_dss_device *dssdev)
{
struct taal_data *td = dev_get_drvdata(&dssdev->dev);
struct backlight_device *bldev = td->bldev;
dev_dbg(&dssdev->dev, "suspend\n");
bldev->props.power = FB_BLANK_POWERDOWN;
taal_bl_update_status(bldev);
if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
return -EINVAL;
taal_power_off(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
return 0;
}
static int taal_resume(struct omap_dss_device *dssdev)
{
int r;
dev_dbg(&dssdev->dev, "resume\n");
if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED)
return -EINVAL;
r = taal_power_on(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return r;
}
static void taal_framedone_cb(int err, void *data)
{
struct omap_dss_device *dssdev = data;
dev_dbg(&dssdev->dev, "framedone, err %d\n", err);
dsi_bus_unlock();
}
static int taal_update(struct omap_dss_device *dssdev,
u16 x, u16 y, u16 w, u16 h)
{
struct taal_data *td = dev_get_drvdata(&dssdev->dev);
struct backlight_device *bldev = td->bldev;
int r;
bldev->props.power = FB_BLANK_UNBLANK;
taal_bl_update_status(bldev);
dev_dbg(&dssdev->dev, "update %d, %d, %d x %d\n", x, y, w, h);
dsi_bus_lock();
if (!td->enabled) {
r = 0;
goto err;
}
r = omap_dsi_prepare_update(dssdev, &x, &y, &w, &h);
if (r)
goto err;
r = taal_set_update_window(x, y, w, h);
if (r)
goto err;
r = omap_dsi_update(dssdev, TCH, x, y, w, h,
taal_framedone_cb, dssdev);
if (r)
goto err;
/* note: no bus_unlock here. unlock is in framedone_cb */
return 0;
err:
dsi_bus_unlock();
return r;
}
static void taal_setup_update(struct omap_dss_device *dssdev,
u16 x, u16 y, u16 w, u16 h)
static int taal_sync(struct omap_dss_device *dssdev)
{
taal_set_update_window(x, y, w, h);
dev_dbg(&dssdev->dev, "sync\n");
dsi_bus_lock();
dsi_bus_unlock();
dev_dbg(&dssdev->dev, "sync done\n");
return 0;
}
static int taal_enable_te(struct omap_dss_device *dssdev, bool enable)
static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable)
{
struct taal_data *td = dev_get_drvdata(&dssdev->dev);
int r;
......@@ -744,25 +846,32 @@ static int taal_enable_te(struct omap_dss_device *dssdev, bool enable)
else
r = taal_dcs_write_0(DCS_TEAR_OFF);
omapdss_dsi_enable_te(dssdev, enable);
/* XXX for some reason, DSI TE breaks if we don't wait here.
* Panel bug? Needs more studying */
msleep(100);
return r;
}
static int taal_wait_te(struct omap_dss_device *dssdev)
static int taal_enable_te(struct omap_dss_device *dssdev, bool enable)
{
struct taal_data *td = dev_get_drvdata(&dssdev->dev);
long wait = msecs_to_jiffies(500);
int r;
if (!td->use_ext_te || !td->te_enabled)
return 0;
dsi_bus_lock();
INIT_COMPLETION(td->te_completion);
wait = wait_for_completion_timeout(&td->te_completion, wait);
if (wait == 0) {
dev_err(&dssdev->dev, "timeout waiting TE\n");
return -ETIME;
}
r = _taal_enable_te(dssdev, enable);
return 0;
dsi_bus_unlock();
return r;
}
static int taal_get_te(struct omap_dss_device *dssdev)
{
struct taal_data *td = dev_get_drvdata(&dssdev->dev);
return td->te_enabled;
}
static int taal_rotate(struct omap_dss_device *dssdev, u8 rotate)
......@@ -772,16 +881,21 @@ static int taal_rotate(struct omap_dss_device *dssdev, u8 rotate)
dev_dbg(&dssdev->dev, "rotate %d\n", rotate);
dsi_bus_lock();
if (td->enabled) {
r = taal_set_addr_mode(rotate, td->mirror);
if (r)
return r;
goto err;
}
td->rotate = rotate;
dsi_bus_unlock();
return 0;
err:
dsi_bus_unlock();
return r;
}
static u8 taal_get_rotate(struct omap_dss_device *dssdev)
......@@ -797,16 +911,20 @@ static int taal_mirror(struct omap_dss_device *dssdev, bool enable)
dev_dbg(&dssdev->dev, "mirror %d\n", enable);
dsi_bus_lock();
if (td->enabled) {
r = taal_set_addr_mode(td->rotate, enable);
if (r)
return r;
goto err;
}
td->mirror = enable;
dsi_bus_unlock();
return 0;
err:
dsi_bus_unlock();
return r;
}
static bool taal_get_mirror(struct omap_dss_device *dssdev)
......@@ -820,17 +938,23 @@ static int taal_run_test(struct omap_dss_device *dssdev, int test_num)
u8 id1, id2, id3;
int r;
dsi_bus_lock();
r = taal_dcs_read_1(DCS_GET_ID1, &id1);
if (r)
return r;
goto err;
r = taal_dcs_read_1(DCS_GET_ID2, &id2);
if (r)
return r;
goto err;
r = taal_dcs_read_1(DCS_GET_ID3, &id3);
if (r)
return r;
goto err;
dsi_bus_unlock();
return 0;
err:
dsi_bus_unlock();
return r;
}
static int taal_memory_read(struct omap_dss_device *dssdev,
......@@ -841,6 +965,10 @@ static int taal_memory_read(struct omap_dss_device *dssdev,
int first = 1;
int plen;
unsigned buf_used = 0;
struct taal_data *td = dev_get_drvdata(&dssdev->dev);
if (!td->enabled)
return -ENODEV;
if (size < w * h * 3)
return -ENOMEM;
......@@ -849,6 +977,8 @@ static int taal_memory_read(struct omap_dss_device *dssdev,
dssdev->panel.timings.x_res *
dssdev->panel.timings.y_res * 3);
dsi_bus_lock();
/* plen 1 or 2 goes into short packet. until checksum error is fixed,
* use short packets. plen 32 works, but bigger packets seem to cause
* an error. */
......@@ -857,11 +987,11 @@ static int taal_memory_read(struct omap_dss_device *dssdev,
else
plen = 2;
taal_setup_update(dssdev, x, y, w, h);
taal_set_update_window(x, y, w, h);
r = dsi_vc_set_max_rx_packet_size(TCH, plen);
if (r)
return r;
goto err0;
while (buf_used < size) {
u8 dcs_cmd = first ? 0x2e : 0x3e;
......@@ -894,7 +1024,8 @@ static int taal_memory_read(struct omap_dss_device *dssdev,
err:
dsi_vc_set_max_rx_packet_size(TCH, 1);
err0:
dsi_bus_unlock();
return r;
}
......@@ -939,8 +1070,11 @@ static void taal_esd_work(struct work_struct *work)
}
/* Self-diagnostics result is also shown on TE GPIO line. We need
* to re-enable TE after self diagnostics */
if (td->use_ext_te && td->te_enabled)
taal_enable_te(dssdev, true);
if (td->use_ext_te && td->te_enabled) {
r = taal_dcs_write_1(DCS_TEAR_ON, 0);
if (r)
goto err;
}
dsi_bus_unlock();
......@@ -958,6 +1092,20 @@ static void taal_esd_work(struct work_struct *work)
queue_delayed_work(td->esd_wq, &td->esd_work, TAAL_ESD_CHECK_PERIOD);
}
static int taal_set_update_mode(struct omap_dss_device *dssdev,
enum omap_dss_update_mode mode)
{
if (mode != OMAP_DSS_UPDATE_MANUAL)
return -EINVAL;
return 0;
}
static enum omap_dss_update_mode taal_get_update_mode(
struct omap_dss_device *dssdev)
{
return OMAP_DSS_UPDATE_MANUAL;
}
static struct omap_dss_driver taal_driver = {
.probe = taal_probe,
.remove = taal_remove,
......@@ -967,9 +1115,18 @@ static struct omap_dss_driver taal_driver = {
.suspend = taal_suspend,
.resume = taal_resume,
.setup_update = taal_setup_update,
.set_update_mode = taal_set_update_mode,
.get_update_mode = taal_get_update_mode,
.update = taal_update,
.sync = taal_sync,
.get_resolution = taal_get_resolution,
.get_recommended_bpp = omapdss_default_get_recommended_bpp,
.enable_te = taal_enable_te,
.wait_for_te = taal_wait_te,
.get_te = taal_get_te,
.set_rotate = taal_rotate,
.get_rotate = taal_get_rotate,
.set_mirror = taal_mirror,
......@@ -977,6 +1134,8 @@ static struct omap_dss_driver taal_driver = {
.run_test = taal_run_test,
.memory_read = taal_memory_read,
.get_timings = taal_get_timings,
.driver = {
.name = "taal",
.owner = THIS_MODULE,
......
/*
* LCD panel driver for Toppoly TDO35S
*
* Copyright (C) 2009 CompuLab, Ltd.
* Author: Mike Rapoport <mike@compulab.co.il>
*
* Based on generic panel support
* Copyright (C) 2008 Nokia Corporation
* Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <plat/display.h>
static struct omap_video_timings toppoly_tdo_panel_timings = {
/* 640 x 480 @ 60 Hz Reduced blanking VESA CVT 0.31M3-R */
.x_res = 480,
.y_res = 640,
.pixel_clock = 26000,
.hfp = 104,
.hsw = 8,
.hbp = 8,
.vfp = 4,
.vsw = 2,
.vbp = 2,
};
static int toppoly_tdo_panel_power_on(struct omap_dss_device *dssdev)
{
int r;
r = omapdss_dpi_display_enable(dssdev);
if (r)
goto err0;
if (dssdev->platform_enable) {
r = dssdev->platform_enable(dssdev);
if (r)
goto err1;
}
return 0;
err1:
omapdss_dpi_display_disable(dssdev);
err0:
return r;
}
static void toppoly_tdo_panel_power_off(struct omap_dss_device *dssdev)
{
if (dssdev->platform_disable)
dssdev->platform_disable(dssdev);
omapdss_dpi_display_disable(dssdev);
}
static int toppoly_tdo_panel_probe(struct omap_dss_device *dssdev)
{
dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
OMAP_DSS_LCD_IHS;
dssdev->panel.timings = toppoly_tdo_panel_timings;
return 0;
}
static void toppoly_tdo_panel_remove(struct omap_dss_device *dssdev)
{
}
static int toppoly_tdo_panel_enable(struct omap_dss_device *dssdev)
{
int r = 0;
r = toppoly_tdo_panel_power_on(dssdev);
if (r)
return r;
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return 0;
}
static void toppoly_tdo_panel_disable(struct omap_dss_device *dssdev)
{
toppoly_tdo_panel_power_off(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
}
static int toppoly_tdo_panel_suspend(struct omap_dss_device *dssdev)
{
toppoly_tdo_panel_power_off(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
return 0;
}
static int toppoly_tdo_panel_resume(struct omap_dss_device *dssdev)
{
int r = 0;
r = toppoly_tdo_panel_power_on(dssdev);
if (r)
return r;
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return 0;
}
static struct omap_dss_driver generic_driver = {
.probe = toppoly_tdo_panel_probe,
.remove = toppoly_tdo_panel_remove,
.enable = toppoly_tdo_panel_enable,
.disable = toppoly_tdo_panel_disable,
.suspend = toppoly_tdo_panel_suspend,
.resume = toppoly_tdo_panel_resume,
.driver = {
.name = "toppoly_tdo35s_panel",
.owner = THIS_MODULE,
},
};
static int __init toppoly_tdo_panel_drv_init(void)
{
return omap_dss_register_driver(&generic_driver);
}
static void __exit toppoly_tdo_panel_drv_exit(void)
{
omap_dss_unregister_driver(&generic_driver);
}
module_init(toppoly_tdo_panel_drv_init);
module_exit(toppoly_tdo_panel_drv_exit);
MODULE_LICENSE("GPL");
/*
* LCD panel driver for TPO TD043MTEA1
*
* Author: Gražvydas Ignotas <notasas@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/spi/spi.h>
#include <linux/regulator/consumer.h>
#include <linux/gpio.h>
#include <linux/err.h>
#include <plat/display.h>
#define TPO_R02_MODE(x) ((x) & 7)
#define TPO_R02_MODE_800x480 7
#define TPO_R02_NCLK_RISING BIT(3)
#define TPO_R02_HSYNC_HIGH BIT(4)
#define TPO_R02_VSYNC_HIGH BIT(5)
#define TPO_R03_NSTANDBY BIT(0)
#define TPO_R03_EN_CP_CLK BIT(1)
#define TPO_R03_EN_VGL_PUMP BIT(2)
#define TPO_R03_EN_PWM BIT(3)
#define TPO_R03_DRIVING_CAP_100 BIT(4)
#define TPO_R03_EN_PRE_CHARGE BIT(6)
#define TPO_R03_SOFTWARE_CTL BIT(7)
#define TPO_R04_NFLIP_H BIT(0)
#define TPO_R04_NFLIP_V BIT(1)
#define TPO_R04_CP_CLK_FREQ_1H BIT(2)
#define TPO_R04_VGL_FREQ_1H BIT(4)
#define TPO_R03_VAL_NORMAL (TPO_R03_NSTANDBY | TPO_R03_EN_CP_CLK | \
TPO_R03_EN_VGL_PUMP | TPO_R03_EN_PWM | \
TPO_R03_DRIVING_CAP_100 | TPO_R03_EN_PRE_CHARGE | \
TPO_R03_SOFTWARE_CTL)
#define TPO_R03_VAL_STANDBY (TPO_R03_DRIVING_CAP_100 | \
TPO_R03_EN_PRE_CHARGE | TPO_R03_SOFTWARE_CTL)
static const u16 tpo_td043_def_gamma[12] = {
106, 200, 289, 375, 460, 543, 625, 705, 785, 864, 942, 1020
};
struct tpo_td043_device {
struct spi_device *spi;
struct regulator *vcc_reg;
u16 gamma[12];
u32 mode;
u32 hmirror:1;
u32 vmirror:1;
};
static int tpo_td043_write(struct spi_device *spi, u8 addr, u8 data)
{
struct spi_message m;
struct spi_transfer xfer;
u16 w;
int r;
spi_message_init(&m);
memset(&xfer, 0, sizeof(xfer));
w = ((u16)addr << 10) | (1 << 8) | data;
xfer.tx_buf = &w;
xfer.bits_per_word = 16;
xfer.len = 2;
spi_message_add_tail(&xfer, &m);
r = spi_sync(spi, &m);
if (r < 0)
dev_warn(&spi->dev, "failed to write to LCD reg (%d)\n", r);
return r;
}
static void tpo_td043_write_gamma(struct spi_device *spi, u16 gamma[12])
{
u8 i, val;
/* gamma bits [9:8] */
for (val = i = 0; i < 4; i++)
val |= (gamma[i] & 0x300) >> ((i + 1) * 2);
tpo_td043_write(spi, 0x11, val);
for (val = i = 0; i < 4; i++)
val |= (gamma[i+4] & 0x300) >> ((i + 1) * 2);
tpo_td043_write(spi, 0x12, val);
for (val = i = 0; i < 4; i++)
val |= (gamma[i+8] & 0x300) >> ((i + 1) * 2);
tpo_td043_write(spi, 0x13, val);
/* gamma bits [7:0] */
for (val = i = 0; i < 12; i++)
tpo_td043_write(spi, 0x14 + i, gamma[i] & 0xff);
}
static int tpo_td043_write_mirror(struct spi_device *spi, bool h, bool v)
{
u8 reg4 = TPO_R04_NFLIP_H | TPO_R04_NFLIP_V | \
TPO_R04_CP_CLK_FREQ_1H | TPO_R04_VGL_FREQ_1H;
if (h)
reg4 &= ~TPO_R04_NFLIP_H;
if (v)
reg4 &= ~TPO_R04_NFLIP_V;
return tpo_td043_write(spi, 4, reg4);
}
static int tpo_td043_set_hmirror(struct omap_dss_device *dssdev, bool enable)
{
struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev);
tpo_td043->hmirror = enable;
return tpo_td043_write_mirror(tpo_td043->spi, tpo_td043->hmirror,
tpo_td043->vmirror);
}
static bool tpo_td043_get_hmirror(struct omap_dss_device *dssdev)
{
struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev);
return tpo_td043->hmirror;
}
static ssize_t tpo_td043_vmirror_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct tpo_td043_device *tpo_td043 = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%d\n", tpo_td043->vmirror);
}
static ssize_t tpo_td043_vmirror_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct tpo_td043_device *tpo_td043 = dev_get_drvdata(dev);
long val;
int ret;
ret = strict_strtol(buf, 0, &val);
if (ret < 0)
return ret;
ret = tpo_td043_write_mirror(tpo_td043->spi, tpo_td043->hmirror, val);
if (ret < 0)
return ret;
tpo_td043->vmirror = val;
return count;
}
static ssize_t tpo_td043_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct tpo_td043_device *tpo_td043 = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%d\n", tpo_td043->mode);
}
static ssize_t tpo_td043_mode_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct tpo_td043_device *tpo_td043 = dev_get_drvdata(dev);
long val;
int ret;
ret = strict_strtol(buf, 0, &val);
if (ret != 0 || val & ~7)
return -EINVAL;
tpo_td043->mode = val;
val |= TPO_R02_NCLK_RISING;
tpo_td043_write(tpo_td043->spi, 2, val);
return count;
}
static ssize_t tpo_td043_gamma_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct tpo_td043_device *tpo_td043 = dev_get_drvdata(dev);
ssize_t len = 0;
int ret;
int i;
for (i = 0; i < ARRAY_SIZE(tpo_td043->gamma); i++) {
ret = snprintf(buf + len, PAGE_SIZE - len, "%u ",
tpo_td043->gamma[i]);
if (ret < 0)
return ret;
len += ret;
}
buf[len - 1] = '\n';
return len;
}
static ssize_t tpo_td043_gamma_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct tpo_td043_device *tpo_td043 = dev_get_drvdata(dev);
unsigned int g[12];
int ret;
int i;
ret = sscanf(buf, "%u %u %u %u %u %u %u %u %u %u %u %u",
&g[0], &g[1], &g[2], &g[3], &g[4], &g[5],
&g[6], &g[7], &g[8], &g[9], &g[10], &g[11]);
if (ret != 12)
return -EINVAL;
for (i = 0; i < 12; i++)
tpo_td043->gamma[i] = g[i];
tpo_td043_write_gamma(tpo_td043->spi, tpo_td043->gamma);
return count;
}
static DEVICE_ATTR(vmirror, S_IRUGO | S_IWUSR,
tpo_td043_vmirror_show, tpo_td043_vmirror_store);
static DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
tpo_td043_mode_show, tpo_td043_mode_store);
static DEVICE_ATTR(gamma, S_IRUGO | S_IWUSR,
tpo_td043_gamma_show, tpo_td043_gamma_store);
static struct attribute *tpo_td043_attrs[] = {
&dev_attr_vmirror.attr,
&dev_attr_mode.attr,
&dev_attr_gamma.attr,
NULL,
};
static struct attribute_group tpo_td043_attr_group = {
.attrs = tpo_td043_attrs,
};
static const struct omap_video_timings tpo_td043_timings = {
.x_res = 800,
.y_res = 480,
.pixel_clock = 36000,
.hsw = 1,
.hfp = 68,
.hbp = 214,
.vsw = 1,
.vfp = 39,
.vbp = 34,
};
static int tpo_td043_power_on(struct omap_dss_device *dssdev)
{
struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev);
int nreset_gpio = dssdev->reset_gpio;
int r;
r = omapdss_dpi_display_enable(dssdev);
if (r)
goto err0;
if (dssdev->platform_enable) {
r = dssdev->platform_enable(dssdev);
if (r)
goto err1;
}
regulator_enable(tpo_td043->vcc_reg);
/* wait for power up */
msleep(160);
if (gpio_is_valid(nreset_gpio))
gpio_set_value(nreset_gpio, 1);
tpo_td043_write(tpo_td043->spi, 2,
TPO_R02_MODE(tpo_td043->mode) | TPO_R02_NCLK_RISING);
tpo_td043_write(tpo_td043->spi, 3, TPO_R03_VAL_NORMAL);
tpo_td043_write(tpo_td043->spi, 0x20, 0xf0);
tpo_td043_write(tpo_td043->spi, 0x21, 0xf0);
tpo_td043_write_mirror(tpo_td043->spi, tpo_td043->hmirror,
tpo_td043->vmirror);
tpo_td043_write_gamma(tpo_td043->spi, tpo_td043->gamma);
return 0;
err1:
omapdss_dpi_display_disable(dssdev);
err0:
return r;
}
static void tpo_td043_power_off(struct omap_dss_device *dssdev)
{
struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev);
int nreset_gpio = dssdev->reset_gpio;
tpo_td043_write(tpo_td043->spi, 3,
TPO_R03_VAL_STANDBY | TPO_R03_EN_PWM);
if (gpio_is_valid(nreset_gpio))
gpio_set_value(nreset_gpio, 0);
/* wait for at least 2 vsyncs before cutting off power */
msleep(50);
tpo_td043_write(tpo_td043->spi, 3, TPO_R03_VAL_STANDBY);
regulator_disable(tpo_td043->vcc_reg);
if (dssdev->platform_disable)
dssdev->platform_disable(dssdev);
omapdss_dpi_display_disable(dssdev);
}
static int tpo_td043_enable(struct omap_dss_device *dssdev)
{
int ret;
dev_dbg(&dssdev->dev, "enable\n");
ret = tpo_td043_power_on(dssdev);
if (ret)
return ret;
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return 0;
}
static void tpo_td043_disable(struct omap_dss_device *dssdev)
{
dev_dbg(&dssdev->dev, "disable\n");
tpo_td043_power_off(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
}
static int tpo_td043_suspend(struct omap_dss_device *dssdev)
{
tpo_td043_power_off(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
return 0;
}
static int tpo_td043_resume(struct omap_dss_device *dssdev)
{
int r = 0;
r = tpo_td043_power_on(dssdev);
if (r)
return r;
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return 0;
}
static int tpo_td043_probe(struct omap_dss_device *dssdev)
{
struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev);
int nreset_gpio = dssdev->reset_gpio;
int ret = 0;
dev_dbg(&dssdev->dev, "probe\n");
if (tpo_td043 == NULL) {
dev_err(&dssdev->dev, "missing tpo_td043_device\n");
return -ENODEV;
}
dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IHS |
OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IPC;
dssdev->panel.timings = tpo_td043_timings;
dssdev->ctrl.pixel_size = 24;
tpo_td043->mode = TPO_R02_MODE_800x480;
memcpy(tpo_td043->gamma, tpo_td043_def_gamma, sizeof(tpo_td043->gamma));
tpo_td043->vcc_reg = regulator_get(&dssdev->dev, "vcc");
if (IS_ERR(tpo_td043->vcc_reg)) {
dev_err(&dssdev->dev, "failed to get LCD VCC regulator\n");
ret = PTR_ERR(tpo_td043->vcc_reg);
goto fail_regulator;
}
if (gpio_is_valid(nreset_gpio)) {
ret = gpio_request(nreset_gpio, "lcd reset");
if (ret < 0) {
dev_err(&dssdev->dev, "couldn't request reset GPIO\n");
goto fail_gpio_req;
}
ret = gpio_direction_output(nreset_gpio, 0);
if (ret < 0) {
dev_err(&dssdev->dev, "couldn't set GPIO direction\n");
goto fail_gpio_direction;
}
}
ret = sysfs_create_group(&dssdev->dev.kobj, &tpo_td043_attr_group);
if (ret)
dev_warn(&dssdev->dev, "failed to create sysfs files\n");
return 0;
fail_gpio_direction:
gpio_free(nreset_gpio);
fail_gpio_req:
regulator_put(tpo_td043->vcc_reg);
fail_regulator:
kfree(tpo_td043);
return ret;
}
static void tpo_td043_remove(struct omap_dss_device *dssdev)
{
struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev);
int nreset_gpio = dssdev->reset_gpio;
dev_dbg(&dssdev->dev, "remove\n");
sysfs_remove_group(&dssdev->dev.kobj, &tpo_td043_attr_group);
regulator_put(tpo_td043->vcc_reg);
if (gpio_is_valid(nreset_gpio))
gpio_free(nreset_gpio);
}
static struct omap_dss_driver tpo_td043_driver = {
.probe = tpo_td043_probe,
.remove = tpo_td043_remove,
.enable = tpo_td043_enable,
.disable = tpo_td043_disable,
.suspend = tpo_td043_suspend,
.resume = tpo_td043_resume,
.set_mirror = tpo_td043_set_hmirror,
.get_mirror = tpo_td043_get_hmirror,
.driver = {
.name = "tpo_td043mtea1_panel",
.owner = THIS_MODULE,
},
};
static int tpo_td043_spi_probe(struct spi_device *spi)
{
struct omap_dss_device *dssdev = spi->dev.platform_data;
struct tpo_td043_device *tpo_td043;
int ret;
if (dssdev == NULL) {
dev_err(&spi->dev, "missing dssdev\n");
return -ENODEV;
}
spi->bits_per_word = 16;
spi->mode = SPI_MODE_0;
ret = spi_setup(spi);
if (ret < 0) {
dev_err(&spi->dev, "spi_setup failed: %d\n", ret);
return ret;
}
tpo_td043 = kzalloc(sizeof(*tpo_td043), GFP_KERNEL);
if (tpo_td043 == NULL)
return -ENOMEM;
tpo_td043->spi = spi;
dev_set_drvdata(&spi->dev, tpo_td043);
dev_set_drvdata(&dssdev->dev, tpo_td043);
omap_dss_register_driver(&tpo_td043_driver);
return 0;
}
static int __devexit tpo_td043_spi_remove(struct spi_device *spi)
{
struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&spi->dev);
omap_dss_unregister_driver(&tpo_td043_driver);
kfree(tpo_td043);
return 0;
}
static struct spi_driver tpo_td043_spi_driver = {
.driver = {
.name = "tpo_td043mtea1_panel_spi",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.probe = tpo_td043_spi_probe,
.remove = __devexit_p(tpo_td043_spi_remove),
};
static int __init tpo_td043_init(void)
{
return spi_register_driver(&tpo_td043_spi_driver);
}
static void __exit tpo_td043_exit(void)
{
spi_unregister_driver(&tpo_td043_spi_driver);
}
module_init(tpo_td043_init);
module_exit(tpo_td043_exit);
MODULE_AUTHOR("Gražvydas Ignotas <notasas@gmail.com>");
MODULE_DESCRIPTION("TPO TD043MTEA1 LCD Driver");
MODULE_LICENSE("GPL");
......@@ -30,19 +30,29 @@ config OMAP2_DSS_COLLECT_IRQ_STATS
depends on OMAP2_DSS_DEBUG_SUPPORT
default n
help
Collect DSS IRQ statistics, printable via debugfs
Collect DSS IRQ statistics, printable via debugfs.
The statistics can be found from
<debugfs>/omapdss/dispc_irq for DISPC interrupts, and
<debugfs>/omapdss/dsi_irq for DSI interrupts.
config OMAP2_DSS_RFBI
bool "RFBI support"
default n
help
MIPI DBI, or RFBI (Remote Framebuffer Interface), support.
MIPI DBI support (RFBI, Remote Framebuffer Interface, in Texas
Instrument's terminology).
DBI is a bus between the host processor and a peripheral,
such as a display or a framebuffer chip.
See http://www.mipi.org/ for DBI spesifications.
config OMAP2_DSS_VENC
bool "VENC support"
default y
help
OMAP Video Encoder support.
OMAP Video Encoder support for S-Video and composite TV-out.
config OMAP2_DSS_SDI
bool "SDI support"
......@@ -51,12 +61,20 @@ config OMAP2_DSS_SDI
help
SDI (Serial Display Interface) support.
SDI is a high speed one-way display serial bus between the host
processor and a display.
config OMAP2_DSS_DSI
bool "DSI support"
depends on ARCH_OMAP3
default n
help
MIPI DSI support.
MIPI DSI (Display Serial Interface) support.
DSI is a high speed half-duplex serial interface between the host
processor and a peripheral, such as a display or a framebuffer chip.
See http://www.mipi.org/ for DSI spesifications.
config OMAP2_DSS_USE_DSI_PLL
bool "Use DSI PLL for PCLK (EXPERIMENTAL)"
......
......@@ -31,6 +31,7 @@
#include <linux/debugfs.h>
#include <linux/io.h>
#include <linux/device.h>
#include <linux/regulator/consumer.h>
#include <plat/display.h>
#include <plat/clock.h>
......@@ -47,6 +48,10 @@ static struct {
struct clk *dss_54m_fck;
struct clk *dss_96m_fck;
unsigned num_clks_enabled;
struct regulator *vdds_dsi_reg;
struct regulator *vdds_sdi_reg;
struct regulator *vdda_dac_reg;
} core;
static void dss_clk_enable_all_no_ctx(void);
......@@ -284,9 +289,11 @@ static void dss_clk_enable_no_ctx(enum dss_clock clks)
void dss_clk_enable(enum dss_clock clks)
{
bool check_ctx = core.num_clks_enabled == 0;
dss_clk_enable_no_ctx(clks);
if (cpu_is_omap34xx() && dss_need_ctx_restore())
if (check_ctx && cpu_is_omap34xx() && dss_need_ctx_restore())
restore_all_ctx();
}
......@@ -352,6 +359,50 @@ static void dss_clk_disable_all(void)
dss_clk_disable(clks);
}
/* REGULATORS */
struct regulator *dss_get_vdds_dsi(void)
{
struct regulator *reg;
if (core.vdds_dsi_reg != NULL)
return core.vdds_dsi_reg;
reg = regulator_get(&core.pdev->dev, "vdds_dsi");
if (!IS_ERR(reg))
core.vdds_dsi_reg = reg;
return reg;
}
struct regulator *dss_get_vdds_sdi(void)
{
struct regulator *reg;
if (core.vdds_sdi_reg != NULL)
return core.vdds_sdi_reg;
reg = regulator_get(&core.pdev->dev, "vdds_sdi");
if (!IS_ERR(reg))
core.vdds_sdi_reg = reg;
return reg;
}
struct regulator *dss_get_vdda_dac(void)
{
struct regulator *reg;
if (core.vdda_dac_reg != NULL)
return core.vdda_dac_reg;
reg = regulator_get(&core.pdev->dev, "vdda_dac");
if (!IS_ERR(reg))
core.vdda_dac_reg = reg;
return reg;
}
/* DEBUGFS */
#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT)
static void dss_debug_dump_clocks(struct seq_file *s)
......@@ -397,10 +448,12 @@ static int dss_initialize_debugfs(void)
debugfs_create_file("clk", S_IRUGO, dss_debugfs_dir,
&dss_debug_dump_clocks, &dss_debug_fops);
#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
debugfs_create_file("dispc_irq", S_IRUGO, dss_debugfs_dir,
&dispc_dump_irqs, &dss_debug_fops);
#endif
#ifdef CONFIG_OMAP2_DSS_DSI
#if defined(CONFIG_OMAP2_DSS_DSI) && defined(CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS)
debugfs_create_file("dsi_irq", S_IRUGO, dss_debugfs_dir,
&dsi_dump_irqs, &dss_debug_fops);
#endif
......@@ -473,7 +526,7 @@ static int omap_dss_probe(struct platform_device *pdev)
}
#endif
r = dpi_init();
r = dpi_init(pdev);
if (r) {
DSSERR("Failed to initialize dpi\n");
goto fail0;
......@@ -718,16 +771,14 @@ static int dss_driver_probe(struct device *dev)
dss_init_device(core.pdev, dssdev);
/* skip this if the device is behind a ctrl */
if (!dssdev->panel.ctrl) {
force = pdata->default_device == dssdev;
dss_recheck_connections(dssdev, force);
}
force = pdata->default_device == dssdev;
dss_recheck_connections(dssdev, force);
r = dssdrv->probe(dssdev);
if (r) {
DSSERR("driver probe failed: %d\n", r);
dss_uninit_device(core.pdev, dssdev);
return r;
}
......@@ -760,6 +811,13 @@ int omap_dss_register_driver(struct omap_dss_driver *dssdriver)
dssdriver->driver.bus = &dss_bus_type;
dssdriver->driver.probe = dss_driver_probe;
dssdriver->driver.remove = dss_driver_remove;
if (dssdriver->get_resolution == NULL)
dssdriver->get_resolution = omapdss_default_get_resolution;
if (dssdriver->get_recommended_bpp == NULL)
dssdriver->get_recommended_bpp =
omapdss_default_get_recommended_bpp;
return driver_register(&dssdriver->driver);
}
EXPORT_SYMBOL(omap_dss_register_driver);
......@@ -808,8 +866,6 @@ static void omap_dss_dev_release(struct device *dev)
int omap_dss_register_device(struct omap_dss_device *dssdev)
{
static int dev_num;
static int panel_num;
int r;
WARN_ON(!dssdev->driver_name);
......@@ -818,36 +874,12 @@ int omap_dss_register_device(struct omap_dss_device *dssdev)
dssdev->dev.parent = &dss_bus;
dssdev->dev.release = omap_dss_dev_release;
dev_set_name(&dssdev->dev, "display%d", dev_num++);
r = device_register(&dssdev->dev);
if (r)
return r;
if (dssdev->ctrl.panel) {
struct omap_dss_device *panel = dssdev->ctrl.panel;
panel->panel.ctrl = dssdev;
reset_device(&panel->dev, 1);
panel->dev.bus = &dss_bus_type;
panel->dev.parent = &dssdev->dev;
panel->dev.release = omap_dss_dev_release;
dev_set_name(&panel->dev, "panel%d", panel_num++);
r = device_register(&panel->dev);
if (r)
return r;
}
return 0;
return device_register(&dssdev->dev);
}
void omap_dss_unregister_device(struct omap_dss_device *dssdev)
{
device_unregister(&dssdev->dev);
if (dssdev->ctrl.panel) {
struct omap_dss_device *panel = dssdev->ctrl.panel;
device_unregister(&panel->dev);
}
}
/* BUS */
......@@ -901,6 +933,21 @@ static int __init omap_dss_init(void)
static void __exit omap_dss_exit(void)
{
if (core.vdds_dsi_reg != NULL) {
regulator_put(core.vdds_dsi_reg);
core.vdds_dsi_reg = NULL;
}
if (core.vdds_sdi_reg != NULL) {
regulator_put(core.vdds_sdi_reg);
core.vdds_sdi_reg = NULL;
}
if (core.vdda_dac_reg != NULL) {
regulator_put(core.vdda_dac_reg);
core.vdda_dac_reg = NULL;
}
platform_driver_unregister(&omap_dss_driver);
omap_dss_bus_unregister();
......
......@@ -1725,7 +1725,7 @@ static void _enable_lcd_out(bool enable)
REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 0, 0);
}
void dispc_enable_lcd_out(bool enable)
static void dispc_enable_lcd_out(bool enable)
{
struct completion frame_done_completion;
bool is_on;
......@@ -1772,7 +1772,7 @@ static void _enable_digit_out(bool enable)
REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 1, 1);
}
void dispc_enable_digit_out(bool enable)
static void dispc_enable_digit_out(bool enable)
{
struct completion frame_done_completion;
int r;
......@@ -1836,6 +1836,26 @@ void dispc_enable_digit_out(bool enable)
enable_clocks(0);
}
bool dispc_is_channel_enabled(enum omap_channel channel)
{
if (channel == OMAP_DSS_CHANNEL_LCD)
return !!REG_GET(DISPC_CONTROL, 0, 0);
else if (channel == OMAP_DSS_CHANNEL_DIGIT)
return !!REG_GET(DISPC_CONTROL, 1, 1);
else
BUG();
}
void dispc_enable_channel(enum omap_channel channel, bool enable)
{
if (channel == OMAP_DSS_CHANNEL_LCD)
dispc_enable_lcd_out(enable);
else if (channel == OMAP_DSS_CHANNEL_DIGIT)
dispc_enable_digit_out(enable);
else
BUG();
}
void dispc_lcd_enable_signal_polarity(bool act_high)
{
enable_clocks(1);
......@@ -2198,7 +2218,7 @@ unsigned long dispc_fclk_rate(void)
{
unsigned long r = 0;
if (dss_get_dispc_clk_source() == 0)
if (dss_get_dispc_clk_source() == DSS_SRC_DSS1_ALWON_FCLK)
r = dss_clk_get_rate(DSS_CLK_FCK1);
else
#ifdef CONFIG_OMAP2_DSS_DSI
......@@ -2251,7 +2271,7 @@ void dispc_dump_clocks(struct seq_file *s)
seq_printf(s, "- DISPC -\n");
seq_printf(s, "dispc fclk source = %s\n",
dss_get_dispc_clk_source() == 0 ?
dss_get_dispc_clk_source() == DSS_SRC_DSS1_ALWON_FCLK ?
"dss1_alwon_fclk" : "dsi1_pll_fclk");
seq_printf(s, "fck\t\t%-16lu\n", dispc_fclk_rate());
......@@ -2301,8 +2321,6 @@ void dispc_dump_irqs(struct seq_file *s)
PIS(WAKEUP);
#undef PIS
}
#else
void dispc_dump_irqs(struct seq_file *s) { }
#endif
void dispc_dump_regs(struct seq_file *s)
......@@ -2854,12 +2872,13 @@ static void dispc_error_worker(struct work_struct *work)
manager = mgr;
enable = mgr->device->state ==
OMAP_DSS_DISPLAY_ACTIVE;
mgr->device->disable(mgr->device);
mgr->device->driver->disable(mgr->device);
break;
}
}
if (manager) {
struct omap_dss_device *dssdev = manager->device;
for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
struct omap_overlay *ovl;
ovl = omap_dss_get_overlay(i);
......@@ -2874,7 +2893,7 @@ static void dispc_error_worker(struct work_struct *work)
dispc_go(manager->id);
mdelay(50);
if (enable)
manager->device->enable(manager->device);
dssdev->driver->enable(dssdev);
}
}
......@@ -2892,12 +2911,13 @@ static void dispc_error_worker(struct work_struct *work)
manager = mgr;
enable = mgr->device->state ==
OMAP_DSS_DISPLAY_ACTIVE;
mgr->device->disable(mgr->device);
mgr->device->driver->disable(mgr->device);
break;
}
}
if (manager) {
struct omap_dss_device *dssdev = manager->device;
for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
struct omap_overlay *ovl;
ovl = omap_dss_get_overlay(i);
......@@ -2912,7 +2932,7 @@ static void dispc_error_worker(struct work_struct *work)
dispc_go(manager->id);
mdelay(50);
if (enable)
manager->device->enable(manager->device);
dssdev->driver->enable(dssdev);
}
}
......@@ -2923,7 +2943,7 @@ static void dispc_error_worker(struct work_struct *work)
mgr = omap_dss_get_overlay_manager(i);
if (mgr->caps & OMAP_DSS_OVL_CAP_DISPC)
mgr->device->disable(mgr->device);
mgr->device->driver->disable(mgr->device);
}
}
......
......@@ -53,11 +53,11 @@ static ssize_t display_enabled_store(struct device *dev,
if (enabled != (dssdev->state != OMAP_DSS_DISPLAY_DISABLED)) {
if (enabled) {
r = dssdev->enable(dssdev);
r = dssdev->driver->enable(dssdev);
if (r)
return r;
} else {
dssdev->disable(dssdev);
dssdev->driver->disable(dssdev);
}
}
......@@ -69,8 +69,8 @@ static ssize_t display_upd_mode_show(struct device *dev,
{
struct omap_dss_device *dssdev = to_dss_device(dev);
enum omap_dss_update_mode mode = OMAP_DSS_UPDATE_AUTO;
if (dssdev->get_update_mode)
mode = dssdev->get_update_mode(dssdev);
if (dssdev->driver->get_update_mode)
mode = dssdev->driver->get_update_mode(dssdev);
return snprintf(buf, PAGE_SIZE, "%d\n", mode);
}
......@@ -94,7 +94,7 @@ static ssize_t display_upd_mode_store(struct device *dev,
return -EINVAL;
}
r = dssdev->set_update_mode(dssdev, mode);
r = dssdev->driver->set_update_mode(dssdev, mode);
if (r)
return r;
......@@ -106,7 +106,8 @@ static ssize_t display_tear_show(struct device *dev,
{
struct omap_dss_device *dssdev = to_dss_device(dev);
return snprintf(buf, PAGE_SIZE, "%d\n",
dssdev->get_te ? dssdev->get_te(dssdev) : 0);
dssdev->driver->get_te ?
dssdev->driver->get_te(dssdev) : 0);
}
static ssize_t display_tear_store(struct device *dev,
......@@ -116,12 +117,12 @@ static ssize_t display_tear_store(struct device *dev,
unsigned long te;
int r;
if (!dssdev->enable_te || !dssdev->get_te)
if (!dssdev->driver->enable_te || !dssdev->driver->get_te)
return -ENOENT;
te = simple_strtoul(buf, NULL, 0);
r = dssdev->enable_te(dssdev, te);
r = dssdev->driver->enable_te(dssdev, te);
if (r)
return r;
......@@ -134,10 +135,10 @@ static ssize_t display_timings_show(struct device *dev,
struct omap_dss_device *dssdev = to_dss_device(dev);
struct omap_video_timings t;
if (!dssdev->get_timings)
if (!dssdev->driver->get_timings)
return -ENOENT;
dssdev->get_timings(dssdev, &t);
dssdev->driver->get_timings(dssdev, &t);
return snprintf(buf, PAGE_SIZE, "%u,%u/%u/%u/%u,%u/%u/%u/%u\n",
t.pixel_clock,
......@@ -152,7 +153,7 @@ static ssize_t display_timings_store(struct device *dev,
struct omap_video_timings t;
int r, found;
if (!dssdev->set_timings || !dssdev->check_timings)
if (!dssdev->driver->set_timings || !dssdev->driver->check_timings)
return -ENOENT;
found = 0;
......@@ -171,11 +172,11 @@ static ssize_t display_timings_store(struct device *dev,
&t.y_res, &t.vfp, &t.vbp, &t.vsw) != 9)
return -EINVAL;
r = dssdev->check_timings(dssdev, &t);
r = dssdev->driver->check_timings(dssdev, &t);
if (r)
return r;
dssdev->set_timings(dssdev, &t);
dssdev->driver->set_timings(dssdev, &t);
return size;
}
......@@ -185,9 +186,9 @@ static ssize_t display_rotate_show(struct device *dev,
{
struct omap_dss_device *dssdev = to_dss_device(dev);
int rotate;
if (!dssdev->get_rotate)
if (!dssdev->driver->get_rotate)
return -ENOENT;
rotate = dssdev->get_rotate(dssdev);
rotate = dssdev->driver->get_rotate(dssdev);
return snprintf(buf, PAGE_SIZE, "%u\n", rotate);
}
......@@ -198,12 +199,12 @@ static ssize_t display_rotate_store(struct device *dev,
unsigned long rot;
int r;
if (!dssdev->set_rotate || !dssdev->get_rotate)
if (!dssdev->driver->set_rotate || !dssdev->driver->get_rotate)
return -ENOENT;
rot = simple_strtoul(buf, NULL, 0);
r = dssdev->set_rotate(dssdev, rot);
r = dssdev->driver->set_rotate(dssdev, rot);
if (r)
return r;
......@@ -215,9 +216,9 @@ static ssize_t display_mirror_show(struct device *dev,
{
struct omap_dss_device *dssdev = to_dss_device(dev);
int mirror;
if (!dssdev->get_mirror)
if (!dssdev->driver->get_mirror)
return -ENOENT;
mirror = dssdev->get_mirror(dssdev);
mirror = dssdev->driver->get_mirror(dssdev);
return snprintf(buf, PAGE_SIZE, "%u\n", mirror);
}
......@@ -228,12 +229,12 @@ static ssize_t display_mirror_store(struct device *dev,
unsigned long mirror;
int r;
if (!dssdev->set_mirror || !dssdev->get_mirror)
if (!dssdev->driver->set_mirror || !dssdev->driver->get_mirror)
return -ENOENT;
mirror = simple_strtoul(buf, NULL, 0);
r = dssdev->set_mirror(dssdev, mirror);
r = dssdev->driver->set_mirror(dssdev, mirror);
if (r)
return r;
......@@ -246,10 +247,10 @@ static ssize_t display_wss_show(struct device *dev,
struct omap_dss_device *dssdev = to_dss_device(dev);
unsigned int wss;
if (!dssdev->get_wss)
if (!dssdev->driver->get_wss)
return -ENOENT;
wss = dssdev->get_wss(dssdev);
wss = dssdev->driver->get_wss(dssdev);
return snprintf(buf, PAGE_SIZE, "0x%05x\n", wss);
}
......@@ -261,7 +262,7 @@ static ssize_t display_wss_store(struct device *dev,
unsigned long wss;
int r;
if (!dssdev->get_wss || !dssdev->set_wss)
if (!dssdev->driver->get_wss || !dssdev->driver->set_wss)
return -ENOENT;
if (strict_strtoul(buf, 0, &wss))
......@@ -270,7 +271,7 @@ static ssize_t display_wss_store(struct device *dev,
if (wss > 0xfffff)
return -EINVAL;
r = dssdev->set_wss(dssdev, wss);
r = dssdev->driver->set_wss(dssdev, wss);
if (r)
return r;
......@@ -303,12 +304,13 @@ static struct device_attribute *display_sysfs_attrs[] = {
NULL
};
static void default_get_resolution(struct omap_dss_device *dssdev,
void omapdss_default_get_resolution(struct omap_dss_device *dssdev,
u16 *xres, u16 *yres)
{
*xres = dssdev->panel.timings.x_res;
*yres = dssdev->panel.timings.y_res;
}
EXPORT_SYMBOL(omapdss_default_get_resolution);
void default_get_overlay_fifo_thresholds(enum omap_plane plane,
u32 fifo_size, enum omap_burst_size *burst_size,
......@@ -323,24 +325,8 @@ void default_get_overlay_fifo_thresholds(enum omap_plane plane,
*fifo_low = fifo_size - burst_size_bytes;
}
static int default_wait_vsync(struct omap_dss_device *dssdev)
int omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev)
{
unsigned long timeout = msecs_to_jiffies(500);
u32 irq;
if (dssdev->type == OMAP_DISPLAY_TYPE_VENC)
irq = DISPC_IRQ_EVSYNC_ODD;
else
irq = DISPC_IRQ_VSYNC;
return omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
}
static int default_get_recommended_bpp(struct omap_dss_device *dssdev)
{
if (dssdev->panel.recommended_bpp)
return dssdev->panel.recommended_bpp;
switch (dssdev->type) {
case OMAP_DISPLAY_TYPE_DPI:
if (dssdev->phy.dpi.data_lines == 24)
......@@ -362,6 +348,7 @@ static int default_get_recommended_bpp(struct omap_dss_device *dssdev)
BUG();
}
}
EXPORT_SYMBOL(omapdss_default_get_recommended_bpp);
/* Checks if replication logic should be used. Only use for active matrix,
* when overlay is in RGB12U or RGB16 mode, and LCD interface is
......@@ -425,10 +412,6 @@ void dss_init_device(struct platform_device *pdev,
return;
}
dssdev->get_resolution = default_get_resolution;
dssdev->get_recommended_bpp = default_get_recommended_bpp;
dssdev->wait_vsync = default_wait_vsync;
switch (dssdev->type) {
case OMAP_DISPLAY_TYPE_DPI:
r = dpi_init_display(dssdev);
......@@ -502,13 +485,13 @@ static int dss_suspend_device(struct device *dev, void *data)
return 0;
}
if (!dssdev->suspend) {
if (!dssdev->driver->suspend) {
DSSERR("display '%s' doesn't implement suspend\n",
dssdev->name);
return -ENOSYS;
}
r = dssdev->suspend(dssdev);
r = dssdev->driver->suspend(dssdev);
if (r)
return r;
......@@ -537,8 +520,8 @@ static int dss_resume_device(struct device *dev, void *data)
int r;
struct omap_dss_device *dssdev = to_dss_device(dev);
if (dssdev->activate_after_resume && dssdev->resume) {
r = dssdev->resume(dssdev);
if (dssdev->activate_after_resume && dssdev->driver->resume) {
r = dssdev->driver->resume(dssdev);
if (r)
return r;
}
......@@ -558,7 +541,7 @@ int dss_resume_all_devices(void)
static int dss_disable_device(struct device *dev, void *data)
{
struct omap_dss_device *dssdev = to_dss_device(dev);
dssdev->disable(dssdev);
dssdev->driver->disable(dssdev);
return 0;
}
......@@ -591,10 +574,6 @@ struct omap_dss_device *omap_dss_get_next_device(struct omap_dss_device *from)
int match(struct device *dev, void *data)
{
/* skip panels connected to controllers */
if (to_dss_device(dev)->panel.ctrl)
return 0;
return 1;
}
......@@ -626,45 +605,21 @@ EXPORT_SYMBOL(omap_dss_find_device);
int omap_dss_start_device(struct omap_dss_device *dssdev)
{
int r;
if (!dssdev->driver) {
DSSDBG("no driver\n");
r = -ENODEV;
goto err0;
}
if (dssdev->ctrl.panel && !dssdev->ctrl.panel->driver) {
DSSDBG("no panel driver\n");
r = -ENODEV;
goto err0;
return -ENODEV;
}
if (!try_module_get(dssdev->dev.driver->owner)) {
r = -ENODEV;
goto err0;
}
if (dssdev->ctrl.panel) {
if (!try_module_get(dssdev->ctrl.panel->dev.driver->owner)) {
r = -ENODEV;
goto err1;
}
return -ENODEV;
}
return 0;
err1:
module_put(dssdev->dev.driver->owner);
err0:
return r;
}
EXPORT_SYMBOL(omap_dss_start_device);
void omap_dss_stop_device(struct omap_dss_device *dssdev)
{
if (dssdev->ctrl.panel)
module_put(dssdev->ctrl.panel->dev.driver->owner);
module_put(dssdev->dev.driver->owner);
}
EXPORT_SYMBOL(omap_dss_stop_device);
......
......@@ -25,7 +25,10 @@
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <plat/display.h>
#include <plat/cpu.h>
......@@ -33,7 +36,7 @@
#include "dss.h"
static struct {
int update_enabled;
struct regulator *vdds_dsi_reg;
} dpi;
#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL
......@@ -53,7 +56,7 @@ static int dpi_set_dsi_clk(bool is_tft, unsigned long pck_req,
if (r)
return r;
dss_select_clk_source(0, 1);
dss_select_dispc_clk_source(DSS_SRC_DSI1_PLL_FCLK);
r = dispc_set_clock_div(&dispc_cinfo);
if (r)
......@@ -150,7 +153,7 @@ static int dpi_basic_init(struct omap_dss_device *dssdev)
return 0;
}
static int dpi_display_enable(struct omap_dss_device *dssdev)
int omapdss_dpi_display_enable(struct omap_dss_device *dssdev)
{
int r;
......@@ -160,10 +163,10 @@ static int dpi_display_enable(struct omap_dss_device *dssdev)
goto err0;
}
if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {
DSSERR("display already enabled\n");
r = -EINVAL;
goto err1;
if (cpu_is_omap34xx()) {
r = regulator_enable(dpi.vdds_dsi_reg);
if (r)
goto err1;
}
dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
......@@ -184,18 +187,10 @@ static int dpi_display_enable(struct omap_dss_device *dssdev)
mdelay(2);
dispc_enable_lcd_out(1);
r = dssdev->driver->enable(dssdev);
if (r)
goto err5;
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
dssdev->manager->enable(dssdev->manager);
return 0;
err5:
dispc_enable_lcd_out(0);
err4:
#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL
dsi_pll_uninit();
......@@ -204,78 +199,35 @@ static int dpi_display_enable(struct omap_dss_device *dssdev)
#endif
err2:
dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
if (cpu_is_omap34xx())
regulator_disable(dpi.vdds_dsi_reg);
err1:
omap_dss_stop_device(dssdev);
err0:
return r;
}
EXPORT_SYMBOL(omapdss_dpi_display_enable);
static int dpi_display_resume(struct omap_dss_device *dssdev);
static void dpi_display_disable(struct omap_dss_device *dssdev)
void omapdss_dpi_display_disable(struct omap_dss_device *dssdev)
{
if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED)
return;
if (dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED)
dpi_display_resume(dssdev);
dssdev->driver->disable(dssdev);
dispc_enable_lcd_out(0);
dssdev->manager->disable(dssdev->manager);
#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL
dss_select_clk_source(0, 0);
dss_select_dispc_clk_source(DSS_SRC_DSS1_ALWON_FCLK);
dsi_pll_uninit();
dss_clk_disable(DSS_CLK_FCK2);
#endif
dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
if (cpu_is_omap34xx())
regulator_disable(dpi.vdds_dsi_reg);
omap_dss_stop_device(dssdev);
}
EXPORT_SYMBOL(omapdss_dpi_display_disable);
static int dpi_display_suspend(struct omap_dss_device *dssdev)
{
if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
return -EINVAL;
DSSDBG("dpi_display_suspend\n");
if (dssdev->driver->suspend)
dssdev->driver->suspend(dssdev);
dispc_enable_lcd_out(0);
dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
return 0;
}
static int dpi_display_resume(struct omap_dss_device *dssdev)
{
if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED)
return -EINVAL;
DSSDBG("dpi_display_resume\n");
dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
dispc_enable_lcd_out(1);
if (dssdev->driver->resume)
dssdev->driver->resume(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return 0;
}
static void dpi_set_timings(struct omap_dss_device *dssdev,
void dpi_set_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
DSSDBG("dpi_set_timings\n");
......@@ -285,8 +237,9 @@ static void dpi_set_timings(struct omap_dss_device *dssdev,
dispc_go(OMAP_DSS_CHANNEL_LCD);
}
}
EXPORT_SYMBOL(dpi_set_timings);
static int dpi_check_timings(struct omap_dss_device *dssdev,
int dpi_check_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
bool is_tft;
......@@ -340,56 +293,25 @@ static int dpi_check_timings(struct omap_dss_device *dssdev,
return 0;
}
static void dpi_get_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
*timings = dssdev->panel.timings;
}
static int dpi_display_set_update_mode(struct omap_dss_device *dssdev,
enum omap_dss_update_mode mode)
{
if (mode == OMAP_DSS_UPDATE_MANUAL)
return -EINVAL;
if (mode == OMAP_DSS_UPDATE_DISABLED) {
dispc_enable_lcd_out(0);
dpi.update_enabled = 0;
} else {
dispc_enable_lcd_out(1);
dpi.update_enabled = 1;
}
return 0;
}
static enum omap_dss_update_mode dpi_display_get_update_mode(
struct omap_dss_device *dssdev)
{
return dpi.update_enabled ? OMAP_DSS_UPDATE_AUTO :
OMAP_DSS_UPDATE_DISABLED;
}
EXPORT_SYMBOL(dpi_check_timings);
int dpi_init_display(struct omap_dss_device *dssdev)
{
DSSDBG("init_display\n");
dssdev->enable = dpi_display_enable;
dssdev->disable = dpi_display_disable;
dssdev->suspend = dpi_display_suspend;
dssdev->resume = dpi_display_resume;
dssdev->set_timings = dpi_set_timings;
dssdev->check_timings = dpi_check_timings;
dssdev->get_timings = dpi_get_timings;
dssdev->set_update_mode = dpi_display_set_update_mode;
dssdev->get_update_mode = dpi_display_get_update_mode;
return 0;
}
int dpi_init(void)
int dpi_init(struct platform_device *pdev)
{
if (cpu_is_omap34xx()) {
dpi.vdds_dsi_reg = dss_get_vdds_dsi();
if (IS_ERR(dpi.vdds_dsi_reg)) {
DSSERR("can't get VDDS_DSI regulator\n");
return PTR_ERR(dpi.vdds_dsi_reg);
}
}
return 0;
}
......
......@@ -27,11 +27,12 @@
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/semaphore.h>
#include <linux/seq_file.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/kthread.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
#include <plat/display.h>
#include <plat/clock.h>
......@@ -199,7 +200,6 @@ enum dsi_vc_mode {
};
struct dsi_update_region {
bool dirty;
u16 x, y, w, h;
struct omap_dss_device *device;
};
......@@ -224,29 +224,25 @@ static struct
enum dsi_vc_mode mode;
struct omap_dss_device *dssdev;
enum fifo_size fifo_size;
int dest_per; /* destination peripheral 0-3 */
} vc[4];
struct mutex lock;
struct mutex bus_lock;
struct semaphore bus_lock;
unsigned pll_locked;
struct completion bta_completion;
struct task_struct *thread;
wait_queue_head_t waitqueue;
spinlock_t update_lock;
bool framedone_received;
int update_channel;
struct dsi_update_region update_region;
struct dsi_update_region active_update_region;
struct completion update_completion;
enum omap_dss_update_mode user_update_mode;
enum omap_dss_update_mode update_mode;
bool te_enabled;
bool use_ext_te;
struct work_struct framedone_work;
void (*framedone_callback)(int, void *);
void *framedone_data;
struct delayed_work framedone_timeout_work;
#ifdef DSI_CATCH_MISSING_TE
struct timer_list te_timer;
......@@ -261,8 +257,6 @@ static struct
#ifdef DEBUG
ktime_t perf_setup_time;
ktime_t perf_start_time;
ktime_t perf_start_time_auto;
int perf_measure_frames;
#endif
int debug_read;
int debug_write;
......@@ -299,16 +293,21 @@ void dsi_restore_context(void)
void dsi_bus_lock(void)
{
mutex_lock(&dsi.bus_lock);
down(&dsi.bus_lock);
}
EXPORT_SYMBOL(dsi_bus_lock);
void dsi_bus_unlock(void)
{
mutex_unlock(&dsi.bus_lock);
up(&dsi.bus_lock);
}
EXPORT_SYMBOL(dsi_bus_unlock);
static bool dsi_bus_is_locked(void)
{
return dsi.bus_lock.count == 0;
}
static inline int wait_for_bit_change(const struct dsi_reg idx, int bitnum,
int value)
{
......@@ -333,12 +332,6 @@ static void dsi_perf_mark_start(void)
dsi.perf_start_time = ktime_get();
}
static void dsi_perf_mark_start_auto(void)
{
dsi.perf_measure_frames = 0;
dsi.perf_start_time_auto = ktime_get();
}
static void dsi_perf_show(const char *name)
{
ktime_t t, setup_time, trans_time;
......@@ -348,9 +341,6 @@ static void dsi_perf_show(const char *name)
if (!dsi_perf)
return;
if (dsi.update_mode == OMAP_DSS_UPDATE_DISABLED)
return;
t = ktime_get();
setup_time = ktime_sub(dsi.perf_start_time, dsi.perf_setup_time);
......@@ -365,76 +355,23 @@ static void dsi_perf_show(const char *name)
total_us = setup_us + trans_us;
total_bytes = dsi.active_update_region.w *
dsi.active_update_region.h *
dsi.active_update_region.device->ctrl.pixel_size / 8;
if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO) {
static u32 s_total_trans_us, s_total_setup_us;
static u32 s_min_trans_us = 0xffffffff, s_min_setup_us;
static u32 s_max_trans_us, s_max_setup_us;
const int numframes = 100;
ktime_t total_time_auto;
u32 total_time_auto_us;
dsi.perf_measure_frames++;
if (setup_us < s_min_setup_us)
s_min_setup_us = setup_us;
total_bytes = dsi.update_region.w *
dsi.update_region.h *
dsi.update_region.device->ctrl.pixel_size / 8;
if (setup_us > s_max_setup_us)
s_max_setup_us = setup_us;
s_total_setup_us += setup_us;
if (trans_us < s_min_trans_us)
s_min_trans_us = trans_us;
if (trans_us > s_max_trans_us)
s_max_trans_us = trans_us;
s_total_trans_us += trans_us;
if (dsi.perf_measure_frames < numframes)
return;
total_time_auto = ktime_sub(t, dsi.perf_start_time_auto);
total_time_auto_us = (u32)ktime_to_us(total_time_auto);
printk(KERN_INFO "DSI(%s): %u fps, setup %u/%u/%u, "
"trans %u/%u/%u\n",
name,
1000 * 1000 * numframes / total_time_auto_us,
s_min_setup_us,
s_max_setup_us,
s_total_setup_us / numframes,
s_min_trans_us,
s_max_trans_us,
s_total_trans_us / numframes);
s_total_setup_us = 0;
s_min_setup_us = 0xffffffff;
s_max_setup_us = 0;
s_total_trans_us = 0;
s_min_trans_us = 0xffffffff;
s_max_trans_us = 0;
dsi_perf_mark_start_auto();
} else {
printk(KERN_INFO "DSI(%s): %u us + %u us = %u us (%uHz), "
"%u bytes, %u kbytes/sec\n",
name,
setup_us,
trans_us,
total_us,
1000*1000 / total_us,
total_bytes,
total_bytes * 1000 / total_us);
}
printk(KERN_INFO "DSI(%s): %u us + %u us = %u us (%uHz), "
"%u bytes, %u kbytes/sec\n",
name,
setup_us,
trans_us,
total_us,
1000*1000 / total_us,
total_bytes,
total_bytes * 1000 / total_us);
}
#else
#define dsi_perf_mark_setup()
#define dsi_perf_mark_start()
#define dsi_perf_mark_start_auto()
#define dsi_perf_show(x)
#endif
......@@ -774,7 +711,7 @@ static unsigned long dsi_fclk_rate(void)
{
unsigned long r;
if (dss_get_dsi_clk_source() == 0) {
if (dss_get_dsi_clk_source() == DSS_SRC_DSS1_ALWON_FCLK) {
/* DSI FCLK source is DSS1_ALWON_FCK, which is dss1_fck */
r = dss_clk_get_rate(DSS_CLK_FCK1);
} else {
......@@ -1227,17 +1164,19 @@ void dsi_dump_clocks(struct seq_file *s)
seq_printf(s, "dsi1_pll_fck\t%-16luregm3 %u\t(%s)\n",
cinfo->dsi1_pll_fclk,
cinfo->regm3,
dss_get_dispc_clk_source() == 0 ? "off" : "on");
dss_get_dispc_clk_source() == DSS_SRC_DSS1_ALWON_FCLK ?
"off" : "on");
seq_printf(s, "dsi2_pll_fck\t%-16luregm4 %u\t(%s)\n",
cinfo->dsi2_pll_fclk,
cinfo->regm4,
dss_get_dsi_clk_source() == 0 ? "off" : "on");
dss_get_dsi_clk_source() == DSS_SRC_DSS1_ALWON_FCLK ?
"off" : "on");
seq_printf(s, "- DSI -\n");
seq_printf(s, "dsi fclk source = %s\n",
dss_get_dsi_clk_source() == 0 ?
dss_get_dsi_clk_source() == DSS_SRC_DSS1_ALWON_FCLK ?
"dss1_alwon_fclk" : "dsi2_pll_fclk");
seq_printf(s, "DSI_FCLK\t%lu\n", dsi_fclk_rate());
......@@ -1756,29 +1695,10 @@ static int dsi_force_tx_stop_mode_io(void)
return 0;
}
static void dsi_vc_print_status(int channel)
{
u32 r;
r = dsi_read_reg(DSI_VC_CTRL(channel));
DSSDBG("vc %d: TX_FIFO_NOT_EMPTY %d, BTA_EN %d, VC_BUSY %d, "
"TX_FIFO_FULL %d, RX_FIFO_NOT_EMPTY %d, ",
channel,
FLD_GET(r, 5, 5),
FLD_GET(r, 6, 6),
FLD_GET(r, 15, 15),
FLD_GET(r, 16, 16),
FLD_GET(r, 20, 20));
r = dsi_read_reg(DSI_TX_FIFO_VC_EMPTINESS);
DSSDBG("EMPTINESS %d\n", (r >> (8 * channel)) & 0xff);
}
static int dsi_vc_enable(int channel, bool enable)
{
if (dsi.update_mode != OMAP_DSS_UPDATE_AUTO)
DSSDBG("dsi_vc_enable channel %d, enable %d\n",
channel, enable);
DSSDBG("dsi_vc_enable channel %d, enable %d\n",
channel, enable);
enable = enable ? 1 : 0;
......@@ -1859,10 +1779,12 @@ static void dsi_vc_config_vp(int channel)
}
static void dsi_vc_enable_hs(int channel, bool enable)
void omapdss_dsi_vc_enable_hs(int channel, bool enable)
{
DSSDBG("dsi_vc_enable_hs(%d, %d)\n", channel, enable);
WARN_ON(!dsi_bus_is_locked());
dsi_vc_enable(channel, 0);
dsi_if_enable(0);
......@@ -1873,6 +1795,7 @@ static void dsi_vc_enable_hs(int channel, bool enable)
dsi_force_tx_stop_mode_io();
}
EXPORT_SYMBOL(omapdss_dsi_vc_enable_hs);
static void dsi_vc_flush_long_data(int channel)
{
......@@ -1955,11 +1878,10 @@ static u16 dsi_vc_flush_receive_data(int channel)
static int dsi_vc_send_bta(int channel)
{
if (dsi.update_mode != OMAP_DSS_UPDATE_AUTO &&
(dsi.debug_write || dsi.debug_read))
if (dsi.debug_write || dsi.debug_read)
DSSDBG("dsi_vc_send_bta %d\n", channel);
WARN_ON(!mutex_is_locked(&dsi.bus_lock));
WARN_ON(!dsi_bus_is_locked());
if (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { /* RX_FIFO_NOT_EMPTY */
DSSERR("rx fifo not empty when sending BTA, dumping data:\n");
......@@ -2010,10 +1932,9 @@ static inline void dsi_vc_write_long_header(int channel, u8 data_type,
u32 val;
u8 data_id;
WARN_ON(!mutex_is_locked(&dsi.bus_lock));
WARN_ON(!dsi_bus_is_locked());
/*data_id = data_type | channel << 6; */
data_id = data_type | dsi.vc[channel].dest_per << 6;
data_id = data_type | channel << 6;
val = FLD_VAL(data_id, 7, 0) | FLD_VAL(len, 23, 8) |
FLD_VAL(ecc, 31, 24);
......@@ -2056,13 +1977,10 @@ static int dsi_vc_send_long(int channel, u8 data_type, u8 *data, u16 len,
dsi_vc_write_long_header(channel, data_type, len, ecc);
/*dsi_vc_print_status(0); */
p = data;
for (i = 0; i < len >> 2; i++) {
if (dsi.debug_write)
DSSDBG("\tsending full packet %d\n", i);
/*dsi_vc_print_status(0); */
b1 = *p++;
b2 = *p++;
......@@ -2105,7 +2023,7 @@ static int dsi_vc_send_short(int channel, u8 data_type, u16 data, u8 ecc)
u32 r;
u8 data_id;
WARN_ON(!mutex_is_locked(&dsi.bus_lock));
WARN_ON(!dsi_bus_is_locked());
if (dsi.debug_write)
DSSDBG("dsi_vc_send_short(ch%d, dt %#x, b1 %#x, b2 %#x)\n",
......@@ -2119,7 +2037,7 @@ static int dsi_vc_send_short(int channel, u8 data_type, u16 data, u8 ecc)
return -EINVAL;
}
data_id = data_type | dsi.vc[channel].dest_per << 6;
data_id = data_type | channel << 6;
r = (data_id << 0) | (data << 8) | (ecc << 24);
......@@ -2163,14 +2081,35 @@ int dsi_vc_dcs_write(int channel, u8 *data, int len)
r = dsi_vc_dcs_write_nosync(channel, data, len);
if (r)
return r;
goto err;
r = dsi_vc_send_bta_sync(channel);
if (r)
goto err;
return 0;
err:
DSSERR("dsi_vc_dcs_write(ch %d, cmd 0x%02x, len %d) failed\n",
channel, data[0], len);
return r;
}
EXPORT_SYMBOL(dsi_vc_dcs_write);
int dsi_vc_dcs_write_0(int channel, u8 dcs_cmd)
{
return dsi_vc_dcs_write(channel, &dcs_cmd, 1);
}
EXPORT_SYMBOL(dsi_vc_dcs_write_0);
int dsi_vc_dcs_write_1(int channel, u8 dcs_cmd, u8 param)
{
u8 buf[2];
buf[0] = dcs_cmd;
buf[1] = param;
return dsi_vc_dcs_write(channel, buf, 2);
}
EXPORT_SYMBOL(dsi_vc_dcs_write_1);
int dsi_vc_dcs_read(int channel, u8 dcs_cmd, u8 *buf, int buflen)
{
u32 val;
......@@ -2182,16 +2121,17 @@ int dsi_vc_dcs_read(int channel, u8 dcs_cmd, u8 *buf, int buflen)
r = dsi_vc_send_short(channel, DSI_DT_DCS_READ, dcs_cmd, 0);
if (r)
return r;
goto err;
r = dsi_vc_send_bta_sync(channel);
if (r)
return r;
goto err;
/* RX_FIFO_NOT_EMPTY */
if (REG_GET(DSI_VC_CTRL(channel), 20, 20) == 0) {
DSSERR("RX fifo empty when trying to read.\n");
return -EIO;
r = -EIO;
goto err;
}
val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel));
......@@ -2201,15 +2141,18 @@ int dsi_vc_dcs_read(int channel, u8 dcs_cmd, u8 *buf, int buflen)
if (dt == DSI_DT_RX_ACK_WITH_ERR) {
u16 err = FLD_GET(val, 23, 8);
dsi_show_rx_ack_with_err(err);
return -EIO;
r = -EIO;
goto err;
} else if (dt == DSI_DT_RX_SHORT_READ_1) {
u8 data = FLD_GET(val, 15, 8);
if (dsi.debug_read)
DSSDBG("\tDCS short response, 1 byte: %02x\n", data);
if (buflen < 1)
return -EIO;
if (buflen < 1) {
r = -EIO;
goto err;
}
buf[0] = data;
......@@ -2219,8 +2162,10 @@ int dsi_vc_dcs_read(int channel, u8 dcs_cmd, u8 *buf, int buflen)
if (dsi.debug_read)
DSSDBG("\tDCS short response, 2 byte: %04x\n", data);
if (buflen < 2)
return -EIO;
if (buflen < 2) {
r = -EIO;
goto err;
}
buf[0] = data & 0xff;
buf[1] = (data >> 8) & 0xff;
......@@ -2232,8 +2177,10 @@ int dsi_vc_dcs_read(int channel, u8 dcs_cmd, u8 *buf, int buflen)
if (dsi.debug_read)
DSSDBG("\tDCS long response, len %d\n", len);
if (len > buflen)
return -EIO;
if (len > buflen) {
r = -EIO;
goto err;
}
/* two byte checksum ends the packet, not included in len */
for (w = 0; w < len + 2;) {
......@@ -2255,14 +2202,52 @@ int dsi_vc_dcs_read(int channel, u8 dcs_cmd, u8 *buf, int buflen)
}
return len;
} else {
DSSERR("\tunknown datatype 0x%02x\n", dt);
return -EIO;
r = -EIO;
goto err;
}
BUG();
err:
DSSERR("dsi_vc_dcs_read(ch %d, cmd 0x%02x) failed\n",
channel, dcs_cmd);
return r;
}
EXPORT_SYMBOL(dsi_vc_dcs_read);
int dsi_vc_dcs_read_1(int channel, u8 dcs_cmd, u8 *data)
{
int r;
r = dsi_vc_dcs_read(channel, dcs_cmd, data, 1);
if (r < 0)
return r;
if (r != 1)
return -EIO;
return 0;
}
EXPORT_SYMBOL(dsi_vc_dcs_read_1);
int dsi_vc_dcs_read_2(int channel, u8 dcs_cmd, u16 *data)
{
int r;
r = dsi_vc_dcs_read(channel, dcs_cmd, (u8 *)data, 2);
if (r < 0)
return r;
if (r != 2)
return -EIO;
return 0;
}
EXPORT_SYMBOL(dsi_vc_dcs_read_2);
int dsi_vc_set_max_rx_packet_size(int channel, u16 len)
{
......@@ -2491,15 +2476,15 @@ static int dsi_proto_config(struct omap_dss_device *dssdev)
u32 r;
int buswidth = 0;
dsi_config_tx_fifo(DSI_FIFO_SIZE_128,
DSI_FIFO_SIZE_0,
DSI_FIFO_SIZE_0,
DSI_FIFO_SIZE_0);
dsi_config_tx_fifo(DSI_FIFO_SIZE_32,
DSI_FIFO_SIZE_32,
DSI_FIFO_SIZE_32,
DSI_FIFO_SIZE_32);
dsi_config_rx_fifo(DSI_FIFO_SIZE_128,
DSI_FIFO_SIZE_0,
DSI_FIFO_SIZE_0,
DSI_FIFO_SIZE_0);
dsi_config_rx_fifo(DSI_FIFO_SIZE_32,
DSI_FIFO_SIZE_32,
DSI_FIFO_SIZE_32,
DSI_FIFO_SIZE_32);
/* XXX what values for the timeouts? */
dsi_set_stop_state_counter(1000);
......@@ -2537,12 +2522,9 @@ static int dsi_proto_config(struct omap_dss_device *dssdev)
dsi_write_reg(DSI_CTRL, r);
dsi_vc_initial_config(0);
/* set all vc targets to peripheral 0 */
dsi.vc[0].dest_per = 0;
dsi.vc[1].dest_per = 0;
dsi.vc[2].dest_per = 0;
dsi.vc[3].dest_per = 0;
dsi_vc_initial_config(1);
dsi_vc_initial_config(2);
dsi_vc_initial_config(3);
return 0;
}
......@@ -2777,18 +2759,16 @@ static void dsi_update_screen_dispc(struct omap_dss_device *dssdev,
unsigned packet_payload;
unsigned packet_len;
u32 l;
bool use_te_trigger;
const unsigned channel = 0;
const unsigned channel = dsi.update_channel;
/* line buffer is 1024 x 24bits */
/* XXX: for some reason using full buffer size causes considerable TX
* slowdown with update sizes that fill the whole buffer */
const unsigned line_buf_size = 1023 * 3;
use_te_trigger = dsi.te_enabled && !dsi.use_ext_te;
DSSDBG("dsi_update_screen_dispc(%d,%d %dx%d)\n",
x, y, w, h);
if (dsi.update_mode != OMAP_DSS_UPDATE_AUTO)
DSSDBG("dsi_update_screen_dispc(%d,%d %dx%d)\n",
x, y, w, h);
dsi_vc_config_vp(channel);
bytespp = dssdev->ctrl.pixel_size / 8;
bytespl = w * bytespp;
......@@ -2808,15 +2788,12 @@ static void dsi_update_screen_dispc(struct omap_dss_device *dssdev,
if (bytespf % packet_payload)
total_len += (bytespf % packet_payload) + 1;
if (0)
dsi_vc_print_status(1);
l = FLD_VAL(total_len, 23, 0); /* TE_SIZE */
dsi_write_reg(DSI_VC_TE(channel), l);
dsi_vc_write_long_header(channel, DSI_DT_DCS_LONG_WRITE, packet_len, 0);
if (use_te_trigger)
if (dsi.te_enabled)
l = FLD_MOD(l, 1, 30, 30); /* TE_EN */
else
l = FLD_MOD(l, 1, 31, 31); /* TE_START */
......@@ -2830,9 +2807,14 @@ static void dsi_update_screen_dispc(struct omap_dss_device *dssdev,
*/
dispc_disable_sidle();
dsi_perf_mark_start();
schedule_delayed_work(&dsi.framedone_timeout_work,
msecs_to_jiffies(250));
dss_start_update(dssdev);
if (use_te_trigger) {
if (dsi.te_enabled) {
/* disable LP_RX_TO, so that we can receive TE. Time to wait
* for TE is longer than the timer allows */
REG_FLD_MOD(DSI_TIMING2, 0, 15, 15); /* LP_RX_TO */
......@@ -2852,110 +2834,64 @@ static void dsi_te_timeout(unsigned long arg)
}
#endif
static void dsi_framedone_irq_callback(void *data, u32 mask)
static void dsi_framedone_timeout_work_callback(struct work_struct *work)
{
/* Note: We get FRAMEDONE when DISPC has finished sending pixels and
* turns itself off. However, DSI still has the pixels in its buffers,
* and is sending the data.
*/
int r;
const int channel = dsi.update_channel;
DSSERR("Framedone not received for 250ms!\n");
/* SIDLEMODE back to smart-idle */
dispc_enable_sidle();
dsi.framedone_received = true;
wake_up(&dsi.waitqueue);
}
static void dsi_set_update_region(struct omap_dss_device *dssdev,
u16 x, u16 y, u16 w, u16 h)
{
spin_lock(&dsi.update_lock);
if (dsi.update_region.dirty) {
dsi.update_region.x = min(x, dsi.update_region.x);
dsi.update_region.y = min(y, dsi.update_region.y);
dsi.update_region.w = max(w, dsi.update_region.w);
dsi.update_region.h = max(h, dsi.update_region.h);
} else {
dsi.update_region.x = x;
dsi.update_region.y = y;
dsi.update_region.w = w;
dsi.update_region.h = h;
if (dsi.te_enabled) {
/* enable LP_RX_TO again after the TE */
REG_FLD_MOD(DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */
}
dsi.update_region.device = dssdev;
dsi.update_region.dirty = true;
spin_unlock(&dsi.update_lock);
}
static int dsi_set_update_mode(struct omap_dss_device *dssdev,
enum omap_dss_update_mode mode)
{
int r = 0;
int i;
WARN_ON(!mutex_is_locked(&dsi.bus_lock));
if (dsi.update_mode != mode) {
dsi.update_mode = mode;
/* Mark the overlays dirty, and do apply(), so that we get the
* overlays configured properly after update mode change. */
for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
struct omap_overlay *ovl;
ovl = omap_dss_get_overlay(i);
if (ovl->manager == dssdev->manager)
ovl->info_dirty = true;
}
r = dssdev->manager->apply(dssdev->manager);
if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE &&
mode == OMAP_DSS_UPDATE_AUTO) {
u16 w, h;
DSSDBG("starting auto update\n");
dssdev->get_resolution(dssdev, &w, &h);
dsi_set_update_region(dssdev, 0, 0, w, h);
dsi_perf_mark_start_auto();
/* Send BTA after the frame. We need this for the TE to work, as TE
* trigger is only sent for BTAs without preceding packet. Thus we need
* to BTA after the pixel packets so that next BTA will cause TE
* trigger.
*
* This is not needed when TE is not in use, but we do it anyway to
* make sure that the transfer has been completed. It would be more
* optimal, but more complex, to wait only just before starting next
* transfer. */
r = dsi_vc_send_bta_sync(channel);
if (r)
DSSERR("BTA after framedone failed\n");
wake_up(&dsi.waitqueue);
}
/* RX_FIFO_NOT_EMPTY */
if (REG_GET(DSI_VC_CTRL(channel), 20, 20)) {
DSSERR("Received error during frame transfer:\n");
dsi_vc_flush_receive_data(channel);
}
return r;
dsi.framedone_callback(-ETIMEDOUT, dsi.framedone_data);
}
static int dsi_set_te(struct omap_dss_device *dssdev, bool enable)
static void dsi_framedone_irq_callback(void *data, u32 mask)
{
int r = 0;
/* Note: We get FRAMEDONE when DISPC has finished sending pixels and
* turns itself off. However, DSI still has the pixels in its buffers,
* and is sending the data.
*/
if (dssdev->driver->enable_te) {
r = dssdev->driver->enable_te(dssdev, enable);
/* XXX for some reason, DSI TE breaks if we don't wait here.
* Panel bug? Needs more studying */
msleep(100);
}
/* SIDLEMODE back to smart-idle */
dispc_enable_sidle();
return r;
schedule_work(&dsi.framedone_work);
}
static void dsi_handle_framedone(void)
{
int r;
const int channel = 0;
bool use_te_trigger;
use_te_trigger = dsi.te_enabled && !dsi.use_ext_te;
const int channel = dsi.update_channel;
if (dsi.update_mode != OMAP_DSS_UPDATE_AUTO)
DSSDBG("FRAMEDONE\n");
DSSDBG("FRAMEDONE\n");
if (use_te_trigger) {
if (dsi.te_enabled) {
/* enable LP_RX_TO again after the TE */
REG_FLD_MOD(DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */
}
......@@ -2976,7 +2912,7 @@ static void dsi_handle_framedone(void)
/* RX_FIFO_NOT_EMPTY */
if (REG_GET(DSI_VC_CTRL(channel), 20, 20)) {
DSSERR("Received error during frame transfer:\n");
dsi_vc_flush_receive_data(0);
dsi_vc_flush_receive_data(channel);
}
#ifdef CONFIG_OMAP2_DSS_FAKE_VSYNC
......@@ -2984,118 +2920,79 @@ static void dsi_handle_framedone(void)
#endif
}
static int dsi_update_thread(void *data)
static void dsi_framedone_work_callback(struct work_struct *work)
{
unsigned long timeout;
struct omap_dss_device *device;
u16 x, y, w, h;
while (1) {
bool sched;
wait_event_interruptible(dsi.waitqueue,
dsi.update_mode == OMAP_DSS_UPDATE_AUTO ||
(dsi.update_mode == OMAP_DSS_UPDATE_MANUAL &&
dsi.update_region.dirty == true) ||
kthread_should_stop());
if (kthread_should_stop())
break;
dsi_bus_lock();
if (dsi.update_mode == OMAP_DSS_UPDATE_DISABLED ||
kthread_should_stop()) {
dsi_bus_unlock();
break;
}
dsi_perf_mark_setup();
if (dsi.update_region.dirty) {
spin_lock(&dsi.update_lock);
dsi.active_update_region = dsi.update_region;
dsi.update_region.dirty = false;
spin_unlock(&dsi.update_lock);
}
DSSDBGF();
device = dsi.active_update_region.device;
x = dsi.active_update_region.x;
y = dsi.active_update_region.y;
w = dsi.active_update_region.w;
h = dsi.active_update_region.h;
cancel_delayed_work_sync(&dsi.framedone_timeout_work);
if (device->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) {
dsi_handle_framedone();
if (dsi.update_mode == OMAP_DSS_UPDATE_MANUAL)
dss_setup_partial_planes(device,
&x, &y, &w, &h);
dsi_perf_show("DISPC");
dispc_set_lcd_size(w, h);
}
dsi.framedone_callback(0, dsi.framedone_data);
}
if (dsi.active_update_region.dirty) {
dsi.active_update_region.dirty = false;
/* XXX TODO we don't need to send the coords, if they
* are the same that are already programmed to the
* panel. That should speed up manual update a bit */
device->driver->setup_update(device, x, y, w, h);
}
int omap_dsi_prepare_update(struct omap_dss_device *dssdev,
u16 *x, u16 *y, u16 *w, u16 *h)
{
u16 dw, dh;
dsi_perf_mark_start();
dssdev->driver->get_resolution(dssdev, &dw, &dh);
if (device->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) {
dsi_vc_config_vp(0);
if (*x > dw || *y > dh)
return -EINVAL;
if (dsi.te_enabled && dsi.use_ext_te)
device->driver->wait_for_te(device);
if (*x + *w > dw)
return -EINVAL;
dsi.framedone_received = false;
if (*y + *h > dh)
return -EINVAL;
dsi_update_screen_dispc(device, x, y, w, h);
if (*w == 1)
return -EINVAL;
/* wait for framedone */
timeout = msecs_to_jiffies(1000);
wait_event_timeout(dsi.waitqueue,
dsi.framedone_received == true,
timeout);
if (*w == 0 || *h == 0)
return -EINVAL;
if (!dsi.framedone_received) {
DSSERR("framedone timeout\n");
DSSERR("failed update %d,%d %dx%d\n",
x, y, w, h);
dsi_perf_mark_setup();
dispc_enable_sidle();
dispc_enable_lcd_out(0);
if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) {
dss_setup_partial_planes(dssdev, x, y, w, h);
dispc_set_lcd_size(*w, *h);
}
dsi_reset_tx_fifo(0);
} else {
dsi_handle_framedone();
dsi_perf_show("DISPC");
}
} else {
dsi_update_screen_l4(device, x, y, w, h);
dsi_perf_show("L4");
}
return 0;
}
EXPORT_SYMBOL(omap_dsi_prepare_update);
sched = atomic_read(&dsi.bus_lock.count) < 0;
int omap_dsi_update(struct omap_dss_device *dssdev,
int channel,
u16 x, u16 y, u16 w, u16 h,
void (*callback)(int, void *), void *data)
{
dsi.update_channel = channel;
complete_all(&dsi.update_completion);
if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) {
dsi.framedone_callback = callback;
dsi.framedone_data = data;
dsi_bus_unlock();
dsi.update_region.x = x;
dsi.update_region.y = y;
dsi.update_region.w = w;
dsi.update_region.h = h;
dsi.update_region.device = dssdev;
/* XXX We need to give others chance to get the bus lock. Is
* there a better way for this? */
if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO && sched)
schedule_timeout_interruptible(1);
dsi_update_screen_dispc(dssdev, x, y, w, h);
} else {
dsi_update_screen_l4(dssdev, x, y, w, h);
dsi_perf_show("L4");
callback(0, data);
}
DSSDBG("update thread exiting\n");
return 0;
}
EXPORT_SYMBOL(omap_dsi_update);
/* Display funcs */
......@@ -3203,7 +3100,8 @@ static int dsi_display_init_dsi(struct omap_dss_device *dssdev)
if (r)
goto err1;
dss_select_clk_source(true, true);
dss_select_dispc_clk_source(DSS_SRC_DSI1_PLL_FCLK);
dss_select_dsi_clk_source(DSS_SRC_DSI2_PLL_FCLK);
DSSDBG("PLL OK\n");
......@@ -3229,25 +3127,18 @@ static int dsi_display_init_dsi(struct omap_dss_device *dssdev)
/* enable interface */
dsi_vc_enable(0, 1);
dsi_vc_enable(1, 1);
dsi_vc_enable(2, 1);
dsi_vc_enable(3, 1);
dsi_if_enable(1);
dsi_force_tx_stop_mode_io();
if (dssdev->driver->enable) {
r = dssdev->driver->enable(dssdev);
if (r)
goto err4;
}
/* enable high-speed after initial config */
dsi_vc_enable_hs(0, 1);
return 0;
err4:
dsi_if_enable(0);
err3:
dsi_complexio_uninit();
err2:
dss_select_clk_source(false, false);
dss_select_dispc_clk_source(DSS_SRC_DSS1_ALWON_FCLK);
dss_select_dsi_clk_source(DSS_SRC_DSS1_ALWON_FCLK);
err1:
dsi_pll_uninit();
err0:
......@@ -3256,10 +3147,8 @@ static int dsi_display_init_dsi(struct omap_dss_device *dssdev)
static void dsi_display_uninit_dsi(struct omap_dss_device *dssdev)
{
if (dssdev->driver->disable)
dssdev->driver->disable(dssdev);
dss_select_clk_source(false, false);
dss_select_dispc_clk_source(DSS_SRC_DSS1_ALWON_FCLK);
dss_select_dsi_clk_source(DSS_SRC_DSS1_ALWON_FCLK);
dsi_complexio_uninit();
dsi_pll_uninit();
}
......@@ -3280,14 +3169,15 @@ static int dsi_core_init(void)
return 0;
}
static int dsi_display_enable(struct omap_dss_device *dssdev)
int omapdss_dsi_display_enable(struct omap_dss_device *dssdev)
{
int r = 0;
DSSDBG("dsi_display_enable\n");
WARN_ON(!dsi_bus_is_locked());
mutex_lock(&dsi.lock);
dsi_bus_lock();
r = omap_dss_start_device(dssdev);
if (r) {
......@@ -3295,100 +3185,47 @@ static int dsi_display_enable(struct omap_dss_device *dssdev)
goto err0;
}
if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {
DSSERR("dssdev already enabled\n");
r = -EINVAL;
goto err1;
}
enable_clocks(1);
dsi_enable_pll_clock(1);
r = _dsi_reset();
if (r)
goto err2;
goto err1;
dsi_core_init();
r = dsi_display_init_dispc(dssdev);
if (r)
goto err2;
goto err1;
r = dsi_display_init_dsi(dssdev);
if (r)
goto err3;
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
dsi.use_ext_te = dssdev->phy.dsi.ext_te;
r = dsi_set_te(dssdev, dsi.te_enabled);
if (r)
goto err4;
dsi_set_update_mode(dssdev, dsi.user_update_mode);
goto err2;
dsi_bus_unlock();
mutex_unlock(&dsi.lock);
return 0;
err4:
dsi_display_uninit_dsi(dssdev);
err3:
dsi_display_uninit_dispc(dssdev);
err2:
dsi_display_uninit_dispc(dssdev);
err1:
enable_clocks(0);
dsi_enable_pll_clock(0);
err1:
omap_dss_stop_device(dssdev);
err0:
dsi_bus_unlock();
mutex_unlock(&dsi.lock);
DSSDBG("dsi_display_enable FAILED\n");
return r;
}
EXPORT_SYMBOL(omapdss_dsi_display_enable);
static void dsi_display_disable(struct omap_dss_device *dssdev)
void omapdss_dsi_display_disable(struct omap_dss_device *dssdev)
{
DSSDBG("dsi_display_disable\n");
mutex_lock(&dsi.lock);
dsi_bus_lock();
if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED ||
dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED)
goto end;
dsi.update_mode = OMAP_DSS_UPDATE_DISABLED;
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
dsi_display_uninit_dispc(dssdev);
dsi_display_uninit_dsi(dssdev);
enable_clocks(0);
dsi_enable_pll_clock(0);
omap_dss_stop_device(dssdev);
end:
dsi_bus_unlock();
mutex_unlock(&dsi.lock);
}
static int dsi_display_suspend(struct omap_dss_device *dssdev)
{
DSSDBG("dsi_display_suspend\n");
WARN_ON(!dsi_bus_is_locked());
mutex_lock(&dsi.lock);
dsi_bus_lock();
if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED ||
dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED)
goto end;
dsi.update_mode = OMAP_DSS_UPDATE_DISABLED;
dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
dsi_display_uninit_dispc(dssdev);
......@@ -3396,312 +3233,19 @@ static int dsi_display_suspend(struct omap_dss_device *dssdev)
enable_clocks(0);
dsi_enable_pll_clock(0);
end:
dsi_bus_unlock();
mutex_unlock(&dsi.lock);
return 0;
}
static int dsi_display_resume(struct omap_dss_device *dssdev)
{
int r;
DSSDBG("dsi_display_resume\n");
mutex_lock(&dsi.lock);
dsi_bus_lock();
if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) {
DSSERR("dssdev not suspended\n");
r = -EINVAL;
goto err0;
}
enable_clocks(1);
dsi_enable_pll_clock(1);
r = _dsi_reset();
if (r)
goto err1;
dsi_core_init();
r = dsi_display_init_dispc(dssdev);
if (r)
goto err1;
r = dsi_display_init_dsi(dssdev);
if (r)
goto err2;
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
r = dsi_set_te(dssdev, dsi.te_enabled);
if (r)
goto err2;
dsi_set_update_mode(dssdev, dsi.user_update_mode);
dsi_bus_unlock();
mutex_unlock(&dsi.lock);
return 0;
err2:
dsi_display_uninit_dispc(dssdev);
err1:
enable_clocks(0);
dsi_enable_pll_clock(0);
err0:
dsi_bus_unlock();
mutex_unlock(&dsi.lock);
DSSDBG("dsi_display_resume FAILED\n");
return r;
}
static int dsi_display_update(struct omap_dss_device *dssdev,
u16 x, u16 y, u16 w, u16 h)
{
int r = 0;
u16 dw, dh;
DSSDBG("dsi_display_update(%d,%d %dx%d)\n", x, y, w, h);
mutex_lock(&dsi.lock);
if (dsi.update_mode != OMAP_DSS_UPDATE_MANUAL)
goto end;
if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
goto end;
dssdev->get_resolution(dssdev, &dw, &dh);
if (x > dw || y > dh)
goto end;
if (x + w > dw)
w = dw - x;
if (y + h > dh)
h = dh - y;
if (w == 0 || h == 0)
goto end;
if (w == 1) {
r = -EINVAL;
goto end;
}
dsi_set_update_region(dssdev, x, y, w, h);
wake_up(&dsi.waitqueue);
end:
mutex_unlock(&dsi.lock);
return r;
}
static int dsi_display_sync(struct omap_dss_device *dssdev)
{
bool wait;
DSSDBG("dsi_display_sync()\n");
mutex_lock(&dsi.lock);
dsi_bus_lock();
if (dsi.update_mode == OMAP_DSS_UPDATE_MANUAL &&
dsi.update_region.dirty) {
INIT_COMPLETION(dsi.update_completion);
wait = true;
} else {
wait = false;
}
dsi_bus_unlock();
mutex_unlock(&dsi.lock);
if (wait)
wait_for_completion_interruptible(&dsi.update_completion);
DSSDBG("dsi_display_sync() done\n");
return 0;
}
static int dsi_display_set_update_mode(struct omap_dss_device *dssdev,
enum omap_dss_update_mode mode)
{
int r = 0;
DSSDBGF("%d", mode);
mutex_lock(&dsi.lock);
dsi_bus_lock();
dsi.user_update_mode = mode;
r = dsi_set_update_mode(dssdev, mode);
omap_dss_stop_device(dssdev);
dsi_bus_unlock();
mutex_unlock(&dsi.lock);
return r;
}
EXPORT_SYMBOL(omapdss_dsi_display_disable);
static enum omap_dss_update_mode dsi_display_get_update_mode(
struct omap_dss_device *dssdev)
int omapdss_dsi_enable_te(struct omap_dss_device *dssdev, bool enable)
{
return dsi.update_mode;
}
static int dsi_display_enable_te(struct omap_dss_device *dssdev, bool enable)
{
int r = 0;
DSSDBGF("%d", enable);
if (!dssdev->driver->enable_te)
return -ENOENT;
dsi_bus_lock();
dsi.te_enabled = enable;
if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
goto end;
r = dsi_set_te(dssdev, enable);
end:
dsi_bus_unlock();
return r;
}
static int dsi_display_get_te(struct omap_dss_device *dssdev)
{
return dsi.te_enabled;
}
static int dsi_display_set_rotate(struct omap_dss_device *dssdev, u8 rotate)
{
DSSDBGF("%d", rotate);
if (!dssdev->driver->set_rotate || !dssdev->driver->get_rotate)
return -EINVAL;
dsi_bus_lock();
dssdev->driver->set_rotate(dssdev, rotate);
if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO) {
u16 w, h;
/* the display dimensions may have changed, so set a new
* update region */
dssdev->get_resolution(dssdev, &w, &h);
dsi_set_update_region(dssdev, 0, 0, w, h);
}
dsi_bus_unlock();
return 0;
}
static u8 dsi_display_get_rotate(struct omap_dss_device *dssdev)
{
if (!dssdev->driver->set_rotate || !dssdev->driver->get_rotate)
return 0;
return dssdev->driver->get_rotate(dssdev);
}
static int dsi_display_set_mirror(struct omap_dss_device *dssdev, bool mirror)
{
DSSDBGF("%d", mirror);
if (!dssdev->driver->set_mirror || !dssdev->driver->get_mirror)
return -EINVAL;
dsi_bus_lock();
dssdev->driver->set_mirror(dssdev, mirror);
dsi_bus_unlock();
return 0;
}
static bool dsi_display_get_mirror(struct omap_dss_device *dssdev)
{
if (!dssdev->driver->set_mirror || !dssdev->driver->get_mirror)
return 0;
return dssdev->driver->get_mirror(dssdev);
}
static int dsi_display_run_test(struct omap_dss_device *dssdev, int test_num)
{
int r;
if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
return -EIO;
DSSDBGF("%d", test_num);
dsi_bus_lock();
/* run test first in low speed mode */
dsi_vc_enable_hs(0, 0);
if (dssdev->driver->run_test) {
r = dssdev->driver->run_test(dssdev, test_num);
if (r)
goto end;
}
/* then in high speed */
dsi_vc_enable_hs(0, 1);
if (dssdev->driver->run_test) {
r = dssdev->driver->run_test(dssdev, test_num);
if (r)
goto end;
}
end:
dsi_vc_enable_hs(0, 1);
dsi_bus_unlock();
return r;
}
static int dsi_display_memory_read(struct omap_dss_device *dssdev,
void *buf, size_t size,
u16 x, u16 y, u16 w, u16 h)
{
int r;
DSSDBGF("");
if (!dssdev->driver->memory_read)
return -EINVAL;
if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
return -EIO;
dsi_bus_lock();
r = dssdev->driver->memory_read(dssdev, buf, size,
x, y, w, h);
/* Memory read usually changes the update area. This will
* force the next update to re-set the update area */
dsi.active_update_region.dirty = true;
dsi_bus_unlock();
return r;
}
EXPORT_SYMBOL(omapdss_dsi_enable_te);
void dsi_get_overlay_fifo_thresholds(enum omap_plane plane,
u32 fifo_size, enum omap_burst_size *burst_size,
......@@ -3720,26 +3264,6 @@ int dsi_init_display(struct omap_dss_device *dssdev)
{
DSSDBG("DSI init\n");
dssdev->enable = dsi_display_enable;
dssdev->disable = dsi_display_disable;
dssdev->suspend = dsi_display_suspend;
dssdev->resume = dsi_display_resume;
dssdev->update = dsi_display_update;
dssdev->sync = dsi_display_sync;
dssdev->set_update_mode = dsi_display_set_update_mode;
dssdev->get_update_mode = dsi_display_get_update_mode;
dssdev->enable_te = dsi_display_enable_te;
dssdev->get_te = dsi_display_get_te;
dssdev->get_rotate = dsi_display_get_rotate;
dssdev->set_rotate = dsi_display_set_rotate;
dssdev->get_mirror = dsi_display_get_mirror;
dssdev->set_mirror = dsi_display_set_mirror;
dssdev->run_test = dsi_display_run_test;
dssdev->memory_read = dsi_display_memory_read;
/* XXX these should be figured out dynamically */
dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE |
OMAP_DSS_DISPLAY_CAP_TEAR_ELIM;
......@@ -3754,9 +3278,6 @@ int dsi_init(struct platform_device *pdev)
{
u32 rev;
int r;
struct sched_param param = {
.sched_priority = MAX_USER_RT_PRIO-1
};
spin_lock_init(&dsi.errors_lock);
dsi.errors = 0;
......@@ -3767,31 +3288,19 @@ int dsi_init(struct platform_device *pdev)
#endif
init_completion(&dsi.bta_completion);
init_completion(&dsi.update_completion);
dsi.thread = kthread_create(dsi_update_thread, NULL, "dsi");
if (IS_ERR(dsi.thread)) {
DSSERR("cannot create kthread\n");
r = PTR_ERR(dsi.thread);
goto err0;
}
sched_setscheduler(dsi.thread, SCHED_FIFO, &param);
init_waitqueue_head(&dsi.waitqueue);
spin_lock_init(&dsi.update_lock);
mutex_init(&dsi.lock);
mutex_init(&dsi.bus_lock);
sema_init(&dsi.bus_lock, 1);
INIT_WORK(&dsi.framedone_work, dsi_framedone_work_callback);
INIT_DELAYED_WORK_DEFERRABLE(&dsi.framedone_timeout_work,
dsi_framedone_timeout_work_callback);
#ifdef DSI_CATCH_MISSING_TE
init_timer(&dsi.te_timer);
dsi.te_timer.function = dsi_te_timeout;
dsi.te_timer.data = 0;
#endif
dsi.update_mode = OMAP_DSS_UPDATE_DISABLED;
dsi.user_update_mode = OMAP_DSS_UPDATE_DISABLED;
dsi.base = ioremap(DSI_BASE, DSI_SZ_REGS);
if (!dsi.base) {
DSSERR("can't ioremap DSI\n");
......@@ -3799,7 +3308,7 @@ int dsi_init(struct platform_device *pdev)
goto err1;
}
dsi.vdds_dsi_reg = regulator_get(&pdev->dev, "vdds_dsi");
dsi.vdds_dsi_reg = dss_get_vdds_dsi();
if (IS_ERR(dsi.vdds_dsi_reg)) {
iounmap(dsi.base);
DSSERR("can't get VDDS_DSI regulator\n");
......@@ -3815,23 +3324,15 @@ int dsi_init(struct platform_device *pdev)
enable_clocks(0);
wake_up_process(dsi.thread);
return 0;
err2:
iounmap(dsi.base);
err1:
kthread_stop(dsi.thread);
err0:
return r;
}
void dsi_exit(void)
{
kthread_stop(dsi.thread);
regulator_put(dsi.vdds_dsi_reg);
iounmap(dsi.base);
DSSDBG("omap_dsi_exit\n");
......
......@@ -68,6 +68,9 @@ static struct {
struct dss_clock_info cache_dss_cinfo;
struct dispc_clock_info cache_dispc_cinfo;
enum dss_clk_source dsi_clk_source;
enum dss_clk_source dispc_clk_source;
u32 ctx[DSS_SZ_REGS / sizeof(u32)];
} dss;
......@@ -247,23 +250,42 @@ void dss_dump_regs(struct seq_file *s)
#undef DUMPREG
}
void dss_select_clk_source(bool dsi, bool dispc)
void dss_select_dispc_clk_source(enum dss_clk_source clk_src)
{
int b;
BUG_ON(clk_src != DSS_SRC_DSI1_PLL_FCLK &&
clk_src != DSS_SRC_DSS1_ALWON_FCLK);
b = clk_src == DSS_SRC_DSS1_ALWON_FCLK ? 0 : 1;
REG_FLD_MOD(DSS_CONTROL, b, 0, 0); /* DISPC_CLK_SWITCH */
dss.dispc_clk_source = clk_src;
}
void dss_select_dsi_clk_source(enum dss_clk_source clk_src)
{
u32 r;
r = dss_read_reg(DSS_CONTROL);
r = FLD_MOD(r, dsi, 1, 1); /* DSI_CLK_SWITCH */
r = FLD_MOD(r, dispc, 0, 0); /* DISPC_CLK_SWITCH */
dss_write_reg(DSS_CONTROL, r);
int b;
BUG_ON(clk_src != DSS_SRC_DSI2_PLL_FCLK &&
clk_src != DSS_SRC_DSS1_ALWON_FCLK);
b = clk_src == DSS_SRC_DSS1_ALWON_FCLK ? 0 : 1;
REG_FLD_MOD(DSS_CONTROL, b, 1, 1); /* DSI_CLK_SWITCH */
dss.dsi_clk_source = clk_src;
}
int dss_get_dsi_clk_source(void)
enum dss_clk_source dss_get_dispc_clk_source(void)
{
return FLD_GET(dss_read_reg(DSS_CONTROL), 1, 1);
return dss.dispc_clk_source;
}
int dss_get_dispc_clk_source(void)
enum dss_clk_source dss_get_dsi_clk_source(void)
{
return FLD_GET(dss_read_reg(DSS_CONTROL), 0, 0);
return dss.dsi_clk_source;
}
/* calculate clock rates using dividers in cinfo */
......
......@@ -119,6 +119,12 @@ enum dss_clock {
DSS_CLK_96M = 1 << 4,
};
enum dss_clk_source {
DSS_SRC_DSI1_PLL_FCLK,
DSS_SRC_DSI2_PLL_FCLK,
DSS_SRC_DSS1_ALWON_FCLK,
};
struct dss_clock_info {
/* rates that we get with dividers below */
unsigned long fck;
......@@ -169,6 +175,9 @@ unsigned long dss_clk_get_rate(enum dss_clock clk);
int dss_need_ctx_restore(void);
void dss_dump_clocks(struct seq_file *s);
struct bus_type *dss_get_bus(void);
struct regulator *dss_get_vdds_dsi(void);
struct regulator *dss_get_vdds_sdi(void);
struct regulator *dss_get_vdda_dac(void);
/* display */
int dss_suspend_all_devices(void);
......@@ -216,9 +225,11 @@ void dss_sdi_init(u8 datapairs);
int dss_sdi_enable(void);
void dss_sdi_disable(void);
void dss_select_clk_source(bool dsi, bool dispc);
int dss_get_dsi_clk_source(void);
int dss_get_dispc_clk_source(void);
void dss_select_dispc_clk_source(enum dss_clk_source clk_src);
void dss_select_dsi_clk_source(enum dss_clk_source clk_src);
enum dss_clk_source dss_get_dispc_clk_source(void);
enum dss_clk_source dss_get_dsi_clk_source(void);
void dss_set_venc_output(enum omap_dss_venc_type type);
void dss_set_dac_pwrdn_bgz(bool enable);
......@@ -261,7 +272,7 @@ void dsi_get_overlay_fifo_thresholds(enum omap_plane plane,
u32 *fifo_low, u32 *fifo_high);
/* DPI */
int dpi_init(void);
int dpi_init(struct platform_device *pdev);
void dpi_exit(void);
int dpi_init_display(struct omap_dss_device *dssdev);
......@@ -313,8 +324,8 @@ int dispc_setup_plane(enum omap_plane plane,
bool dispc_go_busy(enum omap_channel channel);
void dispc_go(enum omap_channel channel);
void dispc_enable_lcd_out(bool enable);
void dispc_enable_digit_out(bool enable);
void dispc_enable_channel(enum omap_channel channel, bool enable);
bool dispc_is_channel_enabled(enum omap_channel channel);
int dispc_enable_plane(enum omap_plane plane, bool enable);
void dispc_enable_replication(enum omap_plane plane, bool enable);
......
......@@ -501,6 +501,19 @@ static int omap_dss_unset_device(struct omap_overlay_manager *mgr)
return 0;
}
static int dss_mgr_wait_for_vsync(struct omap_overlay_manager *mgr)
{
unsigned long timeout = msecs_to_jiffies(500);
u32 irq;
if (mgr->device->type == OMAP_DISPLAY_TYPE_VENC)
irq = DISPC_IRQ_EVSYNC_ODD;
else
irq = DISPC_IRQ_VSYNC;
return omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
}
static int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr)
{
unsigned long timeout = msecs_to_jiffies(500);
......@@ -509,17 +522,18 @@ static int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr)
u32 irq;
int r;
int i;
struct omap_dss_device *dssdev = mgr->device;
if (!mgr->device)
if (!dssdev)
return 0;
if (mgr->device->type == OMAP_DISPLAY_TYPE_VENC) {
if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) {
irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN;
channel = OMAP_DSS_CHANNEL_DIGIT;
} else {
if (mgr->device->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) {
if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) {
enum omap_dss_update_mode mode;
mode = mgr->device->get_update_mode(mgr->device);
mode = dssdev->driver->get_update_mode(dssdev);
if (mode != OMAP_DSS_UPDATE_AUTO)
return 0;
......@@ -592,7 +606,7 @@ int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl)
} else {
if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) {
enum omap_dss_update_mode mode;
mode = dssdev->get_update_mode(dssdev);
mode = dssdev->driver->get_update_mode(dssdev);
if (mode != OMAP_DSS_UPDATE_AUTO)
return 0;
......@@ -1064,7 +1078,7 @@ void dss_start_update(struct omap_dss_device *dssdev)
mc->shadow_dirty = false;
}
dispc_enable_lcd_out(1);
dssdev->manager->enable(dssdev->manager);
}
static void dss_apply_irq_handler(void *data, u32 mask)
......@@ -1196,7 +1210,8 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
oc->manual_update =
dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE &&
dssdev->get_update_mode(dssdev) != OMAP_DSS_UPDATE_AUTO;
dssdev->driver->get_update_mode(dssdev) !=
OMAP_DSS_UPDATE_AUTO;
++num_planes_enabled;
}
......@@ -1237,7 +1252,8 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
mc->manual_update =
dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE &&
dssdev->get_update_mode(dssdev) != OMAP_DSS_UPDATE_AUTO;
dssdev->driver->get_update_mode(dssdev) !=
OMAP_DSS_UPDATE_AUTO;
}
/* XXX TODO: Try to get fifomerge working. The problem is that it
......@@ -1351,6 +1367,18 @@ static void omap_dss_mgr_get_info(struct omap_overlay_manager *mgr,
*info = mgr->info;
}
static int dss_mgr_enable(struct omap_overlay_manager *mgr)
{
dispc_enable_channel(mgr->id, 1);
return 0;
}
static int dss_mgr_disable(struct omap_overlay_manager *mgr)
{
dispc_enable_channel(mgr->id, 0);
return 0;
}
static void omap_dss_add_overlay_manager(struct omap_overlay_manager *manager)
{
++num_managers;
......@@ -1394,6 +1422,10 @@ int dss_init_overlay_managers(struct platform_device *pdev)
mgr->set_manager_info = &omap_dss_mgr_set_info;
mgr->get_manager_info = &omap_dss_mgr_get_info;
mgr->wait_for_go = &dss_mgr_wait_for_go;
mgr->wait_for_vsync = &dss_mgr_wait_for_vsync;
mgr->enable = &dss_mgr_enable;
mgr->disable = &dss_mgr_disable;
mgr->caps = OMAP_DSS_OVL_MGR_CAP_DISPC;
......
......@@ -350,7 +350,7 @@ int dss_check_overlay(struct omap_overlay *ovl, struct omap_dss_device *dssdev)
return -EINVAL;
}
dssdev->get_resolution(dssdev, &dw, &dh);
dssdev->driver->get_resolution(dssdev, &dw, &dh);
DSSDBG("check_overlay %d: (%d,%d %dx%d -> %dx%d) disp (%dx%d)\n",
ovl->id,
......
......@@ -36,8 +36,6 @@
#include <plat/display.h>
#include "dss.h"
/*#define MEASURE_PERF*/
#define RFBI_BASE 0x48050800
struct rfbi_reg { u16 idx; };
......@@ -66,8 +64,6 @@ struct rfbi_reg { u16 idx; };
#define RFBI_VSYNC_WIDTH RFBI_REG(0x0090)
#define RFBI_HSYNC_WIDTH RFBI_REG(0x0094)
#define RFBI_CMD_FIFO_LEN_BYTES (16 * sizeof(struct update_param))
#define REG_FLD_MOD(idx, val, start, end) \
rfbi_write_reg(idx, FLD_MOD(rfbi_read_reg(idx), val, start, end))
......@@ -102,7 +98,6 @@ enum update_cmd {
static int rfbi_convert_timings(struct rfbi_timings *t);
static void rfbi_get_clk_info(u32 *clk_period, u32 *max_clk_div);
static void process_cmd_fifo(void);
static struct {
void __iomem *base;
......@@ -125,11 +120,6 @@ static struct {
struct completion cmd_done;
atomic_t cmd_fifo_full;
atomic_t cmd_pending;
#ifdef MEASURE_PERF
unsigned perf_bytes;
ktime_t perf_setup_time;
ktime_t perf_start_time;
#endif
} rfbi;
struct update_region {
......@@ -139,16 +129,6 @@ struct update_region {
u16 h;
};
struct update_param {
u8 rfbi_module;
u8 cmd;
union {
struct update_region r;
struct completion *sync;
} par;
};
static inline void rfbi_write_reg(const struct rfbi_reg idx, u32 val)
{
__raw_writel(val, rfbi.base + idx.idx);
......@@ -321,55 +301,6 @@ void omap_rfbi_write_pixels(const void __iomem *buf, int scr_width,
}
EXPORT_SYMBOL(omap_rfbi_write_pixels);
#ifdef MEASURE_PERF
static void perf_mark_setup(void)
{
rfbi.perf_setup_time = ktime_get();
}
static void perf_mark_start(void)
{
rfbi.perf_start_time = ktime_get();
}
static void perf_show(const char *name)
{
ktime_t t, setup_time, trans_time;
u32 total_bytes;
u32 setup_us, trans_us, total_us;
t = ktime_get();
setup_time = ktime_sub(rfbi.perf_start_time, rfbi.perf_setup_time);
setup_us = (u32)ktime_to_us(setup_time);
if (setup_us == 0)
setup_us = 1;
trans_time = ktime_sub(t, rfbi.perf_start_time);
trans_us = (u32)ktime_to_us(trans_time);
if (trans_us == 0)
trans_us = 1;
total_us = setup_us + trans_us;
total_bytes = rfbi.perf_bytes;
DSSINFO("%s update %u us + %u us = %u us (%uHz), %u bytes, "
"%u kbytes/sec\n",
name,
setup_us,
trans_us,
total_us,
1000*1000 / total_us,
total_bytes,
total_bytes * 1000 / total_us);
}
#else
#define perf_mark_setup()
#define perf_mark_start()
#define perf_show(x)
#endif
void rfbi_transfer_area(u16 width, u16 height,
void (callback)(void *data), void *data)
{
......@@ -382,7 +313,7 @@ void rfbi_transfer_area(u16 width, u16 height,
dispc_set_lcd_size(width, height);
dispc_enable_lcd_out(1);
dispc_enable_channel(OMAP_DSS_CHANNEL_LCD, true);
rfbi.framedone_callback = callback;
rfbi.framedone_callback_data = data;
......@@ -396,8 +327,6 @@ void rfbi_transfer_area(u16 width, u16 height,
if (!rfbi.te_enabled)
l = FLD_MOD(l, 1, 4, 4); /* ITE */
perf_mark_start();
rfbi_write_reg(RFBI_CONTROL, l);
}
......@@ -407,8 +336,6 @@ static void framedone_callback(void *data, u32 mask)
DSSDBG("FRAMEDONE\n");
perf_show("DISPC");
REG_FLD_MOD(RFBI_CONTROL, 0, 0, 0);
rfbi_enable_clocks(0);
......@@ -416,11 +343,10 @@ static void framedone_callback(void *data, u32 mask)
callback = rfbi.framedone_callback;
rfbi.framedone_callback = NULL;
/*callback(rfbi.framedone_callback_data);*/
if (callback != NULL)
callback(rfbi.framedone_callback_data);
atomic_set(&rfbi.cmd_pending, 0);
process_cmd_fifo();
}
#if 1 /* VERBOSE */
......@@ -937,52 +863,43 @@ int rfbi_configure(int rfbi_module, int bpp, int lines)
}
EXPORT_SYMBOL(rfbi_configure);
static int rfbi_find_display(struct omap_dss_device *dssdev)
int omap_rfbi_prepare_update(struct omap_dss_device *dssdev,
u16 *x, u16 *y, u16 *w, u16 *h)
{
if (dssdev == rfbi.dssdev[0])
return 0;
u16 dw, dh;
if (dssdev == rfbi.dssdev[1])
return 1;
dssdev->driver->get_resolution(dssdev, &dw, &dh);
BUG();
return -1;
}
if (*x > dw || *y > dh)
return -EINVAL;
if (*x + *w > dw)
return -EINVAL;
static void signal_fifo_waiters(void)
{
if (atomic_read(&rfbi.cmd_fifo_full) > 0) {
/* DSSDBG("SIGNALING: Fifo not full for waiter!\n"); */
complete(&rfbi.cmd_done);
atomic_dec(&rfbi.cmd_fifo_full);
}
}
if (*y + *h > dh)
return -EINVAL;
/* returns 1 for async op, and 0 for sync op */
static int do_update(struct omap_dss_device *dssdev, struct update_region *upd)
{
u16 x = upd->x;
u16 y = upd->y;
u16 w = upd->w;
u16 h = upd->h;
if (*w == 1)
return -EINVAL;
perf_mark_setup();
if (*w == 0 || *h == 0)
return -EINVAL;
if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) {
/*dssdev->driver->enable_te(dssdev, 1); */
dss_setup_partial_planes(dssdev, &x, &y, &w, &h);
dss_setup_partial_planes(dssdev, x, y, w, h);
dispc_set_lcd_size(*w, *h);
}
#ifdef MEASURE_PERF
rfbi.perf_bytes = w * h * 2; /* XXX always 16bit */
#endif
dssdev->driver->setup_update(dssdev, x, y, w, h);
return 0;
}
EXPORT_SYMBOL(omap_rfbi_prepare_update);
int omap_rfbi_update(struct omap_dss_device *dssdev,
u16 x, u16 y, u16 w, u16 h,
void (*callback)(void *), void *data)
{
if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) {
rfbi_transfer_area(w, h, NULL, NULL);
return 1;
rfbi_transfer_area(w, h, callback, data);
} else {
struct omap_overlay *ovl;
void __iomem *addr;
......@@ -994,123 +911,12 @@ static int do_update(struct omap_dss_device *dssdev, struct update_region *upd)
omap_rfbi_write_pixels(addr, scr_width, x, y, w, h);
perf_show("L4");
return 0;
callback(data);
}
}
static void process_cmd_fifo(void)
{
int len;
struct update_param p;
struct omap_dss_device *dssdev;
unsigned long flags;
if (atomic_inc_return(&rfbi.cmd_pending) != 1)
return;
while (true) {
spin_lock_irqsave(&rfbi.cmd_lock, flags);
len = kfifo_out(&rfbi.cmd_fifo, (unsigned char *)&p,
sizeof(struct update_param));
if (len == 0) {
DSSDBG("nothing more in fifo\n");
atomic_set(&rfbi.cmd_pending, 0);
spin_unlock_irqrestore(&rfbi.cmd_lock, flags);
break;
}
/* DSSDBG("fifo full %d\n", rfbi.cmd_fifo_full.counter);*/
spin_unlock_irqrestore(&rfbi.cmd_lock, flags);
BUG_ON(len != sizeof(struct update_param));
BUG_ON(p.rfbi_module > 1);
dssdev = rfbi.dssdev[p.rfbi_module];
if (p.cmd == RFBI_CMD_UPDATE) {
if (do_update(dssdev, &p.par.r))
break; /* async op */
} else if (p.cmd == RFBI_CMD_SYNC) {
DSSDBG("Signaling SYNC done!\n");
complete(p.par.sync);
} else
BUG();
}
signal_fifo_waiters();
}
static void rfbi_push_cmd(struct update_param *p)
{
int ret;
while (1) {
unsigned long flags;
int available;
spin_lock_irqsave(&rfbi.cmd_lock, flags);
available = RFBI_CMD_FIFO_LEN_BYTES -
kfifo_len(&rfbi.cmd_fifo);
/* DSSDBG("%d bytes left in fifo\n", available); */
if (available < sizeof(struct update_param)) {
DSSDBG("Going to wait because FIFO FULL..\n");
spin_unlock_irqrestore(&rfbi.cmd_lock, flags);
atomic_inc(&rfbi.cmd_fifo_full);
wait_for_completion(&rfbi.cmd_done);
/*DSSDBG("Woke up because fifo not full anymore\n");*/
continue;
}
ret = kfifo_in(&rfbi.cmd_fifo, (unsigned char *)p,
sizeof(struct update_param));
/* DSSDBG("pushed %d bytes\n", ret);*/
spin_unlock_irqrestore(&rfbi.cmd_lock, flags);
BUG_ON(ret != sizeof(struct update_param));
break;
}
}
static void rfbi_push_update(int rfbi_module, int x, int y, int w, int h)
{
struct update_param p;
p.rfbi_module = rfbi_module;
p.cmd = RFBI_CMD_UPDATE;
p.par.r.x = x;
p.par.r.y = y;
p.par.r.w = w;
p.par.r.h = h;
DSSDBG("RFBI pushed %d,%d %dx%d\n", x, y, w, h);
rfbi_push_cmd(&p);
process_cmd_fifo();
}
static void rfbi_push_sync(int rfbi_module, struct completion *sync_comp)
{
struct update_param p;
p.rfbi_module = rfbi_module;
p.cmd = RFBI_CMD_SYNC;
p.par.sync = sync_comp;
rfbi_push_cmd(&p);
DSSDBG("RFBI sync pushed to cmd fifo\n");
process_cmd_fifo();
return 0;
}
EXPORT_SYMBOL(omap_rfbi_update);
void rfbi_dump_regs(struct seq_file *s)
{
......@@ -1155,12 +961,8 @@ int rfbi_init(void)
{
u32 rev;
u32 l;
int r;
spin_lock_init(&rfbi.cmd_lock);
r = kfifo_alloc(&rfbi.cmd_fifo, RFBI_CMD_FIFO_LEN_BYTES, GFP_KERNEL);
if (r)
return r;
init_completion(&rfbi.cmd_done);
atomic_set(&rfbi.cmd_fifo_full, 0);
......@@ -1196,49 +998,10 @@ void rfbi_exit(void)
{
DSSDBG("rfbi_exit\n");
kfifo_free(&rfbi.cmd_fifo);
iounmap(rfbi.base);
}
/* struct omap_display support */
static int rfbi_display_update(struct omap_dss_device *dssdev,
u16 x, u16 y, u16 w, u16 h)
{
int rfbi_module;
if (w == 0 || h == 0)
return 0;
rfbi_module = rfbi_find_display(dssdev);
rfbi_push_update(rfbi_module, x, y, w, h);
return 0;
}
static int rfbi_display_sync(struct omap_dss_device *dssdev)
{
struct completion sync_comp;
int rfbi_module;
rfbi_module = rfbi_find_display(dssdev);
init_completion(&sync_comp);
rfbi_push_sync(rfbi_module, &sync_comp);
DSSDBG("Waiting for SYNC to happen...\n");
wait_for_completion(&sync_comp);
DSSDBG("Released from SYNC\n");
return 0;
}
static int rfbi_display_enable_te(struct omap_dss_device *dssdev, bool enable)
{
dssdev->driver->enable_te(dssdev, enable);
return 0;
}
static int rfbi_display_enable(struct omap_dss_device *dssdev)
int omapdss_rfbi_display_enable(struct omap_dss_device *dssdev)
{
int r;
......@@ -1269,41 +1032,25 @@ static int rfbi_display_enable(struct omap_dss_device *dssdev)
&dssdev->ctrl.rfbi_timings);
if (dssdev->driver->enable) {
r = dssdev->driver->enable(dssdev);
if (r)
goto err2;
}
return 0;
err2:
omap_dispc_unregister_isr(framedone_callback, NULL,
DISPC_IRQ_FRAMEDONE);
err1:
omap_dss_stop_device(dssdev);
err0:
return r;
}
EXPORT_SYMBOL(omapdss_rfbi_display_enable);
static void rfbi_display_disable(struct omap_dss_device *dssdev)
void omapdss_rfbi_display_disable(struct omap_dss_device *dssdev)
{
dssdev->driver->disable(dssdev);
omap_dispc_unregister_isr(framedone_callback, NULL,
DISPC_IRQ_FRAMEDONE);
omap_dss_stop_device(dssdev);
}
EXPORT_SYMBOL(omapdss_rfbi_display_disable);
int rfbi_init_display(struct omap_dss_device *dssdev)
{
dssdev->enable = rfbi_display_enable;
dssdev->disable = rfbi_display_disable;
dssdev->update = rfbi_display_update;
dssdev->sync = rfbi_display_sync;
dssdev->enable_te = rfbi_display_enable_te;
rfbi.dssdev[dssdev->phy.rfbi.channel] = dssdev;
dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
return 0;
}
......@@ -41,7 +41,7 @@ static void sdi_basic_init(void)
dispc_lcd_enable_signal_polarity(1);
}
static int sdi_display_enable(struct omap_dss_device *dssdev)
int omapdss_sdi_display_enable(struct omap_dss_device *dssdev)
{
struct omap_video_timings *t = &dssdev->panel.timings;
struct dss_clock_info dss_cinfo;
......@@ -57,12 +57,6 @@ static int sdi_display_enable(struct omap_dss_device *dssdev)
goto err0;
}
if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {
DSSERR("dssdev already enabled\n");
r = -EINVAL;
goto err1;
}
/* In case of skip_init sdi_init has already enabled the clocks */
if (!sdi.skip_init)
dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
......@@ -119,7 +113,7 @@ static int sdi_display_enable(struct omap_dss_device *dssdev)
mdelay(2);
}
dispc_enable_lcd_out(1);
dssdev->manager->enable(dssdev->manager);
if (dssdev->driver->enable) {
r = dssdev->driver->enable(dssdev);
......@@ -127,13 +121,11 @@ static int sdi_display_enable(struct omap_dss_device *dssdev)
goto err3;
}
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
sdi.skip_init = 0;
return 0;
err3:
dispc_enable_lcd_out(0);
dssdev->manager->disable(dssdev->manager);
err2:
dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
err1:
......@@ -141,120 +133,27 @@ static int sdi_display_enable(struct omap_dss_device *dssdev)
err0:
return r;
}
EXPORT_SYMBOL(omapdss_sdi_display_enable);
static int sdi_display_resume(struct omap_dss_device *dssdev);
static void sdi_display_disable(struct omap_dss_device *dssdev)
void omapdss_sdi_display_disable(struct omap_dss_device *dssdev)
{
if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED)
return;
if (dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED)
if (sdi_display_resume(dssdev))
return;
if (dssdev->driver->disable)
dssdev->driver->disable(dssdev);
dispc_enable_lcd_out(0);
dssdev->manager->disable(dssdev->manager);
dss_sdi_disable();
dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
omap_dss_stop_device(dssdev);
}
static int sdi_display_suspend(struct omap_dss_device *dssdev)
{
if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
return -EINVAL;
if (dssdev->driver->suspend)
dssdev->driver->suspend(dssdev);
dispc_enable_lcd_out(0);
dss_sdi_disable();
dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
return 0;
}
static int sdi_display_resume(struct omap_dss_device *dssdev)
{
int r;
if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED)
return -EINVAL;
dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
r = dss_sdi_enable();
if (r)
goto err;
mdelay(2);
dispc_enable_lcd_out(1);
if (dssdev->driver->resume)
dssdev->driver->resume(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return 0;
err:
dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
return r;
}
static int sdi_display_set_update_mode(struct omap_dss_device *dssdev,
enum omap_dss_update_mode mode)
{
if (mode == OMAP_DSS_UPDATE_MANUAL)
return -EINVAL;
if (mode == OMAP_DSS_UPDATE_DISABLED) {
dispc_enable_lcd_out(0);
sdi.update_enabled = 0;
} else {
dispc_enable_lcd_out(1);
sdi.update_enabled = 1;
}
return 0;
}
static enum omap_dss_update_mode sdi_display_get_update_mode(
struct omap_dss_device *dssdev)
{
return sdi.update_enabled ? OMAP_DSS_UPDATE_AUTO :
OMAP_DSS_UPDATE_DISABLED;
}
static void sdi_get_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
*timings = dssdev->panel.timings;
}
EXPORT_SYMBOL(omapdss_sdi_display_disable);
int sdi_init_display(struct omap_dss_device *dssdev)
{
DSSDBG("SDI init\n");
dssdev->enable = sdi_display_enable;
dssdev->disable = sdi_display_disable;
dssdev->suspend = sdi_display_suspend;
dssdev->resume = sdi_display_resume;
dssdev->set_update_mode = sdi_display_set_update_mode;
dssdev->get_update_mode = sdi_display_get_update_mode;
dssdev->get_timings = sdi_get_timings;
return 0;
}
......
......@@ -400,114 +400,6 @@ static const struct venc_config *venc_timings_to_config(
BUG();
}
/* driver */
static int venc_panel_probe(struct omap_dss_device *dssdev)
{
dssdev->panel.timings = omap_dss_pal_timings;
return 0;
}
static void venc_panel_remove(struct omap_dss_device *dssdev)
{
}
static int venc_panel_enable(struct omap_dss_device *dssdev)
{
int r = 0;
/* wait couple of vsyncs until enabling the LCD */
msleep(50);
if (dssdev->platform_enable)
r = dssdev->platform_enable(dssdev);
return r;
}
static void venc_panel_disable(struct omap_dss_device *dssdev)
{
if (dssdev->platform_disable)
dssdev->platform_disable(dssdev);
/* wait at least 5 vsyncs after disabling the LCD */
msleep(100);
}
static int venc_panel_suspend(struct omap_dss_device *dssdev)
{
venc_panel_disable(dssdev);
return 0;
}
static int venc_panel_resume(struct omap_dss_device *dssdev)
{
return venc_panel_enable(dssdev);
}
static struct omap_dss_driver venc_driver = {
.probe = venc_panel_probe,
.remove = venc_panel_remove,
.enable = venc_panel_enable,
.disable = venc_panel_disable,
.suspend = venc_panel_suspend,
.resume = venc_panel_resume,
.driver = {
.name = "venc",
.owner = THIS_MODULE,
},
};
/* driver end */
int venc_init(struct platform_device *pdev)
{
u8 rev_id;
mutex_init(&venc.venc_lock);
venc.wss_data = 0;
venc.base = ioremap(VENC_BASE, SZ_1K);
if (!venc.base) {
DSSERR("can't ioremap VENC\n");
return -ENOMEM;
}
venc.vdda_dac_reg = regulator_get(&pdev->dev, "vdda_dac");
if (IS_ERR(venc.vdda_dac_reg)) {
iounmap(venc.base);
DSSERR("can't get VDDA_DAC regulator\n");
return PTR_ERR(venc.vdda_dac_reg);
}
venc_enable_clocks(1);
rev_id = (u8)(venc_read_reg(VENC_REV_ID) & 0xff);
printk(KERN_INFO "OMAP VENC rev %d\n", rev_id);
venc_enable_clocks(0);
return omap_dss_register_driver(&venc_driver);
}
void venc_exit(void)
{
omap_dss_unregister_driver(&venc_driver);
regulator_put(venc.vdda_dac_reg);
iounmap(venc.base);
}
static void venc_power_on(struct omap_dss_device *dssdev)
{
u32 l;
......@@ -540,7 +432,7 @@ static void venc_power_on(struct omap_dss_device *dssdev)
if (dssdev->platform_enable)
dssdev->platform_enable(dssdev);
dispc_enable_digit_out(1);
dssdev->manager->enable(dssdev->manager);
}
static void venc_power_off(struct omap_dss_device *dssdev)
......@@ -548,7 +440,7 @@ static void venc_power_off(struct omap_dss_device *dssdev)
venc_write_reg(VENC_OUTPUT_CONTROL, 0);
dss_set_dac_pwrdn_bgz(0);
dispc_enable_digit_out(0);
dssdev->manager->disable(dssdev->manager);
if (dssdev->platform_disable)
dssdev->platform_disable(dssdev);
......@@ -558,7 +450,23 @@ static void venc_power_off(struct omap_dss_device *dssdev)
venc_enable_clocks(0);
}
static int venc_enable_display(struct omap_dss_device *dssdev)
/* driver */
static int venc_panel_probe(struct omap_dss_device *dssdev)
{
dssdev->panel.timings = omap_dss_pal_timings;
return 0;
}
static void venc_panel_remove(struct omap_dss_device *dssdev)
{
}
static int venc_panel_enable(struct omap_dss_device *dssdev)
{
int r = 0;
......@@ -568,7 +476,13 @@ static int venc_enable_display(struct omap_dss_device *dssdev)
if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {
r = -EINVAL;
goto err;
goto err1;
}
if (dssdev->platform_enable) {
r = dssdev->platform_enable(dssdev);
if (r)
goto err2;
}
venc_power_on(dssdev);
......@@ -576,13 +490,21 @@ static int venc_enable_display(struct omap_dss_device *dssdev)
venc.wss_data = 0;
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
err:
/* wait couple of vsyncs until enabling the LCD */
msleep(50);
mutex_unlock(&venc.venc_lock);
return r;
err2:
venc_power_off(dssdev);
err1:
mutex_unlock(&venc.venc_lock);
return r;
}
static void venc_disable_display(struct omap_dss_device *dssdev)
static void venc_panel_disable(struct omap_dss_device *dssdev)
{
DSSDBG("venc_disable_display\n");
......@@ -599,53 +521,40 @@ static void venc_disable_display(struct omap_dss_device *dssdev)
venc_power_off(dssdev);
/* wait at least 5 vsyncs after disabling the LCD */
msleep(100);
if (dssdev->platform_disable)
dssdev->platform_disable(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
end:
mutex_unlock(&venc.venc_lock);
}
static int venc_display_suspend(struct omap_dss_device *dssdev)
static int venc_panel_suspend(struct omap_dss_device *dssdev)
{
int r = 0;
DSSDBG("venc_display_suspend\n");
mutex_lock(&venc.venc_lock);
if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
r = -EINVAL;
goto err;
}
venc_power_off(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
err:
mutex_unlock(&venc.venc_lock);
return r;
venc_panel_disable(dssdev);
return 0;
}
static int venc_display_resume(struct omap_dss_device *dssdev)
static int venc_panel_resume(struct omap_dss_device *dssdev)
{
int r = 0;
DSSDBG("venc_display_resume\n");
mutex_lock(&venc.venc_lock);
if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) {
r = -EINVAL;
goto err;
}
venc_power_on(dssdev);
return venc_panel_enable(dssdev);
}
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
err:
mutex_unlock(&venc.venc_lock);
static enum omap_dss_update_mode venc_get_update_mode(
struct omap_dss_device *dssdev)
{
return OMAP_DSS_UPDATE_AUTO;
}
return r;
static int venc_set_update_mode(struct omap_dss_device *dssdev,
enum omap_dss_update_mode mode)
{
if (mode != OMAP_DSS_UPDATE_AUTO)
return -EINVAL;
return 0;
}
static void venc_get_timings(struct omap_dss_device *dssdev,
......@@ -666,8 +575,8 @@ static void venc_set_timings(struct omap_dss_device *dssdev,
dssdev->panel.timings = *timings;
if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
/* turn the venc off and on to get new timings to use */
venc_disable_display(dssdev);
venc_enable_display(dssdev);
venc_panel_disable(dssdev);
venc_panel_enable(dssdev);
}
}
......@@ -716,30 +625,79 @@ static int venc_set_wss(struct omap_dss_device *dssdev, u32 wss)
return 0;
}
static enum omap_dss_update_mode venc_display_get_update_mode(
struct omap_dss_device *dssdev)
static struct omap_dss_driver venc_driver = {
.probe = venc_panel_probe,
.remove = venc_panel_remove,
.enable = venc_panel_enable,
.disable = venc_panel_disable,
.suspend = venc_panel_suspend,
.resume = venc_panel_resume,
.get_resolution = omapdss_default_get_resolution,
.get_recommended_bpp = omapdss_default_get_recommended_bpp,
.set_update_mode = venc_set_update_mode,
.get_update_mode = venc_get_update_mode,
.get_timings = venc_get_timings,
.set_timings = venc_set_timings,
.check_timings = venc_check_timings,
.get_wss = venc_get_wss,
.set_wss = venc_set_wss,
.driver = {
.name = "venc",
.owner = THIS_MODULE,
},
};
/* driver end */
int venc_init(struct platform_device *pdev)
{
if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
return OMAP_DSS_UPDATE_AUTO;
else
return OMAP_DSS_UPDATE_DISABLED;
u8 rev_id;
mutex_init(&venc.venc_lock);
venc.wss_data = 0;
venc.base = ioremap(VENC_BASE, SZ_1K);
if (!venc.base) {
DSSERR("can't ioremap VENC\n");
return -ENOMEM;
}
venc.vdda_dac_reg = dss_get_vdda_dac();
if (IS_ERR(venc.vdda_dac_reg)) {
iounmap(venc.base);
DSSERR("can't get VDDA_DAC regulator\n");
return PTR_ERR(venc.vdda_dac_reg);
}
venc_enable_clocks(1);
rev_id = (u8)(venc_read_reg(VENC_REV_ID) & 0xff);
printk(KERN_INFO "OMAP VENC rev %d\n", rev_id);
venc_enable_clocks(0);
return omap_dss_register_driver(&venc_driver);
}
void venc_exit(void)
{
omap_dss_unregister_driver(&venc_driver);
iounmap(venc.base);
}
int venc_init_display(struct omap_dss_device *dssdev)
{
DSSDBG("init_display\n");
dssdev->enable = venc_enable_display;
dssdev->disable = venc_disable_display;
dssdev->suspend = venc_display_suspend;
dssdev->resume = venc_display_resume;
dssdev->get_timings = venc_get_timings;
dssdev->set_timings = venc_set_timings;
dssdev->check_timings = venc_check_timings;
dssdev->get_wss = venc_get_wss;
dssdev->set_wss = venc_set_wss;
dssdev->get_update_mode = venc_display_get_update_mode;
return 0;
}
......
......@@ -16,16 +16,7 @@ config FB_OMAP2_DEBUG_SUPPORT
depends on FB_OMAP2
help
Support for debug output. You have to enable the actual printing
with debug module parameter.
config FB_OMAP2_FORCE_AUTO_UPDATE
bool "Force main display to automatic update mode"
depends on FB_OMAP2
help
Forces main display to automatic update mode (if possible),
and also enables tearsync (if possible). By default
displays that support manual update are started in manual
update mode.
with 'debug' module parameter.
config FB_OMAP2_NUM_FBS
int "Number of framebuffers"
......
......@@ -167,12 +167,12 @@ static int omapfb_update_window_nolock(struct fb_info *fbi,
if (w == 0 || h == 0)
return 0;
display->get_resolution(display, &dw, &dh);
display->driver->get_resolution(display, &dw, &dh);
if (x + w > dw || y + h > dh)
return -EINVAL;
return display->update(display, x, y, w, h);
return display->driver->update(display, x, y, w, h);
}
/* This function is exported for SGX driver use */
......@@ -202,7 +202,7 @@ static int omapfb_set_update_mode(struct fb_info *fbi,
enum omap_dss_update_mode um;
int r;
if (!display || !display->set_update_mode)
if (!display || !display->driver->set_update_mode)
return -EINVAL;
switch (mode) {
......@@ -222,7 +222,7 @@ static int omapfb_set_update_mode(struct fb_info *fbi,
return -EINVAL;
}
r = display->set_update_mode(display, um);
r = display->driver->set_update_mode(display, um);
return r;
}
......@@ -233,10 +233,15 @@ static int omapfb_get_update_mode(struct fb_info *fbi,
struct omap_dss_device *display = fb2display(fbi);
enum omap_dss_update_mode m;
if (!display || !display->get_update_mode)
if (!display)
return -EINVAL;
m = display->get_update_mode(display);
if (!display->driver->get_update_mode) {
*mode = OMAPFB_AUTO_UPDATE;
return 0;
}
m = display->driver->get_update_mode(display);
switch (m) {
case OMAP_DSS_UPDATE_DISABLED:
......@@ -374,7 +379,7 @@ static int omapfb_memory_read(struct fb_info *fbi,
void *buf;
int r;
if (!display || !display->memory_read)
if (!display || !display->driver->memory_read)
return -ENOENT;
if (!access_ok(VERIFY_WRITE, mr->buffer, mr->buffer_size))
......@@ -389,7 +394,7 @@ static int omapfb_memory_read(struct fb_info *fbi,
return -ENOMEM;
}
r = display->memory_read(display, buf, mr->buffer_size,
r = display->driver->memory_read(display, buf, mr->buffer_size,
mr->x, mr->y, mr->w, mr->h);
if (r > 0) {
......@@ -483,6 +488,7 @@ int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
struct omapfb_memory_read memory_read;
struct omapfb_vram_info vram_info;
struct omapfb_tearsync_info tearsync_info;
struct omapfb_display_info display_info;
} p;
int r = 0;
......@@ -490,18 +496,18 @@ int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
switch (cmd) {
case OMAPFB_SYNC_GFX:
DBG("ioctl SYNC_GFX\n");
if (!display || !display->sync) {
if (!display || !display->driver->sync) {
/* DSS1 never returns an error here, so we neither */
/*r = -EINVAL;*/
break;
}
r = display->sync(display);
r = display->driver->sync(display);
break;
case OMAPFB_UPDATE_WINDOW_OLD:
DBG("ioctl UPDATE_WINDOW_OLD\n");
if (!display || !display->update) {
if (!display || !display->driver->update) {
r = -EINVAL;
break;
}
......@@ -519,7 +525,7 @@ int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
case OMAPFB_UPDATE_WINDOW:
DBG("ioctl UPDATE_WINDOW\n");
if (!display || !display->update) {
if (!display || !display->driver->update) {
r = -EINVAL;
break;
}
......@@ -648,7 +654,7 @@ int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
break;
}
r = display->wait_vsync(display);
r = display->manager->wait_for_vsync(display->manager);
break;
case OMAPFB_WAITFORGO:
......@@ -669,12 +675,12 @@ int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
r = -EFAULT;
break;
}
if (!display || !display->run_test) {
if (!display || !display->driver->run_test) {
r = -EINVAL;
break;
}
r = display->run_test(display, p.test_num);
r = display->driver->run_test(display, p.test_num);
break;
......@@ -684,12 +690,12 @@ int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
r = -EFAULT;
break;
}
if (!display || !display->run_test) {
if (!display || !display->driver->run_test) {
r = -EINVAL;
break;
}
r = display->run_test(display, p.test_num);
r = display->driver->run_test(display, p.test_num);
break;
......@@ -731,13 +737,37 @@ int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
break;
}
if (!display->enable_te) {
if (!display->driver->enable_te) {
r = -ENODEV;
break;
}
r = display->enable_te(display, !!p.tearsync_info.enabled);
r = display->driver->enable_te(display,
!!p.tearsync_info.enabled);
break;
}
case OMAPFB_GET_DISPLAY_INFO: {
u16 xres, yres;
DBG("ioctl GET_DISPLAY_INFO\n");
if (display == NULL) {
r = -ENODEV;
break;
}
display->driver->get_resolution(display, &xres, &yres);
p.display_info.xres = xres;
p.display_info.yres = yres;
p.display_info.width = 0;
p.display_info.height = 0;
if (copy_to_user((void __user *)arg, &p.display_info,
sizeof(p.display_info)))
r = -EFAULT;
break;
}
......
......@@ -54,6 +54,8 @@ module_param_named(test, omapfb_test_pattern, bool, 0644);
#endif
static int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi);
static int omapfb_get_recommended_bpp(struct omapfb2_device *fbdev,
struct omap_dss_device *dssdev);
#ifdef DEBUG
static void draw_pixel(struct fb_info *fbi, int x, int y, unsigned color)
......@@ -152,9 +154,9 @@ static void fill_fb(struct fb_info *fbi)
}
#endif
static unsigned omapfb_get_vrfb_offset(struct omapfb_info *ofbi, int rot)
static unsigned omapfb_get_vrfb_offset(const struct omapfb_info *ofbi, int rot)
{
struct vrfb *vrfb = &ofbi->region.vrfb;
const struct vrfb *vrfb = &ofbi->region.vrfb;
unsigned offset;
switch (rot) {
......@@ -179,7 +181,7 @@ static unsigned omapfb_get_vrfb_offset(struct omapfb_info *ofbi, int rot)
return offset;
}
static u32 omapfb_get_region_rot_paddr(struct omapfb_info *ofbi, int rot)
static u32 omapfb_get_region_rot_paddr(const struct omapfb_info *ofbi, int rot)
{
if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) {
return ofbi->region.vrfb.paddr[rot]
......@@ -189,7 +191,7 @@ static u32 omapfb_get_region_rot_paddr(struct omapfb_info *ofbi, int rot)
}
}
static u32 omapfb_get_region_paddr(struct omapfb_info *ofbi)
static u32 omapfb_get_region_paddr(const struct omapfb_info *ofbi)
{
if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB)
return ofbi->region.vrfb.paddr[0];
......@@ -197,7 +199,7 @@ static u32 omapfb_get_region_paddr(struct omapfb_info *ofbi)
return ofbi->region.paddr;
}
static void __iomem *omapfb_get_region_vaddr(struct omapfb_info *ofbi)
static void __iomem *omapfb_get_region_vaddr(const struct omapfb_info *ofbi)
{
if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB)
return ofbi->region.vrfb.vaddr[0];
......@@ -703,9 +705,9 @@ int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var)
var->width = -1;
var->grayscale = 0;
if (display && display->get_timings) {
if (display && display->driver->get_timings) {
struct omap_video_timings timings;
display->get_timings(display, &timings);
display->driver->get_timings(display, &timings);
/* pixclock in ps, the rest in pixclock */
var->pixclock = timings.pixel_clock != 0 ?
......@@ -778,8 +780,8 @@ static int omapfb_release(struct fb_info *fbi, int user)
return 0;
}
static unsigned calc_rotation_offset_dma(struct fb_var_screeninfo *var,
struct fb_fix_screeninfo *fix, int rotation)
static unsigned calc_rotation_offset_dma(const struct fb_var_screeninfo *var,
const struct fb_fix_screeninfo *fix, int rotation)
{
unsigned offset;
......@@ -789,8 +791,8 @@ static unsigned calc_rotation_offset_dma(struct fb_var_screeninfo *var,
return offset;
}
static unsigned calc_rotation_offset_vrfb(struct fb_var_screeninfo *var,
struct fb_fix_screeninfo *fix, int rotation)
static unsigned calc_rotation_offset_vrfb(const struct fb_var_screeninfo *var,
const struct fb_fix_screeninfo *fix, int rotation)
{
unsigned offset;
......@@ -1221,11 +1223,11 @@ static int omapfb_blank(int blank, struct fb_info *fbi)
if (display->state != OMAP_DSS_DISPLAY_SUSPENDED)
goto exit;
if (display->resume)
r = display->resume(display);
if (display->driver->resume)
r = display->driver->resume(display);
if (r == 0 && display->get_update_mode &&
display->get_update_mode(display) ==
if (r == 0 && display->driver->get_update_mode &&
display->driver->get_update_mode(display) ==
OMAP_DSS_UPDATE_MANUAL)
do_update = 1;
......@@ -1240,8 +1242,8 @@ static int omapfb_blank(int blank, struct fb_info *fbi)
if (display->state != OMAP_DSS_DISPLAY_ACTIVE)
goto exit;
if (display->suspend)
r = display->suspend(display);
if (display->driver->suspend)
r = display->driver->suspend(display);
break;
......@@ -1252,11 +1254,11 @@ static int omapfb_blank(int blank, struct fb_info *fbi)
exit:
omapfb_unlock(fbdev);
if (r == 0 && do_update && display->update) {
if (r == 0 && do_update && display->driver->update) {
u16 w, h;
display->get_resolution(display, &w, &h);
display->driver->get_resolution(display, &w, &h);
r = display->update(display, 0, 0, w, h);
r = display->driver->update(display, 0, 0, w, h);
}
return r;
......@@ -1404,6 +1406,7 @@ static int omapfb_alloc_fbmem_display(struct fb_info *fbi, unsigned long size,
unsigned long paddr)
{
struct omapfb_info *ofbi = FB2OFB(fbi);
struct omapfb2_device *fbdev = ofbi->fbdev;
struct omap_dss_device *display;
int bytespp;
......@@ -1412,7 +1415,7 @@ static int omapfb_alloc_fbmem_display(struct fb_info *fbi, unsigned long size,
if (!display)
return 0;
switch (display->get_recommended_bpp(display)) {
switch (omapfb_get_recommended_bpp(fbdev, display)) {
case 16:
bytespp = 2;
break;
......@@ -1427,7 +1430,7 @@ static int omapfb_alloc_fbmem_display(struct fb_info *fbi, unsigned long size,
if (!size) {
u16 w, h;
display->get_resolution(display, &w, &h);
display->driver->get_resolution(display, &w, &h);
if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) {
size = max(omap_vrfb_min_phys_size(w, h, bytespp),
......@@ -1636,8 +1639,8 @@ int omapfb_realloc_fbmem(struct fb_info *fbi, unsigned long size, int type)
if (old_size == size && old_type == type)
return 0;
if (display && display->sync)
display->sync(display);
if (display && display->driver->sync)
display->driver->sync(display);
omapfb_free_fbmem(fbi);
......@@ -1745,7 +1748,7 @@ static int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi)
u16 w, h;
int rotation = (var->rotate + ofbi->rotation[0]) % 4;
display->get_resolution(display, &w, &h);
display->driver->get_resolution(display, &w, &h);
if (rotation == FB_ROTATE_CW ||
rotation == FB_ROTATE_CCW) {
......@@ -1760,7 +1763,7 @@ static int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi)
var->yres_virtual = var->yres;
if (!var->bits_per_pixel) {
switch (display->get_recommended_bpp(display)) {
switch (omapfb_get_recommended_bpp(fbdev, display)) {
case 16:
var->bits_per_pixel = 16;
break;
......@@ -1828,7 +1831,7 @@ static void omapfb_free_resources(struct omapfb2_device *fbdev)
for (i = 0; i < fbdev->num_displays; i++) {
if (fbdev->displays[i]->state != OMAP_DSS_DISPLAY_DISABLED)
fbdev->displays[i]->disable(fbdev->displays[i]);
fbdev->displays[i]->driver->disable(fbdev->displays[i]);
omap_dss_put_device(fbdev->displays[i]);
}
......@@ -2011,7 +2014,8 @@ static int omapfb_mode_to_timings(const char *mode_str,
}
}
static int omapfb_set_def_mode(struct omap_dss_device *display, char *mode_str)
static int omapfb_set_def_mode(struct omapfb2_device *fbdev,
struct omap_dss_device *display, char *mode_str)
{
int r;
u8 bpp;
......@@ -2021,20 +2025,37 @@ static int omapfb_set_def_mode(struct omap_dss_device *display, char *mode_str)
if (r)
return r;
display->panel.recommended_bpp = bpp;
fbdev->bpp_overrides[fbdev->num_bpp_overrides].dssdev = display;
fbdev->bpp_overrides[fbdev->num_bpp_overrides].bpp = bpp;
++fbdev->num_bpp_overrides;
if (!display->check_timings || !display->set_timings)
if (!display->driver->check_timings || !display->driver->set_timings)
return -EINVAL;
r = display->check_timings(display, &timings);
r = display->driver->check_timings(display, &timings);
if (r)
return r;
display->set_timings(display, &timings);
display->driver->set_timings(display, &timings);
return 0;
}
static int omapfb_get_recommended_bpp(struct omapfb2_device *fbdev,
struct omap_dss_device *dssdev)
{
int i;
BUG_ON(dssdev->driver->get_recommended_bpp == NULL);
for (i = 0; i < fbdev->num_bpp_overrides; ++i) {
if (dssdev == fbdev->bpp_overrides[i].dssdev)
return fbdev->bpp_overrides[i].bpp;
}
return dssdev->driver->get_recommended_bpp(dssdev);
}
static int omapfb_parse_def_modes(struct omapfb2_device *fbdev)
{
char *str, *options, *this_opt;
......@@ -2073,7 +2094,7 @@ static int omapfb_parse_def_modes(struct omapfb2_device *fbdev)
break;
}
r = omapfb_set_def_mode(display, mode_str);
r = omapfb_set_def_mode(fbdev, display, mode_str);
if (r)
break;
}
......@@ -2111,18 +2132,23 @@ static int omapfb_probe(struct platform_device *pdev)
fbdev->dev = &pdev->dev;
platform_set_drvdata(pdev, fbdev);
r = 0;
fbdev->num_displays = 0;
dssdev = NULL;
for_each_dss_dev(dssdev) {
omap_dss_get_device(dssdev);
if (!dssdev->driver) {
dev_err(&pdev->dev, "no driver for display\n");
r = -EINVAL;
goto cleanup;
r = -ENODEV;
}
fbdev->displays[fbdev->num_displays++] = dssdev;
}
if (r)
goto cleanup;
if (fbdev->num_displays == 0) {
dev_err(&pdev->dev, "no displays\n");
r = -EINVAL;
......@@ -2167,35 +2193,28 @@ static int omapfb_probe(struct platform_device *pdev)
}
if (def_display) {
#ifndef CONFIG_FB_OMAP2_FORCE_AUTO_UPDATE
u16 w, h;
#endif
r = def_display->enable(def_display);
if (r)
struct omap_dss_driver *dssdrv = def_display->driver;
r = def_display->driver->enable(def_display);
if (r) {
dev_warn(fbdev->dev, "Failed to enable display '%s'\n",
def_display->name);
goto cleanup;
}
/* set the update mode */
if (def_display->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) {
#ifdef CONFIG_FB_OMAP2_FORCE_AUTO_UPDATE
if (def_display->enable_te)
def_display->enable_te(def_display, 1);
if (def_display->set_update_mode)
def_display->set_update_mode(def_display,
OMAP_DSS_UPDATE_AUTO);
#else /* MANUAL_UPDATE */
if (def_display->enable_te)
def_display->enable_te(def_display, 0);
if (def_display->set_update_mode)
def_display->set_update_mode(def_display,
u16 w, h;
if (dssdrv->enable_te)
dssdrv->enable_te(def_display, 1);
if (dssdrv->set_update_mode)
dssdrv->set_update_mode(def_display,
OMAP_DSS_UPDATE_MANUAL);
def_display->get_resolution(def_display, &w, &h);
def_display->update(def_display, 0, 0, w, h);
#endif
dssdrv->get_resolution(def_display, &w, &h);
def_display->driver->update(def_display, 0, 0, w, h);
} else {
if (def_display->set_update_mode)
def_display->set_update_mode(def_display,
if (dssdrv->set_update_mode)
dssdrv->set_update_mode(def_display,
OMAP_DSS_UPDATE_AUTO);
}
}
......
......@@ -83,6 +83,12 @@ struct omapfb2_device {
struct omap_overlay *overlays[10];
unsigned num_managers;
struct omap_overlay_manager *managers[10];
unsigned num_bpp_overrides;
struct {
struct omap_dss_device *dssdev;
u8 bpp;
} bpp_overrides[10];
};
struct omapfb_colormode {
......@@ -105,6 +111,9 @@ void omapfb_remove_sysfs(struct omapfb2_device *fbdev);
int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg);
int omapfb_update_window(struct fb_info *fbi,
u32 x, u32 y, u32 w, u32 h);
int dss_mode_to_fb_mode(enum omap_color_mode dssmode,
struct fb_var_screeninfo *var);
......
......@@ -126,6 +126,7 @@ header-y += nfs2.h
header-y += nfs4_mount.h
header-y += nfs_mount.h
header-y += nl80211.h
header-y += omapfb.h
header-y += param.h
header-y += pci_regs.h
header-y += perf_event.h
......
......@@ -57,6 +57,7 @@
#define OMAPFB_WAITFORGO OMAP_IO(60)
#define OMAPFB_GET_VRAM_INFO OMAP_IOR(61, struct omapfb_vram_info)
#define OMAPFB_SET_TEARSYNC OMAP_IOW(62, struct omapfb_tearsync_info)
#define OMAPFB_GET_DISPLAY_INFO OMAP_IOR(63, struct omapfb_display_info)
#define OMAPFB_CAPS_GENERIC_MASK 0x00000fff
#define OMAPFB_CAPS_LCDC_MASK 0x00fff000
......@@ -206,6 +207,14 @@ struct omapfb_tearsync_info {
__u16 reserved2;
};
struct omapfb_display_info {
__u16 xres;
__u16 yres;
__u32 width; /* phys width of the display in micrometers */
__u32 height; /* phys height of the display in micrometers */
__u32 reserved[5];
};
#ifdef __KERNEL__
#include <plat/board.h>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment