Merge omapdss split between omapdrm and omapfb
Merge changes to create a separate copy of omapdss for omapdrm and omapfb.
This commit is contained in:
commit
caf05780f6
|
@ -62,7 +62,7 @@ obj-$(CONFIG_DRM_ARMADA) += armada/
|
|||
obj-$(CONFIG_DRM_ATMEL_HLCDC) += atmel-hlcdc/
|
||||
obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/
|
||||
obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
|
||||
obj-$(CONFIG_DRM_OMAP) += omapdrm/
|
||||
obj-y += omapdrm/
|
||||
obj-y += tilcdc/
|
||||
obj-$(CONFIG_DRM_QXL) += qxl/
|
||||
obj-$(CONFIG_DRM_BOCHS) += bochs/
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
|
||||
config DRM_OMAP
|
||||
tristate "OMAP DRM"
|
||||
depends on DRM
|
||||
depends on ARCH_OMAP2PLUS || ARCH_MULTIPLATFORM
|
||||
depends on OMAP2_DSS
|
||||
select OMAP2_DSS
|
||||
select DRM_KMS_HELPER
|
||||
select DRM_KMS_FB_HELPER
|
||||
select FB_SYS_FILLRECT
|
||||
|
@ -14,13 +13,18 @@ config DRM_OMAP
|
|||
help
|
||||
DRM display driver for OMAP2/3/4 based boards.
|
||||
|
||||
if DRM_OMAP
|
||||
|
||||
config DRM_OMAP_NUM_CRTCS
|
||||
int "Number of CRTCs"
|
||||
range 1 10
|
||||
default 1 if ARCH_OMAP2 || ARCH_OMAP3
|
||||
default 2 if ARCH_OMAP4
|
||||
depends on DRM_OMAP
|
||||
help
|
||||
Select the number of video overlays which can be used as framebuffers.
|
||||
The remaining overlays are reserved for video.
|
||||
|
||||
source "drivers/gpu/drm/omapdrm/dss/Kconfig"
|
||||
source "drivers/gpu/drm/omapdrm/displays/Kconfig"
|
||||
|
||||
endif
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
# Direct Rendering Infrastructure (DRI)
|
||||
#
|
||||
|
||||
obj-y += dss/
|
||||
obj-y += displays/
|
||||
|
||||
ccflags-y := -Iinclude/drm -Werror
|
||||
omapdrm-y := omap_drv.o \
|
||||
omap_irq.o \
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
menu "OMAP Display Device Drivers (new device model)"
|
||||
depends on OMAP2_DSS
|
||||
menu "OMAPDRM External Display Device Drivers"
|
||||
|
||||
config DISPLAY_ENCODER_OPA362
|
||||
tristate "OPA362 external analog amplifier"
|
|
@ -5,9 +5,9 @@ config VIDEO_OMAP2_VOUT
|
|||
tristate "OMAP2/OMAP3 V4L2-Display driver"
|
||||
depends on MMU
|
||||
depends on ARCH_OMAP2 || ARCH_OMAP3
|
||||
depends on FB_OMAP2
|
||||
select VIDEOBUF_GEN
|
||||
select VIDEOBUF_DMA_CONTIG
|
||||
select OMAP2_DSS if HAS_IOMEM && ARCH_OMAP2PLUS
|
||||
select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3
|
||||
select VIDEO_OMAP2_VOUT_VRFB if VIDEO_OMAP2_VOUT && OMAP2_VRFB
|
||||
select FRAME_VECTOR
|
||||
|
|
|
@ -1,10 +1,5 @@
|
|||
config OMAP2_VRFB
|
||||
bool
|
||||
|
||||
if ARCH_OMAP2PLUS
|
||||
|
||||
source "drivers/video/fbdev/omap2/dss/Kconfig"
|
||||
source "drivers/video/fbdev/omap2/omapfb/Kconfig"
|
||||
source "drivers/video/fbdev/omap2/displays-new/Kconfig"
|
||||
|
||||
endif
|
||||
|
|
|
@ -1,5 +1 @@
|
|||
obj-$(CONFIG_OMAP2_VRFB) += vrfb.o
|
||||
|
||||
obj-y += dss/
|
||||
obj-y += displays-new/
|
||||
obj-$(CONFIG_FB_OMAP2) += omapfb/
|
||||
obj-y += omapfb/
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
config OMAP2_VRFB
|
||||
bool
|
||||
|
||||
menuconfig FB_OMAP2
|
||||
tristate "OMAP2+ frame buffer support"
|
||||
depends on FB && OMAP2_DSS && !DRM_OMAP
|
||||
depends on FB
|
||||
depends on DRM_OMAP = n
|
||||
|
||||
select FB_OMAP2_DSS
|
||||
select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3
|
||||
select FB_CFB_FILLRECT
|
||||
select FB_CFB_COPYAREA
|
||||
|
@ -9,6 +14,8 @@ menuconfig FB_OMAP2
|
|||
help
|
||||
Frame buffer driver for OMAP2+ based boards.
|
||||
|
||||
if FB_OMAP2
|
||||
|
||||
config FB_OMAP2_DEBUG_SUPPORT
|
||||
bool "Debug support for OMAP2+ FB"
|
||||
default y
|
||||
|
@ -25,3 +32,8 @@ config FB_OMAP2_NUM_FBS
|
|||
help
|
||||
Select the number of framebuffers created. OMAP2/3 has 3 overlays
|
||||
so normally this would be 3.
|
||||
|
||||
source "drivers/video/fbdev/omap2/omapfb/dss/Kconfig"
|
||||
source "drivers/video/fbdev/omap2/omapfb/displays/Kconfig"
|
||||
|
||||
endif
|
||||
|
|
|
@ -1,2 +1,5 @@
|
|||
obj-$(CONFIG_OMAP2_VRFB) += vrfb.o
|
||||
obj-y += dss/
|
||||
obj-y += displays/
|
||||
obj-$(CONFIG_FB_OMAP2) += omapfb.o
|
||||
omapfb-y := omapfb-main.o omapfb-sysfs.o omapfb-ioctl.o
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
menu "OMAPFB Panel and Encoder Drivers"
|
||||
depends on FB_OMAP2_DSS
|
||||
|
||||
config FB_OMAP2_ENCODER_OPA362
|
||||
tristate "OPA362 external analog amplifier"
|
||||
help
|
||||
Driver for OPA362 external analog TV amplifier controlled
|
||||
through a GPIO.
|
||||
|
||||
config FB_OMAP2_ENCODER_TFP410
|
||||
tristate "TFP410 DPI to DVI Encoder"
|
||||
help
|
||||
Driver for TFP410 DPI to DVI encoder.
|
||||
|
||||
config FB_OMAP2_ENCODER_TPD12S015
|
||||
tristate "TPD12S015 HDMI ESD protection and level shifter"
|
||||
help
|
||||
Driver for TPD12S015, which offers HDMI ESD protection and level
|
||||
shifting.
|
||||
|
||||
config FB_OMAP2_CONNECTOR_DVI
|
||||
tristate "DVI Connector"
|
||||
depends on I2C
|
||||
help
|
||||
Driver for a generic DVI connector.
|
||||
|
||||
config FB_OMAP2_CONNECTOR_HDMI
|
||||
tristate "HDMI Connector"
|
||||
help
|
||||
Driver for a generic HDMI connector.
|
||||
|
||||
config FB_OMAP2_CONNECTOR_ANALOG_TV
|
||||
tristate "Analog TV Connector"
|
||||
help
|
||||
Driver for a generic analog TV connector.
|
||||
|
||||
config FB_OMAP2_PANEL_DPI
|
||||
tristate "Generic DPI panel"
|
||||
help
|
||||
Driver for generic DPI panels.
|
||||
|
||||
config FB_OMAP2_PANEL_DSI_CM
|
||||
tristate "Generic DSI Command Mode Panel"
|
||||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
help
|
||||
Driver for generic DSI command mode panels.
|
||||
|
||||
config FB_OMAP2_PANEL_SONY_ACX565AKM
|
||||
tristate "ACX565AKM Panel"
|
||||
depends on SPI && BACKLIGHT_CLASS_DEVICE
|
||||
help
|
||||
This is the LCD panel used on Nokia N900
|
||||
|
||||
config FB_OMAP2_PANEL_LGPHILIPS_LB035Q02
|
||||
tristate "LG.Philips LB035Q02 LCD Panel"
|
||||
depends on SPI
|
||||
help
|
||||
LCD Panel used on the Gumstix Overo Palo35
|
||||
|
||||
config FB_OMAP2_PANEL_SHARP_LS037V7DW01
|
||||
tristate "Sharp LS037V7DW01 LCD Panel"
|
||||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
help
|
||||
LCD Panel used in TI's SDP3430 and EVM boards
|
||||
|
||||
config FB_OMAP2_PANEL_TPO_TD028TTEC1
|
||||
tristate "TPO TD028TTEC1 LCD Panel"
|
||||
depends on SPI
|
||||
help
|
||||
LCD panel used in Openmoko.
|
||||
|
||||
config FB_OMAP2_PANEL_TPO_TD043MTEA1
|
||||
tristate "TPO TD043MTEA1 LCD Panel"
|
||||
depends on SPI
|
||||
help
|
||||
LCD Panel used in OMAP3 Pandora
|
||||
|
||||
config FB_OMAP2_PANEL_NEC_NL8048HL11
|
||||
tristate "NEC NL8048HL11 Panel"
|
||||
depends on SPI
|
||||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
help
|
||||
This NEC NL8048HL11 panel is TFT LCD used in the
|
||||
Zoom2/3/3630 sdp boards.
|
||||
|
||||
endmenu
|
|
@ -0,0 +1,14 @@
|
|||
obj-$(CONFIG_FB_OMAP2_ENCODER_OPA362) += encoder-opa362.o
|
||||
obj-$(CONFIG_FB_OMAP2_ENCODER_TFP410) += encoder-tfp410.o
|
||||
obj-$(CONFIG_FB_OMAP2_ENCODER_TPD12S015) += encoder-tpd12s015.o
|
||||
obj-$(CONFIG_FB_OMAP2_CONNECTOR_DVI) += connector-dvi.o
|
||||
obj-$(CONFIG_FB_OMAP2_CONNECTOR_HDMI) += connector-hdmi.o
|
||||
obj-$(CONFIG_FB_OMAP2_CONNECTOR_ANALOG_TV) += connector-analog-tv.o
|
||||
obj-$(CONFIG_FB_OMAP2_PANEL_DPI) += panel-dpi.o
|
||||
obj-$(CONFIG_FB_OMAP2_PANEL_DSI_CM) += panel-dsi-cm.o
|
||||
obj-$(CONFIG_FB_OMAP2_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o
|
||||
obj-$(CONFIG_FB_OMAP2_PANEL_LGPHILIPS_LB035Q02) += panel-lgphilips-lb035q02.o
|
||||
obj-$(CONFIG_FB_OMAP2_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o
|
||||
obj-$(CONFIG_FB_OMAP2_PANEL_TPO_TD028TTEC1) += panel-tpo-td028ttec1.o
|
||||
obj-$(CONFIG_FB_OMAP2_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o
|
||||
obj-$(CONFIG_FB_OMAP2_PANEL_NEC_NL8048HL11) += panel-nec-nl8048hl11.o
|
|
@ -0,0 +1,320 @@
|
|||
/*
|
||||
* Analog TV Connector driver
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments
|
||||
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <video/omapdss.h>
|
||||
#include <video/omap-panel-data.h>
|
||||
|
||||
struct panel_drv_data {
|
||||
struct omap_dss_device dssdev;
|
||||
struct omap_dss_device *in;
|
||||
|
||||
struct device *dev;
|
||||
|
||||
struct omap_video_timings timings;
|
||||
|
||||
enum omap_dss_venc_type connector_type;
|
||||
bool invert_polarity;
|
||||
};
|
||||
|
||||
static const struct omap_video_timings tvc_pal_timings = {
|
||||
.x_res = 720,
|
||||
.y_res = 574,
|
||||
.pixelclock = 13500000,
|
||||
.hsw = 64,
|
||||
.hfp = 12,
|
||||
.hbp = 68,
|
||||
.vsw = 5,
|
||||
.vfp = 5,
|
||||
.vbp = 41,
|
||||
|
||||
.interlace = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id tvc_of_match[];
|
||||
|
||||
struct tvc_of_data {
|
||||
enum omap_dss_venc_type connector_type;
|
||||
};
|
||||
|
||||
#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
|
||||
|
||||
static int tvc_connect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
dev_dbg(ddata->dev, "connect\n");
|
||||
|
||||
if (omapdss_device_is_connected(dssdev))
|
||||
return 0;
|
||||
|
||||
r = in->ops.atv->connect(in, dssdev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tvc_disconnect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
dev_dbg(ddata->dev, "disconnect\n");
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return;
|
||||
|
||||
in->ops.atv->disconnect(in, dssdev);
|
||||
}
|
||||
|
||||
static int tvc_enable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
dev_dbg(ddata->dev, "enable\n");
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return -ENODEV;
|
||||
|
||||
if (omapdss_device_is_enabled(dssdev))
|
||||
return 0;
|
||||
|
||||
in->ops.atv->set_timings(in, &ddata->timings);
|
||||
|
||||
if (!ddata->dev->of_node) {
|
||||
in->ops.atv->set_type(in, ddata->connector_type);
|
||||
|
||||
in->ops.atv->invert_vid_out_polarity(in,
|
||||
ddata->invert_polarity);
|
||||
}
|
||||
|
||||
r = in->ops.atv->enable(in);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void tvc_disable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
dev_dbg(ddata->dev, "disable\n");
|
||||
|
||||
if (!omapdss_device_is_enabled(dssdev))
|
||||
return;
|
||||
|
||||
in->ops.atv->disable(in);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
|
||||
}
|
||||
|
||||
static void tvc_set_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
ddata->timings = *timings;
|
||||
dssdev->panel.timings = *timings;
|
||||
|
||||
in->ops.atv->set_timings(in, timings);
|
||||
}
|
||||
|
||||
static void tvc_get_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
|
||||
*timings = ddata->timings;
|
||||
}
|
||||
|
||||
static int tvc_check_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
return in->ops.atv->check_timings(in, timings);
|
||||
}
|
||||
|
||||
static u32 tvc_get_wss(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
return in->ops.atv->get_wss(in);
|
||||
}
|
||||
|
||||
static int tvc_set_wss(struct omap_dss_device *dssdev, u32 wss)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
return in->ops.atv->set_wss(in, wss);
|
||||
}
|
||||
|
||||
static struct omap_dss_driver tvc_driver = {
|
||||
.connect = tvc_connect,
|
||||
.disconnect = tvc_disconnect,
|
||||
|
||||
.enable = tvc_enable,
|
||||
.disable = tvc_disable,
|
||||
|
||||
.set_timings = tvc_set_timings,
|
||||
.get_timings = tvc_get_timings,
|
||||
.check_timings = tvc_check_timings,
|
||||
|
||||
.get_resolution = omapdss_default_get_resolution,
|
||||
|
||||
.get_wss = tvc_get_wss,
|
||||
.set_wss = tvc_set_wss,
|
||||
};
|
||||
|
||||
static int tvc_probe_pdata(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct connector_atv_platform_data *pdata;
|
||||
struct omap_dss_device *in, *dssdev;
|
||||
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
|
||||
in = omap_dss_find_output(pdata->source);
|
||||
if (in == NULL) {
|
||||
dev_err(&pdev->dev, "Failed to find video source\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
ddata->connector_type = pdata->connector_type;
|
||||
ddata->invert_polarity = pdata->invert_polarity;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->name = pdata->name;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tvc_probe_of(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct omap_dss_device *in;
|
||||
|
||||
in = omapdss_of_find_source_for_first_ep(node);
|
||||
if (IS_ERR(in)) {
|
||||
dev_err(&pdev->dev, "failed to find video source\n");
|
||||
return PTR_ERR(in);
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tvc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata;
|
||||
struct omap_dss_device *dssdev;
|
||||
int r;
|
||||
|
||||
ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
|
||||
if (!ddata)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, ddata);
|
||||
ddata->dev = &pdev->dev;
|
||||
|
||||
if (dev_get_platdata(&pdev->dev)) {
|
||||
r = tvc_probe_pdata(pdev);
|
||||
if (r)
|
||||
return r;
|
||||
} else if (pdev->dev.of_node) {
|
||||
r = tvc_probe_of(pdev);
|
||||
if (r)
|
||||
return r;
|
||||
} else {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ddata->timings = tvc_pal_timings;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->driver = &tvc_driver;
|
||||
dssdev->dev = &pdev->dev;
|
||||
dssdev->type = OMAP_DISPLAY_TYPE_VENC;
|
||||
dssdev->owner = THIS_MODULE;
|
||||
dssdev->panel.timings = tvc_pal_timings;
|
||||
|
||||
r = omapdss_register_display(dssdev);
|
||||
if (r) {
|
||||
dev_err(&pdev->dev, "Failed to register panel\n");
|
||||
goto err_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err_reg:
|
||||
omap_dss_put_device(ddata->in);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int __exit tvc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct omap_dss_device *dssdev = &ddata->dssdev;
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
omapdss_unregister_display(&ddata->dssdev);
|
||||
|
||||
tvc_disable(dssdev);
|
||||
tvc_disconnect(dssdev);
|
||||
|
||||
omap_dss_put_device(in);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id tvc_of_match[] = {
|
||||
{ .compatible = "omapdss,svideo-connector", },
|
||||
{ .compatible = "omapdss,composite-video-connector", },
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, tvc_of_match);
|
||||
|
||||
static struct platform_driver tvc_connector_driver = {
|
||||
.probe = tvc_probe,
|
||||
.remove = __exit_p(tvc_remove),
|
||||
.driver = {
|
||||
.name = "connector-analog-tv",
|
||||
.of_match_table = tvc_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(tvc_connector_driver);
|
||||
|
||||
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
|
||||
MODULE_DESCRIPTION("Analog TV Connector driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,398 @@
|
|||
/*
|
||||
* Generic DVI Connector driver
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments
|
||||
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <drm/drm_edid.h>
|
||||
|
||||
#include <video/omapdss.h>
|
||||
#include <video/omap-panel-data.h>
|
||||
|
||||
static const struct omap_video_timings dvic_default_timings = {
|
||||
.x_res = 640,
|
||||
.y_res = 480,
|
||||
|
||||
.pixelclock = 23500000,
|
||||
|
||||
.hfp = 48,
|
||||
.hsw = 32,
|
||||
.hbp = 80,
|
||||
|
||||
.vfp = 3,
|
||||
.vsw = 4,
|
||||
.vbp = 7,
|
||||
|
||||
.vsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
|
||||
.hsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
|
||||
.data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
|
||||
.de_level = OMAPDSS_SIG_ACTIVE_HIGH,
|
||||
.sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
|
||||
};
|
||||
|
||||
struct panel_drv_data {
|
||||
struct omap_dss_device dssdev;
|
||||
struct omap_dss_device *in;
|
||||
|
||||
struct omap_video_timings timings;
|
||||
|
||||
struct i2c_adapter *i2c_adapter;
|
||||
};
|
||||
|
||||
#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
|
||||
|
||||
static int dvic_connect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (omapdss_device_is_connected(dssdev))
|
||||
return 0;
|
||||
|
||||
r = in->ops.dvi->connect(in, dssdev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dvic_disconnect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return;
|
||||
|
||||
in->ops.dvi->disconnect(in, dssdev);
|
||||
}
|
||||
|
||||
static int dvic_enable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return -ENODEV;
|
||||
|
||||
if (omapdss_device_is_enabled(dssdev))
|
||||
return 0;
|
||||
|
||||
in->ops.dvi->set_timings(in, &ddata->timings);
|
||||
|
||||
r = in->ops.dvi->enable(in);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dvic_disable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
if (!omapdss_device_is_enabled(dssdev))
|
||||
return;
|
||||
|
||||
in->ops.dvi->disable(in);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
|
||||
}
|
||||
|
||||
static void dvic_set_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
ddata->timings = *timings;
|
||||
dssdev->panel.timings = *timings;
|
||||
|
||||
in->ops.dvi->set_timings(in, timings);
|
||||
}
|
||||
|
||||
static void dvic_get_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
|
||||
*timings = ddata->timings;
|
||||
}
|
||||
|
||||
static int dvic_check_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
return in->ops.dvi->check_timings(in, timings);
|
||||
}
|
||||
|
||||
static int dvic_ddc_read(struct i2c_adapter *adapter,
|
||||
unsigned char *buf, u16 count, u8 offset)
|
||||
{
|
||||
int r, retries;
|
||||
|
||||
for (retries = 3; retries > 0; retries--) {
|
||||
struct i2c_msg msgs[] = {
|
||||
{
|
||||
.addr = DDC_ADDR,
|
||||
.flags = 0,
|
||||
.len = 1,
|
||||
.buf = &offset,
|
||||
}, {
|
||||
.addr = DDC_ADDR,
|
||||
.flags = I2C_M_RD,
|
||||
.len = count,
|
||||
.buf = buf,
|
||||
}
|
||||
};
|
||||
|
||||
r = i2c_transfer(adapter, msgs, 2);
|
||||
if (r == 2)
|
||||
return 0;
|
||||
|
||||
if (r != -EAGAIN)
|
||||
break;
|
||||
}
|
||||
|
||||
return r < 0 ? r : -EIO;
|
||||
}
|
||||
|
||||
static int dvic_read_edid(struct omap_dss_device *dssdev,
|
||||
u8 *edid, int len)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
int r, l, bytes_read;
|
||||
|
||||
if (!ddata->i2c_adapter)
|
||||
return -ENODEV;
|
||||
|
||||
l = min(EDID_LENGTH, len);
|
||||
r = dvic_ddc_read(ddata->i2c_adapter, edid, l, 0);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
bytes_read = l;
|
||||
|
||||
/* if there are extensions, read second block */
|
||||
if (len > EDID_LENGTH && edid[0x7e] > 0) {
|
||||
l = min(EDID_LENGTH, len - EDID_LENGTH);
|
||||
|
||||
r = dvic_ddc_read(ddata->i2c_adapter, edid + EDID_LENGTH,
|
||||
l, EDID_LENGTH);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
bytes_read += l;
|
||||
}
|
||||
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
static bool dvic_detect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
unsigned char out;
|
||||
int r;
|
||||
|
||||
if (!ddata->i2c_adapter)
|
||||
return true;
|
||||
|
||||
r = dvic_ddc_read(ddata->i2c_adapter, &out, 1, 0);
|
||||
|
||||
return r == 0;
|
||||
}
|
||||
|
||||
static struct omap_dss_driver dvic_driver = {
|
||||
.connect = dvic_connect,
|
||||
.disconnect = dvic_disconnect,
|
||||
|
||||
.enable = dvic_enable,
|
||||
.disable = dvic_disable,
|
||||
|
||||
.set_timings = dvic_set_timings,
|
||||
.get_timings = dvic_get_timings,
|
||||
.check_timings = dvic_check_timings,
|
||||
|
||||
.get_resolution = omapdss_default_get_resolution,
|
||||
|
||||
.read_edid = dvic_read_edid,
|
||||
.detect = dvic_detect,
|
||||
};
|
||||
|
||||
static int dvic_probe_pdata(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct connector_dvi_platform_data *pdata;
|
||||
struct omap_dss_device *in, *dssdev;
|
||||
int i2c_bus_num;
|
||||
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
i2c_bus_num = pdata->i2c_bus_num;
|
||||
|
||||
if (i2c_bus_num != -1) {
|
||||
struct i2c_adapter *adapter;
|
||||
|
||||
adapter = i2c_get_adapter(i2c_bus_num);
|
||||
if (!adapter) {
|
||||
dev_err(&pdev->dev,
|
||||
"Failed to get I2C adapter, bus %d\n",
|
||||
i2c_bus_num);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
ddata->i2c_adapter = adapter;
|
||||
}
|
||||
|
||||
in = omap_dss_find_output(pdata->source);
|
||||
if (in == NULL) {
|
||||
i2c_put_adapter(ddata->i2c_adapter);
|
||||
|
||||
dev_err(&pdev->dev, "Failed to find video source\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->name = pdata->name;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dvic_probe_of(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct omap_dss_device *in;
|
||||
struct device_node *adapter_node;
|
||||
struct i2c_adapter *adapter;
|
||||
|
||||
in = omapdss_of_find_source_for_first_ep(node);
|
||||
if (IS_ERR(in)) {
|
||||
dev_err(&pdev->dev, "failed to find video source\n");
|
||||
return PTR_ERR(in);
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
adapter_node = of_parse_phandle(node, "ddc-i2c-bus", 0);
|
||||
if (adapter_node) {
|
||||
adapter = of_get_i2c_adapter_by_node(adapter_node);
|
||||
if (adapter == NULL) {
|
||||
dev_err(&pdev->dev, "failed to parse ddc-i2c-bus\n");
|
||||
omap_dss_put_device(ddata->in);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
ddata->i2c_adapter = adapter;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dvic_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata;
|
||||
struct omap_dss_device *dssdev;
|
||||
int r;
|
||||
|
||||
ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
|
||||
if (!ddata)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, ddata);
|
||||
|
||||
if (dev_get_platdata(&pdev->dev)) {
|
||||
r = dvic_probe_pdata(pdev);
|
||||
if (r)
|
||||
return r;
|
||||
} else if (pdev->dev.of_node) {
|
||||
r = dvic_probe_of(pdev);
|
||||
if (r)
|
||||
return r;
|
||||
} else {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ddata->timings = dvic_default_timings;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->driver = &dvic_driver;
|
||||
dssdev->dev = &pdev->dev;
|
||||
dssdev->type = OMAP_DISPLAY_TYPE_DVI;
|
||||
dssdev->owner = THIS_MODULE;
|
||||
dssdev->panel.timings = dvic_default_timings;
|
||||
|
||||
r = omapdss_register_display(dssdev);
|
||||
if (r) {
|
||||
dev_err(&pdev->dev, "Failed to register panel\n");
|
||||
goto err_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_reg:
|
||||
omap_dss_put_device(ddata->in);
|
||||
|
||||
i2c_put_adapter(ddata->i2c_adapter);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int __exit dvic_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct omap_dss_device *dssdev = &ddata->dssdev;
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
omapdss_unregister_display(&ddata->dssdev);
|
||||
|
||||
dvic_disable(dssdev);
|
||||
dvic_disconnect(dssdev);
|
||||
|
||||
omap_dss_put_device(in);
|
||||
|
||||
i2c_put_adapter(ddata->i2c_adapter);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id dvic_of_match[] = {
|
||||
{ .compatible = "omapdss,dvi-connector", },
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, dvic_of_match);
|
||||
|
||||
static struct platform_driver dvi_connector_driver = {
|
||||
.probe = dvic_probe,
|
||||
.remove = __exit_p(dvic_remove),
|
||||
.driver = {
|
||||
.name = "connector-dvi",
|
||||
.of_match_table = dvic_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(dvi_connector_driver);
|
||||
|
||||
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
|
||||
MODULE_DESCRIPTION("Generic DVI Connector driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,348 @@
|
|||
/*
|
||||
* HDMI Connector driver
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments
|
||||
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include <drm/drm_edid.h>
|
||||
|
||||
#include <video/omapdss.h>
|
||||
#include <video/omap-panel-data.h>
|
||||
|
||||
static const struct omap_video_timings hdmic_default_timings = {
|
||||
.x_res = 640,
|
||||
.y_res = 480,
|
||||
.pixelclock = 25175000,
|
||||
.hsw = 96,
|
||||
.hfp = 16,
|
||||
.hbp = 48,
|
||||
.vsw = 2,
|
||||
.vfp = 11,
|
||||
.vbp = 31,
|
||||
|
||||
.vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
|
||||
.hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
|
||||
|
||||
.interlace = false,
|
||||
};
|
||||
|
||||
struct panel_drv_data {
|
||||
struct omap_dss_device dssdev;
|
||||
struct omap_dss_device *in;
|
||||
|
||||
struct device *dev;
|
||||
|
||||
struct omap_video_timings timings;
|
||||
|
||||
int hpd_gpio;
|
||||
};
|
||||
|
||||
#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
|
||||
|
||||
static int hdmic_connect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
dev_dbg(ddata->dev, "connect\n");
|
||||
|
||||
if (omapdss_device_is_connected(dssdev))
|
||||
return 0;
|
||||
|
||||
r = in->ops.hdmi->connect(in, dssdev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hdmic_disconnect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
dev_dbg(ddata->dev, "disconnect\n");
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return;
|
||||
|
||||
in->ops.hdmi->disconnect(in, dssdev);
|
||||
}
|
||||
|
||||
static int hdmic_enable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
dev_dbg(ddata->dev, "enable\n");
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return -ENODEV;
|
||||
|
||||
if (omapdss_device_is_enabled(dssdev))
|
||||
return 0;
|
||||
|
||||
in->ops.hdmi->set_timings(in, &ddata->timings);
|
||||
|
||||
r = in->ops.hdmi->enable(in);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void hdmic_disable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
dev_dbg(ddata->dev, "disable\n");
|
||||
|
||||
if (!omapdss_device_is_enabled(dssdev))
|
||||
return;
|
||||
|
||||
in->ops.hdmi->disable(in);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
|
||||
}
|
||||
|
||||
static void hdmic_set_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
ddata->timings = *timings;
|
||||
dssdev->panel.timings = *timings;
|
||||
|
||||
in->ops.hdmi->set_timings(in, timings);
|
||||
}
|
||||
|
||||
static void hdmic_get_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
|
||||
*timings = ddata->timings;
|
||||
}
|
||||
|
||||
static int hdmic_check_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
return in->ops.hdmi->check_timings(in, timings);
|
||||
}
|
||||
|
||||
static int hdmic_read_edid(struct omap_dss_device *dssdev,
|
||||
u8 *edid, int len)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
return in->ops.hdmi->read_edid(in, edid, len);
|
||||
}
|
||||
|
||||
static bool hdmic_detect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
if (gpio_is_valid(ddata->hpd_gpio))
|
||||
return gpio_get_value_cansleep(ddata->hpd_gpio);
|
||||
else
|
||||
return in->ops.hdmi->detect(in);
|
||||
}
|
||||
|
||||
static int hdmic_set_hdmi_mode(struct omap_dss_device *dssdev, bool hdmi_mode)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
return in->ops.hdmi->set_hdmi_mode(in, hdmi_mode);
|
||||
}
|
||||
|
||||
static int hdmic_set_infoframe(struct omap_dss_device *dssdev,
|
||||
const struct hdmi_avi_infoframe *avi)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
return in->ops.hdmi->set_infoframe(in, avi);
|
||||
}
|
||||
|
||||
static struct omap_dss_driver hdmic_driver = {
|
||||
.connect = hdmic_connect,
|
||||
.disconnect = hdmic_disconnect,
|
||||
|
||||
.enable = hdmic_enable,
|
||||
.disable = hdmic_disable,
|
||||
|
||||
.set_timings = hdmic_set_timings,
|
||||
.get_timings = hdmic_get_timings,
|
||||
.check_timings = hdmic_check_timings,
|
||||
|
||||
.get_resolution = omapdss_default_get_resolution,
|
||||
|
||||
.read_edid = hdmic_read_edid,
|
||||
.detect = hdmic_detect,
|
||||
.set_hdmi_mode = hdmic_set_hdmi_mode,
|
||||
.set_hdmi_infoframe = hdmic_set_infoframe,
|
||||
};
|
||||
|
||||
static int hdmic_probe_pdata(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct connector_hdmi_platform_data *pdata;
|
||||
struct omap_dss_device *in, *dssdev;
|
||||
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
|
||||
ddata->hpd_gpio = -ENODEV;
|
||||
|
||||
in = omap_dss_find_output(pdata->source);
|
||||
if (in == NULL) {
|
||||
dev_err(&pdev->dev, "Failed to find video source\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->name = pdata->name;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdmic_probe_of(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct omap_dss_device *in;
|
||||
int gpio;
|
||||
|
||||
/* HPD GPIO */
|
||||
gpio = of_get_named_gpio(node, "hpd-gpios", 0);
|
||||
if (gpio_is_valid(gpio))
|
||||
ddata->hpd_gpio = gpio;
|
||||
else
|
||||
ddata->hpd_gpio = -ENODEV;
|
||||
|
||||
in = omapdss_of_find_source_for_first_ep(node);
|
||||
if (IS_ERR(in)) {
|
||||
dev_err(&pdev->dev, "failed to find video source\n");
|
||||
return PTR_ERR(in);
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdmic_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata;
|
||||
struct omap_dss_device *dssdev;
|
||||
int r;
|
||||
|
||||
ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
|
||||
if (!ddata)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, ddata);
|
||||
ddata->dev = &pdev->dev;
|
||||
|
||||
if (dev_get_platdata(&pdev->dev)) {
|
||||
r = hdmic_probe_pdata(pdev);
|
||||
if (r)
|
||||
return r;
|
||||
} else if (pdev->dev.of_node) {
|
||||
r = hdmic_probe_of(pdev);
|
||||
if (r)
|
||||
return r;
|
||||
} else {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(ddata->hpd_gpio)) {
|
||||
r = devm_gpio_request_one(&pdev->dev, ddata->hpd_gpio,
|
||||
GPIOF_DIR_IN, "hdmi_hpd");
|
||||
if (r)
|
||||
goto err_reg;
|
||||
}
|
||||
|
||||
ddata->timings = hdmic_default_timings;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->driver = &hdmic_driver;
|
||||
dssdev->dev = &pdev->dev;
|
||||
dssdev->type = OMAP_DISPLAY_TYPE_HDMI;
|
||||
dssdev->owner = THIS_MODULE;
|
||||
dssdev->panel.timings = hdmic_default_timings;
|
||||
|
||||
r = omapdss_register_display(dssdev);
|
||||
if (r) {
|
||||
dev_err(&pdev->dev, "Failed to register panel\n");
|
||||
goto err_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err_reg:
|
||||
omap_dss_put_device(ddata->in);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int __exit hdmic_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct omap_dss_device *dssdev = &ddata->dssdev;
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
omapdss_unregister_display(&ddata->dssdev);
|
||||
|
||||
hdmic_disable(dssdev);
|
||||
hdmic_disconnect(dssdev);
|
||||
|
||||
omap_dss_put_device(in);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id hdmic_of_match[] = {
|
||||
{ .compatible = "omapdss,hdmi-connector", },
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, hdmic_of_match);
|
||||
|
||||
static struct platform_driver hdmi_connector_driver = {
|
||||
.probe = hdmic_probe,
|
||||
.remove = __exit_p(hdmic_remove),
|
||||
.driver = {
|
||||
.name = "connector-hdmi",
|
||||
.of_match_table = hdmic_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(hdmi_connector_driver);
|
||||
|
||||
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
|
||||
MODULE_DESCRIPTION("HDMI Connector driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,278 @@
|
|||
/*
|
||||
* OPA362 analog video amplifier with output/power control
|
||||
*
|
||||
* Copyright (C) 2014 Golden Delicious Computers
|
||||
* Author: H. Nikolaus Schaller <hns@goldelico.com>
|
||||
*
|
||||
* based on encoder-tfp410
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments
|
||||
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include <video/omapdss.h>
|
||||
|
||||
struct panel_drv_data {
|
||||
struct omap_dss_device dssdev;
|
||||
struct omap_dss_device *in;
|
||||
|
||||
struct gpio_desc *enable_gpio;
|
||||
|
||||
struct omap_video_timings timings;
|
||||
};
|
||||
|
||||
#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
|
||||
|
||||
static int opa362_connect(struct omap_dss_device *dssdev,
|
||||
struct omap_dss_device *dst)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
dev_dbg(dssdev->dev, "connect\n");
|
||||
|
||||
if (omapdss_device_is_connected(dssdev))
|
||||
return -EBUSY;
|
||||
|
||||
r = in->ops.atv->connect(in, dssdev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
dst->src = dssdev;
|
||||
dssdev->dst = dst;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void opa362_disconnect(struct omap_dss_device *dssdev,
|
||||
struct omap_dss_device *dst)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
dev_dbg(dssdev->dev, "disconnect\n");
|
||||
|
||||
WARN_ON(!omapdss_device_is_connected(dssdev));
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return;
|
||||
|
||||
WARN_ON(dst != dssdev->dst);
|
||||
if (dst != dssdev->dst)
|
||||
return;
|
||||
|
||||
dst->src = NULL;
|
||||
dssdev->dst = NULL;
|
||||
|
||||
in->ops.atv->disconnect(in, &ddata->dssdev);
|
||||
}
|
||||
|
||||
static int opa362_enable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
dev_dbg(dssdev->dev, "enable\n");
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return -ENODEV;
|
||||
|
||||
if (omapdss_device_is_enabled(dssdev))
|
||||
return 0;
|
||||
|
||||
in->ops.atv->set_timings(in, &ddata->timings);
|
||||
|
||||
r = in->ops.atv->enable(in);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
if (ddata->enable_gpio)
|
||||
gpiod_set_value_cansleep(ddata->enable_gpio, 1);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void opa362_disable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
dev_dbg(dssdev->dev, "disable\n");
|
||||
|
||||
if (!omapdss_device_is_enabled(dssdev))
|
||||
return;
|
||||
|
||||
if (ddata->enable_gpio)
|
||||
gpiod_set_value_cansleep(ddata->enable_gpio, 0);
|
||||
|
||||
in->ops.atv->disable(in);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
|
||||
}
|
||||
|
||||
static void opa362_set_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
dev_dbg(dssdev->dev, "set_timings\n");
|
||||
|
||||
ddata->timings = *timings;
|
||||
dssdev->panel.timings = *timings;
|
||||
|
||||
in->ops.atv->set_timings(in, timings);
|
||||
}
|
||||
|
||||
static void opa362_get_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
|
||||
dev_dbg(dssdev->dev, "get_timings\n");
|
||||
|
||||
*timings = ddata->timings;
|
||||
}
|
||||
|
||||
static int opa362_check_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
dev_dbg(dssdev->dev, "check_timings\n");
|
||||
|
||||
return in->ops.atv->check_timings(in, timings);
|
||||
}
|
||||
|
||||
static void opa362_set_type(struct omap_dss_device *dssdev,
|
||||
enum omap_dss_venc_type type)
|
||||
{
|
||||
/* we can only drive a COMPOSITE output */
|
||||
WARN_ON(type != OMAP_DSS_VENC_TYPE_COMPOSITE);
|
||||
|
||||
}
|
||||
|
||||
static const struct omapdss_atv_ops opa362_atv_ops = {
|
||||
.connect = opa362_connect,
|
||||
.disconnect = opa362_disconnect,
|
||||
|
||||
.enable = opa362_enable,
|
||||
.disable = opa362_disable,
|
||||
|
||||
.check_timings = opa362_check_timings,
|
||||
.set_timings = opa362_set_timings,
|
||||
.get_timings = opa362_get_timings,
|
||||
|
||||
.set_type = opa362_set_type,
|
||||
};
|
||||
|
||||
static int opa362_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct panel_drv_data *ddata;
|
||||
struct omap_dss_device *dssdev, *in;
|
||||
struct gpio_desc *gpio;
|
||||
int r;
|
||||
|
||||
dev_dbg(&pdev->dev, "probe\n");
|
||||
|
||||
if (node == NULL) {
|
||||
dev_err(&pdev->dev, "Unable to find device tree\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
|
||||
if (!ddata)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, ddata);
|
||||
|
||||
gpio = devm_gpiod_get_optional(&pdev->dev, "enable", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(gpio))
|
||||
return PTR_ERR(gpio);
|
||||
|
||||
ddata->enable_gpio = gpio;
|
||||
|
||||
in = omapdss_of_find_source_for_first_ep(node);
|
||||
if (IS_ERR(in)) {
|
||||
dev_err(&pdev->dev, "failed to find video source\n");
|
||||
return PTR_ERR(in);
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->ops.atv = &opa362_atv_ops;
|
||||
dssdev->dev = &pdev->dev;
|
||||
dssdev->type = OMAP_DISPLAY_TYPE_VENC;
|
||||
dssdev->output_type = OMAP_DISPLAY_TYPE_VENC;
|
||||
dssdev->owner = THIS_MODULE;
|
||||
|
||||
r = omapdss_register_output(dssdev);
|
||||
if (r) {
|
||||
dev_err(&pdev->dev, "Failed to register output\n");
|
||||
goto err_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err_reg:
|
||||
omap_dss_put_device(ddata->in);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int __exit opa362_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct omap_dss_device *dssdev = &ddata->dssdev;
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
omapdss_unregister_output(&ddata->dssdev);
|
||||
|
||||
WARN_ON(omapdss_device_is_enabled(dssdev));
|
||||
if (omapdss_device_is_enabled(dssdev))
|
||||
opa362_disable(dssdev);
|
||||
|
||||
WARN_ON(omapdss_device_is_connected(dssdev));
|
||||
if (omapdss_device_is_connected(dssdev))
|
||||
opa362_disconnect(dssdev, dssdev->dst);
|
||||
|
||||
omap_dss_put_device(in);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id opa362_of_match[] = {
|
||||
{ .compatible = "omapdss,ti,opa362", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, opa362_of_match);
|
||||
|
||||
static struct platform_driver opa362_driver = {
|
||||
.probe = opa362_probe,
|
||||
.remove = __exit_p(opa362_remove),
|
||||
.driver = {
|
||||
.name = "amplifier-opa362",
|
||||
.of_match_table = opa362_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(opa362_driver);
|
||||
|
||||
MODULE_AUTHOR("H. Nikolaus Schaller <hns@goldelico.com>");
|
||||
MODULE_DESCRIPTION("OPA362 analog video amplifier with output/power control");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,320 @@
|
|||
/*
|
||||
* TFP410 DPI-to-DVI encoder driver
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments
|
||||
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include <video/omapdss.h>
|
||||
#include <video/omap-panel-data.h>
|
||||
|
||||
struct panel_drv_data {
|
||||
struct omap_dss_device dssdev;
|
||||
struct omap_dss_device *in;
|
||||
|
||||
int pd_gpio;
|
||||
int data_lines;
|
||||
|
||||
struct omap_video_timings timings;
|
||||
};
|
||||
|
||||
#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
|
||||
|
||||
static int tfp410_connect(struct omap_dss_device *dssdev,
|
||||
struct omap_dss_device *dst)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (omapdss_device_is_connected(dssdev))
|
||||
return -EBUSY;
|
||||
|
||||
r = in->ops.dpi->connect(in, dssdev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
dst->src = dssdev;
|
||||
dssdev->dst = dst;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tfp410_disconnect(struct omap_dss_device *dssdev,
|
||||
struct omap_dss_device *dst)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
WARN_ON(!omapdss_device_is_connected(dssdev));
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return;
|
||||
|
||||
WARN_ON(dst != dssdev->dst);
|
||||
if (dst != dssdev->dst)
|
||||
return;
|
||||
|
||||
dst->src = NULL;
|
||||
dssdev->dst = NULL;
|
||||
|
||||
in->ops.dpi->disconnect(in, &ddata->dssdev);
|
||||
}
|
||||
|
||||
static int tfp410_enable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return -ENODEV;
|
||||
|
||||
if (omapdss_device_is_enabled(dssdev))
|
||||
return 0;
|
||||
|
||||
in->ops.dpi->set_timings(in, &ddata->timings);
|
||||
if (ddata->data_lines)
|
||||
in->ops.dpi->set_data_lines(in, ddata->data_lines);
|
||||
|
||||
r = in->ops.dpi->enable(in);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
if (gpio_is_valid(ddata->pd_gpio))
|
||||
gpio_set_value_cansleep(ddata->pd_gpio, 1);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tfp410_disable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
if (!omapdss_device_is_enabled(dssdev))
|
||||
return;
|
||||
|
||||
if (gpio_is_valid(ddata->pd_gpio))
|
||||
gpio_set_value_cansleep(ddata->pd_gpio, 0);
|
||||
|
||||
in->ops.dpi->disable(in);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
|
||||
}
|
||||
|
||||
static void tfp410_fix_timings(struct omap_video_timings *timings)
|
||||
{
|
||||
timings->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
|
||||
timings->sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
|
||||
timings->de_level = OMAPDSS_SIG_ACTIVE_HIGH;
|
||||
}
|
||||
|
||||
static void tfp410_set_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
tfp410_fix_timings(timings);
|
||||
|
||||
ddata->timings = *timings;
|
||||
dssdev->panel.timings = *timings;
|
||||
|
||||
in->ops.dpi->set_timings(in, timings);
|
||||
}
|
||||
|
||||
static void tfp410_get_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
|
||||
*timings = ddata->timings;
|
||||
}
|
||||
|
||||
static int tfp410_check_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
tfp410_fix_timings(timings);
|
||||
|
||||
return in->ops.dpi->check_timings(in, timings);
|
||||
}
|
||||
|
||||
static const struct omapdss_dvi_ops tfp410_dvi_ops = {
|
||||
.connect = tfp410_connect,
|
||||
.disconnect = tfp410_disconnect,
|
||||
|
||||
.enable = tfp410_enable,
|
||||
.disable = tfp410_disable,
|
||||
|
||||
.check_timings = tfp410_check_timings,
|
||||
.set_timings = tfp410_set_timings,
|
||||
.get_timings = tfp410_get_timings,
|
||||
};
|
||||
|
||||
static int tfp410_probe_pdata(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct encoder_tfp410_platform_data *pdata;
|
||||
struct omap_dss_device *dssdev, *in;
|
||||
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
|
||||
ddata->pd_gpio = pdata->power_down_gpio;
|
||||
|
||||
ddata->data_lines = pdata->data_lines;
|
||||
|
||||
in = omap_dss_find_output(pdata->source);
|
||||
if (in == NULL) {
|
||||
dev_err(&pdev->dev, "Failed to find video source\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->name = pdata->name;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tfp410_probe_of(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct omap_dss_device *in;
|
||||
int gpio;
|
||||
|
||||
gpio = of_get_named_gpio(node, "powerdown-gpios", 0);
|
||||
|
||||
if (gpio_is_valid(gpio) || gpio == -ENOENT) {
|
||||
ddata->pd_gpio = gpio;
|
||||
} else {
|
||||
dev_err(&pdev->dev, "failed to parse PD gpio\n");
|
||||
return gpio;
|
||||
}
|
||||
|
||||
in = omapdss_of_find_source_for_first_ep(node);
|
||||
if (IS_ERR(in)) {
|
||||
dev_err(&pdev->dev, "failed to find video source\n");
|
||||
return PTR_ERR(in);
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tfp410_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata;
|
||||
struct omap_dss_device *dssdev;
|
||||
int r;
|
||||
|
||||
ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
|
||||
if (!ddata)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, ddata);
|
||||
|
||||
if (dev_get_platdata(&pdev->dev)) {
|
||||
r = tfp410_probe_pdata(pdev);
|
||||
if (r)
|
||||
return r;
|
||||
} else if (pdev->dev.of_node) {
|
||||
r = tfp410_probe_of(pdev);
|
||||
if (r)
|
||||
return r;
|
||||
} else {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(ddata->pd_gpio)) {
|
||||
r = devm_gpio_request_one(&pdev->dev, ddata->pd_gpio,
|
||||
GPIOF_OUT_INIT_LOW, "tfp410 PD");
|
||||
if (r) {
|
||||
dev_err(&pdev->dev, "Failed to request PD GPIO %d\n",
|
||||
ddata->pd_gpio);
|
||||
goto err_gpio;
|
||||
}
|
||||
}
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->ops.dvi = &tfp410_dvi_ops;
|
||||
dssdev->dev = &pdev->dev;
|
||||
dssdev->type = OMAP_DISPLAY_TYPE_DPI;
|
||||
dssdev->output_type = OMAP_DISPLAY_TYPE_DVI;
|
||||
dssdev->owner = THIS_MODULE;
|
||||
dssdev->phy.dpi.data_lines = ddata->data_lines;
|
||||
dssdev->port_num = 1;
|
||||
|
||||
r = omapdss_register_output(dssdev);
|
||||
if (r) {
|
||||
dev_err(&pdev->dev, "Failed to register output\n");
|
||||
goto err_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err_reg:
|
||||
err_gpio:
|
||||
omap_dss_put_device(ddata->in);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int __exit tfp410_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct omap_dss_device *dssdev = &ddata->dssdev;
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
omapdss_unregister_output(&ddata->dssdev);
|
||||
|
||||
WARN_ON(omapdss_device_is_enabled(dssdev));
|
||||
if (omapdss_device_is_enabled(dssdev))
|
||||
tfp410_disable(dssdev);
|
||||
|
||||
WARN_ON(omapdss_device_is_connected(dssdev));
|
||||
if (omapdss_device_is_connected(dssdev))
|
||||
tfp410_disconnect(dssdev, dssdev->dst);
|
||||
|
||||
omap_dss_put_device(in);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id tfp410_of_match[] = {
|
||||
{ .compatible = "omapdss,ti,tfp410", },
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, tfp410_of_match);
|
||||
|
||||
static struct platform_driver tfp410_driver = {
|
||||
.probe = tfp410_probe,
|
||||
.remove = __exit_p(tfp410_remove),
|
||||
.driver = {
|
||||
.name = "tfp410",
|
||||
.of_match_table = tfp410_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(tfp410_driver);
|
||||
|
||||
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
|
||||
MODULE_DESCRIPTION("TFP410 DPI to DVI encoder driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,328 @@
|
|||
/*
|
||||
* TPD12S015 HDMI ESD protection & level shifter chip driver
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments
|
||||
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/completion.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
#include <video/omapdss.h>
|
||||
#include <video/omap-panel-data.h>
|
||||
|
||||
struct panel_drv_data {
|
||||
struct omap_dss_device dssdev;
|
||||
struct omap_dss_device *in;
|
||||
|
||||
struct gpio_desc *ct_cp_hpd_gpio;
|
||||
struct gpio_desc *ls_oe_gpio;
|
||||
struct gpio_desc *hpd_gpio;
|
||||
|
||||
struct omap_video_timings timings;
|
||||
};
|
||||
|
||||
#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
|
||||
|
||||
static int tpd_connect(struct omap_dss_device *dssdev,
|
||||
struct omap_dss_device *dst)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
r = in->ops.hdmi->connect(in, dssdev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
dst->src = dssdev;
|
||||
dssdev->dst = dst;
|
||||
|
||||
if (ddata->ct_cp_hpd_gpio) {
|
||||
gpiod_set_value_cansleep(ddata->ct_cp_hpd_gpio, 1);
|
||||
/* DC-DC converter needs at max 300us to get to 90% of 5V */
|
||||
udelay(300);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tpd_disconnect(struct omap_dss_device *dssdev,
|
||||
struct omap_dss_device *dst)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
WARN_ON(dst != dssdev->dst);
|
||||
|
||||
if (dst != dssdev->dst)
|
||||
return;
|
||||
|
||||
gpiod_set_value_cansleep(ddata->ct_cp_hpd_gpio, 0);
|
||||
|
||||
dst->src = NULL;
|
||||
dssdev->dst = NULL;
|
||||
|
||||
in->ops.hdmi->disconnect(in, &ddata->dssdev);
|
||||
}
|
||||
|
||||
static int tpd_enable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
|
||||
return 0;
|
||||
|
||||
in->ops.hdmi->set_timings(in, &ddata->timings);
|
||||
|
||||
r = in->ops.hdmi->enable(in);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void tpd_disable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
|
||||
return;
|
||||
|
||||
in->ops.hdmi->disable(in);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
|
||||
}
|
||||
|
||||
static void tpd_set_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
ddata->timings = *timings;
|
||||
dssdev->panel.timings = *timings;
|
||||
|
||||
in->ops.hdmi->set_timings(in, timings);
|
||||
}
|
||||
|
||||
static void tpd_get_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
|
||||
*timings = ddata->timings;
|
||||
}
|
||||
|
||||
static int tpd_check_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
r = in->ops.hdmi->check_timings(in, timings);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int tpd_read_edid(struct omap_dss_device *dssdev,
|
||||
u8 *edid, int len)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (!gpiod_get_value_cansleep(ddata->hpd_gpio))
|
||||
return -ENODEV;
|
||||
|
||||
gpiod_set_value_cansleep(ddata->ls_oe_gpio, 1);
|
||||
|
||||
r = in->ops.hdmi->read_edid(in, edid, len);
|
||||
|
||||
gpiod_set_value_cansleep(ddata->ls_oe_gpio, 0);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static bool tpd_detect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
|
||||
return gpiod_get_value_cansleep(ddata->hpd_gpio);
|
||||
}
|
||||
|
||||
static int tpd_set_infoframe(struct omap_dss_device *dssdev,
|
||||
const struct hdmi_avi_infoframe *avi)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
return in->ops.hdmi->set_infoframe(in, avi);
|
||||
}
|
||||
|
||||
static int tpd_set_hdmi_mode(struct omap_dss_device *dssdev,
|
||||
bool hdmi_mode)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
return in->ops.hdmi->set_hdmi_mode(in, hdmi_mode);
|
||||
}
|
||||
|
||||
static const struct omapdss_hdmi_ops tpd_hdmi_ops = {
|
||||
.connect = tpd_connect,
|
||||
.disconnect = tpd_disconnect,
|
||||
|
||||
.enable = tpd_enable,
|
||||
.disable = tpd_disable,
|
||||
|
||||
.check_timings = tpd_check_timings,
|
||||
.set_timings = tpd_set_timings,
|
||||
.get_timings = tpd_get_timings,
|
||||
|
||||
.read_edid = tpd_read_edid,
|
||||
.detect = tpd_detect,
|
||||
.set_infoframe = tpd_set_infoframe,
|
||||
.set_hdmi_mode = tpd_set_hdmi_mode,
|
||||
};
|
||||
|
||||
static int tpd_probe_of(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct omap_dss_device *in;
|
||||
|
||||
in = omapdss_of_find_source_for_first_ep(node);
|
||||
if (IS_ERR(in)) {
|
||||
dev_err(&pdev->dev, "failed to find video source\n");
|
||||
return PTR_ERR(in);
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpd_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct omap_dss_device *in, *dssdev;
|
||||
struct panel_drv_data *ddata;
|
||||
int r;
|
||||
struct gpio_desc *gpio;
|
||||
|
||||
ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
|
||||
if (!ddata)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, ddata);
|
||||
|
||||
if (pdev->dev.of_node) {
|
||||
r = tpd_probe_of(pdev);
|
||||
if (r)
|
||||
return r;
|
||||
} else {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
||||
gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 0,
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(gpio))
|
||||
goto err_gpio;
|
||||
|
||||
ddata->ct_cp_hpd_gpio = gpio;
|
||||
|
||||
gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 1,
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(gpio))
|
||||
goto err_gpio;
|
||||
|
||||
ddata->ls_oe_gpio = gpio;
|
||||
|
||||
gpio = devm_gpiod_get_index(&pdev->dev, NULL, 2,
|
||||
GPIOD_IN);
|
||||
if (IS_ERR(gpio))
|
||||
goto err_gpio;
|
||||
|
||||
ddata->hpd_gpio = gpio;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->ops.hdmi = &tpd_hdmi_ops;
|
||||
dssdev->dev = &pdev->dev;
|
||||
dssdev->type = OMAP_DISPLAY_TYPE_HDMI;
|
||||
dssdev->output_type = OMAP_DISPLAY_TYPE_HDMI;
|
||||
dssdev->owner = THIS_MODULE;
|
||||
dssdev->port_num = 1;
|
||||
|
||||
in = ddata->in;
|
||||
|
||||
r = omapdss_register_output(dssdev);
|
||||
if (r) {
|
||||
dev_err(&pdev->dev, "Failed to register output\n");
|
||||
goto err_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err_reg:
|
||||
err_gpio:
|
||||
omap_dss_put_device(ddata->in);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int __exit tpd_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct omap_dss_device *dssdev = &ddata->dssdev;
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
omapdss_unregister_output(&ddata->dssdev);
|
||||
|
||||
WARN_ON(omapdss_device_is_enabled(dssdev));
|
||||
if (omapdss_device_is_enabled(dssdev))
|
||||
tpd_disable(dssdev);
|
||||
|
||||
WARN_ON(omapdss_device_is_connected(dssdev));
|
||||
if (omapdss_device_is_connected(dssdev))
|
||||
tpd_disconnect(dssdev, dssdev->dst);
|
||||
|
||||
omap_dss_put_device(in);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id tpd_of_match[] = {
|
||||
{ .compatible = "omapdss,ti,tpd12s015", },
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, tpd_of_match);
|
||||
|
||||
static struct platform_driver tpd_driver = {
|
||||
.probe = tpd_probe,
|
||||
.remove = __exit_p(tpd_remove),
|
||||
.driver = {
|
||||
.name = "tpd12s015",
|
||||
.of_match_table = tpd_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(tpd_driver);
|
||||
|
||||
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
|
||||
MODULE_DESCRIPTION("TPD12S015 driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,328 @@
|
|||
/*
|
||||
* Generic MIPI DPI Panel Driver
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments
|
||||
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include <video/omapdss.h>
|
||||
#include <video/omap-panel-data.h>
|
||||
#include <video/of_display_timing.h>
|
||||
|
||||
struct panel_drv_data {
|
||||
struct omap_dss_device dssdev;
|
||||
struct omap_dss_device *in;
|
||||
|
||||
int data_lines;
|
||||
|
||||
struct omap_video_timings videomode;
|
||||
|
||||
/* used for non-DT boot, to be removed */
|
||||
int backlight_gpio;
|
||||
|
||||
struct gpio_desc *enable_gpio;
|
||||
};
|
||||
|
||||
#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
|
||||
|
||||
static int panel_dpi_connect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (omapdss_device_is_connected(dssdev))
|
||||
return 0;
|
||||
|
||||
r = in->ops.dpi->connect(in, dssdev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void panel_dpi_disconnect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return;
|
||||
|
||||
in->ops.dpi->disconnect(in, dssdev);
|
||||
}
|
||||
|
||||
static int panel_dpi_enable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return -ENODEV;
|
||||
|
||||
if (omapdss_device_is_enabled(dssdev))
|
||||
return 0;
|
||||
|
||||
if (ddata->data_lines)
|
||||
in->ops.dpi->set_data_lines(in, ddata->data_lines);
|
||||
in->ops.dpi->set_timings(in, &ddata->videomode);
|
||||
|
||||
r = in->ops.dpi->enable(in);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
gpiod_set_value_cansleep(ddata->enable_gpio, 1);
|
||||
|
||||
if (gpio_is_valid(ddata->backlight_gpio))
|
||||
gpio_set_value_cansleep(ddata->backlight_gpio, 1);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void panel_dpi_disable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
if (!omapdss_device_is_enabled(dssdev))
|
||||
return;
|
||||
|
||||
if (gpio_is_valid(ddata->backlight_gpio))
|
||||
gpio_set_value_cansleep(ddata->backlight_gpio, 0);
|
||||
|
||||
gpiod_set_value_cansleep(ddata->enable_gpio, 0);
|
||||
|
||||
in->ops.dpi->disable(in);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
|
||||
}
|
||||
|
||||
static void panel_dpi_set_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
ddata->videomode = *timings;
|
||||
dssdev->panel.timings = *timings;
|
||||
|
||||
in->ops.dpi->set_timings(in, timings);
|
||||
}
|
||||
|
||||
static void panel_dpi_get_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
|
||||
*timings = ddata->videomode;
|
||||
}
|
||||
|
||||
static int panel_dpi_check_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
return in->ops.dpi->check_timings(in, timings);
|
||||
}
|
||||
|
||||
static struct omap_dss_driver panel_dpi_ops = {
|
||||
.connect = panel_dpi_connect,
|
||||
.disconnect = panel_dpi_disconnect,
|
||||
|
||||
.enable = panel_dpi_enable,
|
||||
.disable = panel_dpi_disable,
|
||||
|
||||
.set_timings = panel_dpi_set_timings,
|
||||
.get_timings = panel_dpi_get_timings,
|
||||
.check_timings = panel_dpi_check_timings,
|
||||
|
||||
.get_resolution = omapdss_default_get_resolution,
|
||||
};
|
||||
|
||||
static int panel_dpi_probe_pdata(struct platform_device *pdev)
|
||||
{
|
||||
const struct panel_dpi_platform_data *pdata;
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct omap_dss_device *dssdev, *in;
|
||||
struct videomode vm;
|
||||
int r;
|
||||
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
|
||||
in = omap_dss_find_output(pdata->source);
|
||||
if (in == NULL) {
|
||||
dev_err(&pdev->dev, "failed to find video source '%s'\n",
|
||||
pdata->source);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
ddata->data_lines = pdata->data_lines;
|
||||
|
||||
videomode_from_timing(pdata->display_timing, &vm);
|
||||
videomode_to_omap_video_timings(&vm, &ddata->videomode);
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->name = pdata->name;
|
||||
|
||||
r = devm_gpio_request_one(&pdev->dev, pdata->enable_gpio,
|
||||
GPIOF_OUT_INIT_LOW, "panel enable");
|
||||
if (r)
|
||||
goto err_gpio;
|
||||
|
||||
ddata->enable_gpio = gpio_to_desc(pdata->enable_gpio);
|
||||
|
||||
ddata->backlight_gpio = pdata->backlight_gpio;
|
||||
|
||||
return 0;
|
||||
|
||||
err_gpio:
|
||||
omap_dss_put_device(ddata->in);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int panel_dpi_probe_of(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct omap_dss_device *in;
|
||||
int r;
|
||||
struct display_timing timing;
|
||||
struct videomode vm;
|
||||
struct gpio_desc *gpio;
|
||||
|
||||
gpio = devm_gpiod_get_optional(&pdev->dev, "enable", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(gpio))
|
||||
return PTR_ERR(gpio);
|
||||
|
||||
ddata->enable_gpio = gpio;
|
||||
|
||||
ddata->backlight_gpio = -ENOENT;
|
||||
|
||||
r = of_get_display_timing(node, "panel-timing", &timing);
|
||||
if (r) {
|
||||
dev_err(&pdev->dev, "failed to get video timing\n");
|
||||
return r;
|
||||
}
|
||||
|
||||
videomode_from_timing(&timing, &vm);
|
||||
videomode_to_omap_video_timings(&vm, &ddata->videomode);
|
||||
|
||||
in = omapdss_of_find_source_for_first_ep(node);
|
||||
if (IS_ERR(in)) {
|
||||
dev_err(&pdev->dev, "failed to find video source\n");
|
||||
return PTR_ERR(in);
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int panel_dpi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata;
|
||||
struct omap_dss_device *dssdev;
|
||||
int r;
|
||||
|
||||
ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
|
||||
if (ddata == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, ddata);
|
||||
|
||||
if (dev_get_platdata(&pdev->dev)) {
|
||||
r = panel_dpi_probe_pdata(pdev);
|
||||
if (r)
|
||||
return r;
|
||||
} else if (pdev->dev.of_node) {
|
||||
r = panel_dpi_probe_of(pdev);
|
||||
if (r)
|
||||
return r;
|
||||
} else {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(ddata->backlight_gpio)) {
|
||||
r = devm_gpio_request_one(&pdev->dev, ddata->backlight_gpio,
|
||||
GPIOF_OUT_INIT_LOW, "panel backlight");
|
||||
if (r)
|
||||
goto err_gpio;
|
||||
}
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->dev = &pdev->dev;
|
||||
dssdev->driver = &panel_dpi_ops;
|
||||
dssdev->type = OMAP_DISPLAY_TYPE_DPI;
|
||||
dssdev->owner = THIS_MODULE;
|
||||
dssdev->panel.timings = ddata->videomode;
|
||||
dssdev->phy.dpi.data_lines = ddata->data_lines;
|
||||
|
||||
r = omapdss_register_display(dssdev);
|
||||
if (r) {
|
||||
dev_err(&pdev->dev, "Failed to register panel\n");
|
||||
goto err_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_reg:
|
||||
err_gpio:
|
||||
omap_dss_put_device(ddata->in);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int __exit panel_dpi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct omap_dss_device *dssdev = &ddata->dssdev;
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
omapdss_unregister_display(dssdev);
|
||||
|
||||
panel_dpi_disable(dssdev);
|
||||
panel_dpi_disconnect(dssdev);
|
||||
|
||||
omap_dss_put_device(in);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id panel_dpi_of_match[] = {
|
||||
{ .compatible = "omapdss,panel-dpi", },
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, panel_dpi_of_match);
|
||||
|
||||
static struct platform_driver panel_dpi_driver = {
|
||||
.probe = panel_dpi_probe,
|
||||
.remove = __exit_p(panel_dpi_remove),
|
||||
.driver = {
|
||||
.name = "panel-dpi",
|
||||
.of_match_table = panel_dpi_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(panel_dpi_driver);
|
||||
|
||||
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
|
||||
MODULE_DESCRIPTION("Generic MIPI DPI Panel Driver");
|
||||
MODULE_LICENSE("GPL");
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,404 @@
|
|||
/*
|
||||
* LG.Philips LB035Q02 LCD Panel driver
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments
|
||||
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
|
||||
* Based on a driver by: Steve Sakoman <steve@sakoman.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/gpio.h>
|
||||
|
||||
#include <video/omapdss.h>
|
||||
#include <video/omap-panel-data.h>
|
||||
|
||||
static struct omap_video_timings lb035q02_timings = {
|
||||
.x_res = 320,
|
||||
.y_res = 240,
|
||||
|
||||
.pixelclock = 6500000,
|
||||
|
||||
.hsw = 2,
|
||||
.hfp = 20,
|
||||
.hbp = 68,
|
||||
|
||||
.vsw = 2,
|
||||
.vfp = 4,
|
||||
.vbp = 18,
|
||||
|
||||
.vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
|
||||
.hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
|
||||
.data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
|
||||
.de_level = OMAPDSS_SIG_ACTIVE_HIGH,
|
||||
.sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
|
||||
};
|
||||
|
||||
struct panel_drv_data {
|
||||
struct omap_dss_device dssdev;
|
||||
struct omap_dss_device *in;
|
||||
|
||||
struct spi_device *spi;
|
||||
|
||||
int data_lines;
|
||||
|
||||
struct omap_video_timings videomode;
|
||||
|
||||
/* used for non-DT boot, to be removed */
|
||||
int backlight_gpio;
|
||||
|
||||
struct gpio_desc *enable_gpio;
|
||||
};
|
||||
|
||||
#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
|
||||
|
||||
static int lb035q02_write_reg(struct spi_device *spi, u8 reg, u16 val)
|
||||
{
|
||||
struct spi_message msg;
|
||||
struct spi_transfer index_xfer = {
|
||||
.len = 3,
|
||||
.cs_change = 1,
|
||||
};
|
||||
struct spi_transfer value_xfer = {
|
||||
.len = 3,
|
||||
};
|
||||
u8 buffer[16];
|
||||
|
||||
spi_message_init(&msg);
|
||||
|
||||
/* register index */
|
||||
buffer[0] = 0x70;
|
||||
buffer[1] = 0x00;
|
||||
buffer[2] = reg & 0x7f;
|
||||
index_xfer.tx_buf = buffer;
|
||||
spi_message_add_tail(&index_xfer, &msg);
|
||||
|
||||
/* register value */
|
||||
buffer[4] = 0x72;
|
||||
buffer[5] = val >> 8;
|
||||
buffer[6] = val;
|
||||
value_xfer.tx_buf = buffer + 4;
|
||||
spi_message_add_tail(&value_xfer, &msg);
|
||||
|
||||
return spi_sync(spi, &msg);
|
||||
}
|
||||
|
||||
static void init_lb035q02_panel(struct spi_device *spi)
|
||||
{
|
||||
/* Init sequence from page 28 of the lb035q02 spec */
|
||||
lb035q02_write_reg(spi, 0x01, 0x6300);
|
||||
lb035q02_write_reg(spi, 0x02, 0x0200);
|
||||
lb035q02_write_reg(spi, 0x03, 0x0177);
|
||||
lb035q02_write_reg(spi, 0x04, 0x04c7);
|
||||
lb035q02_write_reg(spi, 0x05, 0xffc0);
|
||||
lb035q02_write_reg(spi, 0x06, 0xe806);
|
||||
lb035q02_write_reg(spi, 0x0a, 0x4008);
|
||||
lb035q02_write_reg(spi, 0x0b, 0x0000);
|
||||
lb035q02_write_reg(spi, 0x0d, 0x0030);
|
||||
lb035q02_write_reg(spi, 0x0e, 0x2800);
|
||||
lb035q02_write_reg(spi, 0x0f, 0x0000);
|
||||
lb035q02_write_reg(spi, 0x16, 0x9f80);
|
||||
lb035q02_write_reg(spi, 0x17, 0x0a0f);
|
||||
lb035q02_write_reg(spi, 0x1e, 0x00c1);
|
||||
lb035q02_write_reg(spi, 0x30, 0x0300);
|
||||
lb035q02_write_reg(spi, 0x31, 0x0007);
|
||||
lb035q02_write_reg(spi, 0x32, 0x0000);
|
||||
lb035q02_write_reg(spi, 0x33, 0x0000);
|
||||
lb035q02_write_reg(spi, 0x34, 0x0707);
|
||||
lb035q02_write_reg(spi, 0x35, 0x0004);
|
||||
lb035q02_write_reg(spi, 0x36, 0x0302);
|
||||
lb035q02_write_reg(spi, 0x37, 0x0202);
|
||||
lb035q02_write_reg(spi, 0x3a, 0x0a0d);
|
||||
lb035q02_write_reg(spi, 0x3b, 0x0806);
|
||||
}
|
||||
|
||||
static int lb035q02_connect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (omapdss_device_is_connected(dssdev))
|
||||
return 0;
|
||||
|
||||
r = in->ops.dpi->connect(in, dssdev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
init_lb035q02_panel(ddata->spi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lb035q02_disconnect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return;
|
||||
|
||||
in->ops.dpi->disconnect(in, dssdev);
|
||||
}
|
||||
|
||||
static int lb035q02_enable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return -ENODEV;
|
||||
|
||||
if (omapdss_device_is_enabled(dssdev))
|
||||
return 0;
|
||||
|
||||
if (ddata->data_lines)
|
||||
in->ops.dpi->set_data_lines(in, ddata->data_lines);
|
||||
in->ops.dpi->set_timings(in, &ddata->videomode);
|
||||
|
||||
r = in->ops.dpi->enable(in);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
if (ddata->enable_gpio)
|
||||
gpiod_set_value_cansleep(ddata->enable_gpio, 1);
|
||||
|
||||
if (gpio_is_valid(ddata->backlight_gpio))
|
||||
gpio_set_value_cansleep(ddata->backlight_gpio, 1);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lb035q02_disable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
if (!omapdss_device_is_enabled(dssdev))
|
||||
return;
|
||||
|
||||
if (ddata->enable_gpio)
|
||||
gpiod_set_value_cansleep(ddata->enable_gpio, 0);
|
||||
|
||||
if (gpio_is_valid(ddata->backlight_gpio))
|
||||
gpio_set_value_cansleep(ddata->backlight_gpio, 0);
|
||||
|
||||
in->ops.dpi->disable(in);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
|
||||
}
|
||||
|
||||
static void lb035q02_set_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
ddata->videomode = *timings;
|
||||
dssdev->panel.timings = *timings;
|
||||
|
||||
in->ops.dpi->set_timings(in, timings);
|
||||
}
|
||||
|
||||
static void lb035q02_get_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
|
||||
*timings = ddata->videomode;
|
||||
}
|
||||
|
||||
static int lb035q02_check_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
return in->ops.dpi->check_timings(in, timings);
|
||||
}
|
||||
|
||||
static struct omap_dss_driver lb035q02_ops = {
|
||||
.connect = lb035q02_connect,
|
||||
.disconnect = lb035q02_disconnect,
|
||||
|
||||
.enable = lb035q02_enable,
|
||||
.disable = lb035q02_disable,
|
||||
|
||||
.set_timings = lb035q02_set_timings,
|
||||
.get_timings = lb035q02_get_timings,
|
||||
.check_timings = lb035q02_check_timings,
|
||||
|
||||
.get_resolution = omapdss_default_get_resolution,
|
||||
};
|
||||
|
||||
static int lb035q02_probe_pdata(struct spi_device *spi)
|
||||
{
|
||||
const struct panel_lb035q02_platform_data *pdata;
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
|
||||
struct omap_dss_device *dssdev, *in;
|
||||
int r;
|
||||
|
||||
pdata = dev_get_platdata(&spi->dev);
|
||||
|
||||
in = omap_dss_find_output(pdata->source);
|
||||
if (in == NULL) {
|
||||
dev_err(&spi->dev, "failed to find video source '%s'\n",
|
||||
pdata->source);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
ddata->data_lines = pdata->data_lines;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->name = pdata->name;
|
||||
|
||||
r = devm_gpio_request_one(&spi->dev, pdata->enable_gpio,
|
||||
GPIOF_OUT_INIT_LOW, "panel enable");
|
||||
if (r)
|
||||
goto err_gpio;
|
||||
|
||||
ddata->enable_gpio = gpio_to_desc(pdata->enable_gpio);
|
||||
|
||||
ddata->backlight_gpio = pdata->backlight_gpio;
|
||||
|
||||
return 0;
|
||||
err_gpio:
|
||||
omap_dss_put_device(ddata->in);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int lb035q02_probe_of(struct spi_device *spi)
|
||||
{
|
||||
struct device_node *node = spi->dev.of_node;
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
|
||||
struct omap_dss_device *in;
|
||||
struct gpio_desc *gpio;
|
||||
|
||||
gpio = devm_gpiod_get(&spi->dev, "enable", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(gpio)) {
|
||||
dev_err(&spi->dev, "failed to parse enable gpio\n");
|
||||
return PTR_ERR(gpio);
|
||||
}
|
||||
|
||||
ddata->enable_gpio = gpio;
|
||||
|
||||
ddata->backlight_gpio = -ENOENT;
|
||||
|
||||
in = omapdss_of_find_source_for_first_ep(node);
|
||||
if (IS_ERR(in)) {
|
||||
dev_err(&spi->dev, "failed to find video source\n");
|
||||
return PTR_ERR(in);
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lb035q02_panel_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct panel_drv_data *ddata;
|
||||
struct omap_dss_device *dssdev;
|
||||
int r;
|
||||
|
||||
ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL);
|
||||
if (ddata == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&spi->dev, ddata);
|
||||
|
||||
ddata->spi = spi;
|
||||
|
||||
if (dev_get_platdata(&spi->dev)) {
|
||||
r = lb035q02_probe_pdata(spi);
|
||||
if (r)
|
||||
return r;
|
||||
} else if (spi->dev.of_node) {
|
||||
r = lb035q02_probe_of(spi);
|
||||
if (r)
|
||||
return r;
|
||||
} else {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(ddata->backlight_gpio)) {
|
||||
r = devm_gpio_request_one(&spi->dev, ddata->backlight_gpio,
|
||||
GPIOF_OUT_INIT_LOW, "panel backlight");
|
||||
if (r)
|
||||
goto err_gpio;
|
||||
}
|
||||
|
||||
ddata->videomode = lb035q02_timings;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->dev = &spi->dev;
|
||||
dssdev->driver = &lb035q02_ops;
|
||||
dssdev->type = OMAP_DISPLAY_TYPE_DPI;
|
||||
dssdev->owner = THIS_MODULE;
|
||||
dssdev->panel.timings = ddata->videomode;
|
||||
dssdev->phy.dpi.data_lines = ddata->data_lines;
|
||||
|
||||
r = omapdss_register_display(dssdev);
|
||||
if (r) {
|
||||
dev_err(&spi->dev, "Failed to register panel\n");
|
||||
goto err_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_reg:
|
||||
err_gpio:
|
||||
omap_dss_put_device(ddata->in);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int lb035q02_panel_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
|
||||
struct omap_dss_device *dssdev = &ddata->dssdev;
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
omapdss_unregister_display(dssdev);
|
||||
|
||||
lb035q02_disable(dssdev);
|
||||
lb035q02_disconnect(dssdev);
|
||||
|
||||
omap_dss_put_device(in);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id lb035q02_of_match[] = {
|
||||
{ .compatible = "omapdss,lgphilips,lb035q02", },
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, lb035q02_of_match);
|
||||
|
||||
static struct spi_driver lb035q02_spi_driver = {
|
||||
.probe = lb035q02_panel_spi_probe,
|
||||
.remove = lb035q02_panel_spi_remove,
|
||||
.driver = {
|
||||
.name = "panel_lgphilips_lb035q02",
|
||||
.of_match_table = lb035q02_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
|
||||
module_spi_driver(lb035q02_spi_driver);
|
||||
|
||||
MODULE_ALIAS("spi:lgphilips,lb035q02");
|
||||
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
|
||||
MODULE_DESCRIPTION("LG.Philips LB035Q02 LCD Panel driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,437 @@
|
|||
/*
|
||||
* NEC NL8048HL11 Panel driver
|
||||
*
|
||||
* Copyright (C) 2010 Texas Instruments Inc.
|
||||
* Author: Erik Gilling <konkers@android.com>
|
||||
* Converted to new DSS device model: Tomi Valkeinen <tomi.valkeinen@ti.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/fb.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include <video/omapdss.h>
|
||||
#include <video/omap-panel-data.h>
|
||||
|
||||
struct panel_drv_data {
|
||||
struct omap_dss_device dssdev;
|
||||
struct omap_dss_device *in;
|
||||
|
||||
struct omap_video_timings videomode;
|
||||
|
||||
int data_lines;
|
||||
|
||||
int res_gpio;
|
||||
int qvga_gpio;
|
||||
|
||||
struct spi_device *spi;
|
||||
};
|
||||
|
||||
#define LCD_XRES 800
|
||||
#define LCD_YRES 480
|
||||
/*
|
||||
* NEC PIX Clock Ratings
|
||||
* MIN:21.8MHz TYP:23.8MHz MAX:25.7MHz
|
||||
*/
|
||||
#define LCD_PIXEL_CLOCK 23800000
|
||||
|
||||
static const struct {
|
||||
unsigned char addr;
|
||||
unsigned char dat;
|
||||
} nec_8048_init_seq[] = {
|
||||
{ 3, 0x01 }, { 0, 0x00 }, { 1, 0x01 }, { 4, 0x00 }, { 5, 0x14 },
|
||||
{ 6, 0x24 }, { 16, 0xD7 }, { 17, 0x00 }, { 18, 0x00 }, { 19, 0x55 },
|
||||
{ 20, 0x01 }, { 21, 0x70 }, { 22, 0x1E }, { 23, 0x25 }, { 24, 0x25 },
|
||||
{ 25, 0x02 }, { 26, 0x02 }, { 27, 0xA0 }, { 32, 0x2F }, { 33, 0x0F },
|
||||
{ 34, 0x0F }, { 35, 0x0F }, { 36, 0x0F }, { 37, 0x0F }, { 38, 0x0F },
|
||||
{ 39, 0x00 }, { 40, 0x02 }, { 41, 0x02 }, { 42, 0x02 }, { 43, 0x0F },
|
||||
{ 44, 0x0F }, { 45, 0x0F }, { 46, 0x0F }, { 47, 0x0F }, { 48, 0x0F },
|
||||
{ 49, 0x0F }, { 50, 0x00 }, { 51, 0x02 }, { 52, 0x02 }, { 53, 0x02 },
|
||||
{ 80, 0x0C }, { 83, 0x42 }, { 84, 0x42 }, { 85, 0x41 }, { 86, 0x14 },
|
||||
{ 89, 0x88 }, { 90, 0x01 }, { 91, 0x00 }, { 92, 0x02 }, { 93, 0x0C },
|
||||
{ 94, 0x1C }, { 95, 0x27 }, { 98, 0x49 }, { 99, 0x27 }, { 102, 0x76 },
|
||||
{ 103, 0x27 }, { 112, 0x01 }, { 113, 0x0E }, { 114, 0x02 },
|
||||
{ 115, 0x0C }, { 118, 0x0C }, { 121, 0x30 }, { 130, 0x00 },
|
||||
{ 131, 0x00 }, { 132, 0xFC }, { 134, 0x00 }, { 136, 0x00 },
|
||||
{ 138, 0x00 }, { 139, 0x00 }, { 140, 0x00 }, { 141, 0xFC },
|
||||
{ 143, 0x00 }, { 145, 0x00 }, { 147, 0x00 }, { 148, 0x00 },
|
||||
{ 149, 0x00 }, { 150, 0xFC }, { 152, 0x00 }, { 154, 0x00 },
|
||||
{ 156, 0x00 }, { 157, 0x00 }, { 2, 0x00 },
|
||||
};
|
||||
|
||||
static const struct omap_video_timings nec_8048_panel_timings = {
|
||||
.x_res = LCD_XRES,
|
||||
.y_res = LCD_YRES,
|
||||
.pixelclock = LCD_PIXEL_CLOCK,
|
||||
.hfp = 6,
|
||||
.hsw = 1,
|
||||
.hbp = 4,
|
||||
.vfp = 3,
|
||||
.vsw = 1,
|
||||
.vbp = 4,
|
||||
|
||||
.vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
|
||||
.hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
|
||||
.data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
|
||||
.de_level = OMAPDSS_SIG_ACTIVE_HIGH,
|
||||
.sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
|
||||
};
|
||||
|
||||
#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
|
||||
|
||||
static int nec_8048_spi_send(struct spi_device *spi, unsigned char reg_addr,
|
||||
unsigned char reg_data)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned int cmd = 0, data = 0;
|
||||
|
||||
cmd = 0x0000 | reg_addr; /* register address write */
|
||||
data = 0x0100 | reg_data; /* register data write */
|
||||
data = (cmd << 16) | data;
|
||||
|
||||
ret = spi_write(spi, (unsigned char *)&data, 4);
|
||||
if (ret)
|
||||
pr_err("error in spi_write %x\n", data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int init_nec_8048_wvga_lcd(struct spi_device *spi)
|
||||
{
|
||||
unsigned int i;
|
||||
/* Initialization Sequence */
|
||||
/* nec_8048_spi_send(spi, REG, VAL) */
|
||||
for (i = 0; i < (ARRAY_SIZE(nec_8048_init_seq) - 1); i++)
|
||||
nec_8048_spi_send(spi, nec_8048_init_seq[i].addr,
|
||||
nec_8048_init_seq[i].dat);
|
||||
udelay(20);
|
||||
nec_8048_spi_send(spi, nec_8048_init_seq[i].addr,
|
||||
nec_8048_init_seq[i].dat);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nec_8048_connect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (omapdss_device_is_connected(dssdev))
|
||||
return 0;
|
||||
|
||||
r = in->ops.dpi->connect(in, dssdev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nec_8048_disconnect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return;
|
||||
|
||||
in->ops.dpi->disconnect(in, dssdev);
|
||||
}
|
||||
|
||||
static int nec_8048_enable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return -ENODEV;
|
||||
|
||||
if (omapdss_device_is_enabled(dssdev))
|
||||
return 0;
|
||||
|
||||
if (ddata->data_lines)
|
||||
in->ops.dpi->set_data_lines(in, ddata->data_lines);
|
||||
in->ops.dpi->set_timings(in, &ddata->videomode);
|
||||
|
||||
r = in->ops.dpi->enable(in);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
if (gpio_is_valid(ddata->res_gpio))
|
||||
gpio_set_value_cansleep(ddata->res_gpio, 1);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nec_8048_disable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
if (!omapdss_device_is_enabled(dssdev))
|
||||
return;
|
||||
|
||||
if (gpio_is_valid(ddata->res_gpio))
|
||||
gpio_set_value_cansleep(ddata->res_gpio, 0);
|
||||
|
||||
in->ops.dpi->disable(in);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
|
||||
}
|
||||
|
||||
static void nec_8048_set_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
ddata->videomode = *timings;
|
||||
dssdev->panel.timings = *timings;
|
||||
|
||||
in->ops.dpi->set_timings(in, timings);
|
||||
}
|
||||
|
||||
static void nec_8048_get_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
|
||||
*timings = ddata->videomode;
|
||||
}
|
||||
|
||||
static int nec_8048_check_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
return in->ops.dpi->check_timings(in, timings);
|
||||
}
|
||||
|
||||
static struct omap_dss_driver nec_8048_ops = {
|
||||
.connect = nec_8048_connect,
|
||||
.disconnect = nec_8048_disconnect,
|
||||
|
||||
.enable = nec_8048_enable,
|
||||
.disable = nec_8048_disable,
|
||||
|
||||
.set_timings = nec_8048_set_timings,
|
||||
.get_timings = nec_8048_get_timings,
|
||||
.check_timings = nec_8048_check_timings,
|
||||
|
||||
.get_resolution = omapdss_default_get_resolution,
|
||||
};
|
||||
|
||||
|
||||
static int nec_8048_probe_pdata(struct spi_device *spi)
|
||||
{
|
||||
const struct panel_nec_nl8048hl11_platform_data *pdata;
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
|
||||
struct omap_dss_device *dssdev, *in;
|
||||
|
||||
pdata = dev_get_platdata(&spi->dev);
|
||||
|
||||
ddata->qvga_gpio = pdata->qvga_gpio;
|
||||
ddata->res_gpio = pdata->res_gpio;
|
||||
|
||||
in = omap_dss_find_output(pdata->source);
|
||||
if (in == NULL) {
|
||||
dev_err(&spi->dev, "failed to find video source '%s'\n",
|
||||
pdata->source);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
ddata->in = in;
|
||||
|
||||
ddata->data_lines = pdata->data_lines;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->name = pdata->name;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nec_8048_probe_of(struct spi_device *spi)
|
||||
{
|
||||
struct device_node *node = spi->dev.of_node;
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
|
||||
struct omap_dss_device *in;
|
||||
int gpio;
|
||||
|
||||
gpio = of_get_named_gpio(node, "reset-gpios", 0);
|
||||
if (!gpio_is_valid(gpio)) {
|
||||
dev_err(&spi->dev, "failed to parse enable gpio\n");
|
||||
return gpio;
|
||||
}
|
||||
ddata->res_gpio = gpio;
|
||||
|
||||
/* XXX the panel spec doesn't mention any QVGA pin?? */
|
||||
ddata->qvga_gpio = -ENOENT;
|
||||
|
||||
in = omapdss_of_find_source_for_first_ep(node);
|
||||
if (IS_ERR(in)) {
|
||||
dev_err(&spi->dev, "failed to find video source\n");
|
||||
return PTR_ERR(in);
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nec_8048_probe(struct spi_device *spi)
|
||||
{
|
||||
struct panel_drv_data *ddata;
|
||||
struct omap_dss_device *dssdev;
|
||||
int r;
|
||||
|
||||
dev_dbg(&spi->dev, "%s\n", __func__);
|
||||
|
||||
spi->mode = SPI_MODE_0;
|
||||
spi->bits_per_word = 32;
|
||||
|
||||
r = spi_setup(spi);
|
||||
if (r < 0) {
|
||||
dev_err(&spi->dev, "spi_setup failed: %d\n", r);
|
||||
return r;
|
||||
}
|
||||
|
||||
init_nec_8048_wvga_lcd(spi);
|
||||
|
||||
ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL);
|
||||
if (ddata == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&spi->dev, ddata);
|
||||
|
||||
ddata->spi = spi;
|
||||
|
||||
if (dev_get_platdata(&spi->dev)) {
|
||||
r = nec_8048_probe_pdata(spi);
|
||||
if (r)
|
||||
return r;
|
||||
} else if (spi->dev.of_node) {
|
||||
r = nec_8048_probe_of(spi);
|
||||
if (r)
|
||||
return r;
|
||||
} else {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(ddata->qvga_gpio)) {
|
||||
r = devm_gpio_request_one(&spi->dev, ddata->qvga_gpio,
|
||||
GPIOF_OUT_INIT_HIGH, "lcd QVGA");
|
||||
if (r)
|
||||
goto err_gpio;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(ddata->res_gpio)) {
|
||||
r = devm_gpio_request_one(&spi->dev, ddata->res_gpio,
|
||||
GPIOF_OUT_INIT_LOW, "lcd RES");
|
||||
if (r)
|
||||
goto err_gpio;
|
||||
}
|
||||
|
||||
ddata->videomode = nec_8048_panel_timings;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->dev = &spi->dev;
|
||||
dssdev->driver = &nec_8048_ops;
|
||||
dssdev->type = OMAP_DISPLAY_TYPE_DPI;
|
||||
dssdev->owner = THIS_MODULE;
|
||||
dssdev->panel.timings = ddata->videomode;
|
||||
|
||||
r = omapdss_register_display(dssdev);
|
||||
if (r) {
|
||||
dev_err(&spi->dev, "Failed to register panel\n");
|
||||
goto err_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_reg:
|
||||
err_gpio:
|
||||
omap_dss_put_device(ddata->in);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int nec_8048_remove(struct spi_device *spi)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
|
||||
struct omap_dss_device *dssdev = &ddata->dssdev;
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
dev_dbg(&ddata->spi->dev, "%s\n", __func__);
|
||||
|
||||
omapdss_unregister_display(dssdev);
|
||||
|
||||
nec_8048_disable(dssdev);
|
||||
nec_8048_disconnect(dssdev);
|
||||
|
||||
omap_dss_put_device(in);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int nec_8048_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
|
||||
nec_8048_spi_send(spi, 2, 0x01);
|
||||
mdelay(40);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nec_8048_resume(struct device *dev)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
|
||||
/* reinitialize the panel */
|
||||
spi_setup(spi);
|
||||
nec_8048_spi_send(spi, 2, 0x00);
|
||||
init_nec_8048_wvga_lcd(spi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
static SIMPLE_DEV_PM_OPS(nec_8048_pm_ops, nec_8048_suspend,
|
||||
nec_8048_resume);
|
||||
#define NEC_8048_PM_OPS (&nec_8048_pm_ops)
|
||||
#else
|
||||
#define NEC_8048_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static const struct of_device_id nec_8048_of_match[] = {
|
||||
{ .compatible = "omapdss,nec,nl8048hl11", },
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, nec_8048_of_match);
|
||||
|
||||
static struct spi_driver nec_8048_driver = {
|
||||
.driver = {
|
||||
.name = "panel-nec-nl8048hl11",
|
||||
.pm = NEC_8048_PM_OPS,
|
||||
.of_match_table = nec_8048_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.probe = nec_8048_probe,
|
||||
.remove = nec_8048_remove,
|
||||
};
|
||||
|
||||
module_spi_driver(nec_8048_driver);
|
||||
|
||||
MODULE_ALIAS("spi:nec,nl8048hl11");
|
||||
MODULE_AUTHOR("Erik Gilling <konkers@android.com>");
|
||||
MODULE_DESCRIPTION("NEC-NL8048HL11 Driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,415 @@
|
|||
/*
|
||||
* LCD panel driver for Sharp LS037V7DW01
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments
|
||||
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <video/omapdss.h>
|
||||
#include <video/omap-panel-data.h>
|
||||
|
||||
struct panel_drv_data {
|
||||
struct omap_dss_device dssdev;
|
||||
struct omap_dss_device *in;
|
||||
struct regulator *vcc;
|
||||
|
||||
int data_lines;
|
||||
|
||||
struct omap_video_timings videomode;
|
||||
|
||||
struct gpio_desc *resb_gpio; /* low = reset active min 20 us */
|
||||
struct gpio_desc *ini_gpio; /* high = power on */
|
||||
struct gpio_desc *mo_gpio; /* low = 480x640, high = 240x320 */
|
||||
struct gpio_desc *lr_gpio; /* high = conventional horizontal scanning */
|
||||
struct gpio_desc *ud_gpio; /* high = conventional vertical scanning */
|
||||
};
|
||||
|
||||
static const struct omap_video_timings sharp_ls_timings = {
|
||||
.x_res = 480,
|
||||
.y_res = 640,
|
||||
|
||||
.pixelclock = 19200000,
|
||||
|
||||
.hsw = 2,
|
||||
.hfp = 1,
|
||||
.hbp = 28,
|
||||
|
||||
.vsw = 1,
|
||||
.vfp = 1,
|
||||
.vbp = 1,
|
||||
|
||||
.vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
|
||||
.hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
|
||||
.data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
|
||||
.de_level = OMAPDSS_SIG_ACTIVE_HIGH,
|
||||
.sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
|
||||
};
|
||||
|
||||
#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
|
||||
|
||||
static int sharp_ls_connect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (omapdss_device_is_connected(dssdev))
|
||||
return 0;
|
||||
|
||||
r = in->ops.dpi->connect(in, dssdev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sharp_ls_disconnect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return;
|
||||
|
||||
in->ops.dpi->disconnect(in, dssdev);
|
||||
}
|
||||
|
||||
static int sharp_ls_enable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return -ENODEV;
|
||||
|
||||
if (omapdss_device_is_enabled(dssdev))
|
||||
return 0;
|
||||
|
||||
if (ddata->data_lines)
|
||||
in->ops.dpi->set_data_lines(in, ddata->data_lines);
|
||||
in->ops.dpi->set_timings(in, &ddata->videomode);
|
||||
|
||||
if (ddata->vcc) {
|
||||
r = regulator_enable(ddata->vcc);
|
||||
if (r != 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = in->ops.dpi->enable(in);
|
||||
if (r) {
|
||||
regulator_disable(ddata->vcc);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* wait couple of vsyncs until enabling the LCD */
|
||||
msleep(50);
|
||||
|
||||
if (ddata->resb_gpio)
|
||||
gpiod_set_value_cansleep(ddata->resb_gpio, 1);
|
||||
|
||||
if (ddata->ini_gpio)
|
||||
gpiod_set_value_cansleep(ddata->ini_gpio, 1);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sharp_ls_disable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
if (!omapdss_device_is_enabled(dssdev))
|
||||
return;
|
||||
|
||||
if (ddata->ini_gpio)
|
||||
gpiod_set_value_cansleep(ddata->ini_gpio, 0);
|
||||
|
||||
if (ddata->resb_gpio)
|
||||
gpiod_set_value_cansleep(ddata->resb_gpio, 0);
|
||||
|
||||
/* wait at least 5 vsyncs after disabling the LCD */
|
||||
|
||||
msleep(100);
|
||||
|
||||
in->ops.dpi->disable(in);
|
||||
|
||||
if (ddata->vcc)
|
||||
regulator_disable(ddata->vcc);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
|
||||
}
|
||||
|
||||
static void sharp_ls_set_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
ddata->videomode = *timings;
|
||||
dssdev->panel.timings = *timings;
|
||||
|
||||
in->ops.dpi->set_timings(in, timings);
|
||||
}
|
||||
|
||||
static void sharp_ls_get_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
|
||||
*timings = ddata->videomode;
|
||||
}
|
||||
|
||||
static int sharp_ls_check_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
return in->ops.dpi->check_timings(in, timings);
|
||||
}
|
||||
|
||||
static struct omap_dss_driver sharp_ls_ops = {
|
||||
.connect = sharp_ls_connect,
|
||||
.disconnect = sharp_ls_disconnect,
|
||||
|
||||
.enable = sharp_ls_enable,
|
||||
.disable = sharp_ls_disable,
|
||||
|
||||
.set_timings = sharp_ls_set_timings,
|
||||
.get_timings = sharp_ls_get_timings,
|
||||
.check_timings = sharp_ls_check_timings,
|
||||
|
||||
.get_resolution = omapdss_default_get_resolution,
|
||||
};
|
||||
|
||||
static int sharp_ls_get_gpio(struct device *dev, int gpio, unsigned long flags,
|
||||
char *desc, struct gpio_desc **gpiod)
|
||||
{
|
||||
struct gpio_desc *gd;
|
||||
int r;
|
||||
|
||||
*gpiod = NULL;
|
||||
|
||||
r = devm_gpio_request_one(dev, gpio, flags, desc);
|
||||
if (r)
|
||||
return r == -ENOENT ? 0 : r;
|
||||
|
||||
gd = gpio_to_desc(gpio);
|
||||
if (IS_ERR(gd))
|
||||
return PTR_ERR(gd) == -ENOENT ? 0 : PTR_ERR(gd);
|
||||
|
||||
*gpiod = gd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sharp_ls_probe_pdata(struct platform_device *pdev)
|
||||
{
|
||||
const struct panel_sharp_ls037v7dw01_platform_data *pdata;
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct omap_dss_device *dssdev, *in;
|
||||
int r;
|
||||
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
|
||||
in = omap_dss_find_output(pdata->source);
|
||||
if (in == NULL) {
|
||||
dev_err(&pdev->dev, "failed to find video source '%s'\n",
|
||||
pdata->source);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
ddata->data_lines = pdata->data_lines;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->name = pdata->name;
|
||||
|
||||
r = sharp_ls_get_gpio(&pdev->dev, pdata->mo_gpio, GPIOF_OUT_INIT_LOW,
|
||||
"lcd MO", &ddata->mo_gpio);
|
||||
if (r)
|
||||
return r;
|
||||
r = sharp_ls_get_gpio(&pdev->dev, pdata->lr_gpio, GPIOF_OUT_INIT_HIGH,
|
||||
"lcd LR", &ddata->lr_gpio);
|
||||
if (r)
|
||||
return r;
|
||||
r = sharp_ls_get_gpio(&pdev->dev, pdata->ud_gpio, GPIOF_OUT_INIT_HIGH,
|
||||
"lcd UD", &ddata->ud_gpio);
|
||||
if (r)
|
||||
return r;
|
||||
r = sharp_ls_get_gpio(&pdev->dev, pdata->resb_gpio, GPIOF_OUT_INIT_LOW,
|
||||
"lcd RESB", &ddata->resb_gpio);
|
||||
if (r)
|
||||
return r;
|
||||
r = sharp_ls_get_gpio(&pdev->dev, pdata->ini_gpio, GPIOF_OUT_INIT_LOW,
|
||||
"lcd INI", &ddata->ini_gpio);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sharp_ls_get_gpio_of(struct device *dev, int index, int val,
|
||||
const char *desc, struct gpio_desc **gpiod)
|
||||
{
|
||||
struct gpio_desc *gd;
|
||||
|
||||
*gpiod = NULL;
|
||||
|
||||
gd = devm_gpiod_get_index(dev, desc, index, GPIOD_OUT_LOW);
|
||||
if (IS_ERR(gd))
|
||||
return PTR_ERR(gd);
|
||||
|
||||
*gpiod = gd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sharp_ls_probe_of(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct omap_dss_device *in;
|
||||
int r;
|
||||
|
||||
ddata->vcc = devm_regulator_get(&pdev->dev, "envdd");
|
||||
if (IS_ERR(ddata->vcc)) {
|
||||
dev_err(&pdev->dev, "failed to get regulator\n");
|
||||
return PTR_ERR(ddata->vcc);
|
||||
}
|
||||
|
||||
/* lcd INI */
|
||||
r = sharp_ls_get_gpio_of(&pdev->dev, 0, 0, "enable", &ddata->ini_gpio);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
/* lcd RESB */
|
||||
r = sharp_ls_get_gpio_of(&pdev->dev, 0, 0, "reset", &ddata->resb_gpio);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
/* lcd MO */
|
||||
r = sharp_ls_get_gpio_of(&pdev->dev, 0, 0, "mode", &ddata->mo_gpio);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
/* lcd LR */
|
||||
r = sharp_ls_get_gpio_of(&pdev->dev, 1, 1, "mode", &ddata->lr_gpio);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
/* lcd UD */
|
||||
r = sharp_ls_get_gpio_of(&pdev->dev, 2, 1, "mode", &ddata->ud_gpio);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
in = omapdss_of_find_source_for_first_ep(node);
|
||||
if (IS_ERR(in)) {
|
||||
dev_err(&pdev->dev, "failed to find video source\n");
|
||||
return PTR_ERR(in);
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sharp_ls_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata;
|
||||
struct omap_dss_device *dssdev;
|
||||
int r;
|
||||
|
||||
ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
|
||||
if (ddata == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, ddata);
|
||||
|
||||
if (dev_get_platdata(&pdev->dev)) {
|
||||
r = sharp_ls_probe_pdata(pdev);
|
||||
if (r)
|
||||
return r;
|
||||
} else if (pdev->dev.of_node) {
|
||||
r = sharp_ls_probe_of(pdev);
|
||||
if (r)
|
||||
return r;
|
||||
} else {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ddata->videomode = sharp_ls_timings;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->dev = &pdev->dev;
|
||||
dssdev->driver = &sharp_ls_ops;
|
||||
dssdev->type = OMAP_DISPLAY_TYPE_DPI;
|
||||
dssdev->owner = THIS_MODULE;
|
||||
dssdev->panel.timings = ddata->videomode;
|
||||
dssdev->phy.dpi.data_lines = ddata->data_lines;
|
||||
|
||||
r = omapdss_register_display(dssdev);
|
||||
if (r) {
|
||||
dev_err(&pdev->dev, "Failed to register panel\n");
|
||||
goto err_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_reg:
|
||||
omap_dss_put_device(ddata->in);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int __exit sharp_ls_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||||
struct omap_dss_device *dssdev = &ddata->dssdev;
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
omapdss_unregister_display(dssdev);
|
||||
|
||||
sharp_ls_disable(dssdev);
|
||||
sharp_ls_disconnect(dssdev);
|
||||
|
||||
omap_dss_put_device(in);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id sharp_ls_of_match[] = {
|
||||
{ .compatible = "omapdss,sharp,ls037v7dw01", },
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, sharp_ls_of_match);
|
||||
|
||||
static struct platform_driver sharp_ls_driver = {
|
||||
.probe = sharp_ls_probe,
|
||||
.remove = __exit_p(sharp_ls_remove),
|
||||
.driver = {
|
||||
.name = "panel-sharp-ls037v7dw01",
|
||||
.of_match_table = sharp_ls_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(sharp_ls_driver);
|
||||
|
||||
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
|
||||
MODULE_DESCRIPTION("Sharp LS037V7DW01 Panel Driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,917 @@
|
|||
/*
|
||||
* Sony ACX565AKM LCD Panel driver
|
||||
*
|
||||
* Copyright (C) 2010 Nokia Corporation
|
||||
*
|
||||
* Original Driver Author: Imre Deak <imre.deak@nokia.com>
|
||||
* Based on panel-generic.c by Tomi Valkeinen <tomi.valkeinen@nokia.com>
|
||||
* Adapted to new DSS2 framework: Roger Quadros <roger.quadros@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/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include <video/omapdss.h>
|
||||
#include <video/omap-panel-data.h>
|
||||
|
||||
#define MIPID_CMD_READ_DISP_ID 0x04
|
||||
#define MIPID_CMD_READ_RED 0x06
|
||||
#define MIPID_CMD_READ_GREEN 0x07
|
||||
#define MIPID_CMD_READ_BLUE 0x08
|
||||
#define MIPID_CMD_READ_DISP_STATUS 0x09
|
||||
#define MIPID_CMD_RDDSDR 0x0F
|
||||
#define MIPID_CMD_SLEEP_IN 0x10
|
||||
#define MIPID_CMD_SLEEP_OUT 0x11
|
||||
#define MIPID_CMD_DISP_OFF 0x28
|
||||
#define MIPID_CMD_DISP_ON 0x29
|
||||
#define MIPID_CMD_WRITE_DISP_BRIGHTNESS 0x51
|
||||
#define MIPID_CMD_READ_DISP_BRIGHTNESS 0x52
|
||||
#define MIPID_CMD_WRITE_CTRL_DISP 0x53
|
||||
|
||||
#define CTRL_DISP_BRIGHTNESS_CTRL_ON (1 << 5)
|
||||
#define CTRL_DISP_AMBIENT_LIGHT_CTRL_ON (1 << 4)
|
||||
#define CTRL_DISP_BACKLIGHT_ON (1 << 2)
|
||||
#define CTRL_DISP_AUTO_BRIGHTNESS_ON (1 << 1)
|
||||
|
||||
#define MIPID_CMD_READ_CTRL_DISP 0x54
|
||||
#define MIPID_CMD_WRITE_CABC 0x55
|
||||
#define MIPID_CMD_READ_CABC 0x56
|
||||
|
||||
#define MIPID_VER_LPH8923 3
|
||||
#define MIPID_VER_LS041Y3 4
|
||||
#define MIPID_VER_L4F00311 8
|
||||
#define MIPID_VER_ACX565AKM 9
|
||||
|
||||
struct panel_drv_data {
|
||||
struct omap_dss_device dssdev;
|
||||
struct omap_dss_device *in;
|
||||
|
||||
int reset_gpio;
|
||||
int datapairs;
|
||||
|
||||
struct omap_video_timings videomode;
|
||||
|
||||
char *name;
|
||||
int enabled;
|
||||
int model;
|
||||
int revision;
|
||||
u8 display_id[3];
|
||||
unsigned has_bc:1;
|
||||
unsigned has_cabc:1;
|
||||
unsigned cabc_mode;
|
||||
unsigned long hw_guard_end; /* next value of jiffies
|
||||
when we can issue the
|
||||
next sleep in/out command */
|
||||
unsigned long hw_guard_wait; /* max guard time in jiffies */
|
||||
|
||||
struct spi_device *spi;
|
||||
struct mutex mutex;
|
||||
|
||||
struct backlight_device *bl_dev;
|
||||
};
|
||||
|
||||
static const struct omap_video_timings acx565akm_panel_timings = {
|
||||
.x_res = 800,
|
||||
.y_res = 480,
|
||||
.pixelclock = 24000000,
|
||||
.hfp = 28,
|
||||
.hsw = 4,
|
||||
.hbp = 24,
|
||||
.vfp = 3,
|
||||
.vsw = 3,
|
||||
.vbp = 4,
|
||||
|
||||
.vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
|
||||
.hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
|
||||
|
||||
.data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
|
||||
.de_level = OMAPDSS_SIG_ACTIVE_HIGH,
|
||||
.sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
|
||||
};
|
||||
|
||||
#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
|
||||
|
||||
static void acx565akm_transfer(struct panel_drv_data *ddata, int cmd,
|
||||
const u8 *wbuf, int wlen, u8 *rbuf, int rlen)
|
||||
{
|
||||
struct spi_message m;
|
||||
struct spi_transfer *x, xfer[5];
|
||||
int r;
|
||||
|
||||
BUG_ON(ddata->spi == NULL);
|
||||
|
||||
spi_message_init(&m);
|
||||
|
||||
memset(xfer, 0, sizeof(xfer));
|
||||
x = &xfer[0];
|
||||
|
||||
cmd &= 0xff;
|
||||
x->tx_buf = &cmd;
|
||||
x->bits_per_word = 9;
|
||||
x->len = 2;
|
||||
|
||||
if (rlen > 1 && wlen == 0) {
|
||||
/*
|
||||
* Between the command and the response data there is a
|
||||
* dummy clock cycle. Add an extra bit after the command
|
||||
* word to account for this.
|
||||
*/
|
||||
x->bits_per_word = 10;
|
||||
cmd <<= 1;
|
||||
}
|
||||
spi_message_add_tail(x, &m);
|
||||
|
||||
if (wlen) {
|
||||
x++;
|
||||
x->tx_buf = wbuf;
|
||||
x->len = wlen;
|
||||
x->bits_per_word = 9;
|
||||
spi_message_add_tail(x, &m);
|
||||
}
|
||||
|
||||
if (rlen) {
|
||||
x++;
|
||||
x->rx_buf = rbuf;
|
||||
x->len = rlen;
|
||||
spi_message_add_tail(x, &m);
|
||||
}
|
||||
|
||||
r = spi_sync(ddata->spi, &m);
|
||||
if (r < 0)
|
||||
dev_dbg(&ddata->spi->dev, "spi_sync %d\n", r);
|
||||
}
|
||||
|
||||
static inline void acx565akm_cmd(struct panel_drv_data *ddata, int cmd)
|
||||
{
|
||||
acx565akm_transfer(ddata, cmd, NULL, 0, NULL, 0);
|
||||
}
|
||||
|
||||
static inline void acx565akm_write(struct panel_drv_data *ddata,
|
||||
int reg, const u8 *buf, int len)
|
||||
{
|
||||
acx565akm_transfer(ddata, reg, buf, len, NULL, 0);
|
||||
}
|
||||
|
||||
static inline void acx565akm_read(struct panel_drv_data *ddata,
|
||||
int reg, u8 *buf, int len)
|
||||
{
|
||||
acx565akm_transfer(ddata, reg, NULL, 0, buf, len);
|
||||
}
|
||||
|
||||
static void hw_guard_start(struct panel_drv_data *ddata, int guard_msec)
|
||||
{
|
||||
ddata->hw_guard_wait = msecs_to_jiffies(guard_msec);
|
||||
ddata->hw_guard_end = jiffies + ddata->hw_guard_wait;
|
||||
}
|
||||
|
||||
static void hw_guard_wait(struct panel_drv_data *ddata)
|
||||
{
|
||||
unsigned long wait = ddata->hw_guard_end - jiffies;
|
||||
|
||||
if ((long)wait > 0 && wait <= ddata->hw_guard_wait) {
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
schedule_timeout(wait);
|
||||
}
|
||||
}
|
||||
|
||||
static void set_sleep_mode(struct panel_drv_data *ddata, int on)
|
||||
{
|
||||
int cmd;
|
||||
|
||||
if (on)
|
||||
cmd = MIPID_CMD_SLEEP_IN;
|
||||
else
|
||||
cmd = MIPID_CMD_SLEEP_OUT;
|
||||
/*
|
||||
* We have to keep 120msec between sleep in/out commands.
|
||||
* (8.2.15, 8.2.16).
|
||||
*/
|
||||
hw_guard_wait(ddata);
|
||||
acx565akm_cmd(ddata, cmd);
|
||||
hw_guard_start(ddata, 120);
|
||||
}
|
||||
|
||||
static void set_display_state(struct panel_drv_data *ddata, int enabled)
|
||||
{
|
||||
int cmd = enabled ? MIPID_CMD_DISP_ON : MIPID_CMD_DISP_OFF;
|
||||
|
||||
acx565akm_cmd(ddata, cmd);
|
||||
}
|
||||
|
||||
static int panel_enabled(struct panel_drv_data *ddata)
|
||||
{
|
||||
u32 disp_status;
|
||||
int enabled;
|
||||
|
||||
acx565akm_read(ddata, MIPID_CMD_READ_DISP_STATUS,
|
||||
(u8 *)&disp_status, 4);
|
||||
disp_status = __be32_to_cpu(disp_status);
|
||||
enabled = (disp_status & (1 << 17)) && (disp_status & (1 << 10));
|
||||
dev_dbg(&ddata->spi->dev,
|
||||
"LCD panel %senabled by bootloader (status 0x%04x)\n",
|
||||
enabled ? "" : "not ", disp_status);
|
||||
return enabled;
|
||||
}
|
||||
|
||||
static int panel_detect(struct panel_drv_data *ddata)
|
||||
{
|
||||
acx565akm_read(ddata, MIPID_CMD_READ_DISP_ID, ddata->display_id, 3);
|
||||
dev_dbg(&ddata->spi->dev, "MIPI display ID: %02x%02x%02x\n",
|
||||
ddata->display_id[0],
|
||||
ddata->display_id[1],
|
||||
ddata->display_id[2]);
|
||||
|
||||
switch (ddata->display_id[0]) {
|
||||
case 0x10:
|
||||
ddata->model = MIPID_VER_ACX565AKM;
|
||||
ddata->name = "acx565akm";
|
||||
ddata->has_bc = 1;
|
||||
ddata->has_cabc = 1;
|
||||
break;
|
||||
case 0x29:
|
||||
ddata->model = MIPID_VER_L4F00311;
|
||||
ddata->name = "l4f00311";
|
||||
break;
|
||||
case 0x45:
|
||||
ddata->model = MIPID_VER_LPH8923;
|
||||
ddata->name = "lph8923";
|
||||
break;
|
||||
case 0x83:
|
||||
ddata->model = MIPID_VER_LS041Y3;
|
||||
ddata->name = "ls041y3";
|
||||
break;
|
||||
default:
|
||||
ddata->name = "unknown";
|
||||
dev_err(&ddata->spi->dev, "invalid display ID\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ddata->revision = ddata->display_id[1];
|
||||
|
||||
dev_info(&ddata->spi->dev, "omapfb: %s rev %02x LCD detected\n",
|
||||
ddata->name, ddata->revision);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*----------------------Backlight Control-------------------------*/
|
||||
|
||||
static void enable_backlight_ctrl(struct panel_drv_data *ddata, int enable)
|
||||
{
|
||||
u16 ctrl;
|
||||
|
||||
acx565akm_read(ddata, MIPID_CMD_READ_CTRL_DISP, (u8 *)&ctrl, 1);
|
||||
if (enable) {
|
||||
ctrl |= CTRL_DISP_BRIGHTNESS_CTRL_ON |
|
||||
CTRL_DISP_BACKLIGHT_ON;
|
||||
} else {
|
||||
ctrl &= ~(CTRL_DISP_BRIGHTNESS_CTRL_ON |
|
||||
CTRL_DISP_BACKLIGHT_ON);
|
||||
}
|
||||
|
||||
ctrl |= 1 << 8;
|
||||
acx565akm_write(ddata, MIPID_CMD_WRITE_CTRL_DISP, (u8 *)&ctrl, 2);
|
||||
}
|
||||
|
||||
static void set_cabc_mode(struct panel_drv_data *ddata, unsigned mode)
|
||||
{
|
||||
u16 cabc_ctrl;
|
||||
|
||||
ddata->cabc_mode = mode;
|
||||
if (!ddata->enabled)
|
||||
return;
|
||||
cabc_ctrl = 0;
|
||||
acx565akm_read(ddata, MIPID_CMD_READ_CABC, (u8 *)&cabc_ctrl, 1);
|
||||
cabc_ctrl &= ~3;
|
||||
cabc_ctrl |= (1 << 8) | (mode & 3);
|
||||
acx565akm_write(ddata, MIPID_CMD_WRITE_CABC, (u8 *)&cabc_ctrl, 2);
|
||||
}
|
||||
|
||||
static unsigned get_cabc_mode(struct panel_drv_data *ddata)
|
||||
{
|
||||
return ddata->cabc_mode;
|
||||
}
|
||||
|
||||
static unsigned get_hw_cabc_mode(struct panel_drv_data *ddata)
|
||||
{
|
||||
u8 cabc_ctrl;
|
||||
|
||||
acx565akm_read(ddata, MIPID_CMD_READ_CABC, &cabc_ctrl, 1);
|
||||
return cabc_ctrl & 3;
|
||||
}
|
||||
|
||||
static void acx565akm_set_brightness(struct panel_drv_data *ddata, int level)
|
||||
{
|
||||
int bv;
|
||||
|
||||
bv = level | (1 << 8);
|
||||
acx565akm_write(ddata, MIPID_CMD_WRITE_DISP_BRIGHTNESS, (u8 *)&bv, 2);
|
||||
|
||||
if (level)
|
||||
enable_backlight_ctrl(ddata, 1);
|
||||
else
|
||||
enable_backlight_ctrl(ddata, 0);
|
||||
}
|
||||
|
||||
static int acx565akm_get_actual_brightness(struct panel_drv_data *ddata)
|
||||
{
|
||||
u8 bv;
|
||||
|
||||
acx565akm_read(ddata, MIPID_CMD_READ_DISP_BRIGHTNESS, &bv, 1);
|
||||
|
||||
return bv;
|
||||
}
|
||||
|
||||
|
||||
static int acx565akm_bl_update_status(struct backlight_device *dev)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev);
|
||||
int level;
|
||||
|
||||
dev_dbg(&ddata->spi->dev, "%s\n", __func__);
|
||||
|
||||
if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
|
||||
dev->props.power == FB_BLANK_UNBLANK)
|
||||
level = dev->props.brightness;
|
||||
else
|
||||
level = 0;
|
||||
|
||||
if (ddata->has_bc)
|
||||
acx565akm_set_brightness(ddata, level);
|
||||
else
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acx565akm_bl_get_intensity(struct backlight_device *dev)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev);
|
||||
|
||||
dev_dbg(&dev->dev, "%s\n", __func__);
|
||||
|
||||
if (!ddata->has_bc)
|
||||
return -ENODEV;
|
||||
|
||||
if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
|
||||
dev->props.power == FB_BLANK_UNBLANK) {
|
||||
if (ddata->has_bc)
|
||||
return acx565akm_get_actual_brightness(ddata);
|
||||
else
|
||||
return dev->props.brightness;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acx565akm_bl_update_status_locked(struct backlight_device *dev)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev);
|
||||
int r;
|
||||
|
||||
mutex_lock(&ddata->mutex);
|
||||
r = acx565akm_bl_update_status(dev);
|
||||
mutex_unlock(&ddata->mutex);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int acx565akm_bl_get_intensity_locked(struct backlight_device *dev)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev);
|
||||
int r;
|
||||
|
||||
mutex_lock(&ddata->mutex);
|
||||
r = acx565akm_bl_get_intensity(dev);
|
||||
mutex_unlock(&ddata->mutex);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static const struct backlight_ops acx565akm_bl_ops = {
|
||||
.get_brightness = acx565akm_bl_get_intensity_locked,
|
||||
.update_status = acx565akm_bl_update_status_locked,
|
||||
};
|
||||
|
||||
/*--------------------Auto Brightness control via Sysfs---------------------*/
|
||||
|
||||
static const char * const cabc_modes[] = {
|
||||
"off", /* always used when CABC is not supported */
|
||||
"ui",
|
||||
"still-image",
|
||||
"moving-image",
|
||||
};
|
||||
|
||||
static ssize_t show_cabc_mode(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(dev);
|
||||
const char *mode_str;
|
||||
int mode;
|
||||
int len;
|
||||
|
||||
if (!ddata->has_cabc)
|
||||
mode = 0;
|
||||
else
|
||||
mode = get_cabc_mode(ddata);
|
||||
mode_str = "unknown";
|
||||
if (mode >= 0 && mode < ARRAY_SIZE(cabc_modes))
|
||||
mode_str = cabc_modes[mode];
|
||||
len = snprintf(buf, PAGE_SIZE, "%s\n", mode_str);
|
||||
|
||||
return len < PAGE_SIZE - 1 ? len : PAGE_SIZE - 1;
|
||||
}
|
||||
|
||||
static ssize_t store_cabc_mode(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(dev);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(cabc_modes); i++) {
|
||||
const char *mode_str = cabc_modes[i];
|
||||
int cmp_len = strlen(mode_str);
|
||||
|
||||
if (count > 0 && buf[count - 1] == '\n')
|
||||
count--;
|
||||
if (count != cmp_len)
|
||||
continue;
|
||||
|
||||
if (strncmp(buf, mode_str, cmp_len) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(cabc_modes))
|
||||
return -EINVAL;
|
||||
|
||||
if (!ddata->has_cabc && i != 0)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&ddata->mutex);
|
||||
set_cabc_mode(ddata, i);
|
||||
mutex_unlock(&ddata->mutex);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_cabc_available_modes(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(dev);
|
||||
int len;
|
||||
int i;
|
||||
|
||||
if (!ddata->has_cabc)
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", cabc_modes[0]);
|
||||
|
||||
for (i = 0, len = 0;
|
||||
len < PAGE_SIZE && i < ARRAY_SIZE(cabc_modes); i++)
|
||||
len += snprintf(&buf[len], PAGE_SIZE - len, "%s%s%s",
|
||||
i ? " " : "", cabc_modes[i],
|
||||
i == ARRAY_SIZE(cabc_modes) - 1 ? "\n" : "");
|
||||
|
||||
return len < PAGE_SIZE ? len : PAGE_SIZE - 1;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(cabc_mode, S_IRUGO | S_IWUSR,
|
||||
show_cabc_mode, store_cabc_mode);
|
||||
static DEVICE_ATTR(cabc_available_modes, S_IRUGO,
|
||||
show_cabc_available_modes, NULL);
|
||||
|
||||
static struct attribute *bldev_attrs[] = {
|
||||
&dev_attr_cabc_mode.attr,
|
||||
&dev_attr_cabc_available_modes.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group bldev_attr_group = {
|
||||
.attrs = bldev_attrs,
|
||||
};
|
||||
|
||||
static int acx565akm_connect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (omapdss_device_is_connected(dssdev))
|
||||
return 0;
|
||||
|
||||
r = in->ops.sdi->connect(in, dssdev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void acx565akm_disconnect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return;
|
||||
|
||||
in->ops.sdi->disconnect(in, dssdev);
|
||||
}
|
||||
|
||||
static int acx565akm_panel_power_on(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
dev_dbg(&ddata->spi->dev, "%s\n", __func__);
|
||||
|
||||
in->ops.sdi->set_timings(in, &ddata->videomode);
|
||||
|
||||
if (ddata->datapairs > 0)
|
||||
in->ops.sdi->set_datapairs(in, ddata->datapairs);
|
||||
|
||||
r = in->ops.sdi->enable(in);
|
||||
if (r) {
|
||||
pr_err("%s sdi enable failed\n", __func__);
|
||||
return r;
|
||||
}
|
||||
|
||||
/*FIXME tweak me */
|
||||
msleep(50);
|
||||
|
||||
if (gpio_is_valid(ddata->reset_gpio))
|
||||
gpio_set_value(ddata->reset_gpio, 1);
|
||||
|
||||
if (ddata->enabled) {
|
||||
dev_dbg(&ddata->spi->dev, "panel already enabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We have to meet all the following delay requirements:
|
||||
* 1. tRW: reset pulse width 10usec (7.12.1)
|
||||
* 2. tRT: reset cancel time 5msec (7.12.1)
|
||||
* 3. Providing PCLK,HS,VS signals for 2 frames = ~50msec worst
|
||||
* case (7.6.2)
|
||||
* 4. 120msec before the sleep out command (7.12.1)
|
||||
*/
|
||||
msleep(120);
|
||||
|
||||
set_sleep_mode(ddata, 0);
|
||||
ddata->enabled = 1;
|
||||
|
||||
/* 5msec between sleep out and the next command. (8.2.16) */
|
||||
usleep_range(5000, 10000);
|
||||
set_display_state(ddata, 1);
|
||||
set_cabc_mode(ddata, ddata->cabc_mode);
|
||||
|
||||
return acx565akm_bl_update_status(ddata->bl_dev);
|
||||
}
|
||||
|
||||
static void acx565akm_panel_power_off(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
dev_dbg(dssdev->dev, "%s\n", __func__);
|
||||
|
||||
if (!ddata->enabled)
|
||||
return;
|
||||
|
||||
set_display_state(ddata, 0);
|
||||
set_sleep_mode(ddata, 1);
|
||||
ddata->enabled = 0;
|
||||
/*
|
||||
* We have to provide PCLK,HS,VS signals for 2 frames (worst case
|
||||
* ~50msec) after sending the sleep in command and asserting the
|
||||
* reset signal. We probably could assert the reset w/o the delay
|
||||
* but we still delay to avoid possible artifacts. (7.6.1)
|
||||
*/
|
||||
msleep(50);
|
||||
|
||||
if (gpio_is_valid(ddata->reset_gpio))
|
||||
gpio_set_value(ddata->reset_gpio, 0);
|
||||
|
||||
/* FIXME need to tweak this delay */
|
||||
msleep(100);
|
||||
|
||||
in->ops.sdi->disable(in);
|
||||
}
|
||||
|
||||
static int acx565akm_enable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
int r;
|
||||
|
||||
dev_dbg(dssdev->dev, "%s\n", __func__);
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return -ENODEV;
|
||||
|
||||
if (omapdss_device_is_enabled(dssdev))
|
||||
return 0;
|
||||
|
||||
mutex_lock(&ddata->mutex);
|
||||
r = acx565akm_panel_power_on(dssdev);
|
||||
mutex_unlock(&ddata->mutex);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void acx565akm_disable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
|
||||
dev_dbg(dssdev->dev, "%s\n", __func__);
|
||||
|
||||
if (!omapdss_device_is_enabled(dssdev))
|
||||
return;
|
||||
|
||||
mutex_lock(&ddata->mutex);
|
||||
acx565akm_panel_power_off(dssdev);
|
||||
mutex_unlock(&ddata->mutex);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
|
||||
}
|
||||
|
||||
static void acx565akm_set_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
ddata->videomode = *timings;
|
||||
dssdev->panel.timings = *timings;
|
||||
|
||||
in->ops.sdi->set_timings(in, timings);
|
||||
}
|
||||
|
||||
static void acx565akm_get_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
|
||||
*timings = ddata->videomode;
|
||||
}
|
||||
|
||||
static int acx565akm_check_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
return in->ops.sdi->check_timings(in, timings);
|
||||
}
|
||||
|
||||
static struct omap_dss_driver acx565akm_ops = {
|
||||
.connect = acx565akm_connect,
|
||||
.disconnect = acx565akm_disconnect,
|
||||
|
||||
.enable = acx565akm_enable,
|
||||
.disable = acx565akm_disable,
|
||||
|
||||
.set_timings = acx565akm_set_timings,
|
||||
.get_timings = acx565akm_get_timings,
|
||||
.check_timings = acx565akm_check_timings,
|
||||
|
||||
.get_resolution = omapdss_default_get_resolution,
|
||||
};
|
||||
|
||||
static int acx565akm_probe_pdata(struct spi_device *spi)
|
||||
{
|
||||
const struct panel_acx565akm_platform_data *pdata;
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
|
||||
struct omap_dss_device *dssdev, *in;
|
||||
|
||||
pdata = dev_get_platdata(&spi->dev);
|
||||
|
||||
ddata->reset_gpio = pdata->reset_gpio;
|
||||
|
||||
in = omap_dss_find_output(pdata->source);
|
||||
if (in == NULL) {
|
||||
dev_err(&spi->dev, "failed to find video source '%s'\n",
|
||||
pdata->source);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
ddata->in = in;
|
||||
|
||||
ddata->datapairs = pdata->datapairs;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->name = pdata->name;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acx565akm_probe_of(struct spi_device *spi)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
|
||||
struct device_node *np = spi->dev.of_node;
|
||||
|
||||
ddata->reset_gpio = of_get_named_gpio(np, "reset-gpios", 0);
|
||||
|
||||
ddata->in = omapdss_of_find_source_for_first_ep(np);
|
||||
if (IS_ERR(ddata->in)) {
|
||||
dev_err(&spi->dev, "failed to find video source\n");
|
||||
return PTR_ERR(ddata->in);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acx565akm_probe(struct spi_device *spi)
|
||||
{
|
||||
struct panel_drv_data *ddata;
|
||||
struct omap_dss_device *dssdev;
|
||||
struct backlight_device *bldev;
|
||||
int max_brightness, brightness;
|
||||
struct backlight_properties props;
|
||||
int r;
|
||||
|
||||
dev_dbg(&spi->dev, "%s\n", __func__);
|
||||
|
||||
spi->mode = SPI_MODE_3;
|
||||
|
||||
ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL);
|
||||
if (ddata == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&spi->dev, ddata);
|
||||
|
||||
ddata->spi = spi;
|
||||
|
||||
mutex_init(&ddata->mutex);
|
||||
|
||||
if (dev_get_platdata(&spi->dev)) {
|
||||
r = acx565akm_probe_pdata(spi);
|
||||
if (r)
|
||||
return r;
|
||||
} else if (spi->dev.of_node) {
|
||||
r = acx565akm_probe_of(spi);
|
||||
if (r)
|
||||
return r;
|
||||
} else {
|
||||
dev_err(&spi->dev, "platform data missing!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(ddata->reset_gpio)) {
|
||||
r = devm_gpio_request_one(&spi->dev, ddata->reset_gpio,
|
||||
GPIOF_OUT_INIT_LOW, "lcd reset");
|
||||
if (r)
|
||||
goto err_gpio;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(ddata->reset_gpio))
|
||||
gpio_set_value(ddata->reset_gpio, 1);
|
||||
|
||||
/*
|
||||
* After reset we have to wait 5 msec before the first
|
||||
* command can be sent.
|
||||
*/
|
||||
usleep_range(5000, 10000);
|
||||
|
||||
ddata->enabled = panel_enabled(ddata);
|
||||
|
||||
r = panel_detect(ddata);
|
||||
|
||||
if (!ddata->enabled && gpio_is_valid(ddata->reset_gpio))
|
||||
gpio_set_value(ddata->reset_gpio, 0);
|
||||
|
||||
if (r) {
|
||||
dev_err(&spi->dev, "%s panel detect error\n", __func__);
|
||||
goto err_detect;
|
||||
}
|
||||
|
||||
memset(&props, 0, sizeof(props));
|
||||
props.fb_blank = FB_BLANK_UNBLANK;
|
||||
props.power = FB_BLANK_UNBLANK;
|
||||
props.type = BACKLIGHT_RAW;
|
||||
|
||||
bldev = backlight_device_register("acx565akm", &ddata->spi->dev,
|
||||
ddata, &acx565akm_bl_ops, &props);
|
||||
if (IS_ERR(bldev)) {
|
||||
r = PTR_ERR(bldev);
|
||||
goto err_reg_bl;
|
||||
}
|
||||
ddata->bl_dev = bldev;
|
||||
if (ddata->has_cabc) {
|
||||
r = sysfs_create_group(&bldev->dev.kobj, &bldev_attr_group);
|
||||
if (r) {
|
||||
dev_err(&bldev->dev,
|
||||
"%s failed to create sysfs files\n", __func__);
|
||||
goto err_sysfs;
|
||||
}
|
||||
ddata->cabc_mode = get_hw_cabc_mode(ddata);
|
||||
}
|
||||
|
||||
max_brightness = 255;
|
||||
|
||||
if (ddata->has_bc)
|
||||
brightness = acx565akm_get_actual_brightness(ddata);
|
||||
else
|
||||
brightness = 0;
|
||||
|
||||
bldev->props.max_brightness = max_brightness;
|
||||
bldev->props.brightness = brightness;
|
||||
|
||||
acx565akm_bl_update_status(bldev);
|
||||
|
||||
|
||||
ddata->videomode = acx565akm_panel_timings;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->dev = &spi->dev;
|
||||
dssdev->driver = &acx565akm_ops;
|
||||
dssdev->type = OMAP_DISPLAY_TYPE_SDI;
|
||||
dssdev->owner = THIS_MODULE;
|
||||
dssdev->panel.timings = ddata->videomode;
|
||||
|
||||
r = omapdss_register_display(dssdev);
|
||||
if (r) {
|
||||
dev_err(&spi->dev, "Failed to register panel\n");
|
||||
goto err_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_reg:
|
||||
sysfs_remove_group(&bldev->dev.kobj, &bldev_attr_group);
|
||||
err_sysfs:
|
||||
backlight_device_unregister(bldev);
|
||||
err_reg_bl:
|
||||
err_detect:
|
||||
err_gpio:
|
||||
omap_dss_put_device(ddata->in);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int acx565akm_remove(struct spi_device *spi)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
|
||||
struct omap_dss_device *dssdev = &ddata->dssdev;
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
dev_dbg(&ddata->spi->dev, "%s\n", __func__);
|
||||
|
||||
sysfs_remove_group(&ddata->bl_dev->dev.kobj, &bldev_attr_group);
|
||||
backlight_device_unregister(ddata->bl_dev);
|
||||
|
||||
omapdss_unregister_display(dssdev);
|
||||
|
||||
acx565akm_disable(dssdev);
|
||||
acx565akm_disconnect(dssdev);
|
||||
|
||||
omap_dss_put_device(in);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id acx565akm_of_match[] = {
|
||||
{ .compatible = "omapdss,sony,acx565akm", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, acx565akm_of_match);
|
||||
|
||||
static struct spi_driver acx565akm_driver = {
|
||||
.driver = {
|
||||
.name = "acx565akm",
|
||||
.of_match_table = acx565akm_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.probe = acx565akm_probe,
|
||||
.remove = acx565akm_remove,
|
||||
};
|
||||
|
||||
module_spi_driver(acx565akm_driver);
|
||||
|
||||
MODULE_AUTHOR("Nokia Corporation");
|
||||
MODULE_DESCRIPTION("acx565akm LCD Driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,511 @@
|
|||
/*
|
||||
* Toppoly TD028TTEC1 panel support
|
||||
*
|
||||
* Copyright (C) 2008 Nokia Corporation
|
||||
* Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
|
||||
*
|
||||
* Neo 1973 code (jbt6k74.c):
|
||||
* Copyright (C) 2006-2007 by OpenMoko, Inc.
|
||||
* Author: Harald Welte <laforge@openmoko.org>
|
||||
*
|
||||
* Ported and adapted from Neo 1973 U-Boot by:
|
||||
* H. Nikolaus Schaller <hns@goldelico.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/spi/spi.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <video/omapdss.h>
|
||||
#include <video/omap-panel-data.h>
|
||||
|
||||
struct panel_drv_data {
|
||||
struct omap_dss_device dssdev;
|
||||
struct omap_dss_device *in;
|
||||
|
||||
int data_lines;
|
||||
|
||||
struct omap_video_timings videomode;
|
||||
|
||||
struct spi_device *spi_dev;
|
||||
};
|
||||
|
||||
static struct omap_video_timings td028ttec1_panel_timings = {
|
||||
.x_res = 480,
|
||||
.y_res = 640,
|
||||
.pixelclock = 22153000,
|
||||
.hfp = 24,
|
||||
.hsw = 8,
|
||||
.hbp = 8,
|
||||
.vfp = 4,
|
||||
.vsw = 2,
|
||||
.vbp = 2,
|
||||
|
||||
.vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
|
||||
.hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
|
||||
|
||||
.data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
|
||||
.de_level = OMAPDSS_SIG_ACTIVE_HIGH,
|
||||
.sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
|
||||
};
|
||||
|
||||
#define JBT_COMMAND 0x000
|
||||
#define JBT_DATA 0x100
|
||||
|
||||
static int jbt_ret_write_0(struct panel_drv_data *ddata, u8 reg)
|
||||
{
|
||||
int rc;
|
||||
u16 tx_buf = JBT_COMMAND | reg;
|
||||
|
||||
rc = spi_write(ddata->spi_dev, (u8 *)&tx_buf,
|
||||
1*sizeof(u16));
|
||||
if (rc != 0)
|
||||
dev_err(&ddata->spi_dev->dev,
|
||||
"jbt_ret_write_0 spi_write ret %d\n", rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int jbt_reg_write_1(struct panel_drv_data *ddata, u8 reg, u8 data)
|
||||
{
|
||||
int rc;
|
||||
u16 tx_buf[2];
|
||||
|
||||
tx_buf[0] = JBT_COMMAND | reg;
|
||||
tx_buf[1] = JBT_DATA | data;
|
||||
rc = spi_write(ddata->spi_dev, (u8 *)tx_buf,
|
||||
2*sizeof(u16));
|
||||
if (rc != 0)
|
||||
dev_err(&ddata->spi_dev->dev,
|
||||
"jbt_reg_write_1 spi_write ret %d\n", rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int jbt_reg_write_2(struct panel_drv_data *ddata, u8 reg, u16 data)
|
||||
{
|
||||
int rc;
|
||||
u16 tx_buf[3];
|
||||
|
||||
tx_buf[0] = JBT_COMMAND | reg;
|
||||
tx_buf[1] = JBT_DATA | (data >> 8);
|
||||
tx_buf[2] = JBT_DATA | (data & 0xff);
|
||||
|
||||
rc = spi_write(ddata->spi_dev, (u8 *)tx_buf,
|
||||
3*sizeof(u16));
|
||||
|
||||
if (rc != 0)
|
||||
dev_err(&ddata->spi_dev->dev,
|
||||
"jbt_reg_write_2 spi_write ret %d\n", rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
enum jbt_register {
|
||||
JBT_REG_SLEEP_IN = 0x10,
|
||||
JBT_REG_SLEEP_OUT = 0x11,
|
||||
|
||||
JBT_REG_DISPLAY_OFF = 0x28,
|
||||
JBT_REG_DISPLAY_ON = 0x29,
|
||||
|
||||
JBT_REG_RGB_FORMAT = 0x3a,
|
||||
JBT_REG_QUAD_RATE = 0x3b,
|
||||
|
||||
JBT_REG_POWER_ON_OFF = 0xb0,
|
||||
JBT_REG_BOOSTER_OP = 0xb1,
|
||||
JBT_REG_BOOSTER_MODE = 0xb2,
|
||||
JBT_REG_BOOSTER_FREQ = 0xb3,
|
||||
JBT_REG_OPAMP_SYSCLK = 0xb4,
|
||||
JBT_REG_VSC_VOLTAGE = 0xb5,
|
||||
JBT_REG_VCOM_VOLTAGE = 0xb6,
|
||||
JBT_REG_EXT_DISPL = 0xb7,
|
||||
JBT_REG_OUTPUT_CONTROL = 0xb8,
|
||||
JBT_REG_DCCLK_DCEV = 0xb9,
|
||||
JBT_REG_DISPLAY_MODE1 = 0xba,
|
||||
JBT_REG_DISPLAY_MODE2 = 0xbb,
|
||||
JBT_REG_DISPLAY_MODE = 0xbc,
|
||||
JBT_REG_ASW_SLEW = 0xbd,
|
||||
JBT_REG_DUMMY_DISPLAY = 0xbe,
|
||||
JBT_REG_DRIVE_SYSTEM = 0xbf,
|
||||
|
||||
JBT_REG_SLEEP_OUT_FR_A = 0xc0,
|
||||
JBT_REG_SLEEP_OUT_FR_B = 0xc1,
|
||||
JBT_REG_SLEEP_OUT_FR_C = 0xc2,
|
||||
JBT_REG_SLEEP_IN_LCCNT_D = 0xc3,
|
||||
JBT_REG_SLEEP_IN_LCCNT_E = 0xc4,
|
||||
JBT_REG_SLEEP_IN_LCCNT_F = 0xc5,
|
||||
JBT_REG_SLEEP_IN_LCCNT_G = 0xc6,
|
||||
|
||||
JBT_REG_GAMMA1_FINE_1 = 0xc7,
|
||||
JBT_REG_GAMMA1_FINE_2 = 0xc8,
|
||||
JBT_REG_GAMMA1_INCLINATION = 0xc9,
|
||||
JBT_REG_GAMMA1_BLUE_OFFSET = 0xca,
|
||||
|
||||
JBT_REG_BLANK_CONTROL = 0xcf,
|
||||
JBT_REG_BLANK_TH_TV = 0xd0,
|
||||
JBT_REG_CKV_ON_OFF = 0xd1,
|
||||
JBT_REG_CKV_1_2 = 0xd2,
|
||||
JBT_REG_OEV_TIMING = 0xd3,
|
||||
JBT_REG_ASW_TIMING_1 = 0xd4,
|
||||
JBT_REG_ASW_TIMING_2 = 0xd5,
|
||||
|
||||
JBT_REG_HCLOCK_VGA = 0xec,
|
||||
JBT_REG_HCLOCK_QVGA = 0xed,
|
||||
};
|
||||
|
||||
#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
|
||||
|
||||
static int td028ttec1_panel_connect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (omapdss_device_is_connected(dssdev))
|
||||
return 0;
|
||||
|
||||
r = in->ops.dpi->connect(in, dssdev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void td028ttec1_panel_disconnect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return;
|
||||
|
||||
in->ops.dpi->disconnect(in, dssdev);
|
||||
}
|
||||
|
||||
static int td028ttec1_panel_enable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return -ENODEV;
|
||||
|
||||
if (omapdss_device_is_enabled(dssdev))
|
||||
return 0;
|
||||
|
||||
if (ddata->data_lines)
|
||||
in->ops.dpi->set_data_lines(in, ddata->data_lines);
|
||||
in->ops.dpi->set_timings(in, &ddata->videomode);
|
||||
|
||||
r = in->ops.dpi->enable(in);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
dev_dbg(dssdev->dev, "td028ttec1_panel_enable() - state %d\n",
|
||||
dssdev->state);
|
||||
|
||||
/* three times command zero */
|
||||
r |= jbt_ret_write_0(ddata, 0x00);
|
||||
usleep_range(1000, 2000);
|
||||
r |= jbt_ret_write_0(ddata, 0x00);
|
||||
usleep_range(1000, 2000);
|
||||
r |= jbt_ret_write_0(ddata, 0x00);
|
||||
usleep_range(1000, 2000);
|
||||
|
||||
if (r) {
|
||||
dev_warn(dssdev->dev, "transfer error\n");
|
||||
goto transfer_err;
|
||||
}
|
||||
|
||||
/* deep standby out */
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_POWER_ON_OFF, 0x17);
|
||||
|
||||
/* RGB I/F on, RAM write off, QVGA through, SIGCON enable */
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_DISPLAY_MODE, 0x80);
|
||||
|
||||
/* Quad mode off */
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_QUAD_RATE, 0x00);
|
||||
|
||||
/* AVDD on, XVDD on */
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_POWER_ON_OFF, 0x16);
|
||||
|
||||
/* Output control */
|
||||
r |= jbt_reg_write_2(ddata, JBT_REG_OUTPUT_CONTROL, 0xfff9);
|
||||
|
||||
/* Sleep mode off */
|
||||
r |= jbt_ret_write_0(ddata, JBT_REG_SLEEP_OUT);
|
||||
|
||||
/* at this point we have like 50% grey */
|
||||
|
||||
/* initialize register set */
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_DISPLAY_MODE1, 0x01);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_DISPLAY_MODE2, 0x00);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_RGB_FORMAT, 0x60);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_DRIVE_SYSTEM, 0x10);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_BOOSTER_OP, 0x56);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_BOOSTER_MODE, 0x33);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_BOOSTER_FREQ, 0x11);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_BOOSTER_FREQ, 0x11);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_OPAMP_SYSCLK, 0x02);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_VSC_VOLTAGE, 0x2b);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_VCOM_VOLTAGE, 0x40);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_EXT_DISPL, 0x03);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_DCCLK_DCEV, 0x04);
|
||||
/*
|
||||
* default of 0x02 in JBT_REG_ASW_SLEW responsible for 72Hz requirement
|
||||
* to avoid red / blue flicker
|
||||
*/
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_ASW_SLEW, 0x04);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_DUMMY_DISPLAY, 0x00);
|
||||
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_SLEEP_OUT_FR_A, 0x11);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_SLEEP_OUT_FR_B, 0x11);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_SLEEP_OUT_FR_C, 0x11);
|
||||
r |= jbt_reg_write_2(ddata, JBT_REG_SLEEP_IN_LCCNT_D, 0x2040);
|
||||
r |= jbt_reg_write_2(ddata, JBT_REG_SLEEP_IN_LCCNT_E, 0x60c0);
|
||||
r |= jbt_reg_write_2(ddata, JBT_REG_SLEEP_IN_LCCNT_F, 0x1020);
|
||||
r |= jbt_reg_write_2(ddata, JBT_REG_SLEEP_IN_LCCNT_G, 0x60c0);
|
||||
|
||||
r |= jbt_reg_write_2(ddata, JBT_REG_GAMMA1_FINE_1, 0x5533);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_GAMMA1_FINE_2, 0x00);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_GAMMA1_INCLINATION, 0x00);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_GAMMA1_BLUE_OFFSET, 0x00);
|
||||
|
||||
r |= jbt_reg_write_2(ddata, JBT_REG_HCLOCK_VGA, 0x1f0);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_BLANK_CONTROL, 0x02);
|
||||
r |= jbt_reg_write_2(ddata, JBT_REG_BLANK_TH_TV, 0x0804);
|
||||
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_CKV_ON_OFF, 0x01);
|
||||
r |= jbt_reg_write_2(ddata, JBT_REG_CKV_1_2, 0x0000);
|
||||
|
||||
r |= jbt_reg_write_2(ddata, JBT_REG_OEV_TIMING, 0x0d0e);
|
||||
r |= jbt_reg_write_2(ddata, JBT_REG_ASW_TIMING_1, 0x11a4);
|
||||
r |= jbt_reg_write_1(ddata, JBT_REG_ASW_TIMING_2, 0x0e);
|
||||
|
||||
r |= jbt_ret_write_0(ddata, JBT_REG_DISPLAY_ON);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
|
||||
|
||||
transfer_err:
|
||||
|
||||
return r ? -EIO : 0;
|
||||
}
|
||||
|
||||
static void td028ttec1_panel_disable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
if (!omapdss_device_is_enabled(dssdev))
|
||||
return;
|
||||
|
||||
dev_dbg(dssdev->dev, "td028ttec1_panel_disable()\n");
|
||||
|
||||
jbt_ret_write_0(ddata, JBT_REG_DISPLAY_OFF);
|
||||
jbt_reg_write_2(ddata, JBT_REG_OUTPUT_CONTROL, 0x8002);
|
||||
jbt_ret_write_0(ddata, JBT_REG_SLEEP_IN);
|
||||
jbt_reg_write_1(ddata, JBT_REG_POWER_ON_OFF, 0x00);
|
||||
|
||||
in->ops.dpi->disable(in);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
|
||||
}
|
||||
|
||||
static void td028ttec1_panel_set_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
ddata->videomode = *timings;
|
||||
dssdev->panel.timings = *timings;
|
||||
|
||||
in->ops.dpi->set_timings(in, timings);
|
||||
}
|
||||
|
||||
static void td028ttec1_panel_get_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
|
||||
*timings = ddata->videomode;
|
||||
}
|
||||
|
||||
static int td028ttec1_panel_check_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
return in->ops.dpi->check_timings(in, timings);
|
||||
}
|
||||
|
||||
static struct omap_dss_driver td028ttec1_ops = {
|
||||
.connect = td028ttec1_panel_connect,
|
||||
.disconnect = td028ttec1_panel_disconnect,
|
||||
|
||||
.enable = td028ttec1_panel_enable,
|
||||
.disable = td028ttec1_panel_disable,
|
||||
|
||||
.set_timings = td028ttec1_panel_set_timings,
|
||||
.get_timings = td028ttec1_panel_get_timings,
|
||||
.check_timings = td028ttec1_panel_check_timings,
|
||||
};
|
||||
|
||||
static int td028ttec1_panel_probe_pdata(struct spi_device *spi)
|
||||
{
|
||||
const struct panel_tpo_td028ttec1_platform_data *pdata;
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
|
||||
struct omap_dss_device *dssdev, *in;
|
||||
|
||||
pdata = dev_get_platdata(&spi->dev);
|
||||
|
||||
in = omap_dss_find_output(pdata->source);
|
||||
if (in == NULL) {
|
||||
dev_err(&spi->dev, "failed to find video source '%s'\n",
|
||||
pdata->source);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
ddata->data_lines = pdata->data_lines;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->name = pdata->name;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int td028ttec1_probe_of(struct spi_device *spi)
|
||||
{
|
||||
struct device_node *node = spi->dev.of_node;
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
|
||||
struct omap_dss_device *in;
|
||||
|
||||
in = omapdss_of_find_source_for_first_ep(node);
|
||||
if (IS_ERR(in)) {
|
||||
dev_err(&spi->dev, "failed to find video source\n");
|
||||
return PTR_ERR(in);
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int td028ttec1_panel_probe(struct spi_device *spi)
|
||||
{
|
||||
struct panel_drv_data *ddata;
|
||||
struct omap_dss_device *dssdev;
|
||||
int r;
|
||||
|
||||
dev_dbg(&spi->dev, "%s\n", __func__);
|
||||
|
||||
spi->bits_per_word = 9;
|
||||
spi->mode = SPI_MODE_3;
|
||||
|
||||
r = spi_setup(spi);
|
||||
if (r < 0) {
|
||||
dev_err(&spi->dev, "spi_setup failed: %d\n", r);
|
||||
return r;
|
||||
}
|
||||
|
||||
ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL);
|
||||
if (ddata == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&spi->dev, ddata);
|
||||
|
||||
ddata->spi_dev = spi;
|
||||
|
||||
if (dev_get_platdata(&spi->dev)) {
|
||||
r = td028ttec1_panel_probe_pdata(spi);
|
||||
if (r)
|
||||
return r;
|
||||
} else if (spi->dev.of_node) {
|
||||
r = td028ttec1_probe_of(spi);
|
||||
if (r)
|
||||
return r;
|
||||
} else {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ddata->videomode = td028ttec1_panel_timings;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->dev = &spi->dev;
|
||||
dssdev->driver = &td028ttec1_ops;
|
||||
dssdev->type = OMAP_DISPLAY_TYPE_DPI;
|
||||
dssdev->owner = THIS_MODULE;
|
||||
dssdev->panel.timings = ddata->videomode;
|
||||
dssdev->phy.dpi.data_lines = ddata->data_lines;
|
||||
|
||||
r = omapdss_register_display(dssdev);
|
||||
if (r) {
|
||||
dev_err(&spi->dev, "Failed to register panel\n");
|
||||
goto err_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_reg:
|
||||
omap_dss_put_device(ddata->in);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int td028ttec1_panel_remove(struct spi_device *spi)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
|
||||
struct omap_dss_device *dssdev = &ddata->dssdev;
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
dev_dbg(&ddata->spi_dev->dev, "%s\n", __func__);
|
||||
|
||||
omapdss_unregister_display(dssdev);
|
||||
|
||||
td028ttec1_panel_disable(dssdev);
|
||||
td028ttec1_panel_disconnect(dssdev);
|
||||
|
||||
omap_dss_put_device(in);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id td028ttec1_of_match[] = {
|
||||
{ .compatible = "omapdss,toppoly,td028ttec1", },
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, td028ttec1_of_match);
|
||||
|
||||
static struct spi_driver td028ttec1_spi_driver = {
|
||||
.probe = td028ttec1_panel_probe,
|
||||
.remove = td028ttec1_panel_remove,
|
||||
|
||||
.driver = {
|
||||
.name = "panel-tpo-td028ttec1",
|
||||
.of_match_table = td028ttec1_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
|
||||
module_spi_driver(td028ttec1_spi_driver);
|
||||
|
||||
MODULE_ALIAS("spi:toppoly,td028ttec1");
|
||||
MODULE_AUTHOR("H. Nikolaus Schaller <hns@goldelico.com>");
|
||||
MODULE_DESCRIPTION("Toppoly TD028TTEC1 panel driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,686 @@
|
|||
/*
|
||||
* TPO TD043MTEA1 Panel driver
|
||||
*
|
||||
* Author: Gražvydas Ignotas <notasas@gmail.com>
|
||||
* Converted to new DSS device model: Tomi Valkeinen <tomi.valkeinen@ti.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 <linux/slab.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include <video/omapdss.h>
|
||||
#include <video/omap-panel-data.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] = {
|
||||
105, 315, 381, 431, 490, 537, 579, 686, 780, 837, 880, 1023
|
||||
};
|
||||
|
||||
struct panel_drv_data {
|
||||
struct omap_dss_device dssdev;
|
||||
struct omap_dss_device *in;
|
||||
|
||||
struct omap_video_timings videomode;
|
||||
|
||||
int data_lines;
|
||||
|
||||
struct spi_device *spi;
|
||||
struct regulator *vcc_reg;
|
||||
int nreset_gpio;
|
||||
u16 gamma[12];
|
||||
u32 mode;
|
||||
u32 hmirror:1;
|
||||
u32 vmirror:1;
|
||||
u32 powered_on:1;
|
||||
u32 spi_suspended:1;
|
||||
u32 power_on_resume:1;
|
||||
};
|
||||
|
||||
static const struct omap_video_timings tpo_td043_timings = {
|
||||
.x_res = 800,
|
||||
.y_res = 480,
|
||||
|
||||
.pixelclock = 36000000,
|
||||
|
||||
.hsw = 1,
|
||||
.hfp = 68,
|
||||
.hbp = 214,
|
||||
|
||||
.vsw = 1,
|
||||
.vfp = 39,
|
||||
.vbp = 34,
|
||||
|
||||
.vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
|
||||
.hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
|
||||
.data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
|
||||
.de_level = OMAPDSS_SIG_ACTIVE_HIGH,
|
||||
.sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
|
||||
};
|
||||
|
||||
#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
|
||||
|
||||
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 panel_drv_data *ddata = dev_get_drvdata(dssdev->dev);
|
||||
|
||||
ddata->hmirror = enable;
|
||||
return tpo_td043_write_mirror(ddata->spi, ddata->hmirror,
|
||||
ddata->vmirror);
|
||||
}
|
||||
|
||||
static bool tpo_td043_get_hmirror(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(dssdev->dev);
|
||||
|
||||
return ddata->hmirror;
|
||||
}
|
||||
|
||||
static ssize_t tpo_td043_vmirror_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", ddata->vmirror);
|
||||
}
|
||||
|
||||
static ssize_t tpo_td043_vmirror_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(dev);
|
||||
int val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtoint(buf, 0, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val = !!val;
|
||||
|
||||
ret = tpo_td043_write_mirror(ddata->spi, ddata->hmirror, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ddata->vmirror = val;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t tpo_td043_mode_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", ddata->mode);
|
||||
}
|
||||
|
||||
static ssize_t tpo_td043_mode_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(dev);
|
||||
long val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtol(buf, 0, &val);
|
||||
if (ret != 0 || val & ~7)
|
||||
return -EINVAL;
|
||||
|
||||
ddata->mode = val;
|
||||
|
||||
val |= TPO_R02_NCLK_RISING;
|
||||
tpo_td043_write(ddata->spi, 2, val);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t tpo_td043_gamma_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(dev);
|
||||
ssize_t len = 0;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ddata->gamma); i++) {
|
||||
ret = snprintf(buf + len, PAGE_SIZE - len, "%u ",
|
||||
ddata->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 panel_drv_data *ddata = 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++)
|
||||
ddata->gamma[i] = g[i];
|
||||
|
||||
tpo_td043_write_gamma(ddata->spi, ddata->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 int tpo_td043_power_on(struct panel_drv_data *ddata)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (ddata->powered_on)
|
||||
return 0;
|
||||
|
||||
r = regulator_enable(ddata->vcc_reg);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
/* wait for panel to stabilize */
|
||||
msleep(160);
|
||||
|
||||
if (gpio_is_valid(ddata->nreset_gpio))
|
||||
gpio_set_value(ddata->nreset_gpio, 1);
|
||||
|
||||
tpo_td043_write(ddata->spi, 2,
|
||||
TPO_R02_MODE(ddata->mode) | TPO_R02_NCLK_RISING);
|
||||
tpo_td043_write(ddata->spi, 3, TPO_R03_VAL_NORMAL);
|
||||
tpo_td043_write(ddata->spi, 0x20, 0xf0);
|
||||
tpo_td043_write(ddata->spi, 0x21, 0xf0);
|
||||
tpo_td043_write_mirror(ddata->spi, ddata->hmirror,
|
||||
ddata->vmirror);
|
||||
tpo_td043_write_gamma(ddata->spi, ddata->gamma);
|
||||
|
||||
ddata->powered_on = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tpo_td043_power_off(struct panel_drv_data *ddata)
|
||||
{
|
||||
if (!ddata->powered_on)
|
||||
return;
|
||||
|
||||
tpo_td043_write(ddata->spi, 3,
|
||||
TPO_R03_VAL_STANDBY | TPO_R03_EN_PWM);
|
||||
|
||||
if (gpio_is_valid(ddata->nreset_gpio))
|
||||
gpio_set_value(ddata->nreset_gpio, 0);
|
||||
|
||||
/* wait for at least 2 vsyncs before cutting off power */
|
||||
msleep(50);
|
||||
|
||||
tpo_td043_write(ddata->spi, 3, TPO_R03_VAL_STANDBY);
|
||||
|
||||
regulator_disable(ddata->vcc_reg);
|
||||
|
||||
ddata->powered_on = 0;
|
||||
}
|
||||
|
||||
static int tpo_td043_connect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (omapdss_device_is_connected(dssdev))
|
||||
return 0;
|
||||
|
||||
r = in->ops.dpi->connect(in, dssdev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tpo_td043_disconnect(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return;
|
||||
|
||||
in->ops.dpi->disconnect(in, dssdev);
|
||||
}
|
||||
|
||||
static int tpo_td043_enable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
int r;
|
||||
|
||||
if (!omapdss_device_is_connected(dssdev))
|
||||
return -ENODEV;
|
||||
|
||||
if (omapdss_device_is_enabled(dssdev))
|
||||
return 0;
|
||||
|
||||
if (ddata->data_lines)
|
||||
in->ops.dpi->set_data_lines(in, ddata->data_lines);
|
||||
in->ops.dpi->set_timings(in, &ddata->videomode);
|
||||
|
||||
r = in->ops.dpi->enable(in);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
/*
|
||||
* If we are resuming from system suspend, SPI clocks might not be
|
||||
* enabled yet, so we'll program the LCD from SPI PM resume callback.
|
||||
*/
|
||||
if (!ddata->spi_suspended) {
|
||||
r = tpo_td043_power_on(ddata);
|
||||
if (r) {
|
||||
in->ops.dpi->disable(in);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tpo_td043_disable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
if (!omapdss_device_is_enabled(dssdev))
|
||||
return;
|
||||
|
||||
in->ops.dpi->disable(in);
|
||||
|
||||
if (!ddata->spi_suspended)
|
||||
tpo_td043_power_off(ddata);
|
||||
|
||||
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
|
||||
}
|
||||
|
||||
static void tpo_td043_set_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
ddata->videomode = *timings;
|
||||
dssdev->panel.timings = *timings;
|
||||
|
||||
in->ops.dpi->set_timings(in, timings);
|
||||
}
|
||||
|
||||
static void tpo_td043_get_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
|
||||
*timings = ddata->videomode;
|
||||
}
|
||||
|
||||
static int tpo_td043_check_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
return in->ops.dpi->check_timings(in, timings);
|
||||
}
|
||||
|
||||
static struct omap_dss_driver tpo_td043_ops = {
|
||||
.connect = tpo_td043_connect,
|
||||
.disconnect = tpo_td043_disconnect,
|
||||
|
||||
.enable = tpo_td043_enable,
|
||||
.disable = tpo_td043_disable,
|
||||
|
||||
.set_timings = tpo_td043_set_timings,
|
||||
.get_timings = tpo_td043_get_timings,
|
||||
.check_timings = tpo_td043_check_timings,
|
||||
|
||||
.set_mirror = tpo_td043_set_hmirror,
|
||||
.get_mirror = tpo_td043_get_hmirror,
|
||||
|
||||
.get_resolution = omapdss_default_get_resolution,
|
||||
};
|
||||
|
||||
|
||||
static int tpo_td043_probe_pdata(struct spi_device *spi)
|
||||
{
|
||||
const struct panel_tpo_td043mtea1_platform_data *pdata;
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
|
||||
struct omap_dss_device *dssdev, *in;
|
||||
|
||||
pdata = dev_get_platdata(&spi->dev);
|
||||
|
||||
ddata->nreset_gpio = pdata->nreset_gpio;
|
||||
|
||||
in = omap_dss_find_output(pdata->source);
|
||||
if (in == NULL) {
|
||||
dev_err(&spi->dev, "failed to find video source '%s'\n",
|
||||
pdata->source);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
ddata->in = in;
|
||||
|
||||
ddata->data_lines = pdata->data_lines;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->name = pdata->name;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpo_td043_probe_of(struct spi_device *spi)
|
||||
{
|
||||
struct device_node *node = spi->dev.of_node;
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
|
||||
struct omap_dss_device *in;
|
||||
int gpio;
|
||||
|
||||
gpio = of_get_named_gpio(node, "reset-gpios", 0);
|
||||
if (!gpio_is_valid(gpio)) {
|
||||
dev_err(&spi->dev, "failed to parse enable gpio\n");
|
||||
return gpio;
|
||||
}
|
||||
ddata->nreset_gpio = gpio;
|
||||
|
||||
in = omapdss_of_find_source_for_first_ep(node);
|
||||
if (IS_ERR(in)) {
|
||||
dev_err(&spi->dev, "failed to find video source\n");
|
||||
return PTR_ERR(in);
|
||||
}
|
||||
|
||||
ddata->in = in;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpo_td043_probe(struct spi_device *spi)
|
||||
{
|
||||
struct panel_drv_data *ddata;
|
||||
struct omap_dss_device *dssdev;
|
||||
int r;
|
||||
|
||||
dev_dbg(&spi->dev, "%s\n", __func__);
|
||||
|
||||
spi->bits_per_word = 16;
|
||||
spi->mode = SPI_MODE_0;
|
||||
|
||||
r = spi_setup(spi);
|
||||
if (r < 0) {
|
||||
dev_err(&spi->dev, "spi_setup failed: %d\n", r);
|
||||
return r;
|
||||
}
|
||||
|
||||
ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL);
|
||||
if (ddata == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&spi->dev, ddata);
|
||||
|
||||
ddata->spi = spi;
|
||||
|
||||
if (dev_get_platdata(&spi->dev)) {
|
||||
r = tpo_td043_probe_pdata(spi);
|
||||
if (r)
|
||||
return r;
|
||||
} else if (spi->dev.of_node) {
|
||||
r = tpo_td043_probe_of(spi);
|
||||
if (r)
|
||||
return r;
|
||||
} else {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ddata->mode = TPO_R02_MODE_800x480;
|
||||
memcpy(ddata->gamma, tpo_td043_def_gamma, sizeof(ddata->gamma));
|
||||
|
||||
ddata->vcc_reg = devm_regulator_get(&spi->dev, "vcc");
|
||||
if (IS_ERR(ddata->vcc_reg)) {
|
||||
dev_err(&spi->dev, "failed to get LCD VCC regulator\n");
|
||||
r = PTR_ERR(ddata->vcc_reg);
|
||||
goto err_regulator;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(ddata->nreset_gpio)) {
|
||||
r = devm_gpio_request_one(&spi->dev,
|
||||
ddata->nreset_gpio, GPIOF_OUT_INIT_LOW,
|
||||
"lcd reset");
|
||||
if (r < 0) {
|
||||
dev_err(&spi->dev, "couldn't request reset GPIO\n");
|
||||
goto err_gpio_req;
|
||||
}
|
||||
}
|
||||
|
||||
r = sysfs_create_group(&spi->dev.kobj, &tpo_td043_attr_group);
|
||||
if (r) {
|
||||
dev_err(&spi->dev, "failed to create sysfs files\n");
|
||||
goto err_sysfs;
|
||||
}
|
||||
|
||||
ddata->videomode = tpo_td043_timings;
|
||||
|
||||
dssdev = &ddata->dssdev;
|
||||
dssdev->dev = &spi->dev;
|
||||
dssdev->driver = &tpo_td043_ops;
|
||||
dssdev->type = OMAP_DISPLAY_TYPE_DPI;
|
||||
dssdev->owner = THIS_MODULE;
|
||||
dssdev->panel.timings = ddata->videomode;
|
||||
|
||||
r = omapdss_register_display(dssdev);
|
||||
if (r) {
|
||||
dev_err(&spi->dev, "Failed to register panel\n");
|
||||
goto err_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_reg:
|
||||
sysfs_remove_group(&spi->dev.kobj, &tpo_td043_attr_group);
|
||||
err_sysfs:
|
||||
err_gpio_req:
|
||||
err_regulator:
|
||||
omap_dss_put_device(ddata->in);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int tpo_td043_remove(struct spi_device *spi)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
|
||||
struct omap_dss_device *dssdev = &ddata->dssdev;
|
||||
struct omap_dss_device *in = ddata->in;
|
||||
|
||||
dev_dbg(&ddata->spi->dev, "%s\n", __func__);
|
||||
|
||||
omapdss_unregister_display(dssdev);
|
||||
|
||||
tpo_td043_disable(dssdev);
|
||||
tpo_td043_disconnect(dssdev);
|
||||
|
||||
omap_dss_put_device(in);
|
||||
|
||||
sysfs_remove_group(&spi->dev.kobj, &tpo_td043_attr_group);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int tpo_td043_spi_suspend(struct device *dev)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(dev);
|
||||
|
||||
dev_dbg(dev, "tpo_td043_spi_suspend, tpo %p\n", ddata);
|
||||
|
||||
ddata->power_on_resume = ddata->powered_on;
|
||||
tpo_td043_power_off(ddata);
|
||||
ddata->spi_suspended = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpo_td043_spi_resume(struct device *dev)
|
||||
{
|
||||
struct panel_drv_data *ddata = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
dev_dbg(dev, "tpo_td043_spi_resume\n");
|
||||
|
||||
if (ddata->power_on_resume) {
|
||||
ret = tpo_td043_power_on(ddata);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
ddata->spi_suspended = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(tpo_td043_spi_pm,
|
||||
tpo_td043_spi_suspend, tpo_td043_spi_resume);
|
||||
|
||||
static const struct of_device_id tpo_td043_of_match[] = {
|
||||
{ .compatible = "omapdss,tpo,td043mtea1", },
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, tpo_td043_of_match);
|
||||
|
||||
static struct spi_driver tpo_td043_spi_driver = {
|
||||
.driver = {
|
||||
.name = "panel-tpo-td043mtea1",
|
||||
.pm = &tpo_td043_spi_pm,
|
||||
.of_match_table = tpo_td043_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.probe = tpo_td043_probe,
|
||||
.remove = tpo_td043_remove,
|
||||
};
|
||||
|
||||
module_spi_driver(tpo_td043_spi_driver);
|
||||
|
||||
MODULE_ALIAS("spi:tpo,td043mtea1");
|
||||
MODULE_AUTHOR("Gražvydas Ignotas <notasas@gmail.com>");
|
||||
MODULE_DESCRIPTION("TPO TD043MTEA1 LCD Driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,129 @@
|
|||
config FB_OMAP2_DSS_INIT
|
||||
bool
|
||||
|
||||
config FB_OMAP2_DSS
|
||||
tristate
|
||||
select VIDEOMODE_HELPERS
|
||||
select FB_OMAP2_DSS_INIT
|
||||
select HDMI
|
||||
|
||||
config FB_OMAP2_DSS_DEBUG
|
||||
bool "Debug support"
|
||||
default n
|
||||
help
|
||||
This enables printing of debug messages. Alternatively, debug messages
|
||||
can also be enabled by setting CONFIG_DYNAMIC_DEBUG and then setting
|
||||
appropriate flags in <debugfs>/dynamic_debug/control.
|
||||
|
||||
config FB_OMAP2_DSS_DEBUGFS
|
||||
bool "Debugfs filesystem support"
|
||||
depends on DEBUG_FS
|
||||
default n
|
||||
help
|
||||
This enables debugfs for OMAPDSS at <debugfs>/omapdss. This enables
|
||||
querying about clock configuration and register configuration of dss,
|
||||
dispc, dsi, hdmi and rfbi.
|
||||
|
||||
config FB_OMAP2_DSS_COLLECT_IRQ_STATS
|
||||
bool "Collect DSS IRQ statistics"
|
||||
depends on FB_OMAP2_DSS_DEBUGFS
|
||||
default n
|
||||
help
|
||||
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 FB_OMAP2_DSS_DPI
|
||||
bool "DPI support"
|
||||
default y
|
||||
help
|
||||
DPI Interface. This is the Parallel Display Interface.
|
||||
|
||||
config FB_OMAP2_DSS_RFBI
|
||||
bool "RFBI support"
|
||||
depends on BROKEN
|
||||
default n
|
||||
help
|
||||
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 specifications.
|
||||
|
||||
config FB_OMAP2_DSS_VENC
|
||||
bool "VENC support"
|
||||
default y
|
||||
help
|
||||
OMAP Video Encoder support for S-Video and composite TV-out.
|
||||
|
||||
config FB_OMAP2_DSS_HDMI_COMMON
|
||||
bool
|
||||
|
||||
config FB_OMAP4_DSS_HDMI
|
||||
bool "HDMI support for OMAP4"
|
||||
default y
|
||||
select FB_OMAP2_DSS_HDMI_COMMON
|
||||
help
|
||||
HDMI support for OMAP4 based SoCs.
|
||||
|
||||
config FB_OMAP5_DSS_HDMI
|
||||
bool "HDMI support for OMAP5"
|
||||
default n
|
||||
select FB_OMAP2_DSS_HDMI_COMMON
|
||||
help
|
||||
HDMI Interface for OMAP5 and similar cores. This adds the High
|
||||
Definition Multimedia Interface. See http://www.hdmi.org/ for HDMI
|
||||
specification.
|
||||
|
||||
config FB_OMAP2_DSS_SDI
|
||||
bool "SDI support"
|
||||
default n
|
||||
help
|
||||
SDI (Serial Display Interface) support.
|
||||
|
||||
SDI is a high speed one-way display serial bus between the host
|
||||
processor and a display.
|
||||
|
||||
config FB_OMAP2_DSS_DSI
|
||||
bool "DSI support"
|
||||
default n
|
||||
help
|
||||
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 specifications.
|
||||
|
||||
config FB_OMAP2_DSS_MIN_FCK_PER_PCK
|
||||
int "Minimum FCK/PCK ratio (for scaling)"
|
||||
range 0 32
|
||||
default 0
|
||||
help
|
||||
This can be used to adjust the minimum FCK/PCK ratio.
|
||||
|
||||
With this you can make sure that DISPC FCK is at least
|
||||
n x PCK. Video plane scaling requires higher FCK than
|
||||
normally.
|
||||
|
||||
If this is set to 0, there's no extra constraint on the
|
||||
DISPC FCK. However, the FCK will at minimum be
|
||||
2xPCK (if active matrix) or 3xPCK (if passive matrix).
|
||||
|
||||
Max FCK is 173MHz, so this doesn't work if your PCK
|
||||
is very high.
|
||||
|
||||
config FB_OMAP2_DSS_SLEEP_AFTER_VENC_RESET
|
||||
bool "Sleep 20ms after VENC reset"
|
||||
default y
|
||||
help
|
||||
There is a 20ms sleep after VENC reset which seemed to fix the
|
||||
reset. The reason for the bug is unclear, and it's also unclear
|
||||
on what platforms this happens.
|
||||
|
||||
This option enables the sleep, and is enabled by default. You can
|
||||
disable the sleep if it doesn't cause problems on your platform.
|
|
@ -0,0 +1,18 @@
|
|||
obj-$(CONFIG_FB_OMAP2_DSS_INIT) += omapdss-boot-init.o
|
||||
obj-$(CONFIG_FB_OMAP2_DSS) += omapdss.o
|
||||
# Core DSS files
|
||||
omapdss-y := core.o dss.o dss_features.o dispc.o dispc_coefs.o display.o \
|
||||
output.o dss-of.o pll.o video-pll.o
|
||||
# DSS compat layer files
|
||||
omapdss-y += manager.o manager-sysfs.o overlay.o overlay-sysfs.o apply.o \
|
||||
dispc-compat.o display-sysfs.o
|
||||
omapdss-$(CONFIG_FB_OMAP2_DSS_DPI) += dpi.o
|
||||
omapdss-$(CONFIG_FB_OMAP2_DSS_RFBI) += rfbi.o
|
||||
omapdss-$(CONFIG_FB_OMAP2_DSS_VENC) += venc.o
|
||||
omapdss-$(CONFIG_FB_OMAP2_DSS_SDI) += sdi.o
|
||||
omapdss-$(CONFIG_FB_OMAP2_DSS_DSI) += dsi.o
|
||||
omapdss-$(CONFIG_FB_OMAP2_DSS_HDMI_COMMON) += hdmi_common.o hdmi_wp.o hdmi_pll.o \
|
||||
hdmi_phy.o
|
||||
omapdss-$(CONFIG_FB_OMAP4_DSS_HDMI) += hdmi4.o hdmi4_core.o
|
||||
omapdss-$(CONFIG_FB_OMAP5_DSS_HDMI) += hdmi5.o hdmi5_core.o
|
||||
ccflags-$(CONFIG_FB_OMAP2_DSS_DEBUG) += -DDEBUG
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,343 @@
|
|||
/*
|
||||
* linux/drivers/video/omap2/dss/core.c
|
||||
*
|
||||
* Copyright (C) 2009 Nokia Corporation
|
||||
* Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
|
||||
*
|
||||
* Some code and ideas taken from drivers/video/omap/ driver
|
||||
* by Imre Deak.
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#define DSS_SUBSYS_NAME "CORE"
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <video/omapdss.h>
|
||||
|
||||
#include "dss.h"
|
||||
#include "dss_features.h"
|
||||
|
||||
static struct {
|
||||
struct platform_device *pdev;
|
||||
|
||||
const char *default_display_name;
|
||||
} core;
|
||||
|
||||
static char *def_disp_name;
|
||||
module_param_named(def_disp, def_disp_name, charp, 0);
|
||||
MODULE_PARM_DESC(def_disp, "default display name");
|
||||
|
||||
const char *omapdss_get_default_display_name(void)
|
||||
{
|
||||
return core.default_display_name;
|
||||
}
|
||||
EXPORT_SYMBOL(omapdss_get_default_display_name);
|
||||
|
||||
enum omapdss_version omapdss_get_version(void)
|
||||
{
|
||||
struct omap_dss_board_info *pdata = core.pdev->dev.platform_data;
|
||||
return pdata->version;
|
||||
}
|
||||
EXPORT_SYMBOL(omapdss_get_version);
|
||||
|
||||
struct platform_device *dss_get_core_pdev(void)
|
||||
{
|
||||
return core.pdev;
|
||||
}
|
||||
|
||||
int dss_dsi_enable_pads(int dsi_id, unsigned lane_mask)
|
||||
{
|
||||
struct omap_dss_board_info *board_data = core.pdev->dev.platform_data;
|
||||
|
||||
if (!board_data->dsi_enable_pads)
|
||||
return -ENOENT;
|
||||
|
||||
return board_data->dsi_enable_pads(dsi_id, lane_mask);
|
||||
}
|
||||
|
||||
void dss_dsi_disable_pads(int dsi_id, unsigned lane_mask)
|
||||
{
|
||||
struct omap_dss_board_info *board_data = core.pdev->dev.platform_data;
|
||||
|
||||
if (!board_data->dsi_disable_pads)
|
||||
return;
|
||||
|
||||
return board_data->dsi_disable_pads(dsi_id, lane_mask);
|
||||
}
|
||||
|
||||
int dss_set_min_bus_tput(struct device *dev, unsigned long tput)
|
||||
{
|
||||
struct omap_dss_board_info *pdata = core.pdev->dev.platform_data;
|
||||
|
||||
if (pdata->set_min_bus_tput)
|
||||
return pdata->set_min_bus_tput(dev, tput);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_FB_OMAP2_DSS_DEBUGFS)
|
||||
static int dss_debug_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
void (*func)(struct seq_file *) = s->private;
|
||||
func(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dss_debug_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, dss_debug_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations dss_debug_fops = {
|
||||
.open = dss_debug_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static struct dentry *dss_debugfs_dir;
|
||||
|
||||
static int dss_initialize_debugfs(void)
|
||||
{
|
||||
dss_debugfs_dir = debugfs_create_dir("omapdss", NULL);
|
||||
if (IS_ERR(dss_debugfs_dir)) {
|
||||
int err = PTR_ERR(dss_debugfs_dir);
|
||||
dss_debugfs_dir = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
debugfs_create_file("clk", S_IRUGO, dss_debugfs_dir,
|
||||
&dss_debug_dump_clocks, &dss_debug_fops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dss_uninitialize_debugfs(void)
|
||||
{
|
||||
if (dss_debugfs_dir)
|
||||
debugfs_remove_recursive(dss_debugfs_dir);
|
||||
}
|
||||
|
||||
int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *))
|
||||
{
|
||||
struct dentry *d;
|
||||
|
||||
d = debugfs_create_file(name, S_IRUGO, dss_debugfs_dir,
|
||||
write, &dss_debug_fops);
|
||||
|
||||
return PTR_ERR_OR_ZERO(d);
|
||||
}
|
||||
#else /* CONFIG_FB_OMAP2_DSS_DEBUGFS */
|
||||
static inline int dss_initialize_debugfs(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void dss_uninitialize_debugfs(void)
|
||||
{
|
||||
}
|
||||
int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_FB_OMAP2_DSS_DEBUGFS */
|
||||
|
||||
/* PLATFORM DEVICE */
|
||||
static int omap_dss_pm_notif(struct notifier_block *b, unsigned long v, void *d)
|
||||
{
|
||||
DSSDBG("pm notif %lu\n", v);
|
||||
|
||||
switch (v) {
|
||||
case PM_SUSPEND_PREPARE:
|
||||
case PM_HIBERNATION_PREPARE:
|
||||
case PM_RESTORE_PREPARE:
|
||||
DSSDBG("suspending displays\n");
|
||||
return dss_suspend_all_devices();
|
||||
|
||||
case PM_POST_SUSPEND:
|
||||
case PM_POST_HIBERNATION:
|
||||
case PM_POST_RESTORE:
|
||||
DSSDBG("resuming displays\n");
|
||||
return dss_resume_all_devices();
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static struct notifier_block omap_dss_pm_notif_block = {
|
||||
.notifier_call = omap_dss_pm_notif,
|
||||
};
|
||||
|
||||
static int __init omap_dss_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct omap_dss_board_info *pdata = pdev->dev.platform_data;
|
||||
int r;
|
||||
|
||||
core.pdev = pdev;
|
||||
|
||||
dss_features_init(omapdss_get_version());
|
||||
|
||||
r = dss_initialize_debugfs();
|
||||
if (r)
|
||||
goto err_debugfs;
|
||||
|
||||
if (def_disp_name)
|
||||
core.default_display_name = def_disp_name;
|
||||
else if (pdata->default_display_name)
|
||||
core.default_display_name = pdata->default_display_name;
|
||||
else if (pdata->default_device)
|
||||
core.default_display_name = pdata->default_device->name;
|
||||
|
||||
register_pm_notifier(&omap_dss_pm_notif_block);
|
||||
|
||||
return 0;
|
||||
|
||||
err_debugfs:
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int omap_dss_remove(struct platform_device *pdev)
|
||||
{
|
||||
unregister_pm_notifier(&omap_dss_pm_notif_block);
|
||||
|
||||
dss_uninitialize_debugfs();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void omap_dss_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
DSSDBG("shutdown\n");
|
||||
dss_disable_all_devices();
|
||||
}
|
||||
|
||||
static struct platform_driver omap_dss_driver = {
|
||||
.remove = omap_dss_remove,
|
||||
.shutdown = omap_dss_shutdown,
|
||||
.driver = {
|
||||
.name = "omapdss",
|
||||
},
|
||||
};
|
||||
|
||||
/* INIT */
|
||||
static int (*dss_output_drv_reg_funcs[])(void) __initdata = {
|
||||
dss_init_platform_driver,
|
||||
dispc_init_platform_driver,
|
||||
#ifdef CONFIG_FB_OMAP2_DSS_DSI
|
||||
dsi_init_platform_driver,
|
||||
#endif
|
||||
#ifdef CONFIG_FB_OMAP2_DSS_DPI
|
||||
dpi_init_platform_driver,
|
||||
#endif
|
||||
#ifdef CONFIG_FB_OMAP2_DSS_SDI
|
||||
sdi_init_platform_driver,
|
||||
#endif
|
||||
#ifdef CONFIG_FB_OMAP2_DSS_RFBI
|
||||
rfbi_init_platform_driver,
|
||||
#endif
|
||||
#ifdef CONFIG_FB_OMAP2_DSS_VENC
|
||||
venc_init_platform_driver,
|
||||
#endif
|
||||
#ifdef CONFIG_FB_OMAP4_DSS_HDMI
|
||||
hdmi4_init_platform_driver,
|
||||
#endif
|
||||
#ifdef CONFIG_FB_OMAP5_DSS_HDMI
|
||||
hdmi5_init_platform_driver,
|
||||
#endif
|
||||
};
|
||||
|
||||
static void (*dss_output_drv_unreg_funcs[])(void) = {
|
||||
#ifdef CONFIG_FB_OMAP5_DSS_HDMI
|
||||
hdmi5_uninit_platform_driver,
|
||||
#endif
|
||||
#ifdef CONFIG_FB_OMAP4_DSS_HDMI
|
||||
hdmi4_uninit_platform_driver,
|
||||
#endif
|
||||
#ifdef CONFIG_FB_OMAP2_DSS_VENC
|
||||
venc_uninit_platform_driver,
|
||||
#endif
|
||||
#ifdef CONFIG_FB_OMAP2_DSS_RFBI
|
||||
rfbi_uninit_platform_driver,
|
||||
#endif
|
||||
#ifdef CONFIG_FB_OMAP2_DSS_SDI
|
||||
sdi_uninit_platform_driver,
|
||||
#endif
|
||||
#ifdef CONFIG_FB_OMAP2_DSS_DPI
|
||||
dpi_uninit_platform_driver,
|
||||
#endif
|
||||
#ifdef CONFIG_FB_OMAP2_DSS_DSI
|
||||
dsi_uninit_platform_driver,
|
||||
#endif
|
||||
dispc_uninit_platform_driver,
|
||||
dss_uninit_platform_driver,
|
||||
};
|
||||
|
||||
static int __init omap_dss_init(void)
|
||||
{
|
||||
int r;
|
||||
int i;
|
||||
|
||||
r = platform_driver_probe(&omap_dss_driver, omap_dss_probe);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dss_output_drv_reg_funcs); ++i) {
|
||||
r = dss_output_drv_reg_funcs[i]();
|
||||
if (r)
|
||||
goto err_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_reg:
|
||||
for (i = ARRAY_SIZE(dss_output_drv_reg_funcs) - i;
|
||||
i < ARRAY_SIZE(dss_output_drv_reg_funcs);
|
||||
++i)
|
||||
dss_output_drv_unreg_funcs[i]();
|
||||
|
||||
platform_driver_unregister(&omap_dss_driver);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void __exit omap_dss_exit(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dss_output_drv_unreg_funcs); ++i)
|
||||
dss_output_drv_unreg_funcs[i]();
|
||||
|
||||
platform_driver_unregister(&omap_dss_driver);
|
||||
}
|
||||
|
||||
module_init(omap_dss_init);
|
||||
module_exit(omap_dss_exit);
|
||||
|
||||
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>");
|
||||
MODULE_DESCRIPTION("OMAP2/3 Display Subsystem");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
@ -0,0 +1,667 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Texas Instruments
|
||||
* Author: Tomi Valkeinen <tomi.valkeinen@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/>.
|
||||
*/
|
||||
|
||||
#define DSS_SUBSYS_NAME "APPLY"
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
#include <video/omapdss.h>
|
||||
|
||||
#include "dss.h"
|
||||
#include "dss_features.h"
|
||||
#include "dispc-compat.h"
|
||||
|
||||
#define DISPC_IRQ_MASK_ERROR (DISPC_IRQ_GFX_FIFO_UNDERFLOW | \
|
||||
DISPC_IRQ_OCP_ERR | \
|
||||
DISPC_IRQ_VID1_FIFO_UNDERFLOW | \
|
||||
DISPC_IRQ_VID2_FIFO_UNDERFLOW | \
|
||||
DISPC_IRQ_SYNC_LOST | \
|
||||
DISPC_IRQ_SYNC_LOST_DIGIT)
|
||||
|
||||
#define DISPC_MAX_NR_ISRS 8
|
||||
|
||||
struct omap_dispc_isr_data {
|
||||
omap_dispc_isr_t isr;
|
||||
void *arg;
|
||||
u32 mask;
|
||||
};
|
||||
|
||||
struct dispc_irq_stats {
|
||||
unsigned long last_reset;
|
||||
unsigned irq_count;
|
||||
unsigned irqs[32];
|
||||
};
|
||||
|
||||
static struct {
|
||||
spinlock_t irq_lock;
|
||||
u32 irq_error_mask;
|
||||
struct omap_dispc_isr_data registered_isr[DISPC_MAX_NR_ISRS];
|
||||
u32 error_irqs;
|
||||
struct work_struct error_work;
|
||||
|
||||
#ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS
|
||||
spinlock_t irq_stats_lock;
|
||||
struct dispc_irq_stats irq_stats;
|
||||
#endif
|
||||
} dispc_compat;
|
||||
|
||||
|
||||
#ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS
|
||||
static void dispc_dump_irqs(struct seq_file *s)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct dispc_irq_stats stats;
|
||||
|
||||
spin_lock_irqsave(&dispc_compat.irq_stats_lock, flags);
|
||||
|
||||
stats = dispc_compat.irq_stats;
|
||||
memset(&dispc_compat.irq_stats, 0, sizeof(dispc_compat.irq_stats));
|
||||
dispc_compat.irq_stats.last_reset = jiffies;
|
||||
|
||||
spin_unlock_irqrestore(&dispc_compat.irq_stats_lock, flags);
|
||||
|
||||
seq_printf(s, "period %u ms\n",
|
||||
jiffies_to_msecs(jiffies - stats.last_reset));
|
||||
|
||||
seq_printf(s, "irqs %d\n", stats.irq_count);
|
||||
#define PIS(x) \
|
||||
seq_printf(s, "%-20s %10d\n", #x, stats.irqs[ffs(DISPC_IRQ_##x)-1]);
|
||||
|
||||
PIS(FRAMEDONE);
|
||||
PIS(VSYNC);
|
||||
PIS(EVSYNC_EVEN);
|
||||
PIS(EVSYNC_ODD);
|
||||
PIS(ACBIAS_COUNT_STAT);
|
||||
PIS(PROG_LINE_NUM);
|
||||
PIS(GFX_FIFO_UNDERFLOW);
|
||||
PIS(GFX_END_WIN);
|
||||
PIS(PAL_GAMMA_MASK);
|
||||
PIS(OCP_ERR);
|
||||
PIS(VID1_FIFO_UNDERFLOW);
|
||||
PIS(VID1_END_WIN);
|
||||
PIS(VID2_FIFO_UNDERFLOW);
|
||||
PIS(VID2_END_WIN);
|
||||
if (dss_feat_get_num_ovls() > 3) {
|
||||
PIS(VID3_FIFO_UNDERFLOW);
|
||||
PIS(VID3_END_WIN);
|
||||
}
|
||||
PIS(SYNC_LOST);
|
||||
PIS(SYNC_LOST_DIGIT);
|
||||
PIS(WAKEUP);
|
||||
if (dss_has_feature(FEAT_MGR_LCD2)) {
|
||||
PIS(FRAMEDONE2);
|
||||
PIS(VSYNC2);
|
||||
PIS(ACBIAS_COUNT_STAT2);
|
||||
PIS(SYNC_LOST2);
|
||||
}
|
||||
if (dss_has_feature(FEAT_MGR_LCD3)) {
|
||||
PIS(FRAMEDONE3);
|
||||
PIS(VSYNC3);
|
||||
PIS(ACBIAS_COUNT_STAT3);
|
||||
PIS(SYNC_LOST3);
|
||||
}
|
||||
#undef PIS
|
||||
}
|
||||
#endif
|
||||
|
||||
/* dispc.irq_lock has to be locked by the caller */
|
||||
static void _omap_dispc_set_irqs(void)
|
||||
{
|
||||
u32 mask;
|
||||
int i;
|
||||
struct omap_dispc_isr_data *isr_data;
|
||||
|
||||
mask = dispc_compat.irq_error_mask;
|
||||
|
||||
for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
|
||||
isr_data = &dispc_compat.registered_isr[i];
|
||||
|
||||
if (isr_data->isr == NULL)
|
||||
continue;
|
||||
|
||||
mask |= isr_data->mask;
|
||||
}
|
||||
|
||||
dispc_write_irqenable(mask);
|
||||
}
|
||||
|
||||
int omap_dispc_register_isr(omap_dispc_isr_t isr, void *arg, u32 mask)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
struct omap_dispc_isr_data *isr_data;
|
||||
|
||||
if (isr == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&dispc_compat.irq_lock, flags);
|
||||
|
||||
/* check for duplicate entry */
|
||||
for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
|
||||
isr_data = &dispc_compat.registered_isr[i];
|
||||
if (isr_data->isr == isr && isr_data->arg == arg &&
|
||||
isr_data->mask == mask) {
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
isr_data = NULL;
|
||||
ret = -EBUSY;
|
||||
|
||||
for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
|
||||
isr_data = &dispc_compat.registered_isr[i];
|
||||
|
||||
if (isr_data->isr != NULL)
|
||||
continue;
|
||||
|
||||
isr_data->isr = isr;
|
||||
isr_data->arg = arg;
|
||||
isr_data->mask = mask;
|
||||
ret = 0;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
_omap_dispc_set_irqs();
|
||||
|
||||
spin_unlock_irqrestore(&dispc_compat.irq_lock, flags);
|
||||
|
||||
return 0;
|
||||
err:
|
||||
spin_unlock_irqrestore(&dispc_compat.irq_lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(omap_dispc_register_isr);
|
||||
|
||||
int omap_dispc_unregister_isr(omap_dispc_isr_t isr, void *arg, u32 mask)
|
||||
{
|
||||
int i;
|
||||
unsigned long flags;
|
||||
int ret = -EINVAL;
|
||||
struct omap_dispc_isr_data *isr_data;
|
||||
|
||||
spin_lock_irqsave(&dispc_compat.irq_lock, flags);
|
||||
|
||||
for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
|
||||
isr_data = &dispc_compat.registered_isr[i];
|
||||
if (isr_data->isr != isr || isr_data->arg != arg ||
|
||||
isr_data->mask != mask)
|
||||
continue;
|
||||
|
||||
/* found the correct isr */
|
||||
|
||||
isr_data->isr = NULL;
|
||||
isr_data->arg = NULL;
|
||||
isr_data->mask = 0;
|
||||
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
_omap_dispc_set_irqs();
|
||||
|
||||
spin_unlock_irqrestore(&dispc_compat.irq_lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(omap_dispc_unregister_isr);
|
||||
|
||||
static void print_irq_status(u32 status)
|
||||
{
|
||||
if ((status & dispc_compat.irq_error_mask) == 0)
|
||||
return;
|
||||
|
||||
#define PIS(x) (status & DISPC_IRQ_##x) ? (#x " ") : ""
|
||||
|
||||
pr_debug("DISPC IRQ: 0x%x: %s%s%s%s%s%s%s%s%s\n",
|
||||
status,
|
||||
PIS(OCP_ERR),
|
||||
PIS(GFX_FIFO_UNDERFLOW),
|
||||
PIS(VID1_FIFO_UNDERFLOW),
|
||||
PIS(VID2_FIFO_UNDERFLOW),
|
||||
dss_feat_get_num_ovls() > 3 ? PIS(VID3_FIFO_UNDERFLOW) : "",
|
||||
PIS(SYNC_LOST),
|
||||
PIS(SYNC_LOST_DIGIT),
|
||||
dss_has_feature(FEAT_MGR_LCD2) ? PIS(SYNC_LOST2) : "",
|
||||
dss_has_feature(FEAT_MGR_LCD3) ? PIS(SYNC_LOST3) : "");
|
||||
#undef PIS
|
||||
}
|
||||
|
||||
/* Called from dss.c. Note that we don't touch clocks here,
|
||||
* but we presume they are on because we got an IRQ. However,
|
||||
* an irq handler may turn the clocks off, so we may not have
|
||||
* clock later in the function. */
|
||||
static irqreturn_t omap_dispc_irq_handler(int irq, void *arg)
|
||||
{
|
||||
int i;
|
||||
u32 irqstatus, irqenable;
|
||||
u32 handledirqs = 0;
|
||||
u32 unhandled_errors;
|
||||
struct omap_dispc_isr_data *isr_data;
|
||||
struct omap_dispc_isr_data registered_isr[DISPC_MAX_NR_ISRS];
|
||||
|
||||
spin_lock(&dispc_compat.irq_lock);
|
||||
|
||||
irqstatus = dispc_read_irqstatus();
|
||||
irqenable = dispc_read_irqenable();
|
||||
|
||||
/* IRQ is not for us */
|
||||
if (!(irqstatus & irqenable)) {
|
||||
spin_unlock(&dispc_compat.irq_lock);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS
|
||||
spin_lock(&dispc_compat.irq_stats_lock);
|
||||
dispc_compat.irq_stats.irq_count++;
|
||||
dss_collect_irq_stats(irqstatus, dispc_compat.irq_stats.irqs);
|
||||
spin_unlock(&dispc_compat.irq_stats_lock);
|
||||
#endif
|
||||
|
||||
print_irq_status(irqstatus);
|
||||
|
||||
/* Ack the interrupt. Do it here before clocks are possibly turned
|
||||
* off */
|
||||
dispc_clear_irqstatus(irqstatus);
|
||||
/* flush posted write */
|
||||
dispc_read_irqstatus();
|
||||
|
||||
/* make a copy and unlock, so that isrs can unregister
|
||||
* themselves */
|
||||
memcpy(registered_isr, dispc_compat.registered_isr,
|
||||
sizeof(registered_isr));
|
||||
|
||||
spin_unlock(&dispc_compat.irq_lock);
|
||||
|
||||
for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
|
||||
isr_data = ®istered_isr[i];
|
||||
|
||||
if (!isr_data->isr)
|
||||
continue;
|
||||
|
||||
if (isr_data->mask & irqstatus) {
|
||||
isr_data->isr(isr_data->arg, irqstatus);
|
||||
handledirqs |= isr_data->mask;
|
||||
}
|
||||
}
|
||||
|
||||
spin_lock(&dispc_compat.irq_lock);
|
||||
|
||||
unhandled_errors = irqstatus & ~handledirqs & dispc_compat.irq_error_mask;
|
||||
|
||||
if (unhandled_errors) {
|
||||
dispc_compat.error_irqs |= unhandled_errors;
|
||||
|
||||
dispc_compat.irq_error_mask &= ~unhandled_errors;
|
||||
_omap_dispc_set_irqs();
|
||||
|
||||
schedule_work(&dispc_compat.error_work);
|
||||
}
|
||||
|
||||
spin_unlock(&dispc_compat.irq_lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void dispc_error_worker(struct work_struct *work)
|
||||
{
|
||||
int i;
|
||||
u32 errors;
|
||||
unsigned long flags;
|
||||
static const unsigned fifo_underflow_bits[] = {
|
||||
DISPC_IRQ_GFX_FIFO_UNDERFLOW,
|
||||
DISPC_IRQ_VID1_FIFO_UNDERFLOW,
|
||||
DISPC_IRQ_VID2_FIFO_UNDERFLOW,
|
||||
DISPC_IRQ_VID3_FIFO_UNDERFLOW,
|
||||
};
|
||||
|
||||
spin_lock_irqsave(&dispc_compat.irq_lock, flags);
|
||||
errors = dispc_compat.error_irqs;
|
||||
dispc_compat.error_irqs = 0;
|
||||
spin_unlock_irqrestore(&dispc_compat.irq_lock, flags);
|
||||
|
||||
dispc_runtime_get();
|
||||
|
||||
for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
|
||||
struct omap_overlay *ovl;
|
||||
unsigned bit;
|
||||
|
||||
ovl = omap_dss_get_overlay(i);
|
||||
bit = fifo_underflow_bits[i];
|
||||
|
||||
if (bit & errors) {
|
||||
DSSERR("FIFO UNDERFLOW on %s, disabling the overlay\n",
|
||||
ovl->name);
|
||||
ovl->disable(ovl);
|
||||
msleep(50);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
|
||||
struct omap_overlay_manager *mgr;
|
||||
unsigned bit;
|
||||
|
||||
mgr = omap_dss_get_overlay_manager(i);
|
||||
bit = dispc_mgr_get_sync_lost_irq(i);
|
||||
|
||||
if (bit & errors) {
|
||||
int j;
|
||||
|
||||
DSSERR("SYNC_LOST on channel %s, restarting the output "
|
||||
"with video overlays disabled\n",
|
||||
mgr->name);
|
||||
|
||||
dss_mgr_disable(mgr);
|
||||
|
||||
for (j = 0; j < omap_dss_get_num_overlays(); ++j) {
|
||||
struct omap_overlay *ovl;
|
||||
ovl = omap_dss_get_overlay(j);
|
||||
|
||||
if (ovl->id != OMAP_DSS_GFX &&
|
||||
ovl->manager == mgr)
|
||||
ovl->disable(ovl);
|
||||
}
|
||||
|
||||
dss_mgr_enable(mgr);
|
||||
}
|
||||
}
|
||||
|
||||
if (errors & DISPC_IRQ_OCP_ERR) {
|
||||
DSSERR("OCP_ERR\n");
|
||||
for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
|
||||
struct omap_overlay_manager *mgr;
|
||||
|
||||
mgr = omap_dss_get_overlay_manager(i);
|
||||
dss_mgr_disable(mgr);
|
||||
}
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dispc_compat.irq_lock, flags);
|
||||
dispc_compat.irq_error_mask |= errors;
|
||||
_omap_dispc_set_irqs();
|
||||
spin_unlock_irqrestore(&dispc_compat.irq_lock, flags);
|
||||
|
||||
dispc_runtime_put();
|
||||
}
|
||||
|
||||
int dss_dispc_initialize_irq(void)
|
||||
{
|
||||
int r;
|
||||
|
||||
#ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS
|
||||
spin_lock_init(&dispc_compat.irq_stats_lock);
|
||||
dispc_compat.irq_stats.last_reset = jiffies;
|
||||
dss_debugfs_create_file("dispc_irq", dispc_dump_irqs);
|
||||
#endif
|
||||
|
||||
spin_lock_init(&dispc_compat.irq_lock);
|
||||
|
||||
memset(dispc_compat.registered_isr, 0,
|
||||
sizeof(dispc_compat.registered_isr));
|
||||
|
||||
dispc_compat.irq_error_mask = DISPC_IRQ_MASK_ERROR;
|
||||
if (dss_has_feature(FEAT_MGR_LCD2))
|
||||
dispc_compat.irq_error_mask |= DISPC_IRQ_SYNC_LOST2;
|
||||
if (dss_has_feature(FEAT_MGR_LCD3))
|
||||
dispc_compat.irq_error_mask |= DISPC_IRQ_SYNC_LOST3;
|
||||
if (dss_feat_get_num_ovls() > 3)
|
||||
dispc_compat.irq_error_mask |= DISPC_IRQ_VID3_FIFO_UNDERFLOW;
|
||||
|
||||
/*
|
||||
* there's SYNC_LOST_DIGIT waiting after enabling the DSS,
|
||||
* so clear it
|
||||
*/
|
||||
dispc_clear_irqstatus(dispc_read_irqstatus());
|
||||
|
||||
INIT_WORK(&dispc_compat.error_work, dispc_error_worker);
|
||||
|
||||
_omap_dispc_set_irqs();
|
||||
|
||||
r = dispc_request_irq(omap_dispc_irq_handler, &dispc_compat);
|
||||
if (r) {
|
||||
DSSERR("dispc_request_irq failed\n");
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dss_dispc_uninitialize_irq(void)
|
||||
{
|
||||
dispc_free_irq(&dispc_compat);
|
||||
}
|
||||
|
||||
static void dispc_mgr_disable_isr(void *data, u32 mask)
|
||||
{
|
||||
struct completion *compl = data;
|
||||
complete(compl);
|
||||
}
|
||||
|
||||
static void dispc_mgr_enable_lcd_out(enum omap_channel channel)
|
||||
{
|
||||
dispc_mgr_enable(channel, true);
|
||||
}
|
||||
|
||||
static void dispc_mgr_disable_lcd_out(enum omap_channel channel)
|
||||
{
|
||||
DECLARE_COMPLETION_ONSTACK(framedone_compl);
|
||||
int r;
|
||||
u32 irq;
|
||||
|
||||
if (!dispc_mgr_is_enabled(channel))
|
||||
return;
|
||||
|
||||
/*
|
||||
* When we disable LCD output, we need to wait for FRAMEDONE to know
|
||||
* that DISPC has finished with the LCD output.
|
||||
*/
|
||||
|
||||
irq = dispc_mgr_get_framedone_irq(channel);
|
||||
|
||||
r = omap_dispc_register_isr(dispc_mgr_disable_isr, &framedone_compl,
|
||||
irq);
|
||||
if (r)
|
||||
DSSERR("failed to register FRAMEDONE isr\n");
|
||||
|
||||
dispc_mgr_enable(channel, false);
|
||||
|
||||
/* if we couldn't register for framedone, just sleep and exit */
|
||||
if (r) {
|
||||
msleep(100);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!wait_for_completion_timeout(&framedone_compl,
|
||||
msecs_to_jiffies(100)))
|
||||
DSSERR("timeout waiting for FRAME DONE\n");
|
||||
|
||||
r = omap_dispc_unregister_isr(dispc_mgr_disable_isr, &framedone_compl,
|
||||
irq);
|
||||
if (r)
|
||||
DSSERR("failed to unregister FRAMEDONE isr\n");
|
||||
}
|
||||
|
||||
static void dispc_digit_out_enable_isr(void *data, u32 mask)
|
||||
{
|
||||
struct completion *compl = data;
|
||||
|
||||
/* ignore any sync lost interrupts */
|
||||
if (mask & (DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD))
|
||||
complete(compl);
|
||||
}
|
||||
|
||||
static void dispc_mgr_enable_digit_out(void)
|
||||
{
|
||||
DECLARE_COMPLETION_ONSTACK(vsync_compl);
|
||||
int r;
|
||||
u32 irq_mask;
|
||||
|
||||
if (dispc_mgr_is_enabled(OMAP_DSS_CHANNEL_DIGIT))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Digit output produces some sync lost interrupts during the first
|
||||
* frame when enabling. Those need to be ignored, so we register for the
|
||||
* sync lost irq to prevent the error handler from triggering.
|
||||
*/
|
||||
|
||||
irq_mask = dispc_mgr_get_vsync_irq(OMAP_DSS_CHANNEL_DIGIT) |
|
||||
dispc_mgr_get_sync_lost_irq(OMAP_DSS_CHANNEL_DIGIT);
|
||||
|
||||
r = omap_dispc_register_isr(dispc_digit_out_enable_isr, &vsync_compl,
|
||||
irq_mask);
|
||||
if (r) {
|
||||
DSSERR("failed to register %x isr\n", irq_mask);
|
||||
return;
|
||||
}
|
||||
|
||||
dispc_mgr_enable(OMAP_DSS_CHANNEL_DIGIT, true);
|
||||
|
||||
/* wait for the first evsync */
|
||||
if (!wait_for_completion_timeout(&vsync_compl, msecs_to_jiffies(100)))
|
||||
DSSERR("timeout waiting for digit out to start\n");
|
||||
|
||||
r = omap_dispc_unregister_isr(dispc_digit_out_enable_isr, &vsync_compl,
|
||||
irq_mask);
|
||||
if (r)
|
||||
DSSERR("failed to unregister %x isr\n", irq_mask);
|
||||
}
|
||||
|
||||
static void dispc_mgr_disable_digit_out(void)
|
||||
{
|
||||
DECLARE_COMPLETION_ONSTACK(framedone_compl);
|
||||
int r, i;
|
||||
u32 irq_mask;
|
||||
int num_irqs;
|
||||
|
||||
if (!dispc_mgr_is_enabled(OMAP_DSS_CHANNEL_DIGIT))
|
||||
return;
|
||||
|
||||
/*
|
||||
* When we disable the digit output, we need to wait for FRAMEDONE to
|
||||
* know that DISPC has finished with the output.
|
||||
*/
|
||||
|
||||
irq_mask = dispc_mgr_get_framedone_irq(OMAP_DSS_CHANNEL_DIGIT);
|
||||
num_irqs = 1;
|
||||
|
||||
if (!irq_mask) {
|
||||
/*
|
||||
* omap 2/3 don't have framedone irq for TV, so we need to use
|
||||
* vsyncs for this.
|
||||
*/
|
||||
|
||||
irq_mask = dispc_mgr_get_vsync_irq(OMAP_DSS_CHANNEL_DIGIT);
|
||||
/*
|
||||
* We need to wait for both even and odd vsyncs. Note that this
|
||||
* is not totally reliable, as we could get a vsync interrupt
|
||||
* before we disable the output, which leads to timeout in the
|
||||
* wait_for_completion.
|
||||
*/
|
||||
num_irqs = 2;
|
||||
}
|
||||
|
||||
r = omap_dispc_register_isr(dispc_mgr_disable_isr, &framedone_compl,
|
||||
irq_mask);
|
||||
if (r)
|
||||
DSSERR("failed to register %x isr\n", irq_mask);
|
||||
|
||||
dispc_mgr_enable(OMAP_DSS_CHANNEL_DIGIT, false);
|
||||
|
||||
/* if we couldn't register the irq, just sleep and exit */
|
||||
if (r) {
|
||||
msleep(100);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_irqs; ++i) {
|
||||
if (!wait_for_completion_timeout(&framedone_compl,
|
||||
msecs_to_jiffies(100)))
|
||||
DSSERR("timeout waiting for digit out to stop\n");
|
||||
}
|
||||
|
||||
r = omap_dispc_unregister_isr(dispc_mgr_disable_isr, &framedone_compl,
|
||||
irq_mask);
|
||||
if (r)
|
||||
DSSERR("failed to unregister %x isr\n", irq_mask);
|
||||
}
|
||||
|
||||
void dispc_mgr_enable_sync(enum omap_channel channel)
|
||||
{
|
||||
if (dss_mgr_is_lcd(channel))
|
||||
dispc_mgr_enable_lcd_out(channel);
|
||||
else if (channel == OMAP_DSS_CHANNEL_DIGIT)
|
||||
dispc_mgr_enable_digit_out();
|
||||
else
|
||||
WARN_ON(1);
|
||||
}
|
||||
|
||||
void dispc_mgr_disable_sync(enum omap_channel channel)
|
||||
{
|
||||
if (dss_mgr_is_lcd(channel))
|
||||
dispc_mgr_disable_lcd_out(channel);
|
||||
else if (channel == OMAP_DSS_CHANNEL_DIGIT)
|
||||
dispc_mgr_disable_digit_out();
|
||||
else
|
||||
WARN_ON(1);
|
||||
}
|
||||
|
||||
static inline void dispc_irq_wait_handler(void *data, u32 mask)
|
||||
{
|
||||
complete((struct completion *)data);
|
||||
}
|
||||
|
||||
int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask,
|
||||
unsigned long timeout)
|
||||
{
|
||||
|
||||
int r;
|
||||
DECLARE_COMPLETION_ONSTACK(completion);
|
||||
|
||||
r = omap_dispc_register_isr(dispc_irq_wait_handler, &completion,
|
||||
irqmask);
|
||||
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
timeout = wait_for_completion_interruptible_timeout(&completion,
|
||||
timeout);
|
||||
|
||||
omap_dispc_unregister_isr(dispc_irq_wait_handler, &completion, irqmask);
|
||||
|
||||
if (timeout == 0)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
if (timeout == -ERESTARTSYS)
|
||||
return -ERESTARTSYS;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Texas Instruments
|
||||
* Author: Tomi Valkeinen <tomi.valkeinen@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/>.
|
||||
*/
|
||||
|
||||
#ifndef __OMAP2_DSS_DISPC_COMPAT_H
|
||||
#define __OMAP2_DSS_DISPC_COMPAT_H
|
||||
|
||||
void dispc_mgr_enable_sync(enum omap_channel channel);
|
||||
void dispc_mgr_disable_sync(enum omap_channel channel);
|
||||
|
||||
int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask,
|
||||
unsigned long timeout);
|
||||
|
||||
int dss_dispc_initialize_irq(void);
|
||||
void dss_dispc_uninitialize_irq(void);
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,918 @@
|
|||
/*
|
||||
* linux/drivers/video/omap2/dss/dispc.h
|
||||
*
|
||||
* Copyright (C) 2011 Texas Instruments
|
||||
* Author: Archit Taneja <archit@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/>.
|
||||
*/
|
||||
|
||||
#ifndef __OMAP2_DISPC_REG_H
|
||||
#define __OMAP2_DISPC_REG_H
|
||||
|
||||
/* DISPC common registers */
|
||||
#define DISPC_REVISION 0x0000
|
||||
#define DISPC_SYSCONFIG 0x0010
|
||||
#define DISPC_SYSSTATUS 0x0014
|
||||
#define DISPC_IRQSTATUS 0x0018
|
||||
#define DISPC_IRQENABLE 0x001C
|
||||
#define DISPC_CONTROL 0x0040
|
||||
#define DISPC_CONFIG 0x0044
|
||||
#define DISPC_CAPABLE 0x0048
|
||||
#define DISPC_LINE_STATUS 0x005C
|
||||
#define DISPC_LINE_NUMBER 0x0060
|
||||
#define DISPC_GLOBAL_ALPHA 0x0074
|
||||
#define DISPC_CONTROL2 0x0238
|
||||
#define DISPC_CONFIG2 0x0620
|
||||
#define DISPC_DIVISOR 0x0804
|
||||
#define DISPC_GLOBAL_BUFFER 0x0800
|
||||
#define DISPC_CONTROL3 0x0848
|
||||
#define DISPC_CONFIG3 0x084C
|
||||
#define DISPC_MSTANDBY_CTRL 0x0858
|
||||
#define DISPC_GLOBAL_MFLAG_ATTRIBUTE 0x085C
|
||||
|
||||
/* DISPC overlay registers */
|
||||
#define DISPC_OVL_BA0(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_BA0_OFFSET(n))
|
||||
#define DISPC_OVL_BA1(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_BA1_OFFSET(n))
|
||||
#define DISPC_OVL_BA0_UV(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_BA0_UV_OFFSET(n))
|
||||
#define DISPC_OVL_BA1_UV(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_BA1_UV_OFFSET(n))
|
||||
#define DISPC_OVL_POSITION(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_POS_OFFSET(n))
|
||||
#define DISPC_OVL_SIZE(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_SIZE_OFFSET(n))
|
||||
#define DISPC_OVL_ATTRIBUTES(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_ATTR_OFFSET(n))
|
||||
#define DISPC_OVL_ATTRIBUTES2(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_ATTR2_OFFSET(n))
|
||||
#define DISPC_OVL_FIFO_THRESHOLD(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_FIFO_THRESH_OFFSET(n))
|
||||
#define DISPC_OVL_FIFO_SIZE_STATUS(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_FIFO_SIZE_STATUS_OFFSET(n))
|
||||
#define DISPC_OVL_ROW_INC(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_ROW_INC_OFFSET(n))
|
||||
#define DISPC_OVL_PIXEL_INC(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_PIX_INC_OFFSET(n))
|
||||
#define DISPC_OVL_WINDOW_SKIP(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_WINDOW_SKIP_OFFSET(n))
|
||||
#define DISPC_OVL_TABLE_BA(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_TABLE_BA_OFFSET(n))
|
||||
#define DISPC_OVL_FIR(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_FIR_OFFSET(n))
|
||||
#define DISPC_OVL_FIR2(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_FIR2_OFFSET(n))
|
||||
#define DISPC_OVL_PICTURE_SIZE(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_PIC_SIZE_OFFSET(n))
|
||||
#define DISPC_OVL_ACCU0(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_ACCU0_OFFSET(n))
|
||||
#define DISPC_OVL_ACCU1(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_ACCU1_OFFSET(n))
|
||||
#define DISPC_OVL_ACCU2_0(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_ACCU2_0_OFFSET(n))
|
||||
#define DISPC_OVL_ACCU2_1(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_ACCU2_1_OFFSET(n))
|
||||
#define DISPC_OVL_FIR_COEF_H(n, i) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_FIR_COEF_H_OFFSET(n, i))
|
||||
#define DISPC_OVL_FIR_COEF_HV(n, i) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_FIR_COEF_HV_OFFSET(n, i))
|
||||
#define DISPC_OVL_FIR_COEF_H2(n, i) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_FIR_COEF_H2_OFFSET(n, i))
|
||||
#define DISPC_OVL_FIR_COEF_HV2(n, i) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_FIR_COEF_HV2_OFFSET(n, i))
|
||||
#define DISPC_OVL_CONV_COEF(n, i) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_CONV_COEF_OFFSET(n, i))
|
||||
#define DISPC_OVL_FIR_COEF_V(n, i) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_FIR_COEF_V_OFFSET(n, i))
|
||||
#define DISPC_OVL_FIR_COEF_V2(n, i) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_FIR_COEF_V2_OFFSET(n, i))
|
||||
#define DISPC_OVL_PRELOAD(n) (DISPC_OVL_BASE(n) + \
|
||||
DISPC_PRELOAD_OFFSET(n))
|
||||
#define DISPC_OVL_MFLAG_THRESHOLD(n) DISPC_MFLAG_THRESHOLD_OFFSET(n)
|
||||
|
||||
/* DISPC up/downsampling FIR filter coefficient structure */
|
||||
struct dispc_coef {
|
||||
s8 hc4_vc22;
|
||||
s8 hc3_vc2;
|
||||
u8 hc2_vc1;
|
||||
s8 hc1_vc0;
|
||||
s8 hc0_vc00;
|
||||
};
|
||||
|
||||
const struct dispc_coef *dispc_ovl_get_scale_coef(int inc, int five_taps);
|
||||
|
||||
/* DISPC manager/channel specific registers */
|
||||
static inline u16 DISPC_DEFAULT_COLOR(enum omap_channel channel)
|
||||
{
|
||||
switch (channel) {
|
||||
case OMAP_DSS_CHANNEL_LCD:
|
||||
return 0x004C;
|
||||
case OMAP_DSS_CHANNEL_DIGIT:
|
||||
return 0x0050;
|
||||
case OMAP_DSS_CHANNEL_LCD2:
|
||||
return 0x03AC;
|
||||
case OMAP_DSS_CHANNEL_LCD3:
|
||||
return 0x0814;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_TRANS_COLOR(enum omap_channel channel)
|
||||
{
|
||||
switch (channel) {
|
||||
case OMAP_DSS_CHANNEL_LCD:
|
||||
return 0x0054;
|
||||
case OMAP_DSS_CHANNEL_DIGIT:
|
||||
return 0x0058;
|
||||
case OMAP_DSS_CHANNEL_LCD2:
|
||||
return 0x03B0;
|
||||
case OMAP_DSS_CHANNEL_LCD3:
|
||||
return 0x0818;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_TIMING_H(enum omap_channel channel)
|
||||
{
|
||||
switch (channel) {
|
||||
case OMAP_DSS_CHANNEL_LCD:
|
||||
return 0x0064;
|
||||
case OMAP_DSS_CHANNEL_DIGIT:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_CHANNEL_LCD2:
|
||||
return 0x0400;
|
||||
case OMAP_DSS_CHANNEL_LCD3:
|
||||
return 0x0840;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_TIMING_V(enum omap_channel channel)
|
||||
{
|
||||
switch (channel) {
|
||||
case OMAP_DSS_CHANNEL_LCD:
|
||||
return 0x0068;
|
||||
case OMAP_DSS_CHANNEL_DIGIT:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_CHANNEL_LCD2:
|
||||
return 0x0404;
|
||||
case OMAP_DSS_CHANNEL_LCD3:
|
||||
return 0x0844;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_POL_FREQ(enum omap_channel channel)
|
||||
{
|
||||
switch (channel) {
|
||||
case OMAP_DSS_CHANNEL_LCD:
|
||||
return 0x006C;
|
||||
case OMAP_DSS_CHANNEL_DIGIT:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_CHANNEL_LCD2:
|
||||
return 0x0408;
|
||||
case OMAP_DSS_CHANNEL_LCD3:
|
||||
return 0x083C;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_DIVISORo(enum omap_channel channel)
|
||||
{
|
||||
switch (channel) {
|
||||
case OMAP_DSS_CHANNEL_LCD:
|
||||
return 0x0070;
|
||||
case OMAP_DSS_CHANNEL_DIGIT:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_CHANNEL_LCD2:
|
||||
return 0x040C;
|
||||
case OMAP_DSS_CHANNEL_LCD3:
|
||||
return 0x0838;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Named as DISPC_SIZE_LCD, DISPC_SIZE_DIGIT and DISPC_SIZE_LCD2 in TRM */
|
||||
static inline u16 DISPC_SIZE_MGR(enum omap_channel channel)
|
||||
{
|
||||
switch (channel) {
|
||||
case OMAP_DSS_CHANNEL_LCD:
|
||||
return 0x007C;
|
||||
case OMAP_DSS_CHANNEL_DIGIT:
|
||||
return 0x0078;
|
||||
case OMAP_DSS_CHANNEL_LCD2:
|
||||
return 0x03CC;
|
||||
case OMAP_DSS_CHANNEL_LCD3:
|
||||
return 0x0834;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_DATA_CYCLE1(enum omap_channel channel)
|
||||
{
|
||||
switch (channel) {
|
||||
case OMAP_DSS_CHANNEL_LCD:
|
||||
return 0x01D4;
|
||||
case OMAP_DSS_CHANNEL_DIGIT:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_CHANNEL_LCD2:
|
||||
return 0x03C0;
|
||||
case OMAP_DSS_CHANNEL_LCD3:
|
||||
return 0x0828;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_DATA_CYCLE2(enum omap_channel channel)
|
||||
{
|
||||
switch (channel) {
|
||||
case OMAP_DSS_CHANNEL_LCD:
|
||||
return 0x01D8;
|
||||
case OMAP_DSS_CHANNEL_DIGIT:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_CHANNEL_LCD2:
|
||||
return 0x03C4;
|
||||
case OMAP_DSS_CHANNEL_LCD3:
|
||||
return 0x082C;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_DATA_CYCLE3(enum omap_channel channel)
|
||||
{
|
||||
switch (channel) {
|
||||
case OMAP_DSS_CHANNEL_LCD:
|
||||
return 0x01DC;
|
||||
case OMAP_DSS_CHANNEL_DIGIT:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_CHANNEL_LCD2:
|
||||
return 0x03C8;
|
||||
case OMAP_DSS_CHANNEL_LCD3:
|
||||
return 0x0830;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_CPR_COEF_R(enum omap_channel channel)
|
||||
{
|
||||
switch (channel) {
|
||||
case OMAP_DSS_CHANNEL_LCD:
|
||||
return 0x0220;
|
||||
case OMAP_DSS_CHANNEL_DIGIT:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_CHANNEL_LCD2:
|
||||
return 0x03BC;
|
||||
case OMAP_DSS_CHANNEL_LCD3:
|
||||
return 0x0824;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_CPR_COEF_G(enum omap_channel channel)
|
||||
{
|
||||
switch (channel) {
|
||||
case OMAP_DSS_CHANNEL_LCD:
|
||||
return 0x0224;
|
||||
case OMAP_DSS_CHANNEL_DIGIT:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_CHANNEL_LCD2:
|
||||
return 0x03B8;
|
||||
case OMAP_DSS_CHANNEL_LCD3:
|
||||
return 0x0820;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_CPR_COEF_B(enum omap_channel channel)
|
||||
{
|
||||
switch (channel) {
|
||||
case OMAP_DSS_CHANNEL_LCD:
|
||||
return 0x0228;
|
||||
case OMAP_DSS_CHANNEL_DIGIT:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_CHANNEL_LCD2:
|
||||
return 0x03B4;
|
||||
case OMAP_DSS_CHANNEL_LCD3:
|
||||
return 0x081C;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* DISPC overlay register base addresses */
|
||||
static inline u16 DISPC_OVL_BASE(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
return 0x0080;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
return 0x00BC;
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x014C;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
return 0x0300;
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0500;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* DISPC overlay register offsets */
|
||||
static inline u16 DISPC_BA0_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
case OMAP_DSS_VIDEO1:
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x0000;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0008;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_BA1_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
case OMAP_DSS_VIDEO1:
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x0004;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
case OMAP_DSS_WB:
|
||||
return 0x000C;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_BA0_UV_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
return 0x0544;
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x04BC;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
return 0x0310;
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0118;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_BA1_UV_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
return 0x0548;
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x04C0;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
return 0x0314;
|
||||
case OMAP_DSS_WB:
|
||||
return 0x011C;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_POS_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
case OMAP_DSS_VIDEO1:
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x0008;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
return 0x009C;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_SIZE_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
case OMAP_DSS_VIDEO1:
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x000C;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
case OMAP_DSS_WB:
|
||||
return 0x00A8;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_ATTR_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
return 0x0020;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x0010;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0070;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_ATTR2_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
return 0x0568;
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x04DC;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
return 0x032C;
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0310;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_FIFO_THRESH_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
return 0x0024;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x0014;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
case OMAP_DSS_WB:
|
||||
return 0x008C;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_FIFO_SIZE_STATUS_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
return 0x0028;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x0018;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0088;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_ROW_INC_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
return 0x002C;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x001C;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
case OMAP_DSS_WB:
|
||||
return 0x00A4;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_PIX_INC_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
return 0x0030;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x0020;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0098;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_WINDOW_SKIP_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
return 0x0034;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
case OMAP_DSS_VIDEO2:
|
||||
case OMAP_DSS_VIDEO3:
|
||||
BUG();
|
||||
return 0;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_TABLE_BA_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
return 0x0038;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
case OMAP_DSS_VIDEO2:
|
||||
case OMAP_DSS_VIDEO3:
|
||||
BUG();
|
||||
return 0;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_FIR_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x0024;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0090;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_FIR2_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
return 0x0580;
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x055C;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
return 0x0424;
|
||||
case OMAP_DSS_WB:
|
||||
return 0x290;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_PIC_SIZE_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x0028;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0094;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static inline u16 DISPC_ACCU0_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x002C;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0000;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_ACCU2_0_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
return 0x0584;
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x0560;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
return 0x0428;
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0294;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_ACCU1_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x0030;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0004;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_ACCU2_1_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
return 0x0588;
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x0564;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
return 0x042C;
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0298;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */
|
||||
static inline u16 DISPC_FIR_COEF_H_OFFSET(enum omap_plane plane, u16 i)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x0034 + i * 0x8;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0010 + i * 0x8;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */
|
||||
static inline u16 DISPC_FIR_COEF_H2_OFFSET(enum omap_plane plane, u16 i)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
return 0x058C + i * 0x8;
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x0568 + i * 0x8;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
return 0x0430 + i * 0x8;
|
||||
case OMAP_DSS_WB:
|
||||
return 0x02A0 + i * 0x8;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */
|
||||
static inline u16 DISPC_FIR_COEF_HV_OFFSET(enum omap_plane plane, u16 i)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x0038 + i * 0x8;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0014 + i * 0x8;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */
|
||||
static inline u16 DISPC_FIR_COEF_HV2_OFFSET(enum omap_plane plane, u16 i)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
return 0x0590 + i * 8;
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x056C + i * 0x8;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
return 0x0434 + i * 0x8;
|
||||
case OMAP_DSS_WB:
|
||||
return 0x02A4 + i * 0x8;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* coef index i = {0, 1, 2, 3, 4,} */
|
||||
static inline u16 DISPC_CONV_COEF_OFFSET(enum omap_plane plane, u16 i)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
case OMAP_DSS_VIDEO2:
|
||||
case OMAP_DSS_VIDEO3:
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0074 + i * 0x4;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */
|
||||
static inline u16 DISPC_FIR_COEF_V_OFFSET(enum omap_plane plane, u16 i)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
return 0x0124 + i * 0x4;
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x00B4 + i * 0x4;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0050 + i * 0x4;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */
|
||||
static inline u16 DISPC_FIR_COEF_V2_OFFSET(enum omap_plane plane, u16 i)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
BUG();
|
||||
return 0;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
return 0x05CC + i * 0x4;
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x05A8 + i * 0x4;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
return 0x0470 + i * 0x4;
|
||||
case OMAP_DSS_WB:
|
||||
return 0x02E0 + i * 0x4;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_PRELOAD_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
return 0x01AC;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
return 0x0174;
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x00E8;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
return 0x00A0;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 DISPC_MFLAG_THRESHOLD_OFFSET(enum omap_plane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case OMAP_DSS_GFX:
|
||||
return 0x0860;
|
||||
case OMAP_DSS_VIDEO1:
|
||||
return 0x0864;
|
||||
case OMAP_DSS_VIDEO2:
|
||||
return 0x0868;
|
||||
case OMAP_DSS_VIDEO3:
|
||||
return 0x086c;
|
||||
case OMAP_DSS_WB:
|
||||
return 0x0870;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,325 @@
|
|||
/*
|
||||
* linux/drivers/video/omap2/dss/dispc_coefs.c
|
||||
*
|
||||
* Copyright (C) 2011 Texas Instruments
|
||||
* Author: Chandrabhanu Mahapatra <cmahapatra@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/kernel.h>
|
||||
#include <video/omapdss.h>
|
||||
|
||||
#include "dispc.h"
|
||||
|
||||
static const struct dispc_coef coef3_M8[8] = {
|
||||
{ 0, 0, 128, 0, 0 },
|
||||
{ 0, -4, 123, 9, 0 },
|
||||
{ 0, -4, 108, 24, 0 },
|
||||
{ 0, -2, 87, 43, 0 },
|
||||
{ 0, 64, 64, 0, 0 },
|
||||
{ 0, 43, 87, -2, 0 },
|
||||
{ 0, 24, 108, -4, 0 },
|
||||
{ 0, 9, 123, -4, 0 },
|
||||
};
|
||||
|
||||
static const struct dispc_coef coef3_M9[8] = {
|
||||
{ 0, 6, 116, 6, 0 },
|
||||
{ 0, 0, 112, 16, 0 },
|
||||
{ 0, -2, 100, 30, 0 },
|
||||
{ 0, -2, 83, 47, 0 },
|
||||
{ 0, 64, 64, 0, 0 },
|
||||
{ 0, 47, 83, -2, 0 },
|
||||
{ 0, 30, 100, -2, 0 },
|
||||
{ 0, 16, 112, 0, 0 },
|
||||
};
|
||||
|
||||
static const struct dispc_coef coef3_M10[8] = {
|
||||
{ 0, 10, 108, 10, 0 },
|
||||
{ 0, 3, 104, 21, 0 },
|
||||
{ 0, 0, 94, 34, 0 },
|
||||
{ 0, -1, 80, 49, 0 },
|
||||
{ 0, 64, 64, 0, 0 },
|
||||
{ 0, 49, 80, -1, 0 },
|
||||
{ 0, 34, 94, 0, 0 },
|
||||
{ 0, 21, 104, 3, 0 },
|
||||
};
|
||||
|
||||
static const struct dispc_coef coef3_M11[8] = {
|
||||
{ 0, 14, 100, 14, 0 },
|
||||
{ 0, 6, 98, 24, 0 },
|
||||
{ 0, 2, 90, 36, 0 },
|
||||
{ 0, 0, 78, 50, 0 },
|
||||
{ 0, 64, 64, 0, 0 },
|
||||
{ 0, 50, 78, 0, 0 },
|
||||
{ 0, 36, 90, 2, 0 },
|
||||
{ 0, 24, 98, 6, 0 },
|
||||
};
|
||||
|
||||
static const struct dispc_coef coef3_M12[8] = {
|
||||
{ 0, 16, 96, 16, 0 },
|
||||
{ 0, 9, 93, 26, 0 },
|
||||
{ 0, 4, 86, 38, 0 },
|
||||
{ 0, 1, 76, 51, 0 },
|
||||
{ 0, 64, 64, 0, 0 },
|
||||
{ 0, 51, 76, 1, 0 },
|
||||
{ 0, 38, 86, 4, 0 },
|
||||
{ 0, 26, 93, 9, 0 },
|
||||
};
|
||||
|
||||
static const struct dispc_coef coef3_M13[8] = {
|
||||
{ 0, 18, 92, 18, 0 },
|
||||
{ 0, 10, 90, 28, 0 },
|
||||
{ 0, 5, 83, 40, 0 },
|
||||
{ 0, 1, 75, 52, 0 },
|
||||
{ 0, 64, 64, 0, 0 },
|
||||
{ 0, 52, 75, 1, 0 },
|
||||
{ 0, 40, 83, 5, 0 },
|
||||
{ 0, 28, 90, 10, 0 },
|
||||
};
|
||||
|
||||
static const struct dispc_coef coef3_M14[8] = {
|
||||
{ 0, 20, 88, 20, 0 },
|
||||
{ 0, 12, 86, 30, 0 },
|
||||
{ 0, 6, 81, 41, 0 },
|
||||
{ 0, 2, 74, 52, 0 },
|
||||
{ 0, 64, 64, 0, 0 },
|
||||
{ 0, 52, 74, 2, 0 },
|
||||
{ 0, 41, 81, 6, 0 },
|
||||
{ 0, 30, 86, 12, 0 },
|
||||
};
|
||||
|
||||
static const struct dispc_coef coef3_M16[8] = {
|
||||
{ 0, 22, 84, 22, 0 },
|
||||
{ 0, 14, 82, 32, 0 },
|
||||
{ 0, 8, 78, 42, 0 },
|
||||
{ 0, 3, 72, 53, 0 },
|
||||
{ 0, 64, 64, 0, 0 },
|
||||
{ 0, 53, 72, 3, 0 },
|
||||
{ 0, 42, 78, 8, 0 },
|
||||
{ 0, 32, 82, 14, 0 },
|
||||
};
|
||||
|
||||
static const struct dispc_coef coef3_M19[8] = {
|
||||
{ 0, 24, 80, 24, 0 },
|
||||
{ 0, 16, 79, 33, 0 },
|
||||
{ 0, 9, 76, 43, 0 },
|
||||
{ 0, 4, 70, 54, 0 },
|
||||
{ 0, 64, 64, 0, 0 },
|
||||
{ 0, 54, 70, 4, 0 },
|
||||
{ 0, 43, 76, 9, 0 },
|
||||
{ 0, 33, 79, 16, 0 },
|
||||
};
|
||||
|
||||
static const struct dispc_coef coef3_M22[8] = {
|
||||
{ 0, 25, 78, 25, 0 },
|
||||
{ 0, 17, 77, 34, 0 },
|
||||
{ 0, 10, 74, 44, 0 },
|
||||
{ 0, 5, 69, 54, 0 },
|
||||
{ 0, 64, 64, 0, 0 },
|
||||
{ 0, 54, 69, 5, 0 },
|
||||
{ 0, 44, 74, 10, 0 },
|
||||
{ 0, 34, 77, 17, 0 },
|
||||
};
|
||||
|
||||
static const struct dispc_coef coef3_M26[8] = {
|
||||
{ 0, 26, 76, 26, 0 },
|
||||
{ 0, 19, 74, 35, 0 },
|
||||
{ 0, 11, 72, 45, 0 },
|
||||
{ 0, 5, 69, 54, 0 },
|
||||
{ 0, 64, 64, 0, 0 },
|
||||
{ 0, 54, 69, 5, 0 },
|
||||
{ 0, 45, 72, 11, 0 },
|
||||
{ 0, 35, 74, 19, 0 },
|
||||
};
|
||||
|
||||
static const struct dispc_coef coef3_M32[8] = {
|
||||
{ 0, 27, 74, 27, 0 },
|
||||
{ 0, 19, 73, 36, 0 },
|
||||
{ 0, 12, 71, 45, 0 },
|
||||
{ 0, 6, 68, 54, 0 },
|
||||
{ 0, 64, 64, 0, 0 },
|
||||
{ 0, 54, 68, 6, 0 },
|
||||
{ 0, 45, 71, 12, 0 },
|
||||
{ 0, 36, 73, 19, 0 },
|
||||
};
|
||||
|
||||
static const struct dispc_coef coef5_M8[8] = {
|
||||
{ 0, 0, 128, 0, 0 },
|
||||
{ -2, 14, 125, -10, 1 },
|
||||
{ -6, 33, 114, -15, 2 },
|
||||
{ -10, 55, 98, -16, 1 },
|
||||
{ 0, -14, 78, 78, -14 },
|
||||
{ 1, -16, 98, 55, -10 },
|
||||
{ 2, -15, 114, 33, -6 },
|
||||
{ 1, -10, 125, 14, -2 },
|
||||
};
|
||||
|
||||
static const struct dispc_coef coef5_M9[8] = {
|
||||
{ -3, 10, 114, 10, -3 },
|
||||
{ -6, 24, 111, 0, -1 },
|
||||
{ -8, 40, 103, -7, 0 },
|
||||
{ -11, 58, 91, -11, 1 },
|
||||
{ 0, -12, 76, 76, -12 },
|
||||
{ 1, -11, 91, 58, -11 },
|
||||
{ 0, -7, 103, 40, -8 },
|
||||
{ -1, 0, 111, 24, -6 },
|
||||
};
|
||||
|
||||
static const struct dispc_coef coef5_M10[8] = {
|
||||
{ -4, 18, 100, 18, -4 },
|
||||
{ -6, 30, 99, 8, -3 },
|
||||
{ -8, 44, 93, 0, -1 },
|
||||
{ -9, 58, 84, -5, 0 },
|
||||
{ 0, -8, 72, 72, -8 },
|
||||
{ 0, -5, 84, 58, -9 },
|
||||
{ -1, 0, 93, 44, -8 },
|
||||
{ -3, 8, 99, 30, -6 },
|
||||
};
|
||||
|
||||
static const struct dispc_coef coef5_M11[8] = {
|
||||
{ -5, 23, 92, 23, -5 },
|
||||
{ -6, 34, 90, 13, -3 },
|
||||
{ -6, 45, 85, 6, -2 },
|
||||
{ -6, 57, 78, 0, -1 },
|
||||
{ 0, -4, 68, 68, -4 },
|
||||
{ -1, 0, 78, 57, -6 },
|
||||
{ -2, 6, 85, 45, -6 },
|
||||
{ -3, 13, 90, 34, -6 },
|
||||
};
|
||||
|
||||
static const struct dispc_coef coef5_M12[8] = {
|
||||
{ -4, 26, 84, 26, -4 },
|
||||
{ -5, 36, 82, 18, -3 },
|
||||
{ -4, 46, 78, 10, -2 },
|
||||
{ -3, 55, 72, 5, -1 },
|
||||
{ 0, 0, 64, 64, 0 },
|
||||
{ -1, 5, 72, 55, -3 },
|
||||
{ -2, 10, 78, 46, -4 },
|
||||
{ -3, 18, 82, 36, -5 },
|
||||
};
|
||||
|
||||
static const struct dispc_coef coef5_M13[8] = {
|
||||
{ -3, 28, 78, 28, -3 },
|
||||
{ -3, 37, 76, 21, -3 },
|
||||
{ -2, 45, 73, 14, -2 },
|
||||
{ 0, 53, 68, 8, -1 },
|
||||
{ 0, 3, 61, 61, 3 },
|
||||
{ -1, 8, 68, 53, 0 },
|
||||
{ -2, 14, 73, 45, -2 },
|
||||
{ -3, 21, 76, 37, -3 },
|
||||
};
|
||||
|
||||
static const struct dispc_coef coef5_M14[8] = {
|
||||
{ -2, 30, 72, 30, -2 },
|
||||
{ -1, 37, 71, 23, -2 },
|
||||
{ 0, 45, 69, 16, -2 },
|
||||
{ 3, 52, 64, 10, -1 },
|
||||
{ 0, 6, 58, 58, 6 },
|
||||
{ -1, 10, 64, 52, 3 },
|
||||
{ -2, 16, 69, 45, 0 },
|
||||
{ -2, 23, 71, 37, -1 },
|
||||
};
|
||||
|
||||
static const struct dispc_coef coef5_M16[8] = {
|
||||
{ 0, 31, 66, 31, 0 },
|
||||
{ 1, 38, 65, 25, -1 },
|
||||
{ 3, 44, 62, 20, -1 },
|
||||
{ 6, 49, 59, 14, 0 },
|
||||
{ 0, 10, 54, 54, 10 },
|
||||
{ 0, 14, 59, 49, 6 },
|
||||
{ -1, 20, 62, 44, 3 },
|
||||
{ -1, 25, 65, 38, 1 },
|
||||
};
|
||||
|
||||
static const struct dispc_coef coef5_M19[8] = {
|
||||
{ 3, 32, 58, 32, 3 },
|
||||
{ 4, 38, 58, 27, 1 },
|
||||
{ 7, 42, 55, 23, 1 },
|
||||
{ 10, 46, 54, 18, 0 },
|
||||
{ 0, 14, 50, 50, 14 },
|
||||
{ 0, 18, 54, 46, 10 },
|
||||
{ 1, 23, 55, 42, 7 },
|
||||
{ 1, 27, 58, 38, 4 },
|
||||
};
|
||||
|
||||
static const struct dispc_coef coef5_M22[8] = {
|
||||
{ 4, 33, 54, 33, 4 },
|
||||
{ 6, 37, 54, 28, 3 },
|
||||
{ 9, 41, 53, 24, 1 },
|
||||
{ 12, 45, 51, 20, 0 },
|
||||
{ 0, 16, 48, 48, 16 },
|
||||
{ 0, 20, 51, 45, 12 },
|
||||
{ 1, 24, 53, 41, 9 },
|
||||
{ 3, 28, 54, 37, 6 },
|
||||
};
|
||||
|
||||
static const struct dispc_coef coef5_M26[8] = {
|
||||
{ 6, 33, 50, 33, 6 },
|
||||
{ 8, 36, 51, 29, 4 },
|
||||
{ 11, 40, 50, 25, 2 },
|
||||
{ 14, 43, 48, 22, 1 },
|
||||
{ 0, 18, 46, 46, 18 },
|
||||
{ 1, 22, 48, 43, 14 },
|
||||
{ 2, 25, 50, 40, 11 },
|
||||
{ 4, 29, 51, 36, 8 },
|
||||
};
|
||||
|
||||
static const struct dispc_coef coef5_M32[8] = {
|
||||
{ 7, 33, 48, 33, 7 },
|
||||
{ 10, 36, 48, 29, 5 },
|
||||
{ 13, 39, 47, 26, 3 },
|
||||
{ 16, 42, 46, 23, 1 },
|
||||
{ 0, 19, 45, 45, 19 },
|
||||
{ 1, 23, 46, 42, 16 },
|
||||
{ 3, 26, 47, 39, 13 },
|
||||
{ 5, 29, 48, 36, 10 },
|
||||
};
|
||||
|
||||
const struct dispc_coef *dispc_ovl_get_scale_coef(int inc, int five_taps)
|
||||
{
|
||||
int i;
|
||||
static const struct {
|
||||
int Mmin;
|
||||
int Mmax;
|
||||
const struct dispc_coef *coef_3;
|
||||
const struct dispc_coef *coef_5;
|
||||
} coefs[] = {
|
||||
{ 27, 32, coef3_M32, coef5_M32 },
|
||||
{ 23, 26, coef3_M26, coef5_M26 },
|
||||
{ 20, 22, coef3_M22, coef5_M22 },
|
||||
{ 17, 19, coef3_M19, coef5_M19 },
|
||||
{ 15, 16, coef3_M16, coef5_M16 },
|
||||
{ 14, 14, coef3_M14, coef5_M14 },
|
||||
{ 13, 13, coef3_M13, coef5_M13 },
|
||||
{ 12, 12, coef3_M12, coef5_M12 },
|
||||
{ 11, 11, coef3_M11, coef5_M11 },
|
||||
{ 10, 10, coef3_M10, coef5_M10 },
|
||||
{ 9, 9, coef3_M9, coef5_M9 },
|
||||
{ 4, 8, coef3_M8, coef5_M8 },
|
||||
/*
|
||||
* When upscaling more than two times, blockiness and outlines
|
||||
* around the image are observed when M8 tables are used. M11,
|
||||
* M16 and M19 tables are used to prevent this.
|
||||
*/
|
||||
{ 3, 3, coef3_M11, coef5_M11 },
|
||||
{ 2, 2, coef3_M16, coef5_M16 },
|
||||
{ 0, 1, coef3_M19, coef5_M19 },
|
||||
};
|
||||
|
||||
inc /= 128;
|
||||
for (i = 0; i < ARRAY_SIZE(coefs); ++i)
|
||||
if (inc >= coefs[i].Mmin && inc <= coefs[i].Mmax)
|
||||
return five_taps ? coefs[i].coef_5 : coefs[i].coef_3;
|
||||
return NULL;
|
||||
}
|
|
@ -0,0 +1,356 @@
|
|||
/*
|
||||
* Copyright (C) 2009 Nokia Corporation
|
||||
* Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
|
||||
*
|
||||
* Some code and ideas taken from drivers/video/omap/ driver
|
||||
* by Imre Deak.
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#define DSS_SUBSYS_NAME "DISPLAY"
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
#include <video/omapdss.h>
|
||||
#include "dss.h"
|
||||
|
||||
static ssize_t display_name_show(struct omap_dss_device *dssdev, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n",
|
||||
dssdev->name ?
|
||||
dssdev->name : "");
|
||||
}
|
||||
|
||||
static ssize_t display_enabled_show(struct omap_dss_device *dssdev, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n",
|
||||
omapdss_device_is_enabled(dssdev));
|
||||
}
|
||||
|
||||
static ssize_t display_enabled_store(struct omap_dss_device *dssdev,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
int r;
|
||||
bool enable;
|
||||
|
||||
r = strtobool(buf, &enable);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
if (enable == omapdss_device_is_enabled(dssdev))
|
||||
return size;
|
||||
|
||||
if (omapdss_device_is_connected(dssdev) == false)
|
||||
return -ENODEV;
|
||||
|
||||
if (enable) {
|
||||
r = dssdev->driver->enable(dssdev);
|
||||
if (r)
|
||||
return r;
|
||||
} else {
|
||||
dssdev->driver->disable(dssdev);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t display_tear_show(struct omap_dss_device *dssdev, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n",
|
||||
dssdev->driver->get_te ?
|
||||
dssdev->driver->get_te(dssdev) : 0);
|
||||
}
|
||||
|
||||
static ssize_t display_tear_store(struct omap_dss_device *dssdev,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
int r;
|
||||
bool te;
|
||||
|
||||
if (!dssdev->driver->enable_te || !dssdev->driver->get_te)
|
||||
return -ENOENT;
|
||||
|
||||
r = strtobool(buf, &te);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
r = dssdev->driver->enable_te(dssdev, te);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t display_timings_show(struct omap_dss_device *dssdev, char *buf)
|
||||
{
|
||||
struct omap_video_timings t;
|
||||
|
||||
if (!dssdev->driver->get_timings)
|
||||
return -ENOENT;
|
||||
|
||||
dssdev->driver->get_timings(dssdev, &t);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u,%u/%u/%u/%u,%u/%u/%u/%u\n",
|
||||
t.pixelclock,
|
||||
t.x_res, t.hfp, t.hbp, t.hsw,
|
||||
t.y_res, t.vfp, t.vbp, t.vsw);
|
||||
}
|
||||
|
||||
static ssize_t display_timings_store(struct omap_dss_device *dssdev,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct omap_video_timings t = dssdev->panel.timings;
|
||||
int r, found;
|
||||
|
||||
if (!dssdev->driver->set_timings || !dssdev->driver->check_timings)
|
||||
return -ENOENT;
|
||||
|
||||
found = 0;
|
||||
#ifdef CONFIG_FB_OMAP2_DSS_VENC
|
||||
if (strncmp("pal", buf, 3) == 0) {
|
||||
t = omap_dss_pal_timings;
|
||||
found = 1;
|
||||
} else if (strncmp("ntsc", buf, 4) == 0) {
|
||||
t = omap_dss_ntsc_timings;
|
||||
found = 1;
|
||||
}
|
||||
#endif
|
||||
if (!found && sscanf(buf, "%u,%hu/%hu/%hu/%hu,%hu/%hu/%hu/%hu",
|
||||
&t.pixelclock,
|
||||
&t.x_res, &t.hfp, &t.hbp, &t.hsw,
|
||||
&t.y_res, &t.vfp, &t.vbp, &t.vsw) != 9)
|
||||
return -EINVAL;
|
||||
|
||||
r = dssdev->driver->check_timings(dssdev, &t);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
dssdev->driver->disable(dssdev);
|
||||
dssdev->driver->set_timings(dssdev, &t);
|
||||
r = dssdev->driver->enable(dssdev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t display_rotate_show(struct omap_dss_device *dssdev, char *buf)
|
||||
{
|
||||
int rotate;
|
||||
if (!dssdev->driver->get_rotate)
|
||||
return -ENOENT;
|
||||
rotate = dssdev->driver->get_rotate(dssdev);
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", rotate);
|
||||
}
|
||||
|
||||
static ssize_t display_rotate_store(struct omap_dss_device *dssdev,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
int rot, r;
|
||||
|
||||
if (!dssdev->driver->set_rotate || !dssdev->driver->get_rotate)
|
||||
return -ENOENT;
|
||||
|
||||
r = kstrtoint(buf, 0, &rot);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
r = dssdev->driver->set_rotate(dssdev, rot);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t display_mirror_show(struct omap_dss_device *dssdev, char *buf)
|
||||
{
|
||||
int mirror;
|
||||
if (!dssdev->driver->get_mirror)
|
||||
return -ENOENT;
|
||||
mirror = dssdev->driver->get_mirror(dssdev);
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", mirror);
|
||||
}
|
||||
|
||||
static ssize_t display_mirror_store(struct omap_dss_device *dssdev,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
int r;
|
||||
bool mirror;
|
||||
|
||||
if (!dssdev->driver->set_mirror || !dssdev->driver->get_mirror)
|
||||
return -ENOENT;
|
||||
|
||||
r = strtobool(buf, &mirror);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
r = dssdev->driver->set_mirror(dssdev, mirror);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t display_wss_show(struct omap_dss_device *dssdev, char *buf)
|
||||
{
|
||||
unsigned int wss;
|
||||
|
||||
if (!dssdev->driver->get_wss)
|
||||
return -ENOENT;
|
||||
|
||||
wss = dssdev->driver->get_wss(dssdev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "0x%05x\n", wss);
|
||||
}
|
||||
|
||||
static ssize_t display_wss_store(struct omap_dss_device *dssdev,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
u32 wss;
|
||||
int r;
|
||||
|
||||
if (!dssdev->driver->get_wss || !dssdev->driver->set_wss)
|
||||
return -ENOENT;
|
||||
|
||||
r = kstrtou32(buf, 0, &wss);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
if (wss > 0xfffff)
|
||||
return -EINVAL;
|
||||
|
||||
r = dssdev->driver->set_wss(dssdev, wss);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
struct display_attribute {
|
||||
struct attribute attr;
|
||||
ssize_t (*show)(struct omap_dss_device *, char *);
|
||||
ssize_t (*store)(struct omap_dss_device *, const char *, size_t);
|
||||
};
|
||||
|
||||
#define DISPLAY_ATTR(_name, _mode, _show, _store) \
|
||||
struct display_attribute display_attr_##_name = \
|
||||
__ATTR(_name, _mode, _show, _store)
|
||||
|
||||
static DISPLAY_ATTR(name, S_IRUGO, display_name_show, NULL);
|
||||
static DISPLAY_ATTR(display_name, S_IRUGO, display_name_show, NULL);
|
||||
static DISPLAY_ATTR(enabled, S_IRUGO|S_IWUSR,
|
||||
display_enabled_show, display_enabled_store);
|
||||
static DISPLAY_ATTR(tear_elim, S_IRUGO|S_IWUSR,
|
||||
display_tear_show, display_tear_store);
|
||||
static DISPLAY_ATTR(timings, S_IRUGO|S_IWUSR,
|
||||
display_timings_show, display_timings_store);
|
||||
static DISPLAY_ATTR(rotate, S_IRUGO|S_IWUSR,
|
||||
display_rotate_show, display_rotate_store);
|
||||
static DISPLAY_ATTR(mirror, S_IRUGO|S_IWUSR,
|
||||
display_mirror_show, display_mirror_store);
|
||||
static DISPLAY_ATTR(wss, S_IRUGO|S_IWUSR,
|
||||
display_wss_show, display_wss_store);
|
||||
|
||||
static struct attribute *display_sysfs_attrs[] = {
|
||||
&display_attr_name.attr,
|
||||
&display_attr_display_name.attr,
|
||||
&display_attr_enabled.attr,
|
||||
&display_attr_tear_elim.attr,
|
||||
&display_attr_timings.attr,
|
||||
&display_attr_rotate.attr,
|
||||
&display_attr_mirror.attr,
|
||||
&display_attr_wss.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static ssize_t display_attr_show(struct kobject *kobj, struct attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct omap_dss_device *dssdev;
|
||||
struct display_attribute *display_attr;
|
||||
|
||||
dssdev = container_of(kobj, struct omap_dss_device, kobj);
|
||||
display_attr = container_of(attr, struct display_attribute, attr);
|
||||
|
||||
if (!display_attr->show)
|
||||
return -ENOENT;
|
||||
|
||||
return display_attr->show(dssdev, buf);
|
||||
}
|
||||
|
||||
static ssize_t display_attr_store(struct kobject *kobj, struct attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct omap_dss_device *dssdev;
|
||||
struct display_attribute *display_attr;
|
||||
|
||||
dssdev = container_of(kobj, struct omap_dss_device, kobj);
|
||||
display_attr = container_of(attr, struct display_attribute, attr);
|
||||
|
||||
if (!display_attr->store)
|
||||
return -ENOENT;
|
||||
|
||||
return display_attr->store(dssdev, buf, size);
|
||||
}
|
||||
|
||||
static const struct sysfs_ops display_sysfs_ops = {
|
||||
.show = display_attr_show,
|
||||
.store = display_attr_store,
|
||||
};
|
||||
|
||||
static struct kobj_type display_ktype = {
|
||||
.sysfs_ops = &display_sysfs_ops,
|
||||
.default_attrs = display_sysfs_attrs,
|
||||
};
|
||||
|
||||
int display_init_sysfs(struct platform_device *pdev)
|
||||
{
|
||||
struct omap_dss_device *dssdev = NULL;
|
||||
int r;
|
||||
|
||||
for_each_dss_dev(dssdev) {
|
||||
r = kobject_init_and_add(&dssdev->kobj, &display_ktype,
|
||||
&pdev->dev.kobj, "%s", dssdev->alias);
|
||||
if (r) {
|
||||
DSSERR("failed to create sysfs files\n");
|
||||
omap_dss_put_device(dssdev);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
display_uninit_sysfs(pdev);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void display_uninit_sysfs(struct platform_device *pdev)
|
||||
{
|
||||
struct omap_dss_device *dssdev = NULL;
|
||||
|
||||
for_each_dss_dev(dssdev) {
|
||||
if (kobject_name(&dssdev->kobj) == NULL)
|
||||
continue;
|
||||
|
||||
kobject_del(&dssdev->kobj);
|
||||
kobject_put(&dssdev->kobj);
|
||||
|
||||
memset(&dssdev->kobj, 0, sizeof(dssdev->kobj));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,338 @@
|
|||
/*
|
||||
* linux/drivers/video/omap2/dss/display.c
|
||||
*
|
||||
* Copyright (C) 2009 Nokia Corporation
|
||||
* Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
|
||||
*
|
||||
* Some code and ideas taken from drivers/video/omap/ driver
|
||||
* by Imre Deak.
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#define DSS_SUBSYS_NAME "DISPLAY"
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <video/omapdss.h>
|
||||
#include "dss.h"
|
||||
#include "dss_features.h"
|
||||
|
||||
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);
|
||||
|
||||
int omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev)
|
||||
{
|
||||
switch (dssdev->type) {
|
||||
case OMAP_DISPLAY_TYPE_DPI:
|
||||
if (dssdev->phy.dpi.data_lines == 24)
|
||||
return 24;
|
||||
else
|
||||
return 16;
|
||||
|
||||
case OMAP_DISPLAY_TYPE_DBI:
|
||||
if (dssdev->ctrl.pixel_size == 24)
|
||||
return 24;
|
||||
else
|
||||
return 16;
|
||||
case OMAP_DISPLAY_TYPE_DSI:
|
||||
if (dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt) > 16)
|
||||
return 24;
|
||||
else
|
||||
return 16;
|
||||
case OMAP_DISPLAY_TYPE_VENC:
|
||||
case OMAP_DISPLAY_TYPE_SDI:
|
||||
case OMAP_DISPLAY_TYPE_HDMI:
|
||||
case OMAP_DISPLAY_TYPE_DVI:
|
||||
return 24;
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(omapdss_default_get_recommended_bpp);
|
||||
|
||||
void omapdss_default_get_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
*timings = dssdev->panel.timings;
|
||||
}
|
||||
EXPORT_SYMBOL(omapdss_default_get_timings);
|
||||
|
||||
int dss_suspend_all_devices(void)
|
||||
{
|
||||
struct omap_dss_device *dssdev = NULL;
|
||||
|
||||
for_each_dss_dev(dssdev) {
|
||||
if (!dssdev->driver)
|
||||
continue;
|
||||
|
||||
if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
|
||||
dssdev->driver->disable(dssdev);
|
||||
dssdev->activate_after_resume = true;
|
||||
} else {
|
||||
dssdev->activate_after_resume = false;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dss_resume_all_devices(void)
|
||||
{
|
||||
struct omap_dss_device *dssdev = NULL;
|
||||
|
||||
for_each_dss_dev(dssdev) {
|
||||
if (!dssdev->driver)
|
||||
continue;
|
||||
|
||||
if (dssdev->activate_after_resume) {
|
||||
dssdev->driver->enable(dssdev);
|
||||
dssdev->activate_after_resume = false;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dss_disable_all_devices(void)
|
||||
{
|
||||
struct omap_dss_device *dssdev = NULL;
|
||||
|
||||
for_each_dss_dev(dssdev) {
|
||||
if (!dssdev->driver)
|
||||
continue;
|
||||
|
||||
if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
|
||||
dssdev->driver->disable(dssdev);
|
||||
}
|
||||
}
|
||||
|
||||
static LIST_HEAD(panel_list);
|
||||
static DEFINE_MUTEX(panel_list_mutex);
|
||||
static int disp_num_counter;
|
||||
|
||||
int omapdss_register_display(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct omap_dss_driver *drv = dssdev->driver;
|
||||
int id;
|
||||
|
||||
/*
|
||||
* Note: this presumes all the displays are either using DT or non-DT,
|
||||
* which normally should be the case. This also presumes that all
|
||||
* displays either have an DT alias, or none has.
|
||||
*/
|
||||
|
||||
if (dssdev->dev->of_node) {
|
||||
id = of_alias_get_id(dssdev->dev->of_node, "display");
|
||||
|
||||
if (id < 0)
|
||||
id = disp_num_counter++;
|
||||
} else {
|
||||
id = disp_num_counter++;
|
||||
}
|
||||
|
||||
snprintf(dssdev->alias, sizeof(dssdev->alias), "display%d", id);
|
||||
|
||||
/* Use 'label' property for name, if it exists */
|
||||
if (dssdev->dev->of_node)
|
||||
of_property_read_string(dssdev->dev->of_node, "label",
|
||||
&dssdev->name);
|
||||
|
||||
if (dssdev->name == NULL)
|
||||
dssdev->name = dssdev->alias;
|
||||
|
||||
if (drv && drv->get_resolution == NULL)
|
||||
drv->get_resolution = omapdss_default_get_resolution;
|
||||
if (drv && drv->get_recommended_bpp == NULL)
|
||||
drv->get_recommended_bpp = omapdss_default_get_recommended_bpp;
|
||||
if (drv && drv->get_timings == NULL)
|
||||
drv->get_timings = omapdss_default_get_timings;
|
||||
|
||||
mutex_lock(&panel_list_mutex);
|
||||
list_add_tail(&dssdev->panel_list, &panel_list);
|
||||
mutex_unlock(&panel_list_mutex);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(omapdss_register_display);
|
||||
|
||||
void omapdss_unregister_display(struct omap_dss_device *dssdev)
|
||||
{
|
||||
mutex_lock(&panel_list_mutex);
|
||||
list_del(&dssdev->panel_list);
|
||||
mutex_unlock(&panel_list_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL(omapdss_unregister_display);
|
||||
|
||||
struct omap_dss_device *omap_dss_get_device(struct omap_dss_device *dssdev)
|
||||
{
|
||||
if (!try_module_get(dssdev->owner))
|
||||
return NULL;
|
||||
|
||||
if (get_device(dssdev->dev) == NULL) {
|
||||
module_put(dssdev->owner);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return dssdev;
|
||||
}
|
||||
EXPORT_SYMBOL(omap_dss_get_device);
|
||||
|
||||
void omap_dss_put_device(struct omap_dss_device *dssdev)
|
||||
{
|
||||
put_device(dssdev->dev);
|
||||
module_put(dssdev->owner);
|
||||
}
|
||||
EXPORT_SYMBOL(omap_dss_put_device);
|
||||
|
||||
/*
|
||||
* ref count of the found device is incremented.
|
||||
* ref count of from-device is decremented.
|
||||
*/
|
||||
struct omap_dss_device *omap_dss_get_next_device(struct omap_dss_device *from)
|
||||
{
|
||||
struct list_head *l;
|
||||
struct omap_dss_device *dssdev;
|
||||
|
||||
mutex_lock(&panel_list_mutex);
|
||||
|
||||
if (list_empty(&panel_list)) {
|
||||
dssdev = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (from == NULL) {
|
||||
dssdev = list_first_entry(&panel_list, struct omap_dss_device,
|
||||
panel_list);
|
||||
omap_dss_get_device(dssdev);
|
||||
goto out;
|
||||
}
|
||||
|
||||
omap_dss_put_device(from);
|
||||
|
||||
list_for_each(l, &panel_list) {
|
||||
dssdev = list_entry(l, struct omap_dss_device, panel_list);
|
||||
if (dssdev == from) {
|
||||
if (list_is_last(l, &panel_list)) {
|
||||
dssdev = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dssdev = list_entry(l->next, struct omap_dss_device,
|
||||
panel_list);
|
||||
omap_dss_get_device(dssdev);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
WARN(1, "'from' dssdev not found\n");
|
||||
|
||||
dssdev = NULL;
|
||||
out:
|
||||
mutex_unlock(&panel_list_mutex);
|
||||
return dssdev;
|
||||
}
|
||||
EXPORT_SYMBOL(omap_dss_get_next_device);
|
||||
|
||||
struct omap_dss_device *omap_dss_find_device(void *data,
|
||||
int (*match)(struct omap_dss_device *dssdev, void *data))
|
||||
{
|
||||
struct omap_dss_device *dssdev = NULL;
|
||||
|
||||
while ((dssdev = omap_dss_get_next_device(dssdev)) != NULL) {
|
||||
if (match(dssdev, data))
|
||||
return dssdev;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(omap_dss_find_device);
|
||||
|
||||
void videomode_to_omap_video_timings(const struct videomode *vm,
|
||||
struct omap_video_timings *ovt)
|
||||
{
|
||||
memset(ovt, 0, sizeof(*ovt));
|
||||
|
||||
ovt->pixelclock = vm->pixelclock;
|
||||
ovt->x_res = vm->hactive;
|
||||
ovt->hbp = vm->hback_porch;
|
||||
ovt->hfp = vm->hfront_porch;
|
||||
ovt->hsw = vm->hsync_len;
|
||||
ovt->y_res = vm->vactive;
|
||||
ovt->vbp = vm->vback_porch;
|
||||
ovt->vfp = vm->vfront_porch;
|
||||
ovt->vsw = vm->vsync_len;
|
||||
|
||||
ovt->vsync_level = vm->flags & DISPLAY_FLAGS_VSYNC_HIGH ?
|
||||
OMAPDSS_SIG_ACTIVE_HIGH :
|
||||
OMAPDSS_SIG_ACTIVE_LOW;
|
||||
ovt->hsync_level = vm->flags & DISPLAY_FLAGS_HSYNC_HIGH ?
|
||||
OMAPDSS_SIG_ACTIVE_HIGH :
|
||||
OMAPDSS_SIG_ACTIVE_LOW;
|
||||
ovt->de_level = vm->flags & DISPLAY_FLAGS_DE_HIGH ?
|
||||
OMAPDSS_SIG_ACTIVE_HIGH :
|
||||
OMAPDSS_SIG_ACTIVE_LOW;
|
||||
ovt->data_pclk_edge = vm->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE ?
|
||||
OMAPDSS_DRIVE_SIG_RISING_EDGE :
|
||||
OMAPDSS_DRIVE_SIG_FALLING_EDGE;
|
||||
|
||||
ovt->sync_pclk_edge = ovt->data_pclk_edge;
|
||||
}
|
||||
EXPORT_SYMBOL(videomode_to_omap_video_timings);
|
||||
|
||||
void omap_video_timings_to_videomode(const struct omap_video_timings *ovt,
|
||||
struct videomode *vm)
|
||||
{
|
||||
memset(vm, 0, sizeof(*vm));
|
||||
|
||||
vm->pixelclock = ovt->pixelclock;
|
||||
|
||||
vm->hactive = ovt->x_res;
|
||||
vm->hback_porch = ovt->hbp;
|
||||
vm->hfront_porch = ovt->hfp;
|
||||
vm->hsync_len = ovt->hsw;
|
||||
vm->vactive = ovt->y_res;
|
||||
vm->vback_porch = ovt->vbp;
|
||||
vm->vfront_porch = ovt->vfp;
|
||||
vm->vsync_len = ovt->vsw;
|
||||
|
||||
if (ovt->hsync_level == OMAPDSS_SIG_ACTIVE_HIGH)
|
||||
vm->flags |= DISPLAY_FLAGS_HSYNC_HIGH;
|
||||
else
|
||||
vm->flags |= DISPLAY_FLAGS_HSYNC_LOW;
|
||||
|
||||
if (ovt->vsync_level == OMAPDSS_SIG_ACTIVE_HIGH)
|
||||
vm->flags |= DISPLAY_FLAGS_VSYNC_HIGH;
|
||||
else
|
||||
vm->flags |= DISPLAY_FLAGS_VSYNC_LOW;
|
||||
|
||||
if (ovt->de_level == OMAPDSS_SIG_ACTIVE_HIGH)
|
||||
vm->flags |= DISPLAY_FLAGS_DE_HIGH;
|
||||
else
|
||||
vm->flags |= DISPLAY_FLAGS_DE_LOW;
|
||||
|
||||
if (ovt->data_pclk_edge == OMAPDSS_DRIVE_SIG_RISING_EDGE)
|
||||
vm->flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE;
|
||||
else
|
||||
vm->flags |= DISPLAY_FLAGS_PIXDATA_NEGEDGE;
|
||||
}
|
||||
EXPORT_SYMBOL(omap_video_timings_to_videomode);
|
|
@ -0,0 +1,899 @@
|
|||
/*
|
||||
* linux/drivers/video/omap2/dss/dpi.c
|
||||
*
|
||||
* Copyright (C) 2009 Nokia Corporation
|
||||
* Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
|
||||
*
|
||||
* Some code and ideas taken from drivers/video/omap/ driver
|
||||
* by Imre Deak.
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#define DSS_SUBSYS_NAME "DPI"
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/component.h>
|
||||
|
||||
#include <video/omapdss.h>
|
||||
|
||||
#include "dss.h"
|
||||
#include "dss_features.h"
|
||||
|
||||
#define HSDIV_DISPC 0
|
||||
|
||||
struct dpi_data {
|
||||
struct platform_device *pdev;
|
||||
|
||||
struct regulator *vdds_dsi_reg;
|
||||
struct dss_pll *pll;
|
||||
|
||||
struct mutex lock;
|
||||
|
||||
struct omap_video_timings timings;
|
||||
struct dss_lcd_mgr_config mgr_config;
|
||||
int data_lines;
|
||||
|
||||
struct omap_dss_device output;
|
||||
|
||||
bool port_initialized;
|
||||
};
|
||||
|
||||
static struct dpi_data *dpi_get_data_from_dssdev(struct omap_dss_device *dssdev)
|
||||
{
|
||||
return container_of(dssdev, struct dpi_data, output);
|
||||
}
|
||||
|
||||
/* only used in non-DT mode */
|
||||
static struct dpi_data *dpi_get_data_from_pdev(struct platform_device *pdev)
|
||||
{
|
||||
return dev_get_drvdata(&pdev->dev);
|
||||
}
|
||||
|
||||
static struct dss_pll *dpi_get_pll(enum omap_channel channel)
|
||||
{
|
||||
/*
|
||||
* XXX we can't currently use DSI PLL for DPI with OMAP3, as the DSI PLL
|
||||
* would also be used for DISPC fclk. Meaning, when the DPI output is
|
||||
* disabled, DISPC clock will be disabled, and TV out will stop.
|
||||
*/
|
||||
switch (omapdss_get_version()) {
|
||||
case OMAPDSS_VER_OMAP24xx:
|
||||
case OMAPDSS_VER_OMAP34xx_ES1:
|
||||
case OMAPDSS_VER_OMAP34xx_ES3:
|
||||
case OMAPDSS_VER_OMAP3630:
|
||||
case OMAPDSS_VER_AM35xx:
|
||||
case OMAPDSS_VER_AM43xx:
|
||||
return NULL;
|
||||
|
||||
case OMAPDSS_VER_OMAP4430_ES1:
|
||||
case OMAPDSS_VER_OMAP4430_ES2:
|
||||
case OMAPDSS_VER_OMAP4:
|
||||
switch (channel) {
|
||||
case OMAP_DSS_CHANNEL_LCD:
|
||||
return dss_pll_find("dsi0");
|
||||
case OMAP_DSS_CHANNEL_LCD2:
|
||||
return dss_pll_find("dsi1");
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
case OMAPDSS_VER_OMAP5:
|
||||
switch (channel) {
|
||||
case OMAP_DSS_CHANNEL_LCD:
|
||||
return dss_pll_find("dsi0");
|
||||
case OMAP_DSS_CHANNEL_LCD3:
|
||||
return dss_pll_find("dsi1");
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
case OMAPDSS_VER_DRA7xx:
|
||||
switch (channel) {
|
||||
case OMAP_DSS_CHANNEL_LCD:
|
||||
case OMAP_DSS_CHANNEL_LCD2:
|
||||
return dss_pll_find("video0");
|
||||
case OMAP_DSS_CHANNEL_LCD3:
|
||||
return dss_pll_find("video1");
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static enum omap_dss_clk_source dpi_get_alt_clk_src(enum omap_channel channel)
|
||||
{
|
||||
switch (channel) {
|
||||
case OMAP_DSS_CHANNEL_LCD:
|
||||
return OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC;
|
||||
case OMAP_DSS_CHANNEL_LCD2:
|
||||
return OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC;
|
||||
case OMAP_DSS_CHANNEL_LCD3:
|
||||
return OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC;
|
||||
default:
|
||||
/* this shouldn't happen */
|
||||
WARN_ON(1);
|
||||
return OMAP_DSS_CLK_SRC_FCK;
|
||||
}
|
||||
}
|
||||
|
||||
struct dpi_clk_calc_ctx {
|
||||
struct dss_pll *pll;
|
||||
|
||||
/* inputs */
|
||||
|
||||
unsigned long pck_min, pck_max;
|
||||
|
||||
/* outputs */
|
||||
|
||||
struct dss_pll_clock_info dsi_cinfo;
|
||||
unsigned long fck;
|
||||
struct dispc_clock_info dispc_cinfo;
|
||||
};
|
||||
|
||||
static bool dpi_calc_dispc_cb(int lckd, int pckd, unsigned long lck,
|
||||
unsigned long pck, void *data)
|
||||
{
|
||||
struct dpi_clk_calc_ctx *ctx = data;
|
||||
|
||||
/*
|
||||
* Odd dividers give us uneven duty cycle, causing problem when level
|
||||
* shifted. So skip all odd dividers when the pixel clock is on the
|
||||
* higher side.
|
||||
*/
|
||||
if (ctx->pck_min >= 100000000) {
|
||||
if (lckd > 1 && lckd % 2 != 0)
|
||||
return false;
|
||||
|
||||
if (pckd > 1 && pckd % 2 != 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
ctx->dispc_cinfo.lck_div = lckd;
|
||||
ctx->dispc_cinfo.pck_div = pckd;
|
||||
ctx->dispc_cinfo.lck = lck;
|
||||
ctx->dispc_cinfo.pck = pck;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool dpi_calc_hsdiv_cb(int m_dispc, unsigned long dispc,
|
||||
void *data)
|
||||
{
|
||||
struct dpi_clk_calc_ctx *ctx = data;
|
||||
|
||||
/*
|
||||
* Odd dividers give us uneven duty cycle, causing problem when level
|
||||
* shifted. So skip all odd dividers when the pixel clock is on the
|
||||
* higher side.
|
||||
*/
|
||||
if (m_dispc > 1 && m_dispc % 2 != 0 && ctx->pck_min >= 100000000)
|
||||
return false;
|
||||
|
||||
ctx->dsi_cinfo.mX[HSDIV_DISPC] = m_dispc;
|
||||
ctx->dsi_cinfo.clkout[HSDIV_DISPC] = dispc;
|
||||
|
||||
return dispc_div_calc(dispc, ctx->pck_min, ctx->pck_max,
|
||||
dpi_calc_dispc_cb, ctx);
|
||||
}
|
||||
|
||||
|
||||
static bool dpi_calc_pll_cb(int n, int m, unsigned long fint,
|
||||
unsigned long clkdco,
|
||||
void *data)
|
||||
{
|
||||
struct dpi_clk_calc_ctx *ctx = data;
|
||||
|
||||
ctx->dsi_cinfo.n = n;
|
||||
ctx->dsi_cinfo.m = m;
|
||||
ctx->dsi_cinfo.fint = fint;
|
||||
ctx->dsi_cinfo.clkdco = clkdco;
|
||||
|
||||
return dss_pll_hsdiv_calc(ctx->pll, clkdco,
|
||||
ctx->pck_min, dss_feat_get_param_max(FEAT_PARAM_DSS_FCK),
|
||||
dpi_calc_hsdiv_cb, ctx);
|
||||
}
|
||||
|
||||
static bool dpi_calc_dss_cb(unsigned long fck, void *data)
|
||||
{
|
||||
struct dpi_clk_calc_ctx *ctx = data;
|
||||
|
||||
ctx->fck = fck;
|
||||
|
||||
return dispc_div_calc(fck, ctx->pck_min, ctx->pck_max,
|
||||
dpi_calc_dispc_cb, ctx);
|
||||
}
|
||||
|
||||
static bool dpi_dsi_clk_calc(struct dpi_data *dpi, unsigned long pck,
|
||||
struct dpi_clk_calc_ctx *ctx)
|
||||
{
|
||||
unsigned long clkin;
|
||||
unsigned long pll_min, pll_max;
|
||||
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
ctx->pll = dpi->pll;
|
||||
ctx->pck_min = pck - 1000;
|
||||
ctx->pck_max = pck + 1000;
|
||||
|
||||
pll_min = 0;
|
||||
pll_max = 0;
|
||||
|
||||
clkin = clk_get_rate(ctx->pll->clkin);
|
||||
|
||||
return dss_pll_calc(ctx->pll, clkin,
|
||||
pll_min, pll_max,
|
||||
dpi_calc_pll_cb, ctx);
|
||||
}
|
||||
|
||||
static bool dpi_dss_clk_calc(unsigned long pck, struct dpi_clk_calc_ctx *ctx)
|
||||
{
|
||||
int i;
|
||||
|
||||
/*
|
||||
* DSS fck gives us very few possibilities, so finding a good pixel
|
||||
* clock may not be possible. We try multiple times to find the clock,
|
||||
* each time widening the pixel clock range we look for, up to
|
||||
* +/- ~15MHz.
|
||||
*/
|
||||
|
||||
for (i = 0; i < 25; ++i) {
|
||||
bool ok;
|
||||
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
if (pck > 1000 * i * i * i)
|
||||
ctx->pck_min = max(pck - 1000 * i * i * i, 0lu);
|
||||
else
|
||||
ctx->pck_min = 0;
|
||||
ctx->pck_max = pck + 1000 * i * i * i;
|
||||
|
||||
ok = dss_div_calc(pck, ctx->pck_min, dpi_calc_dss_cb, ctx);
|
||||
if (ok)
|
||||
return ok;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int dpi_set_dsi_clk(struct dpi_data *dpi, enum omap_channel channel,
|
||||
unsigned long pck_req, unsigned long *fck, int *lck_div,
|
||||
int *pck_div)
|
||||
{
|
||||
struct dpi_clk_calc_ctx ctx;
|
||||
int r;
|
||||
bool ok;
|
||||
|
||||
ok = dpi_dsi_clk_calc(dpi, pck_req, &ctx);
|
||||
if (!ok)
|
||||
return -EINVAL;
|
||||
|
||||
r = dss_pll_set_config(dpi->pll, &ctx.dsi_cinfo);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
dss_select_lcd_clk_source(channel,
|
||||
dpi_get_alt_clk_src(channel));
|
||||
|
||||
dpi->mgr_config.clock_info = ctx.dispc_cinfo;
|
||||
|
||||
*fck = ctx.dsi_cinfo.clkout[HSDIV_DISPC];
|
||||
*lck_div = ctx.dispc_cinfo.lck_div;
|
||||
*pck_div = ctx.dispc_cinfo.pck_div;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dpi_set_dispc_clk(struct dpi_data *dpi, unsigned long pck_req,
|
||||
unsigned long *fck, int *lck_div, int *pck_div)
|
||||
{
|
||||
struct dpi_clk_calc_ctx ctx;
|
||||
int r;
|
||||
bool ok;
|
||||
|
||||
ok = dpi_dss_clk_calc(pck_req, &ctx);
|
||||
if (!ok)
|
||||
return -EINVAL;
|
||||
|
||||
r = dss_set_fck_rate(ctx.fck);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
dpi->mgr_config.clock_info = ctx.dispc_cinfo;
|
||||
|
||||
*fck = ctx.fck;
|
||||
*lck_div = ctx.dispc_cinfo.lck_div;
|
||||
*pck_div = ctx.dispc_cinfo.pck_div;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dpi_set_mode(struct dpi_data *dpi)
|
||||
{
|
||||
struct omap_dss_device *out = &dpi->output;
|
||||
struct omap_overlay_manager *mgr = out->manager;
|
||||
struct omap_video_timings *t = &dpi->timings;
|
||||
int lck_div = 0, pck_div = 0;
|
||||
unsigned long fck = 0;
|
||||
unsigned long pck;
|
||||
int r = 0;
|
||||
|
||||
if (dpi->pll)
|
||||
r = dpi_set_dsi_clk(dpi, mgr->id, t->pixelclock, &fck,
|
||||
&lck_div, &pck_div);
|
||||
else
|
||||
r = dpi_set_dispc_clk(dpi, t->pixelclock, &fck,
|
||||
&lck_div, &pck_div);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
pck = fck / lck_div / pck_div;
|
||||
|
||||
if (pck != t->pixelclock) {
|
||||
DSSWARN("Could not find exact pixel clock. Requested %d Hz, got %lu Hz\n",
|
||||
t->pixelclock, pck);
|
||||
|
||||
t->pixelclock = pck;
|
||||
}
|
||||
|
||||
dss_mgr_set_timings(mgr, t);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dpi_config_lcd_manager(struct dpi_data *dpi)
|
||||
{
|
||||
struct omap_dss_device *out = &dpi->output;
|
||||
struct omap_overlay_manager *mgr = out->manager;
|
||||
|
||||
dpi->mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS;
|
||||
|
||||
dpi->mgr_config.stallmode = false;
|
||||
dpi->mgr_config.fifohandcheck = false;
|
||||
|
||||
dpi->mgr_config.video_port_width = dpi->data_lines;
|
||||
|
||||
dpi->mgr_config.lcden_sig_polarity = 0;
|
||||
|
||||
dss_mgr_set_lcd_config(mgr, &dpi->mgr_config);
|
||||
}
|
||||
|
||||
static int dpi_display_enable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
|
||||
struct omap_dss_device *out = &dpi->output;
|
||||
int r;
|
||||
|
||||
mutex_lock(&dpi->lock);
|
||||
|
||||
if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI) && !dpi->vdds_dsi_reg) {
|
||||
DSSERR("no VDSS_DSI regulator\n");
|
||||
r = -ENODEV;
|
||||
goto err_no_reg;
|
||||
}
|
||||
|
||||
if (out->manager == NULL) {
|
||||
DSSERR("failed to enable display: no output/manager\n");
|
||||
r = -ENODEV;
|
||||
goto err_no_out_mgr;
|
||||
}
|
||||
|
||||
if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI)) {
|
||||
r = regulator_enable(dpi->vdds_dsi_reg);
|
||||
if (r)
|
||||
goto err_reg_enable;
|
||||
}
|
||||
|
||||
r = dispc_runtime_get();
|
||||
if (r)
|
||||
goto err_get_dispc;
|
||||
|
||||
r = dss_dpi_select_source(out->port_num, out->manager->id);
|
||||
if (r)
|
||||
goto err_src_sel;
|
||||
|
||||
if (dpi->pll) {
|
||||
r = dss_pll_enable(dpi->pll);
|
||||
if (r)
|
||||
goto err_dsi_pll_init;
|
||||
}
|
||||
|
||||
r = dpi_set_mode(dpi);
|
||||
if (r)
|
||||
goto err_set_mode;
|
||||
|
||||
dpi_config_lcd_manager(dpi);
|
||||
|
||||
mdelay(2);
|
||||
|
||||
r = dss_mgr_enable(out->manager);
|
||||
if (r)
|
||||
goto err_mgr_enable;
|
||||
|
||||
mutex_unlock(&dpi->lock);
|
||||
|
||||
return 0;
|
||||
|
||||
err_mgr_enable:
|
||||
err_set_mode:
|
||||
if (dpi->pll)
|
||||
dss_pll_disable(dpi->pll);
|
||||
err_dsi_pll_init:
|
||||
err_src_sel:
|
||||
dispc_runtime_put();
|
||||
err_get_dispc:
|
||||
if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI))
|
||||
regulator_disable(dpi->vdds_dsi_reg);
|
||||
err_reg_enable:
|
||||
err_no_out_mgr:
|
||||
err_no_reg:
|
||||
mutex_unlock(&dpi->lock);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void dpi_display_disable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
|
||||
struct omap_overlay_manager *mgr = dpi->output.manager;
|
||||
|
||||
mutex_lock(&dpi->lock);
|
||||
|
||||
dss_mgr_disable(mgr);
|
||||
|
||||
if (dpi->pll) {
|
||||
dss_select_lcd_clk_source(mgr->id, OMAP_DSS_CLK_SRC_FCK);
|
||||
dss_pll_disable(dpi->pll);
|
||||
}
|
||||
|
||||
dispc_runtime_put();
|
||||
|
||||
if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI))
|
||||
regulator_disable(dpi->vdds_dsi_reg);
|
||||
|
||||
mutex_unlock(&dpi->lock);
|
||||
}
|
||||
|
||||
static void dpi_set_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
|
||||
|
||||
DSSDBG("dpi_set_timings\n");
|
||||
|
||||
mutex_lock(&dpi->lock);
|
||||
|
||||
dpi->timings = *timings;
|
||||
|
||||
mutex_unlock(&dpi->lock);
|
||||
}
|
||||
|
||||
static void dpi_get_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
|
||||
|
||||
mutex_lock(&dpi->lock);
|
||||
|
||||
*timings = dpi->timings;
|
||||
|
||||
mutex_unlock(&dpi->lock);
|
||||
}
|
||||
|
||||
static int dpi_check_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
|
||||
struct omap_overlay_manager *mgr = dpi->output.manager;
|
||||
int lck_div, pck_div;
|
||||
unsigned long fck;
|
||||
unsigned long pck;
|
||||
struct dpi_clk_calc_ctx ctx;
|
||||
bool ok;
|
||||
|
||||
if (mgr && !dispc_mgr_timings_ok(mgr->id, timings))
|
||||
return -EINVAL;
|
||||
|
||||
if (timings->pixelclock == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (dpi->pll) {
|
||||
ok = dpi_dsi_clk_calc(dpi, timings->pixelclock, &ctx);
|
||||
if (!ok)
|
||||
return -EINVAL;
|
||||
|
||||
fck = ctx.dsi_cinfo.clkout[HSDIV_DISPC];
|
||||
} else {
|
||||
ok = dpi_dss_clk_calc(timings->pixelclock, &ctx);
|
||||
if (!ok)
|
||||
return -EINVAL;
|
||||
|
||||
fck = ctx.fck;
|
||||
}
|
||||
|
||||
lck_div = ctx.dispc_cinfo.lck_div;
|
||||
pck_div = ctx.dispc_cinfo.pck_div;
|
||||
|
||||
pck = fck / lck_div / pck_div;
|
||||
|
||||
timings->pixelclock = pck;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dpi_set_data_lines(struct omap_dss_device *dssdev, int data_lines)
|
||||
{
|
||||
struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
|
||||
|
||||
mutex_lock(&dpi->lock);
|
||||
|
||||
dpi->data_lines = data_lines;
|
||||
|
||||
mutex_unlock(&dpi->lock);
|
||||
}
|
||||
|
||||
static int dpi_verify_dsi_pll(struct dss_pll *pll)
|
||||
{
|
||||
int r;
|
||||
|
||||
/* do initial setup with the PLL to see if it is operational */
|
||||
|
||||
r = dss_pll_enable(pll);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
dss_pll_disable(pll);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dpi_init_regulator(struct dpi_data *dpi)
|
||||
{
|
||||
struct regulator *vdds_dsi;
|
||||
|
||||
if (!dss_has_feature(FEAT_DPI_USES_VDDS_DSI))
|
||||
return 0;
|
||||
|
||||
if (dpi->vdds_dsi_reg)
|
||||
return 0;
|
||||
|
||||
vdds_dsi = devm_regulator_get(&dpi->pdev->dev, "vdds_dsi");
|
||||
if (IS_ERR(vdds_dsi)) {
|
||||
if (PTR_ERR(vdds_dsi) != -EPROBE_DEFER)
|
||||
DSSERR("can't get VDDS_DSI regulator\n");
|
||||
return PTR_ERR(vdds_dsi);
|
||||
}
|
||||
|
||||
dpi->vdds_dsi_reg = vdds_dsi;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dpi_init_pll(struct dpi_data *dpi)
|
||||
{
|
||||
struct dss_pll *pll;
|
||||
|
||||
if (dpi->pll)
|
||||
return;
|
||||
|
||||
pll = dpi_get_pll(dpi->output.dispc_channel);
|
||||
if (!pll)
|
||||
return;
|
||||
|
||||
/* On DRA7 we need to set a mux to use the PLL */
|
||||
if (omapdss_get_version() == OMAPDSS_VER_DRA7xx)
|
||||
dss_ctrl_pll_set_control_mux(pll->id, dpi->output.dispc_channel);
|
||||
|
||||
if (dpi_verify_dsi_pll(pll)) {
|
||||
DSSWARN("DSI PLL not operational\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dpi->pll = pll;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a hardcoded channel for the DPI output. This should work for
|
||||
* current use cases, but this can be later expanded to either resolve
|
||||
* the channel in some more dynamic manner, or get the channel as a user
|
||||
* parameter.
|
||||
*/
|
||||
static enum omap_channel dpi_get_channel(int port_num)
|
||||
{
|
||||
switch (omapdss_get_version()) {
|
||||
case OMAPDSS_VER_OMAP24xx:
|
||||
case OMAPDSS_VER_OMAP34xx_ES1:
|
||||
case OMAPDSS_VER_OMAP34xx_ES3:
|
||||
case OMAPDSS_VER_OMAP3630:
|
||||
case OMAPDSS_VER_AM35xx:
|
||||
case OMAPDSS_VER_AM43xx:
|
||||
return OMAP_DSS_CHANNEL_LCD;
|
||||
|
||||
case OMAPDSS_VER_DRA7xx:
|
||||
switch (port_num) {
|
||||
case 2:
|
||||
return OMAP_DSS_CHANNEL_LCD3;
|
||||
case 1:
|
||||
return OMAP_DSS_CHANNEL_LCD2;
|
||||
case 0:
|
||||
default:
|
||||
return OMAP_DSS_CHANNEL_LCD;
|
||||
}
|
||||
|
||||
case OMAPDSS_VER_OMAP4430_ES1:
|
||||
case OMAPDSS_VER_OMAP4430_ES2:
|
||||
case OMAPDSS_VER_OMAP4:
|
||||
return OMAP_DSS_CHANNEL_LCD2;
|
||||
|
||||
case OMAPDSS_VER_OMAP5:
|
||||
return OMAP_DSS_CHANNEL_LCD3;
|
||||
|
||||
default:
|
||||
DSSWARN("unsupported DSS version\n");
|
||||
return OMAP_DSS_CHANNEL_LCD;
|
||||
}
|
||||
}
|
||||
|
||||
static int dpi_connect(struct omap_dss_device *dssdev,
|
||||
struct omap_dss_device *dst)
|
||||
{
|
||||
struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
|
||||
struct omap_overlay_manager *mgr;
|
||||
int r;
|
||||
|
||||
r = dpi_init_regulator(dpi);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
dpi_init_pll(dpi);
|
||||
|
||||
mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel);
|
||||
if (!mgr)
|
||||
return -ENODEV;
|
||||
|
||||
r = dss_mgr_connect(mgr, dssdev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
r = omapdss_output_set_device(dssdev, dst);
|
||||
if (r) {
|
||||
DSSERR("failed to connect output to new device: %s\n",
|
||||
dst->name);
|
||||
dss_mgr_disconnect(mgr, dssdev);
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dpi_disconnect(struct omap_dss_device *dssdev,
|
||||
struct omap_dss_device *dst)
|
||||
{
|
||||
WARN_ON(dst != dssdev->dst);
|
||||
|
||||
if (dst != dssdev->dst)
|
||||
return;
|
||||
|
||||
omapdss_output_unset_device(dssdev);
|
||||
|
||||
if (dssdev->manager)
|
||||
dss_mgr_disconnect(dssdev->manager, dssdev);
|
||||
}
|
||||
|
||||
static const struct omapdss_dpi_ops dpi_ops = {
|
||||
.connect = dpi_connect,
|
||||
.disconnect = dpi_disconnect,
|
||||
|
||||
.enable = dpi_display_enable,
|
||||
.disable = dpi_display_disable,
|
||||
|
||||
.check_timings = dpi_check_timings,
|
||||
.set_timings = dpi_set_timings,
|
||||
.get_timings = dpi_get_timings,
|
||||
|
||||
.set_data_lines = dpi_set_data_lines,
|
||||
};
|
||||
|
||||
static void dpi_init_output(struct platform_device *pdev)
|
||||
{
|
||||
struct dpi_data *dpi = dpi_get_data_from_pdev(pdev);
|
||||
struct omap_dss_device *out = &dpi->output;
|
||||
|
||||
out->dev = &pdev->dev;
|
||||
out->id = OMAP_DSS_OUTPUT_DPI;
|
||||
out->output_type = OMAP_DISPLAY_TYPE_DPI;
|
||||
out->name = "dpi.0";
|
||||
out->dispc_channel = dpi_get_channel(0);
|
||||
out->ops.dpi = &dpi_ops;
|
||||
out->owner = THIS_MODULE;
|
||||
|
||||
omapdss_register_output(out);
|
||||
}
|
||||
|
||||
static void dpi_uninit_output(struct platform_device *pdev)
|
||||
{
|
||||
struct dpi_data *dpi = dpi_get_data_from_pdev(pdev);
|
||||
struct omap_dss_device *out = &dpi->output;
|
||||
|
||||
omapdss_unregister_output(out);
|
||||
}
|
||||
|
||||
static void dpi_init_output_port(struct platform_device *pdev,
|
||||
struct device_node *port)
|
||||
{
|
||||
struct dpi_data *dpi = port->data;
|
||||
struct omap_dss_device *out = &dpi->output;
|
||||
int r;
|
||||
u32 port_num;
|
||||
|
||||
r = of_property_read_u32(port, "reg", &port_num);
|
||||
if (r)
|
||||
port_num = 0;
|
||||
|
||||
switch (port_num) {
|
||||
case 2:
|
||||
out->name = "dpi.2";
|
||||
break;
|
||||
case 1:
|
||||
out->name = "dpi.1";
|
||||
break;
|
||||
case 0:
|
||||
default:
|
||||
out->name = "dpi.0";
|
||||
break;
|
||||
}
|
||||
|
||||
out->dev = &pdev->dev;
|
||||
out->id = OMAP_DSS_OUTPUT_DPI;
|
||||
out->output_type = OMAP_DISPLAY_TYPE_DPI;
|
||||
out->dispc_channel = dpi_get_channel(port_num);
|
||||
out->port_num = port_num;
|
||||
out->ops.dpi = &dpi_ops;
|
||||
out->owner = THIS_MODULE;
|
||||
|
||||
omapdss_register_output(out);
|
||||
}
|
||||
|
||||
static void dpi_uninit_output_port(struct device_node *port)
|
||||
{
|
||||
struct dpi_data *dpi = port->data;
|
||||
struct omap_dss_device *out = &dpi->output;
|
||||
|
||||
omapdss_unregister_output(out);
|
||||
}
|
||||
|
||||
static int dpi_bind(struct device *dev, struct device *master, void *data)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct dpi_data *dpi;
|
||||
|
||||
dpi = devm_kzalloc(&pdev->dev, sizeof(*dpi), GFP_KERNEL);
|
||||
if (!dpi)
|
||||
return -ENOMEM;
|
||||
|
||||
dpi->pdev = pdev;
|
||||
|
||||
dev_set_drvdata(&pdev->dev, dpi);
|
||||
|
||||
mutex_init(&dpi->lock);
|
||||
|
||||
dpi_init_output(pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dpi_unbind(struct device *dev, struct device *master, void *data)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
|
||||
dpi_uninit_output(pdev);
|
||||
}
|
||||
|
||||
static const struct component_ops dpi_component_ops = {
|
||||
.bind = dpi_bind,
|
||||
.unbind = dpi_unbind,
|
||||
};
|
||||
|
||||
static int dpi_probe(struct platform_device *pdev)
|
||||
{
|
||||
return component_add(&pdev->dev, &dpi_component_ops);
|
||||
}
|
||||
|
||||
static int dpi_remove(struct platform_device *pdev)
|
||||
{
|
||||
component_del(&pdev->dev, &dpi_component_ops);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver omap_dpi_driver = {
|
||||
.probe = dpi_probe,
|
||||
.remove = dpi_remove,
|
||||
.driver = {
|
||||
.name = "omapdss_dpi",
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
|
||||
int __init dpi_init_platform_driver(void)
|
||||
{
|
||||
return platform_driver_register(&omap_dpi_driver);
|
||||
}
|
||||
|
||||
void dpi_uninit_platform_driver(void)
|
||||
{
|
||||
platform_driver_unregister(&omap_dpi_driver);
|
||||
}
|
||||
|
||||
int dpi_init_port(struct platform_device *pdev, struct device_node *port)
|
||||
{
|
||||
struct dpi_data *dpi;
|
||||
struct device_node *ep;
|
||||
u32 datalines;
|
||||
int r;
|
||||
|
||||
dpi = devm_kzalloc(&pdev->dev, sizeof(*dpi), GFP_KERNEL);
|
||||
if (!dpi)
|
||||
return -ENOMEM;
|
||||
|
||||
ep = omapdss_of_get_next_endpoint(port, NULL);
|
||||
if (!ep)
|
||||
return 0;
|
||||
|
||||
r = of_property_read_u32(ep, "data-lines", &datalines);
|
||||
if (r) {
|
||||
DSSERR("failed to parse datalines\n");
|
||||
goto err_datalines;
|
||||
}
|
||||
|
||||
dpi->data_lines = datalines;
|
||||
|
||||
of_node_put(ep);
|
||||
|
||||
dpi->pdev = pdev;
|
||||
port->data = dpi;
|
||||
|
||||
mutex_init(&dpi->lock);
|
||||
|
||||
dpi_init_output_port(pdev, port);
|
||||
|
||||
dpi->port_initialized = true;
|
||||
|
||||
return 0;
|
||||
|
||||
err_datalines:
|
||||
of_node_put(ep);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void dpi_uninit_port(struct device_node *port)
|
||||
{
|
||||
struct dpi_data *dpi = port->data;
|
||||
|
||||
if (!dpi->port_initialized)
|
||||
return;
|
||||
|
||||
dpi_uninit_output_port(port);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Texas Instruments
|
||||
* Author: Tomi Valkeinen <tomi.valkeinen@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.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
#include <video/omapdss.h>
|
||||
|
||||
#include "dss.h"
|
||||
|
||||
struct device_node *
|
||||
omapdss_of_get_next_port(const struct device_node *parent,
|
||||
struct device_node *prev)
|
||||
{
|
||||
struct device_node *port = NULL;
|
||||
|
||||
if (!parent)
|
||||
return NULL;
|
||||
|
||||
if (!prev) {
|
||||
struct device_node *ports;
|
||||
/*
|
||||
* It's the first call, we have to find a port subnode
|
||||
* within this node or within an optional 'ports' node.
|
||||
*/
|
||||
ports = of_get_child_by_name(parent, "ports");
|
||||
if (ports)
|
||||
parent = ports;
|
||||
|
||||
port = of_get_child_by_name(parent, "port");
|
||||
|
||||
/* release the 'ports' node */
|
||||
of_node_put(ports);
|
||||
} else {
|
||||
struct device_node *ports;
|
||||
|
||||
ports = of_get_parent(prev);
|
||||
if (!ports)
|
||||
return NULL;
|
||||
|
||||
do {
|
||||
port = of_get_next_child(ports, prev);
|
||||
if (!port) {
|
||||
of_node_put(ports);
|
||||
return NULL;
|
||||
}
|
||||
prev = port;
|
||||
} while (of_node_cmp(port->name, "port") != 0);
|
||||
|
||||
of_node_put(ports);
|
||||
}
|
||||
|
||||
return port;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(omapdss_of_get_next_port);
|
||||
|
||||
struct device_node *
|
||||
omapdss_of_get_next_endpoint(const struct device_node *parent,
|
||||
struct device_node *prev)
|
||||
{
|
||||
struct device_node *ep = NULL;
|
||||
|
||||
if (!parent)
|
||||
return NULL;
|
||||
|
||||
do {
|
||||
ep = of_get_next_child(parent, prev);
|
||||
if (!ep)
|
||||
return NULL;
|
||||
prev = ep;
|
||||
} while (of_node_cmp(ep->name, "endpoint") != 0);
|
||||
|
||||
return ep;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(omapdss_of_get_next_endpoint);
|
||||
|
||||
struct device_node *dss_of_port_get_parent_device(struct device_node *port)
|
||||
{
|
||||
struct device_node *np;
|
||||
int i;
|
||||
|
||||
if (!port)
|
||||
return NULL;
|
||||
|
||||
np = of_get_parent(port);
|
||||
|
||||
for (i = 0; i < 2 && np; ++i) {
|
||||
struct property *prop;
|
||||
|
||||
prop = of_find_property(np, "compatible", NULL);
|
||||
|
||||
if (prop)
|
||||
return np;
|
||||
|
||||
np = of_get_next_parent(np);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
u32 dss_of_port_get_port_number(struct device_node *port)
|
||||
{
|
||||
int r;
|
||||
u32 reg;
|
||||
|
||||
r = of_property_read_u32(port, "reg", ®);
|
||||
if (r)
|
||||
reg = 0;
|
||||
|
||||
return reg;
|
||||
}
|
||||
|
||||
static struct device_node *omapdss_of_get_remote_port(const struct device_node *node)
|
||||
{
|
||||
struct device_node *np;
|
||||
|
||||
np = of_parse_phandle(node, "remote-endpoint", 0);
|
||||
if (!np)
|
||||
return NULL;
|
||||
|
||||
np = of_get_next_parent(np);
|
||||
|
||||
return np;
|
||||
}
|
||||
|
||||
struct device_node *
|
||||
omapdss_of_get_first_endpoint(const struct device_node *parent)
|
||||
{
|
||||
struct device_node *port, *ep;
|
||||
|
||||
port = omapdss_of_get_next_port(parent, NULL);
|
||||
|
||||
if (!port)
|
||||
return NULL;
|
||||
|
||||
ep = omapdss_of_get_next_endpoint(port, NULL);
|
||||
|
||||
of_node_put(port);
|
||||
|
||||
return ep;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(omapdss_of_get_first_endpoint);
|
||||
|
||||
struct omap_dss_device *
|
||||
omapdss_of_find_source_for_first_ep(struct device_node *node)
|
||||
{
|
||||
struct device_node *ep;
|
||||
struct device_node *src_port;
|
||||
struct omap_dss_device *src;
|
||||
|
||||
ep = omapdss_of_get_first_endpoint(node);
|
||||
if (!ep)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
src_port = omapdss_of_get_remote_port(ep);
|
||||
if (!src_port) {
|
||||
of_node_put(ep);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
of_node_put(ep);
|
||||
|
||||
src = omap_dss_find_output_by_port_node(src_port);
|
||||
|
||||
of_node_put(src_port);
|
||||
|
||||
return src ? src : ERR_PTR(-EPROBE_DEFER);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(omapdss_of_find_source_for_first_ep);
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,468 @@
|
|||
/*
|
||||
* linux/drivers/video/omap2/dss/dss.h
|
||||
*
|
||||
* Copyright (C) 2009 Nokia Corporation
|
||||
* Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
|
||||
*
|
||||
* Some code and ideas taken from drivers/video/omap/ driver
|
||||
* by Imre Deak.
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef __OMAP2_DSS_H
|
||||
#define __OMAP2_DSS_H
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#ifdef pr_fmt
|
||||
#undef pr_fmt
|
||||
#endif
|
||||
|
||||
#ifdef DSS_SUBSYS_NAME
|
||||
#define pr_fmt(fmt) DSS_SUBSYS_NAME ": " fmt
|
||||
#else
|
||||
#define pr_fmt(fmt) fmt
|
||||
#endif
|
||||
|
||||
#define DSSDBG(format, ...) \
|
||||
pr_debug(format, ## __VA_ARGS__)
|
||||
|
||||
#ifdef DSS_SUBSYS_NAME
|
||||
#define DSSERR(format, ...) \
|
||||
printk(KERN_ERR "omapdss " DSS_SUBSYS_NAME " error: " format, \
|
||||
## __VA_ARGS__)
|
||||
#else
|
||||
#define DSSERR(format, ...) \
|
||||
printk(KERN_ERR "omapdss error: " format, ## __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#ifdef DSS_SUBSYS_NAME
|
||||
#define DSSINFO(format, ...) \
|
||||
printk(KERN_INFO "omapdss " DSS_SUBSYS_NAME ": " format, \
|
||||
## __VA_ARGS__)
|
||||
#else
|
||||
#define DSSINFO(format, ...) \
|
||||
printk(KERN_INFO "omapdss: " format, ## __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#ifdef DSS_SUBSYS_NAME
|
||||
#define DSSWARN(format, ...) \
|
||||
printk(KERN_WARNING "omapdss " DSS_SUBSYS_NAME ": " format, \
|
||||
## __VA_ARGS__)
|
||||
#else
|
||||
#define DSSWARN(format, ...) \
|
||||
printk(KERN_WARNING "omapdss: " format, ## __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
/* OMAP TRM gives bitfields as start:end, where start is the higher bit
|
||||
number. For example 7:0 */
|
||||
#define FLD_MASK(start, end) (((1 << ((start) - (end) + 1)) - 1) << (end))
|
||||
#define FLD_VAL(val, start, end) (((val) << (end)) & FLD_MASK(start, end))
|
||||
#define FLD_GET(val, start, end) (((val) & FLD_MASK(start, end)) >> (end))
|
||||
#define FLD_MOD(orig, val, start, end) \
|
||||
(((orig) & ~FLD_MASK(start, end)) | FLD_VAL(val, start, end))
|
||||
|
||||
enum dss_io_pad_mode {
|
||||
DSS_IO_PAD_MODE_RESET,
|
||||
DSS_IO_PAD_MODE_RFBI,
|
||||
DSS_IO_PAD_MODE_BYPASS,
|
||||
};
|
||||
|
||||
enum dss_hdmi_venc_clk_source_select {
|
||||
DSS_VENC_TV_CLK = 0,
|
||||
DSS_HDMI_M_PCLK = 1,
|
||||
};
|
||||
|
||||
enum dss_dsi_content_type {
|
||||
DSS_DSI_CONTENT_DCS,
|
||||
DSS_DSI_CONTENT_GENERIC,
|
||||
};
|
||||
|
||||
enum dss_writeback_channel {
|
||||
DSS_WB_LCD1_MGR = 0,
|
||||
DSS_WB_LCD2_MGR = 1,
|
||||
DSS_WB_TV_MGR = 2,
|
||||
DSS_WB_OVL0 = 3,
|
||||
DSS_WB_OVL1 = 4,
|
||||
DSS_WB_OVL2 = 5,
|
||||
DSS_WB_OVL3 = 6,
|
||||
DSS_WB_LCD3_MGR = 7,
|
||||
};
|
||||
|
||||
enum dss_pll_id {
|
||||
DSS_PLL_DSI1,
|
||||
DSS_PLL_DSI2,
|
||||
DSS_PLL_HDMI,
|
||||
DSS_PLL_VIDEO1,
|
||||
DSS_PLL_VIDEO2,
|
||||
};
|
||||
|
||||
struct dss_pll;
|
||||
|
||||
#define DSS_PLL_MAX_HSDIVS 4
|
||||
|
||||
/*
|
||||
* Type-A PLLs: clkout[]/mX[] refer to hsdiv outputs m4, m5, m6, m7.
|
||||
* Type-B PLLs: clkout[0] refers to m2.
|
||||
*/
|
||||
struct dss_pll_clock_info {
|
||||
/* rates that we get with dividers below */
|
||||
unsigned long fint;
|
||||
unsigned long clkdco;
|
||||
unsigned long clkout[DSS_PLL_MAX_HSDIVS];
|
||||
|
||||
/* dividers */
|
||||
u16 n;
|
||||
u16 m;
|
||||
u32 mf;
|
||||
u16 mX[DSS_PLL_MAX_HSDIVS];
|
||||
u16 sd;
|
||||
};
|
||||
|
||||
struct dss_pll_ops {
|
||||
int (*enable)(struct dss_pll *pll);
|
||||
void (*disable)(struct dss_pll *pll);
|
||||
int (*set_config)(struct dss_pll *pll,
|
||||
const struct dss_pll_clock_info *cinfo);
|
||||
};
|
||||
|
||||
struct dss_pll_hw {
|
||||
unsigned n_max;
|
||||
unsigned m_min;
|
||||
unsigned m_max;
|
||||
unsigned mX_max;
|
||||
|
||||
unsigned long fint_min, fint_max;
|
||||
unsigned long clkdco_min, clkdco_low, clkdco_max;
|
||||
|
||||
u8 n_msb, n_lsb;
|
||||
u8 m_msb, m_lsb;
|
||||
u8 mX_msb[DSS_PLL_MAX_HSDIVS], mX_lsb[DSS_PLL_MAX_HSDIVS];
|
||||
|
||||
bool has_stopmode;
|
||||
bool has_freqsel;
|
||||
bool has_selfreqdco;
|
||||
bool has_refsel;
|
||||
};
|
||||
|
||||
struct dss_pll {
|
||||
const char *name;
|
||||
enum dss_pll_id id;
|
||||
|
||||
struct clk *clkin;
|
||||
struct regulator *regulator;
|
||||
|
||||
void __iomem *base;
|
||||
|
||||
const struct dss_pll_hw *hw;
|
||||
|
||||
const struct dss_pll_ops *ops;
|
||||
|
||||
struct dss_pll_clock_info cinfo;
|
||||
};
|
||||
|
||||
struct dispc_clock_info {
|
||||
/* rates that we get with dividers below */
|
||||
unsigned long lck;
|
||||
unsigned long pck;
|
||||
|
||||
/* dividers */
|
||||
u16 lck_div;
|
||||
u16 pck_div;
|
||||
};
|
||||
|
||||
struct dss_lcd_mgr_config {
|
||||
enum dss_io_pad_mode io_pad_mode;
|
||||
|
||||
bool stallmode;
|
||||
bool fifohandcheck;
|
||||
|
||||
struct dispc_clock_info clock_info;
|
||||
|
||||
int video_port_width;
|
||||
|
||||
int lcden_sig_polarity;
|
||||
};
|
||||
|
||||
struct seq_file;
|
||||
struct platform_device;
|
||||
|
||||
/* core */
|
||||
struct platform_device *dss_get_core_pdev(void);
|
||||
int dss_dsi_enable_pads(int dsi_id, unsigned lane_mask);
|
||||
void dss_dsi_disable_pads(int dsi_id, unsigned lane_mask);
|
||||
int dss_set_min_bus_tput(struct device *dev, unsigned long tput);
|
||||
int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *));
|
||||
|
||||
/* display */
|
||||
int dss_suspend_all_devices(void);
|
||||
int dss_resume_all_devices(void);
|
||||
void dss_disable_all_devices(void);
|
||||
|
||||
int display_init_sysfs(struct platform_device *pdev);
|
||||
void display_uninit_sysfs(struct platform_device *pdev);
|
||||
|
||||
/* manager */
|
||||
int dss_init_overlay_managers(void);
|
||||
void dss_uninit_overlay_managers(void);
|
||||
int dss_init_overlay_managers_sysfs(struct platform_device *pdev);
|
||||
void dss_uninit_overlay_managers_sysfs(struct platform_device *pdev);
|
||||
int dss_mgr_simple_check(struct omap_overlay_manager *mgr,
|
||||
const struct omap_overlay_manager_info *info);
|
||||
int dss_mgr_check_timings(struct omap_overlay_manager *mgr,
|
||||
const struct omap_video_timings *timings);
|
||||
int dss_mgr_check(struct omap_overlay_manager *mgr,
|
||||
struct omap_overlay_manager_info *info,
|
||||
const struct omap_video_timings *mgr_timings,
|
||||
const struct dss_lcd_mgr_config *config,
|
||||
struct omap_overlay_info **overlay_infos);
|
||||
|
||||
static inline bool dss_mgr_is_lcd(enum omap_channel id)
|
||||
{
|
||||
if (id == OMAP_DSS_CHANNEL_LCD || id == OMAP_DSS_CHANNEL_LCD2 ||
|
||||
id == OMAP_DSS_CHANNEL_LCD3)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
int dss_manager_kobj_init(struct omap_overlay_manager *mgr,
|
||||
struct platform_device *pdev);
|
||||
void dss_manager_kobj_uninit(struct omap_overlay_manager *mgr);
|
||||
|
||||
/* overlay */
|
||||
void dss_init_overlays(struct platform_device *pdev);
|
||||
void dss_uninit_overlays(struct platform_device *pdev);
|
||||
void dss_overlay_setup_dispc_manager(struct omap_overlay_manager *mgr);
|
||||
int dss_ovl_simple_check(struct omap_overlay *ovl,
|
||||
const struct omap_overlay_info *info);
|
||||
int dss_ovl_check(struct omap_overlay *ovl, struct omap_overlay_info *info,
|
||||
const struct omap_video_timings *mgr_timings);
|
||||
bool dss_ovl_use_replication(struct dss_lcd_mgr_config config,
|
||||
enum omap_color_mode mode);
|
||||
int dss_overlay_kobj_init(struct omap_overlay *ovl,
|
||||
struct platform_device *pdev);
|
||||
void dss_overlay_kobj_uninit(struct omap_overlay *ovl);
|
||||
|
||||
/* DSS */
|
||||
int dss_init_platform_driver(void) __init;
|
||||
void dss_uninit_platform_driver(void);
|
||||
|
||||
int dss_runtime_get(void);
|
||||
void dss_runtime_put(void);
|
||||
|
||||
unsigned long dss_get_dispc_clk_rate(void);
|
||||
int dss_dpi_select_source(int port, enum omap_channel channel);
|
||||
void dss_select_hdmi_venc_clk_source(enum dss_hdmi_venc_clk_source_select);
|
||||
enum dss_hdmi_venc_clk_source_select dss_get_hdmi_venc_clk_source(void);
|
||||
const char *dss_get_generic_clk_source_name(enum omap_dss_clk_source clk_src);
|
||||
void dss_dump_clocks(struct seq_file *s);
|
||||
|
||||
/* DSS VIDEO PLL */
|
||||
struct dss_pll *dss_video_pll_init(struct platform_device *pdev, int id,
|
||||
struct regulator *regulator);
|
||||
void dss_video_pll_uninit(struct dss_pll *pll);
|
||||
|
||||
/* dss-of */
|
||||
struct device_node *dss_of_port_get_parent_device(struct device_node *port);
|
||||
u32 dss_of_port_get_port_number(struct device_node *port);
|
||||
|
||||
#if defined(CONFIG_FB_OMAP2_DSS_DEBUGFS)
|
||||
void dss_debug_dump_clocks(struct seq_file *s);
|
||||
#endif
|
||||
|
||||
void dss_ctrl_pll_enable(enum dss_pll_id pll_id, bool enable);
|
||||
void dss_ctrl_pll_set_control_mux(enum dss_pll_id pll_id,
|
||||
enum omap_channel channel);
|
||||
|
||||
void dss_sdi_init(int datapairs);
|
||||
int dss_sdi_enable(void);
|
||||
void dss_sdi_disable(void);
|
||||
|
||||
void dss_select_dsi_clk_source(int dsi_module,
|
||||
enum omap_dss_clk_source clk_src);
|
||||
void dss_select_lcd_clk_source(enum omap_channel channel,
|
||||
enum omap_dss_clk_source clk_src);
|
||||
enum omap_dss_clk_source dss_get_dispc_clk_source(void);
|
||||
enum omap_dss_clk_source dss_get_dsi_clk_source(int dsi_module);
|
||||
enum omap_dss_clk_source dss_get_lcd_clk_source(enum omap_channel channel);
|
||||
|
||||
void dss_set_venc_output(enum omap_dss_venc_type type);
|
||||
void dss_set_dac_pwrdn_bgz(bool enable);
|
||||
|
||||
int dss_set_fck_rate(unsigned long rate);
|
||||
|
||||
typedef bool (*dss_div_calc_func)(unsigned long fck, void *data);
|
||||
bool dss_div_calc(unsigned long pck, unsigned long fck_min,
|
||||
dss_div_calc_func func, void *data);
|
||||
|
||||
/* SDI */
|
||||
int sdi_init_platform_driver(void) __init;
|
||||
void sdi_uninit_platform_driver(void);
|
||||
|
||||
#ifdef CONFIG_FB_OMAP2_DSS_SDI
|
||||
int sdi_init_port(struct platform_device *pdev, struct device_node *port);
|
||||
void sdi_uninit_port(struct device_node *port);
|
||||
#else
|
||||
static inline int sdi_init_port(struct platform_device *pdev,
|
||||
struct device_node *port)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void sdi_uninit_port(struct device_node *port)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/* DSI */
|
||||
|
||||
#ifdef CONFIG_FB_OMAP2_DSS_DSI
|
||||
|
||||
struct dentry;
|
||||
struct file_operations;
|
||||
|
||||
int dsi_init_platform_driver(void) __init;
|
||||
void dsi_uninit_platform_driver(void);
|
||||
|
||||
void dsi_dump_clocks(struct seq_file *s);
|
||||
|
||||
void dsi_irq_handler(void);
|
||||
u8 dsi_get_pixel_size(enum omap_dss_dsi_pixel_format fmt);
|
||||
|
||||
#else
|
||||
static inline u8 dsi_get_pixel_size(enum omap_dss_dsi_pixel_format fmt)
|
||||
{
|
||||
WARN(1, "%s: DSI not compiled in, returning pixel_size as 0\n",
|
||||
__func__);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* DPI */
|
||||
int dpi_init_platform_driver(void) __init;
|
||||
void dpi_uninit_platform_driver(void);
|
||||
|
||||
#ifdef CONFIG_FB_OMAP2_DSS_DPI
|
||||
int dpi_init_port(struct platform_device *pdev, struct device_node *port);
|
||||
void dpi_uninit_port(struct device_node *port);
|
||||
#else
|
||||
static inline int dpi_init_port(struct platform_device *pdev,
|
||||
struct device_node *port)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void dpi_uninit_port(struct device_node *port)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/* DISPC */
|
||||
int dispc_init_platform_driver(void) __init;
|
||||
void dispc_uninit_platform_driver(void);
|
||||
void dispc_dump_clocks(struct seq_file *s);
|
||||
|
||||
void dispc_enable_sidle(void);
|
||||
void dispc_disable_sidle(void);
|
||||
|
||||
void dispc_lcd_enable_signal(bool enable);
|
||||
void dispc_pck_free_enable(bool enable);
|
||||
void dispc_enable_fifomerge(bool enable);
|
||||
void dispc_enable_gamma_table(bool enable);
|
||||
|
||||
typedef bool (*dispc_div_calc_func)(int lckd, int pckd, unsigned long lck,
|
||||
unsigned long pck, void *data);
|
||||
bool dispc_div_calc(unsigned long dispc,
|
||||
unsigned long pck_min, unsigned long pck_max,
|
||||
dispc_div_calc_func func, void *data);
|
||||
|
||||
bool dispc_mgr_timings_ok(enum omap_channel channel,
|
||||
const struct omap_video_timings *timings);
|
||||
int dispc_calc_clock_rates(unsigned long dispc_fclk_rate,
|
||||
struct dispc_clock_info *cinfo);
|
||||
|
||||
|
||||
void dispc_ovl_set_fifo_threshold(enum omap_plane plane, u32 low, u32 high);
|
||||
void dispc_ovl_compute_fifo_thresholds(enum omap_plane plane,
|
||||
u32 *fifo_low, u32 *fifo_high, bool use_fifomerge,
|
||||
bool manual_update);
|
||||
|
||||
void dispc_mgr_set_clock_div(enum omap_channel channel,
|
||||
const struct dispc_clock_info *cinfo);
|
||||
int dispc_mgr_get_clock_div(enum omap_channel channel,
|
||||
struct dispc_clock_info *cinfo);
|
||||
void dispc_set_tv_pclk(unsigned long pclk);
|
||||
|
||||
u32 dispc_wb_get_framedone_irq(void);
|
||||
bool dispc_wb_go_busy(void);
|
||||
void dispc_wb_go(void);
|
||||
void dispc_wb_enable(bool enable);
|
||||
bool dispc_wb_is_enabled(void);
|
||||
void dispc_wb_set_channel_in(enum dss_writeback_channel channel);
|
||||
int dispc_wb_setup(const struct omap_dss_writeback_info *wi,
|
||||
bool mem_to_mem, const struct omap_video_timings *timings);
|
||||
|
||||
/* VENC */
|
||||
int venc_init_platform_driver(void) __init;
|
||||
void venc_uninit_platform_driver(void);
|
||||
|
||||
/* HDMI */
|
||||
int hdmi4_init_platform_driver(void) __init;
|
||||
void hdmi4_uninit_platform_driver(void);
|
||||
|
||||
int hdmi5_init_platform_driver(void) __init;
|
||||
void hdmi5_uninit_platform_driver(void);
|
||||
|
||||
/* RFBI */
|
||||
int rfbi_init_platform_driver(void) __init;
|
||||
void rfbi_uninit_platform_driver(void);
|
||||
|
||||
|
||||
#ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS
|
||||
static inline void dss_collect_irq_stats(u32 irqstatus, unsigned *irq_arr)
|
||||
{
|
||||
int b;
|
||||
for (b = 0; b < 32; ++b) {
|
||||
if (irqstatus & (1 << b))
|
||||
irq_arr[b]++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* PLL */
|
||||
typedef bool (*dss_pll_calc_func)(int n, int m, unsigned long fint,
|
||||
unsigned long clkdco, void *data);
|
||||
typedef bool (*dss_hsdiv_calc_func)(int m_dispc, unsigned long dispc,
|
||||
void *data);
|
||||
|
||||
int dss_pll_register(struct dss_pll *pll);
|
||||
void dss_pll_unregister(struct dss_pll *pll);
|
||||
struct dss_pll *dss_pll_find(const char *name);
|
||||
int dss_pll_enable(struct dss_pll *pll);
|
||||
void dss_pll_disable(struct dss_pll *pll);
|
||||
int dss_pll_set_config(struct dss_pll *pll,
|
||||
const struct dss_pll_clock_info *cinfo);
|
||||
|
||||
bool dss_pll_hsdiv_calc(const struct dss_pll *pll, unsigned long clkdco,
|
||||
unsigned long out_min, unsigned long out_max,
|
||||
dss_hsdiv_calc_func func, void *data);
|
||||
bool dss_pll_calc(const struct dss_pll *pll, unsigned long clkin,
|
||||
unsigned long pll_min, unsigned long pll_max,
|
||||
dss_pll_calc_func func, void *data);
|
||||
int dss_pll_write_config_type_a(struct dss_pll *pll,
|
||||
const struct dss_pll_clock_info *cinfo);
|
||||
int dss_pll_write_config_type_b(struct dss_pll *pll,
|
||||
const struct dss_pll_clock_info *cinfo);
|
||||
int dss_pll_wait_reset_done(struct dss_pll *pll);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,951 @@
|
|||
/*
|
||||
* linux/drivers/video/omap2/dss/dss_features.c
|
||||
*
|
||||
* Copyright (C) 2010 Texas Instruments
|
||||
* Author: Archit Taneja <archit@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/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <video/omapdss.h>
|
||||
|
||||
#include "dss.h"
|
||||
#include "dss_features.h"
|
||||
|
||||
/* Defines a generic omap register field */
|
||||
struct dss_reg_field {
|
||||
u8 start, end;
|
||||
};
|
||||
|
||||
struct dss_param_range {
|
||||
int min, max;
|
||||
};
|
||||
|
||||
struct omap_dss_features {
|
||||
const struct dss_reg_field *reg_fields;
|
||||
const int num_reg_fields;
|
||||
|
||||
const enum dss_feat_id *features;
|
||||
const int num_features;
|
||||
|
||||
const int num_mgrs;
|
||||
const int num_ovls;
|
||||
const enum omap_display_type *supported_displays;
|
||||
const enum omap_dss_output_id *supported_outputs;
|
||||
const enum omap_color_mode *supported_color_modes;
|
||||
const enum omap_overlay_caps *overlay_caps;
|
||||
const char * const *clksrc_names;
|
||||
const struct dss_param_range *dss_params;
|
||||
|
||||
const enum omap_dss_rotation_type supported_rotation_types;
|
||||
|
||||
const u32 buffer_size_unit;
|
||||
const u32 burst_size_unit;
|
||||
};
|
||||
|
||||
/* This struct is assigned to one of the below during initialization */
|
||||
static const struct omap_dss_features *omap_current_dss_features;
|
||||
|
||||
static const struct dss_reg_field omap2_dss_reg_fields[] = {
|
||||
[FEAT_REG_FIRHINC] = { 11, 0 },
|
||||
[FEAT_REG_FIRVINC] = { 27, 16 },
|
||||
[FEAT_REG_FIFOLOWTHRESHOLD] = { 8, 0 },
|
||||
[FEAT_REG_FIFOHIGHTHRESHOLD] = { 24, 16 },
|
||||
[FEAT_REG_FIFOSIZE] = { 8, 0 },
|
||||
[FEAT_REG_HORIZONTALACCU] = { 9, 0 },
|
||||
[FEAT_REG_VERTICALACCU] = { 25, 16 },
|
||||
[FEAT_REG_DISPC_CLK_SWITCH] = { 0, 0 },
|
||||
};
|
||||
|
||||
static const struct dss_reg_field omap3_dss_reg_fields[] = {
|
||||
[FEAT_REG_FIRHINC] = { 12, 0 },
|
||||
[FEAT_REG_FIRVINC] = { 28, 16 },
|
||||
[FEAT_REG_FIFOLOWTHRESHOLD] = { 11, 0 },
|
||||
[FEAT_REG_FIFOHIGHTHRESHOLD] = { 27, 16 },
|
||||
[FEAT_REG_FIFOSIZE] = { 10, 0 },
|
||||
[FEAT_REG_HORIZONTALACCU] = { 9, 0 },
|
||||
[FEAT_REG_VERTICALACCU] = { 25, 16 },
|
||||
[FEAT_REG_DISPC_CLK_SWITCH] = { 0, 0 },
|
||||
};
|
||||
|
||||
static const struct dss_reg_field am43xx_dss_reg_fields[] = {
|
||||
[FEAT_REG_FIRHINC] = { 12, 0 },
|
||||
[FEAT_REG_FIRVINC] = { 28, 16 },
|
||||
[FEAT_REG_FIFOLOWTHRESHOLD] = { 11, 0 },
|
||||
[FEAT_REG_FIFOHIGHTHRESHOLD] = { 27, 16 },
|
||||
[FEAT_REG_FIFOSIZE] = { 10, 0 },
|
||||
[FEAT_REG_HORIZONTALACCU] = { 9, 0 },
|
||||
[FEAT_REG_VERTICALACCU] = { 25, 16 },
|
||||
[FEAT_REG_DISPC_CLK_SWITCH] = { 0, 0 },
|
||||
};
|
||||
|
||||
static const struct dss_reg_field omap4_dss_reg_fields[] = {
|
||||
[FEAT_REG_FIRHINC] = { 12, 0 },
|
||||
[FEAT_REG_FIRVINC] = { 28, 16 },
|
||||
[FEAT_REG_FIFOLOWTHRESHOLD] = { 15, 0 },
|
||||
[FEAT_REG_FIFOHIGHTHRESHOLD] = { 31, 16 },
|
||||
[FEAT_REG_FIFOSIZE] = { 15, 0 },
|
||||
[FEAT_REG_HORIZONTALACCU] = { 10, 0 },
|
||||
[FEAT_REG_VERTICALACCU] = { 26, 16 },
|
||||
[FEAT_REG_DISPC_CLK_SWITCH] = { 9, 8 },
|
||||
};
|
||||
|
||||
static const struct dss_reg_field omap5_dss_reg_fields[] = {
|
||||
[FEAT_REG_FIRHINC] = { 12, 0 },
|
||||
[FEAT_REG_FIRVINC] = { 28, 16 },
|
||||
[FEAT_REG_FIFOLOWTHRESHOLD] = { 15, 0 },
|
||||
[FEAT_REG_FIFOHIGHTHRESHOLD] = { 31, 16 },
|
||||
[FEAT_REG_FIFOSIZE] = { 15, 0 },
|
||||
[FEAT_REG_HORIZONTALACCU] = { 10, 0 },
|
||||
[FEAT_REG_VERTICALACCU] = { 26, 16 },
|
||||
[FEAT_REG_DISPC_CLK_SWITCH] = { 9, 7 },
|
||||
};
|
||||
|
||||
static const enum omap_display_type omap2_dss_supported_displays[] = {
|
||||
/* OMAP_DSS_CHANNEL_LCD */
|
||||
OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI,
|
||||
|
||||
/* OMAP_DSS_CHANNEL_DIGIT */
|
||||
OMAP_DISPLAY_TYPE_VENC,
|
||||
};
|
||||
|
||||
static const enum omap_display_type omap3430_dss_supported_displays[] = {
|
||||
/* OMAP_DSS_CHANNEL_LCD */
|
||||
OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI |
|
||||
OMAP_DISPLAY_TYPE_SDI | OMAP_DISPLAY_TYPE_DSI,
|
||||
|
||||
/* OMAP_DSS_CHANNEL_DIGIT */
|
||||
OMAP_DISPLAY_TYPE_VENC,
|
||||
};
|
||||
|
||||
static const enum omap_display_type omap3630_dss_supported_displays[] = {
|
||||
/* OMAP_DSS_CHANNEL_LCD */
|
||||
OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI |
|
||||
OMAP_DISPLAY_TYPE_DSI,
|
||||
|
||||
/* OMAP_DSS_CHANNEL_DIGIT */
|
||||
OMAP_DISPLAY_TYPE_VENC,
|
||||
};
|
||||
|
||||
static const enum omap_display_type am43xx_dss_supported_displays[] = {
|
||||
/* OMAP_DSS_CHANNEL_LCD */
|
||||
OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI,
|
||||
};
|
||||
|
||||
static const enum omap_display_type omap4_dss_supported_displays[] = {
|
||||
/* OMAP_DSS_CHANNEL_LCD */
|
||||
OMAP_DISPLAY_TYPE_DBI | OMAP_DISPLAY_TYPE_DSI,
|
||||
|
||||
/* OMAP_DSS_CHANNEL_DIGIT */
|
||||
OMAP_DISPLAY_TYPE_VENC | OMAP_DISPLAY_TYPE_HDMI,
|
||||
|
||||
/* OMAP_DSS_CHANNEL_LCD2 */
|
||||
OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI |
|
||||
OMAP_DISPLAY_TYPE_DSI,
|
||||
};
|
||||
|
||||
static const enum omap_display_type omap5_dss_supported_displays[] = {
|
||||
/* OMAP_DSS_CHANNEL_LCD */
|
||||
OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI |
|
||||
OMAP_DISPLAY_TYPE_DSI,
|
||||
|
||||
/* OMAP_DSS_CHANNEL_DIGIT */
|
||||
OMAP_DISPLAY_TYPE_HDMI | OMAP_DISPLAY_TYPE_DPI,
|
||||
|
||||
/* OMAP_DSS_CHANNEL_LCD2 */
|
||||
OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI |
|
||||
OMAP_DISPLAY_TYPE_DSI,
|
||||
};
|
||||
|
||||
static const enum omap_dss_output_id omap2_dss_supported_outputs[] = {
|
||||
/* OMAP_DSS_CHANNEL_LCD */
|
||||
OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI,
|
||||
|
||||
/* OMAP_DSS_CHANNEL_DIGIT */
|
||||
OMAP_DSS_OUTPUT_VENC,
|
||||
};
|
||||
|
||||
static const enum omap_dss_output_id omap3430_dss_supported_outputs[] = {
|
||||
/* OMAP_DSS_CHANNEL_LCD */
|
||||
OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI |
|
||||
OMAP_DSS_OUTPUT_SDI | OMAP_DSS_OUTPUT_DSI1,
|
||||
|
||||
/* OMAP_DSS_CHANNEL_DIGIT */
|
||||
OMAP_DSS_OUTPUT_VENC,
|
||||
};
|
||||
|
||||
static const enum omap_dss_output_id omap3630_dss_supported_outputs[] = {
|
||||
/* OMAP_DSS_CHANNEL_LCD */
|
||||
OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI |
|
||||
OMAP_DSS_OUTPUT_DSI1,
|
||||
|
||||
/* OMAP_DSS_CHANNEL_DIGIT */
|
||||
OMAP_DSS_OUTPUT_VENC,
|
||||
};
|
||||
|
||||
static const enum omap_dss_output_id am43xx_dss_supported_outputs[] = {
|
||||
/* OMAP_DSS_CHANNEL_LCD */
|
||||
OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI,
|
||||
};
|
||||
|
||||
static const enum omap_dss_output_id omap4_dss_supported_outputs[] = {
|
||||
/* OMAP_DSS_CHANNEL_LCD */
|
||||
OMAP_DSS_OUTPUT_DBI | OMAP_DSS_OUTPUT_DSI1,
|
||||
|
||||
/* OMAP_DSS_CHANNEL_DIGIT */
|
||||
OMAP_DSS_OUTPUT_VENC | OMAP_DSS_OUTPUT_HDMI,
|
||||
|
||||
/* OMAP_DSS_CHANNEL_LCD2 */
|
||||
OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI |
|
||||
OMAP_DSS_OUTPUT_DSI2,
|
||||
};
|
||||
|
||||
static const enum omap_dss_output_id omap5_dss_supported_outputs[] = {
|
||||
/* OMAP_DSS_CHANNEL_LCD */
|
||||
OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI |
|
||||
OMAP_DSS_OUTPUT_DSI1 | OMAP_DSS_OUTPUT_DSI2,
|
||||
|
||||
/* OMAP_DSS_CHANNEL_DIGIT */
|
||||
OMAP_DSS_OUTPUT_HDMI,
|
||||
|
||||
/* OMAP_DSS_CHANNEL_LCD2 */
|
||||
OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI |
|
||||
OMAP_DSS_OUTPUT_DSI1,
|
||||
|
||||
/* OMAP_DSS_CHANNEL_LCD3 */
|
||||
OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI |
|
||||
OMAP_DSS_OUTPUT_DSI2,
|
||||
};
|
||||
|
||||
static const enum omap_color_mode omap2_dss_supported_color_modes[] = {
|
||||
/* OMAP_DSS_GFX */
|
||||
OMAP_DSS_COLOR_CLUT1 | OMAP_DSS_COLOR_CLUT2 |
|
||||
OMAP_DSS_COLOR_CLUT4 | OMAP_DSS_COLOR_CLUT8 |
|
||||
OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_RGB16 |
|
||||
OMAP_DSS_COLOR_RGB24U | OMAP_DSS_COLOR_RGB24P,
|
||||
|
||||
/* OMAP_DSS_VIDEO1 */
|
||||
OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U |
|
||||
OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_YUV2 |
|
||||
OMAP_DSS_COLOR_UYVY,
|
||||
|
||||
/* OMAP_DSS_VIDEO2 */
|
||||
OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U |
|
||||
OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_YUV2 |
|
||||
OMAP_DSS_COLOR_UYVY,
|
||||
};
|
||||
|
||||
static const enum omap_color_mode omap3_dss_supported_color_modes[] = {
|
||||
/* OMAP_DSS_GFX */
|
||||
OMAP_DSS_COLOR_CLUT1 | OMAP_DSS_COLOR_CLUT2 |
|
||||
OMAP_DSS_COLOR_CLUT4 | OMAP_DSS_COLOR_CLUT8 |
|
||||
OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_ARGB16 |
|
||||
OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U |
|
||||
OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_ARGB32 |
|
||||
OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_RGBX32,
|
||||
|
||||
/* OMAP_DSS_VIDEO1 */
|
||||
OMAP_DSS_COLOR_RGB24U | OMAP_DSS_COLOR_RGB24P |
|
||||
OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_RGB16 |
|
||||
OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_UYVY,
|
||||
|
||||
/* OMAP_DSS_VIDEO2 */
|
||||
OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_ARGB16 |
|
||||
OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U |
|
||||
OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_YUV2 |
|
||||
OMAP_DSS_COLOR_UYVY | OMAP_DSS_COLOR_ARGB32 |
|
||||
OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_RGBX32,
|
||||
};
|
||||
|
||||
static const enum omap_color_mode omap4_dss_supported_color_modes[] = {
|
||||
/* OMAP_DSS_GFX */
|
||||
OMAP_DSS_COLOR_CLUT1 | OMAP_DSS_COLOR_CLUT2 |
|
||||
OMAP_DSS_COLOR_CLUT4 | OMAP_DSS_COLOR_CLUT8 |
|
||||
OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_ARGB16 |
|
||||
OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U |
|
||||
OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_ARGB32 |
|
||||
OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_RGBX32 |
|
||||
OMAP_DSS_COLOR_ARGB16_1555 | OMAP_DSS_COLOR_RGBX16 |
|
||||
OMAP_DSS_COLOR_RGBA16 | OMAP_DSS_COLOR_XRGB16_1555,
|
||||
|
||||
/* OMAP_DSS_VIDEO1 */
|
||||
OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB12U |
|
||||
OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_ARGB16_1555 |
|
||||
OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_NV12 |
|
||||
OMAP_DSS_COLOR_RGBA16 | OMAP_DSS_COLOR_RGB24U |
|
||||
OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_UYVY |
|
||||
OMAP_DSS_COLOR_ARGB16 | OMAP_DSS_COLOR_XRGB16_1555 |
|
||||
OMAP_DSS_COLOR_ARGB32 | OMAP_DSS_COLOR_RGBX16 |
|
||||
OMAP_DSS_COLOR_RGBX32,
|
||||
|
||||
/* OMAP_DSS_VIDEO2 */
|
||||
OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB12U |
|
||||
OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_ARGB16_1555 |
|
||||
OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_NV12 |
|
||||
OMAP_DSS_COLOR_RGBA16 | OMAP_DSS_COLOR_RGB24U |
|
||||
OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_UYVY |
|
||||
OMAP_DSS_COLOR_ARGB16 | OMAP_DSS_COLOR_XRGB16_1555 |
|
||||
OMAP_DSS_COLOR_ARGB32 | OMAP_DSS_COLOR_RGBX16 |
|
||||
OMAP_DSS_COLOR_RGBX32,
|
||||
|
||||
/* OMAP_DSS_VIDEO3 */
|
||||
OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB12U |
|
||||
OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_ARGB16_1555 |
|
||||
OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_NV12 |
|
||||
OMAP_DSS_COLOR_RGBA16 | OMAP_DSS_COLOR_RGB24U |
|
||||
OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_UYVY |
|
||||
OMAP_DSS_COLOR_ARGB16 | OMAP_DSS_COLOR_XRGB16_1555 |
|
||||
OMAP_DSS_COLOR_ARGB32 | OMAP_DSS_COLOR_RGBX16 |
|
||||
OMAP_DSS_COLOR_RGBX32,
|
||||
|
||||
/* OMAP_DSS_WB */
|
||||
OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB12U |
|
||||
OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_ARGB16_1555 |
|
||||
OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_NV12 |
|
||||
OMAP_DSS_COLOR_RGBA16 | OMAP_DSS_COLOR_RGB24U |
|
||||
OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_UYVY |
|
||||
OMAP_DSS_COLOR_ARGB16 | OMAP_DSS_COLOR_XRGB16_1555 |
|
||||
OMAP_DSS_COLOR_ARGB32 | OMAP_DSS_COLOR_RGBX16 |
|
||||
OMAP_DSS_COLOR_RGBX32,
|
||||
};
|
||||
|
||||
static const enum omap_overlay_caps omap2_dss_overlay_caps[] = {
|
||||
/* OMAP_DSS_GFX */
|
||||
OMAP_DSS_OVL_CAP_POS | OMAP_DSS_OVL_CAP_REPLICATION,
|
||||
|
||||
/* OMAP_DSS_VIDEO1 */
|
||||
OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_POS |
|
||||
OMAP_DSS_OVL_CAP_REPLICATION,
|
||||
|
||||
/* OMAP_DSS_VIDEO2 */
|
||||
OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_POS |
|
||||
OMAP_DSS_OVL_CAP_REPLICATION,
|
||||
};
|
||||
|
||||
static const enum omap_overlay_caps omap3430_dss_overlay_caps[] = {
|
||||
/* OMAP_DSS_GFX */
|
||||
OMAP_DSS_OVL_CAP_GLOBAL_ALPHA | OMAP_DSS_OVL_CAP_POS |
|
||||
OMAP_DSS_OVL_CAP_REPLICATION,
|
||||
|
||||
/* OMAP_DSS_VIDEO1 */
|
||||
OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_POS |
|
||||
OMAP_DSS_OVL_CAP_REPLICATION,
|
||||
|
||||
/* OMAP_DSS_VIDEO2 */
|
||||
OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_GLOBAL_ALPHA |
|
||||
OMAP_DSS_OVL_CAP_POS | OMAP_DSS_OVL_CAP_REPLICATION,
|
||||
};
|
||||
|
||||
static const enum omap_overlay_caps omap3630_dss_overlay_caps[] = {
|
||||
/* OMAP_DSS_GFX */
|
||||
OMAP_DSS_OVL_CAP_GLOBAL_ALPHA | OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA |
|
||||
OMAP_DSS_OVL_CAP_POS | OMAP_DSS_OVL_CAP_REPLICATION,
|
||||
|
||||
/* OMAP_DSS_VIDEO1 */
|
||||
OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_POS |
|
||||
OMAP_DSS_OVL_CAP_REPLICATION,
|
||||
|
||||
/* OMAP_DSS_VIDEO2 */
|
||||
OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_GLOBAL_ALPHA |
|
||||
OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA | OMAP_DSS_OVL_CAP_POS |
|
||||
OMAP_DSS_OVL_CAP_REPLICATION,
|
||||
};
|
||||
|
||||
static const enum omap_overlay_caps omap4_dss_overlay_caps[] = {
|
||||
/* OMAP_DSS_GFX */
|
||||
OMAP_DSS_OVL_CAP_GLOBAL_ALPHA | OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA |
|
||||
OMAP_DSS_OVL_CAP_ZORDER | OMAP_DSS_OVL_CAP_POS |
|
||||
OMAP_DSS_OVL_CAP_REPLICATION,
|
||||
|
||||
/* OMAP_DSS_VIDEO1 */
|
||||
OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_GLOBAL_ALPHA |
|
||||
OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA | OMAP_DSS_OVL_CAP_ZORDER |
|
||||
OMAP_DSS_OVL_CAP_POS | OMAP_DSS_OVL_CAP_REPLICATION,
|
||||
|
||||
/* OMAP_DSS_VIDEO2 */
|
||||
OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_GLOBAL_ALPHA |
|
||||
OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA | OMAP_DSS_OVL_CAP_ZORDER |
|
||||
OMAP_DSS_OVL_CAP_POS | OMAP_DSS_OVL_CAP_REPLICATION,
|
||||
|
||||
/* OMAP_DSS_VIDEO3 */
|
||||
OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_GLOBAL_ALPHA |
|
||||
OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA | OMAP_DSS_OVL_CAP_ZORDER |
|
||||
OMAP_DSS_OVL_CAP_POS | OMAP_DSS_OVL_CAP_REPLICATION,
|
||||
};
|
||||
|
||||
static const char * const omap2_dss_clk_source_names[] = {
|
||||
[OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC] = "N/A",
|
||||
[OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI] = "N/A",
|
||||
[OMAP_DSS_CLK_SRC_FCK] = "DSS_FCLK1",
|
||||
};
|
||||
|
||||
static const char * const omap3_dss_clk_source_names[] = {
|
||||
[OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC] = "DSI1_PLL_FCLK",
|
||||
[OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI] = "DSI2_PLL_FCLK",
|
||||
[OMAP_DSS_CLK_SRC_FCK] = "DSS1_ALWON_FCLK",
|
||||
};
|
||||
|
||||
static const char * const omap4_dss_clk_source_names[] = {
|
||||
[OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC] = "PLL1_CLK1",
|
||||
[OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI] = "PLL1_CLK2",
|
||||
[OMAP_DSS_CLK_SRC_FCK] = "DSS_FCLK",
|
||||
[OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC] = "PLL2_CLK1",
|
||||
[OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DSI] = "PLL2_CLK2",
|
||||
};
|
||||
|
||||
static const char * const omap5_dss_clk_source_names[] = {
|
||||
[OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC] = "DPLL_DSI1_A_CLK1",
|
||||
[OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI] = "DPLL_DSI1_A_CLK2",
|
||||
[OMAP_DSS_CLK_SRC_FCK] = "DSS_CLK",
|
||||
[OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC] = "DPLL_DSI1_C_CLK1",
|
||||
[OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DSI] = "DPLL_DSI1_C_CLK2",
|
||||
};
|
||||
|
||||
static const struct dss_param_range omap2_dss_param_range[] = {
|
||||
[FEAT_PARAM_DSS_FCK] = { 0, 133000000 },
|
||||
[FEAT_PARAM_DSS_PCD] = { 2, 255 },
|
||||
[FEAT_PARAM_DOWNSCALE] = { 1, 2 },
|
||||
/*
|
||||
* Assuming the line width buffer to be 768 pixels as OMAP2 DISPC
|
||||
* scaler cannot scale a image with width more than 768.
|
||||
*/
|
||||
[FEAT_PARAM_LINEWIDTH] = { 1, 768 },
|
||||
};
|
||||
|
||||
static const struct dss_param_range omap3_dss_param_range[] = {
|
||||
[FEAT_PARAM_DSS_FCK] = { 0, 173000000 },
|
||||
[FEAT_PARAM_DSS_PCD] = { 1, 255 },
|
||||
[FEAT_PARAM_DSIPLL_LPDIV] = { 1, (1 << 13) - 1},
|
||||
[FEAT_PARAM_DSI_FCK] = { 0, 173000000 },
|
||||
[FEAT_PARAM_DOWNSCALE] = { 1, 4 },
|
||||
[FEAT_PARAM_LINEWIDTH] = { 1, 1024 },
|
||||
};
|
||||
|
||||
static const struct dss_param_range am43xx_dss_param_range[] = {
|
||||
[FEAT_PARAM_DSS_FCK] = { 0, 200000000 },
|
||||
[FEAT_PARAM_DSS_PCD] = { 1, 255 },
|
||||
[FEAT_PARAM_DOWNSCALE] = { 1, 4 },
|
||||
[FEAT_PARAM_LINEWIDTH] = { 1, 1024 },
|
||||
};
|
||||
|
||||
static const struct dss_param_range omap4_dss_param_range[] = {
|
||||
[FEAT_PARAM_DSS_FCK] = { 0, 186000000 },
|
||||
[FEAT_PARAM_DSS_PCD] = { 1, 255 },
|
||||
[FEAT_PARAM_DSIPLL_LPDIV] = { 0, (1 << 13) - 1 },
|
||||
[FEAT_PARAM_DSI_FCK] = { 0, 170000000 },
|
||||
[FEAT_PARAM_DOWNSCALE] = { 1, 4 },
|
||||
[FEAT_PARAM_LINEWIDTH] = { 1, 2048 },
|
||||
};
|
||||
|
||||
static const struct dss_param_range omap5_dss_param_range[] = {
|
||||
[FEAT_PARAM_DSS_FCK] = { 0, 209250000 },
|
||||
[FEAT_PARAM_DSS_PCD] = { 1, 255 },
|
||||
[FEAT_PARAM_DSIPLL_LPDIV] = { 0, (1 << 13) - 1 },
|
||||
[FEAT_PARAM_DSI_FCK] = { 0, 209250000 },
|
||||
[FEAT_PARAM_DOWNSCALE] = { 1, 4 },
|
||||
[FEAT_PARAM_LINEWIDTH] = { 1, 2048 },
|
||||
};
|
||||
|
||||
static const enum dss_feat_id omap2_dss_feat_list[] = {
|
||||
FEAT_LCDENABLEPOL,
|
||||
FEAT_LCDENABLESIGNAL,
|
||||
FEAT_PCKFREEENABLE,
|
||||
FEAT_FUNCGATED,
|
||||
FEAT_ROWREPEATENABLE,
|
||||
FEAT_RESIZECONF,
|
||||
};
|
||||
|
||||
static const enum dss_feat_id omap3430_dss_feat_list[] = {
|
||||
FEAT_LCDENABLEPOL,
|
||||
FEAT_LCDENABLESIGNAL,
|
||||
FEAT_PCKFREEENABLE,
|
||||
FEAT_FUNCGATED,
|
||||
FEAT_LINEBUFFERSPLIT,
|
||||
FEAT_ROWREPEATENABLE,
|
||||
FEAT_RESIZECONF,
|
||||
FEAT_DSI_REVERSE_TXCLKESC,
|
||||
FEAT_VENC_REQUIRES_TV_DAC_CLK,
|
||||
FEAT_CPR,
|
||||
FEAT_PRELOAD,
|
||||
FEAT_FIR_COEF_V,
|
||||
FEAT_ALPHA_FIXED_ZORDER,
|
||||
FEAT_FIFO_MERGE,
|
||||
FEAT_OMAP3_DSI_FIFO_BUG,
|
||||
FEAT_DPI_USES_VDDS_DSI,
|
||||
};
|
||||
|
||||
static const enum dss_feat_id am35xx_dss_feat_list[] = {
|
||||
FEAT_LCDENABLEPOL,
|
||||
FEAT_LCDENABLESIGNAL,
|
||||
FEAT_PCKFREEENABLE,
|
||||
FEAT_FUNCGATED,
|
||||
FEAT_LINEBUFFERSPLIT,
|
||||
FEAT_ROWREPEATENABLE,
|
||||
FEAT_RESIZECONF,
|
||||
FEAT_DSI_REVERSE_TXCLKESC,
|
||||
FEAT_VENC_REQUIRES_TV_DAC_CLK,
|
||||
FEAT_CPR,
|
||||
FEAT_PRELOAD,
|
||||
FEAT_FIR_COEF_V,
|
||||
FEAT_ALPHA_FIXED_ZORDER,
|
||||
FEAT_FIFO_MERGE,
|
||||
FEAT_OMAP3_DSI_FIFO_BUG,
|
||||
};
|
||||
|
||||
static const enum dss_feat_id am43xx_dss_feat_list[] = {
|
||||
FEAT_LCDENABLEPOL,
|
||||
FEAT_LCDENABLESIGNAL,
|
||||
FEAT_PCKFREEENABLE,
|
||||
FEAT_FUNCGATED,
|
||||
FEAT_LINEBUFFERSPLIT,
|
||||
FEAT_ROWREPEATENABLE,
|
||||
FEAT_RESIZECONF,
|
||||
FEAT_CPR,
|
||||
FEAT_PRELOAD,
|
||||
FEAT_FIR_COEF_V,
|
||||
FEAT_ALPHA_FIXED_ZORDER,
|
||||
FEAT_FIFO_MERGE,
|
||||
};
|
||||
|
||||
static const enum dss_feat_id omap3630_dss_feat_list[] = {
|
||||
FEAT_LCDENABLEPOL,
|
||||
FEAT_LCDENABLESIGNAL,
|
||||
FEAT_PCKFREEENABLE,
|
||||
FEAT_FUNCGATED,
|
||||
FEAT_LINEBUFFERSPLIT,
|
||||
FEAT_ROWREPEATENABLE,
|
||||
FEAT_RESIZECONF,
|
||||
FEAT_DSI_PLL_PWR_BUG,
|
||||
FEAT_CPR,
|
||||
FEAT_PRELOAD,
|
||||
FEAT_FIR_COEF_V,
|
||||
FEAT_ALPHA_FIXED_ZORDER,
|
||||
FEAT_FIFO_MERGE,
|
||||
FEAT_OMAP3_DSI_FIFO_BUG,
|
||||
FEAT_DPI_USES_VDDS_DSI,
|
||||
};
|
||||
|
||||
static const enum dss_feat_id omap4430_es1_0_dss_feat_list[] = {
|
||||
FEAT_MGR_LCD2,
|
||||
FEAT_CORE_CLK_DIV,
|
||||
FEAT_LCD_CLK_SRC,
|
||||
FEAT_DSI_DCS_CMD_CONFIG_VC,
|
||||
FEAT_DSI_VC_OCP_WIDTH,
|
||||
FEAT_DSI_GNQ,
|
||||
FEAT_HANDLE_UV_SEPARATE,
|
||||
FEAT_ATTR2,
|
||||
FEAT_CPR,
|
||||
FEAT_PRELOAD,
|
||||
FEAT_FIR_COEF_V,
|
||||
FEAT_ALPHA_FREE_ZORDER,
|
||||
FEAT_FIFO_MERGE,
|
||||
FEAT_BURST_2D,
|
||||
};
|
||||
|
||||
static const enum dss_feat_id omap4430_es2_0_1_2_dss_feat_list[] = {
|
||||
FEAT_MGR_LCD2,
|
||||
FEAT_CORE_CLK_DIV,
|
||||
FEAT_LCD_CLK_SRC,
|
||||
FEAT_DSI_DCS_CMD_CONFIG_VC,
|
||||
FEAT_DSI_VC_OCP_WIDTH,
|
||||
FEAT_DSI_GNQ,
|
||||
FEAT_HDMI_CTS_SWMODE,
|
||||
FEAT_HANDLE_UV_SEPARATE,
|
||||
FEAT_ATTR2,
|
||||
FEAT_CPR,
|
||||
FEAT_PRELOAD,
|
||||
FEAT_FIR_COEF_V,
|
||||
FEAT_ALPHA_FREE_ZORDER,
|
||||
FEAT_FIFO_MERGE,
|
||||
FEAT_BURST_2D,
|
||||
};
|
||||
|
||||
static const enum dss_feat_id omap4_dss_feat_list[] = {
|
||||
FEAT_MGR_LCD2,
|
||||
FEAT_CORE_CLK_DIV,
|
||||
FEAT_LCD_CLK_SRC,
|
||||
FEAT_DSI_DCS_CMD_CONFIG_VC,
|
||||
FEAT_DSI_VC_OCP_WIDTH,
|
||||
FEAT_DSI_GNQ,
|
||||
FEAT_HDMI_CTS_SWMODE,
|
||||
FEAT_HDMI_AUDIO_USE_MCLK,
|
||||
FEAT_HANDLE_UV_SEPARATE,
|
||||
FEAT_ATTR2,
|
||||
FEAT_CPR,
|
||||
FEAT_PRELOAD,
|
||||
FEAT_FIR_COEF_V,
|
||||
FEAT_ALPHA_FREE_ZORDER,
|
||||
FEAT_FIFO_MERGE,
|
||||
FEAT_BURST_2D,
|
||||
};
|
||||
|
||||
static const enum dss_feat_id omap5_dss_feat_list[] = {
|
||||
FEAT_MGR_LCD2,
|
||||
FEAT_MGR_LCD3,
|
||||
FEAT_CORE_CLK_DIV,
|
||||
FEAT_LCD_CLK_SRC,
|
||||
FEAT_DSI_DCS_CMD_CONFIG_VC,
|
||||
FEAT_DSI_VC_OCP_WIDTH,
|
||||
FEAT_DSI_GNQ,
|
||||
FEAT_HDMI_CTS_SWMODE,
|
||||
FEAT_HDMI_AUDIO_USE_MCLK,
|
||||
FEAT_HANDLE_UV_SEPARATE,
|
||||
FEAT_ATTR2,
|
||||
FEAT_CPR,
|
||||
FEAT_PRELOAD,
|
||||
FEAT_FIR_COEF_V,
|
||||
FEAT_ALPHA_FREE_ZORDER,
|
||||
FEAT_FIFO_MERGE,
|
||||
FEAT_BURST_2D,
|
||||
FEAT_DSI_PHY_DCC,
|
||||
FEAT_MFLAG,
|
||||
};
|
||||
|
||||
/* OMAP2 DSS Features */
|
||||
static const struct omap_dss_features omap2_dss_features = {
|
||||
.reg_fields = omap2_dss_reg_fields,
|
||||
.num_reg_fields = ARRAY_SIZE(omap2_dss_reg_fields),
|
||||
|
||||
.features = omap2_dss_feat_list,
|
||||
.num_features = ARRAY_SIZE(omap2_dss_feat_list),
|
||||
|
||||
.num_mgrs = 2,
|
||||
.num_ovls = 3,
|
||||
.supported_displays = omap2_dss_supported_displays,
|
||||
.supported_outputs = omap2_dss_supported_outputs,
|
||||
.supported_color_modes = omap2_dss_supported_color_modes,
|
||||
.overlay_caps = omap2_dss_overlay_caps,
|
||||
.clksrc_names = omap2_dss_clk_source_names,
|
||||
.dss_params = omap2_dss_param_range,
|
||||
.supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_VRFB,
|
||||
.buffer_size_unit = 1,
|
||||
.burst_size_unit = 8,
|
||||
};
|
||||
|
||||
/* OMAP3 DSS Features */
|
||||
static const struct omap_dss_features omap3430_dss_features = {
|
||||
.reg_fields = omap3_dss_reg_fields,
|
||||
.num_reg_fields = ARRAY_SIZE(omap3_dss_reg_fields),
|
||||
|
||||
.features = omap3430_dss_feat_list,
|
||||
.num_features = ARRAY_SIZE(omap3430_dss_feat_list),
|
||||
|
||||
.num_mgrs = 2,
|
||||
.num_ovls = 3,
|
||||
.supported_displays = omap3430_dss_supported_displays,
|
||||
.supported_outputs = omap3430_dss_supported_outputs,
|
||||
.supported_color_modes = omap3_dss_supported_color_modes,
|
||||
.overlay_caps = omap3430_dss_overlay_caps,
|
||||
.clksrc_names = omap3_dss_clk_source_names,
|
||||
.dss_params = omap3_dss_param_range,
|
||||
.supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_VRFB,
|
||||
.buffer_size_unit = 1,
|
||||
.burst_size_unit = 8,
|
||||
};
|
||||
|
||||
/*
|
||||
* AM35xx DSS Features. This is basically OMAP3 DSS Features without the
|
||||
* vdds_dsi regulator.
|
||||
*/
|
||||
static const struct omap_dss_features am35xx_dss_features = {
|
||||
.reg_fields = omap3_dss_reg_fields,
|
||||
.num_reg_fields = ARRAY_SIZE(omap3_dss_reg_fields),
|
||||
|
||||
.features = am35xx_dss_feat_list,
|
||||
.num_features = ARRAY_SIZE(am35xx_dss_feat_list),
|
||||
|
||||
.num_mgrs = 2,
|
||||
.num_ovls = 3,
|
||||
.supported_displays = omap3430_dss_supported_displays,
|
||||
.supported_outputs = omap3430_dss_supported_outputs,
|
||||
.supported_color_modes = omap3_dss_supported_color_modes,
|
||||
.overlay_caps = omap3430_dss_overlay_caps,
|
||||
.clksrc_names = omap3_dss_clk_source_names,
|
||||
.dss_params = omap3_dss_param_range,
|
||||
.supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_VRFB,
|
||||
.buffer_size_unit = 1,
|
||||
.burst_size_unit = 8,
|
||||
};
|
||||
|
||||
static const struct omap_dss_features am43xx_dss_features = {
|
||||
.reg_fields = am43xx_dss_reg_fields,
|
||||
.num_reg_fields = ARRAY_SIZE(am43xx_dss_reg_fields),
|
||||
|
||||
.features = am43xx_dss_feat_list,
|
||||
.num_features = ARRAY_SIZE(am43xx_dss_feat_list),
|
||||
|
||||
.num_mgrs = 1,
|
||||
.num_ovls = 3,
|
||||
.supported_displays = am43xx_dss_supported_displays,
|
||||
.supported_outputs = am43xx_dss_supported_outputs,
|
||||
.supported_color_modes = omap3_dss_supported_color_modes,
|
||||
.overlay_caps = omap3430_dss_overlay_caps,
|
||||
.clksrc_names = omap2_dss_clk_source_names,
|
||||
.dss_params = am43xx_dss_param_range,
|
||||
.supported_rotation_types = OMAP_DSS_ROT_DMA,
|
||||
.buffer_size_unit = 1,
|
||||
.burst_size_unit = 8,
|
||||
};
|
||||
|
||||
static const struct omap_dss_features omap3630_dss_features = {
|
||||
.reg_fields = omap3_dss_reg_fields,
|
||||
.num_reg_fields = ARRAY_SIZE(omap3_dss_reg_fields),
|
||||
|
||||
.features = omap3630_dss_feat_list,
|
||||
.num_features = ARRAY_SIZE(omap3630_dss_feat_list),
|
||||
|
||||
.num_mgrs = 2,
|
||||
.num_ovls = 3,
|
||||
.supported_displays = omap3630_dss_supported_displays,
|
||||
.supported_outputs = omap3630_dss_supported_outputs,
|
||||
.supported_color_modes = omap3_dss_supported_color_modes,
|
||||
.overlay_caps = omap3630_dss_overlay_caps,
|
||||
.clksrc_names = omap3_dss_clk_source_names,
|
||||
.dss_params = omap3_dss_param_range,
|
||||
.supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_VRFB,
|
||||
.buffer_size_unit = 1,
|
||||
.burst_size_unit = 8,
|
||||
};
|
||||
|
||||
/* OMAP4 DSS Features */
|
||||
/* For OMAP4430 ES 1.0 revision */
|
||||
static const struct omap_dss_features omap4430_es1_0_dss_features = {
|
||||
.reg_fields = omap4_dss_reg_fields,
|
||||
.num_reg_fields = ARRAY_SIZE(omap4_dss_reg_fields),
|
||||
|
||||
.features = omap4430_es1_0_dss_feat_list,
|
||||
.num_features = ARRAY_SIZE(omap4430_es1_0_dss_feat_list),
|
||||
|
||||
.num_mgrs = 3,
|
||||
.num_ovls = 4,
|
||||
.supported_displays = omap4_dss_supported_displays,
|
||||
.supported_outputs = omap4_dss_supported_outputs,
|
||||
.supported_color_modes = omap4_dss_supported_color_modes,
|
||||
.overlay_caps = omap4_dss_overlay_caps,
|
||||
.clksrc_names = omap4_dss_clk_source_names,
|
||||
.dss_params = omap4_dss_param_range,
|
||||
.supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_TILER,
|
||||
.buffer_size_unit = 16,
|
||||
.burst_size_unit = 16,
|
||||
};
|
||||
|
||||
/* For OMAP4430 ES 2.0, 2.1 and 2.2 revisions */
|
||||
static const struct omap_dss_features omap4430_es2_0_1_2_dss_features = {
|
||||
.reg_fields = omap4_dss_reg_fields,
|
||||
.num_reg_fields = ARRAY_SIZE(omap4_dss_reg_fields),
|
||||
|
||||
.features = omap4430_es2_0_1_2_dss_feat_list,
|
||||
.num_features = ARRAY_SIZE(omap4430_es2_0_1_2_dss_feat_list),
|
||||
|
||||
.num_mgrs = 3,
|
||||
.num_ovls = 4,
|
||||
.supported_displays = omap4_dss_supported_displays,
|
||||
.supported_outputs = omap4_dss_supported_outputs,
|
||||
.supported_color_modes = omap4_dss_supported_color_modes,
|
||||
.overlay_caps = omap4_dss_overlay_caps,
|
||||
.clksrc_names = omap4_dss_clk_source_names,
|
||||
.dss_params = omap4_dss_param_range,
|
||||
.supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_TILER,
|
||||
.buffer_size_unit = 16,
|
||||
.burst_size_unit = 16,
|
||||
};
|
||||
|
||||
/* For all the other OMAP4 versions */
|
||||
static const struct omap_dss_features omap4_dss_features = {
|
||||
.reg_fields = omap4_dss_reg_fields,
|
||||
.num_reg_fields = ARRAY_SIZE(omap4_dss_reg_fields),
|
||||
|
||||
.features = omap4_dss_feat_list,
|
||||
.num_features = ARRAY_SIZE(omap4_dss_feat_list),
|
||||
|
||||
.num_mgrs = 3,
|
||||
.num_ovls = 4,
|
||||
.supported_displays = omap4_dss_supported_displays,
|
||||
.supported_outputs = omap4_dss_supported_outputs,
|
||||
.supported_color_modes = omap4_dss_supported_color_modes,
|
||||
.overlay_caps = omap4_dss_overlay_caps,
|
||||
.clksrc_names = omap4_dss_clk_source_names,
|
||||
.dss_params = omap4_dss_param_range,
|
||||
.supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_TILER,
|
||||
.buffer_size_unit = 16,
|
||||
.burst_size_unit = 16,
|
||||
};
|
||||
|
||||
/* OMAP5 DSS Features */
|
||||
static const struct omap_dss_features omap5_dss_features = {
|
||||
.reg_fields = omap5_dss_reg_fields,
|
||||
.num_reg_fields = ARRAY_SIZE(omap5_dss_reg_fields),
|
||||
|
||||
.features = omap5_dss_feat_list,
|
||||
.num_features = ARRAY_SIZE(omap5_dss_feat_list),
|
||||
|
||||
.num_mgrs = 4,
|
||||
.num_ovls = 4,
|
||||
.supported_displays = omap5_dss_supported_displays,
|
||||
.supported_outputs = omap5_dss_supported_outputs,
|
||||
.supported_color_modes = omap4_dss_supported_color_modes,
|
||||
.overlay_caps = omap4_dss_overlay_caps,
|
||||
.clksrc_names = omap5_dss_clk_source_names,
|
||||
.dss_params = omap5_dss_param_range,
|
||||
.supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_TILER,
|
||||
.buffer_size_unit = 16,
|
||||
.burst_size_unit = 16,
|
||||
};
|
||||
|
||||
/* Functions returning values related to a DSS feature */
|
||||
int dss_feat_get_num_mgrs(void)
|
||||
{
|
||||
return omap_current_dss_features->num_mgrs;
|
||||
}
|
||||
EXPORT_SYMBOL(dss_feat_get_num_mgrs);
|
||||
|
||||
int dss_feat_get_num_ovls(void)
|
||||
{
|
||||
return omap_current_dss_features->num_ovls;
|
||||
}
|
||||
EXPORT_SYMBOL(dss_feat_get_num_ovls);
|
||||
|
||||
unsigned long dss_feat_get_param_min(enum dss_range_param param)
|
||||
{
|
||||
return omap_current_dss_features->dss_params[param].min;
|
||||
}
|
||||
|
||||
unsigned long dss_feat_get_param_max(enum dss_range_param param)
|
||||
{
|
||||
return omap_current_dss_features->dss_params[param].max;
|
||||
}
|
||||
|
||||
enum omap_display_type dss_feat_get_supported_displays(enum omap_channel channel)
|
||||
{
|
||||
return omap_current_dss_features->supported_displays[channel];
|
||||
}
|
||||
|
||||
enum omap_dss_output_id dss_feat_get_supported_outputs(enum omap_channel channel)
|
||||
{
|
||||
return omap_current_dss_features->supported_outputs[channel];
|
||||
}
|
||||
|
||||
enum omap_color_mode dss_feat_get_supported_color_modes(enum omap_plane plane)
|
||||
{
|
||||
return omap_current_dss_features->supported_color_modes[plane];
|
||||
}
|
||||
EXPORT_SYMBOL(dss_feat_get_supported_color_modes);
|
||||
|
||||
enum omap_overlay_caps dss_feat_get_overlay_caps(enum omap_plane plane)
|
||||
{
|
||||
return omap_current_dss_features->overlay_caps[plane];
|
||||
}
|
||||
|
||||
bool dss_feat_color_mode_supported(enum omap_plane plane,
|
||||
enum omap_color_mode color_mode)
|
||||
{
|
||||
return omap_current_dss_features->supported_color_modes[plane] &
|
||||
color_mode;
|
||||
}
|
||||
|
||||
const char *dss_feat_get_clk_source_name(enum omap_dss_clk_source id)
|
||||
{
|
||||
return omap_current_dss_features->clksrc_names[id];
|
||||
}
|
||||
|
||||
u32 dss_feat_get_buffer_size_unit(void)
|
||||
{
|
||||
return omap_current_dss_features->buffer_size_unit;
|
||||
}
|
||||
|
||||
u32 dss_feat_get_burst_size_unit(void)
|
||||
{
|
||||
return omap_current_dss_features->burst_size_unit;
|
||||
}
|
||||
|
||||
/* DSS has_feature check */
|
||||
bool dss_has_feature(enum dss_feat_id id)
|
||||
{
|
||||
int i;
|
||||
const enum dss_feat_id *features = omap_current_dss_features->features;
|
||||
const int num_features = omap_current_dss_features->num_features;
|
||||
|
||||
for (i = 0; i < num_features; i++) {
|
||||
if (features[i] == id)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void dss_feat_get_reg_field(enum dss_feat_reg_field id, u8 *start, u8 *end)
|
||||
{
|
||||
if (id >= omap_current_dss_features->num_reg_fields)
|
||||
BUG();
|
||||
|
||||
*start = omap_current_dss_features->reg_fields[id].start;
|
||||
*end = omap_current_dss_features->reg_fields[id].end;
|
||||
}
|
||||
|
||||
bool dss_feat_rotation_type_supported(enum omap_dss_rotation_type rot_type)
|
||||
{
|
||||
return omap_current_dss_features->supported_rotation_types & rot_type;
|
||||
}
|
||||
|
||||
void dss_features_init(enum omapdss_version version)
|
||||
{
|
||||
switch (version) {
|
||||
case OMAPDSS_VER_OMAP24xx:
|
||||
omap_current_dss_features = &omap2_dss_features;
|
||||
break;
|
||||
|
||||
case OMAPDSS_VER_OMAP34xx_ES1:
|
||||
case OMAPDSS_VER_OMAP34xx_ES3:
|
||||
omap_current_dss_features = &omap3430_dss_features;
|
||||
break;
|
||||
|
||||
case OMAPDSS_VER_OMAP3630:
|
||||
omap_current_dss_features = &omap3630_dss_features;
|
||||
break;
|
||||
|
||||
case OMAPDSS_VER_OMAP4430_ES1:
|
||||
omap_current_dss_features = &omap4430_es1_0_dss_features;
|
||||
break;
|
||||
|
||||
case OMAPDSS_VER_OMAP4430_ES2:
|
||||
omap_current_dss_features = &omap4430_es2_0_1_2_dss_features;
|
||||
break;
|
||||
|
||||
case OMAPDSS_VER_OMAP4:
|
||||
omap_current_dss_features = &omap4_dss_features;
|
||||
break;
|
||||
|
||||
case OMAPDSS_VER_OMAP5:
|
||||
case OMAPDSS_VER_DRA7xx:
|
||||
omap_current_dss_features = &omap5_dss_features;
|
||||
break;
|
||||
|
||||
case OMAPDSS_VER_AM35xx:
|
||||
omap_current_dss_features = &am35xx_dss_features;
|
||||
break;
|
||||
|
||||
case OMAPDSS_VER_AM43xx:
|
||||
omap_current_dss_features = &am43xx_dss_features;
|
||||
break;
|
||||
|
||||
default:
|
||||
DSSWARN("Unsupported OMAP version");
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* linux/drivers/video/omap2/dss/dss_features.h
|
||||
*
|
||||
* Copyright (C) 2010 Texas Instruments
|
||||
* Author: Archit Taneja <archit@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/>.
|
||||
*/
|
||||
|
||||
#ifndef __OMAP2_DSS_FEATURES_H
|
||||
#define __OMAP2_DSS_FEATURES_H
|
||||
|
||||
#define MAX_DSS_MANAGERS 4
|
||||
#define MAX_DSS_OVERLAYS 4
|
||||
#define MAX_DSS_LCD_MANAGERS 3
|
||||
#define MAX_NUM_DSI 2
|
||||
|
||||
/* DSS has feature id */
|
||||
enum dss_feat_id {
|
||||
FEAT_LCDENABLEPOL,
|
||||
FEAT_LCDENABLESIGNAL,
|
||||
FEAT_PCKFREEENABLE,
|
||||
FEAT_FUNCGATED,
|
||||
FEAT_MGR_LCD2,
|
||||
FEAT_MGR_LCD3,
|
||||
FEAT_LINEBUFFERSPLIT,
|
||||
FEAT_ROWREPEATENABLE,
|
||||
FEAT_RESIZECONF,
|
||||
/* Independent core clk divider */
|
||||
FEAT_CORE_CLK_DIV,
|
||||
FEAT_LCD_CLK_SRC,
|
||||
/* DSI-PLL power command 0x3 is not working */
|
||||
FEAT_DSI_PLL_PWR_BUG,
|
||||
FEAT_DSI_DCS_CMD_CONFIG_VC,
|
||||
FEAT_DSI_VC_OCP_WIDTH,
|
||||
FEAT_DSI_REVERSE_TXCLKESC,
|
||||
FEAT_DSI_GNQ,
|
||||
FEAT_DPI_USES_VDDS_DSI,
|
||||
FEAT_HDMI_CTS_SWMODE,
|
||||
FEAT_HDMI_AUDIO_USE_MCLK,
|
||||
FEAT_HANDLE_UV_SEPARATE,
|
||||
FEAT_ATTR2,
|
||||
FEAT_VENC_REQUIRES_TV_DAC_CLK,
|
||||
FEAT_CPR,
|
||||
FEAT_PRELOAD,
|
||||
FEAT_FIR_COEF_V,
|
||||
FEAT_ALPHA_FIXED_ZORDER,
|
||||
FEAT_ALPHA_FREE_ZORDER,
|
||||
FEAT_FIFO_MERGE,
|
||||
/* An unknown HW bug causing the normal FIFO thresholds not to work */
|
||||
FEAT_OMAP3_DSI_FIFO_BUG,
|
||||
FEAT_BURST_2D,
|
||||
FEAT_DSI_PHY_DCC,
|
||||
FEAT_MFLAG,
|
||||
};
|
||||
|
||||
/* DSS register field id */
|
||||
enum dss_feat_reg_field {
|
||||
FEAT_REG_FIRHINC,
|
||||
FEAT_REG_FIRVINC,
|
||||
FEAT_REG_FIFOHIGHTHRESHOLD,
|
||||
FEAT_REG_FIFOLOWTHRESHOLD,
|
||||
FEAT_REG_FIFOSIZE,
|
||||
FEAT_REG_HORIZONTALACCU,
|
||||
FEAT_REG_VERTICALACCU,
|
||||
FEAT_REG_DISPC_CLK_SWITCH,
|
||||
};
|
||||
|
||||
enum dss_range_param {
|
||||
FEAT_PARAM_DSS_FCK,
|
||||
FEAT_PARAM_DSS_PCD,
|
||||
FEAT_PARAM_DSIPLL_LPDIV,
|
||||
FEAT_PARAM_DSI_FCK,
|
||||
FEAT_PARAM_DOWNSCALE,
|
||||
FEAT_PARAM_LINEWIDTH,
|
||||
};
|
||||
|
||||
/* DSS Feature Functions */
|
||||
unsigned long dss_feat_get_param_min(enum dss_range_param param);
|
||||
unsigned long dss_feat_get_param_max(enum dss_range_param param);
|
||||
enum omap_overlay_caps dss_feat_get_overlay_caps(enum omap_plane plane);
|
||||
bool dss_feat_color_mode_supported(enum omap_plane plane,
|
||||
enum omap_color_mode color_mode);
|
||||
const char *dss_feat_get_clk_source_name(enum omap_dss_clk_source id);
|
||||
|
||||
u32 dss_feat_get_buffer_size_unit(void); /* in bytes */
|
||||
u32 dss_feat_get_burst_size_unit(void); /* in bytes */
|
||||
|
||||
bool dss_feat_rotation_type_supported(enum omap_dss_rotation_type rot_type);
|
||||
|
||||
bool dss_has_feature(enum dss_feat_id id);
|
||||
void dss_feat_get_reg_field(enum dss_feat_reg_field id, u8 *start, u8 *end);
|
||||
void dss_features_init(enum omapdss_version version);
|
||||
|
||||
enum omap_display_type dss_feat_get_supported_displays(enum omap_channel channel);
|
||||
enum omap_dss_output_id dss_feat_get_supported_outputs(enum omap_channel channel);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,370 @@
|
|||
/*
|
||||
* HDMI driver definition for TI OMAP4 Processor.
|
||||
*
|
||||
* Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.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/>.
|
||||
*/
|
||||
|
||||
#ifndef _HDMI_H
|
||||
#define _HDMI_H
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/hdmi.h>
|
||||
#include <video/omapdss.h>
|
||||
|
||||
#include "dss.h"
|
||||
|
||||
/* HDMI Wrapper */
|
||||
|
||||
#define HDMI_WP_REVISION 0x0
|
||||
#define HDMI_WP_SYSCONFIG 0x10
|
||||
#define HDMI_WP_IRQSTATUS_RAW 0x24
|
||||
#define HDMI_WP_IRQSTATUS 0x28
|
||||
#define HDMI_WP_IRQENABLE_SET 0x2C
|
||||
#define HDMI_WP_IRQENABLE_CLR 0x30
|
||||
#define HDMI_WP_IRQWAKEEN 0x34
|
||||
#define HDMI_WP_PWR_CTRL 0x40
|
||||
#define HDMI_WP_DEBOUNCE 0x44
|
||||
#define HDMI_WP_VIDEO_CFG 0x50
|
||||
#define HDMI_WP_VIDEO_SIZE 0x60
|
||||
#define HDMI_WP_VIDEO_TIMING_H 0x68
|
||||
#define HDMI_WP_VIDEO_TIMING_V 0x6C
|
||||
#define HDMI_WP_CLK 0x70
|
||||
#define HDMI_WP_AUDIO_CFG 0x80
|
||||
#define HDMI_WP_AUDIO_CFG2 0x84
|
||||
#define HDMI_WP_AUDIO_CTRL 0x88
|
||||
#define HDMI_WP_AUDIO_DATA 0x8C
|
||||
|
||||
/* HDMI WP IRQ flags */
|
||||
#define HDMI_IRQ_CORE (1 << 0)
|
||||
#define HDMI_IRQ_OCP_TIMEOUT (1 << 4)
|
||||
#define HDMI_IRQ_AUDIO_FIFO_UNDERFLOW (1 << 8)
|
||||
#define HDMI_IRQ_AUDIO_FIFO_OVERFLOW (1 << 9)
|
||||
#define HDMI_IRQ_AUDIO_FIFO_SAMPLE_REQ (1 << 10)
|
||||
#define HDMI_IRQ_VIDEO_VSYNC (1 << 16)
|
||||
#define HDMI_IRQ_VIDEO_FRAME_DONE (1 << 17)
|
||||
#define HDMI_IRQ_PHY_LINE5V_ASSERT (1 << 24)
|
||||
#define HDMI_IRQ_LINK_CONNECT (1 << 25)
|
||||
#define HDMI_IRQ_LINK_DISCONNECT (1 << 26)
|
||||
#define HDMI_IRQ_PLL_LOCK (1 << 29)
|
||||
#define HDMI_IRQ_PLL_UNLOCK (1 << 30)
|
||||
#define HDMI_IRQ_PLL_RECAL (1 << 31)
|
||||
|
||||
/* HDMI PLL */
|
||||
|
||||
#define PLLCTRL_PLL_CONTROL 0x0
|
||||
#define PLLCTRL_PLL_STATUS 0x4
|
||||
#define PLLCTRL_PLL_GO 0x8
|
||||
#define PLLCTRL_CFG1 0xC
|
||||
#define PLLCTRL_CFG2 0x10
|
||||
#define PLLCTRL_CFG3 0x14
|
||||
#define PLLCTRL_SSC_CFG1 0x18
|
||||
#define PLLCTRL_SSC_CFG2 0x1C
|
||||
#define PLLCTRL_CFG4 0x20
|
||||
|
||||
/* HDMI PHY */
|
||||
|
||||
#define HDMI_TXPHY_TX_CTRL 0x0
|
||||
#define HDMI_TXPHY_DIGITAL_CTRL 0x4
|
||||
#define HDMI_TXPHY_POWER_CTRL 0x8
|
||||
#define HDMI_TXPHY_PAD_CFG_CTRL 0xC
|
||||
#define HDMI_TXPHY_BIST_CONTROL 0x1C
|
||||
|
||||
enum hdmi_pll_pwr {
|
||||
HDMI_PLLPWRCMD_ALLOFF = 0,
|
||||
HDMI_PLLPWRCMD_PLLONLY = 1,
|
||||
HDMI_PLLPWRCMD_BOTHON_ALLCLKS = 2,
|
||||
HDMI_PLLPWRCMD_BOTHON_NOPHYCLK = 3
|
||||
};
|
||||
|
||||
enum hdmi_phy_pwr {
|
||||
HDMI_PHYPWRCMD_OFF = 0,
|
||||
HDMI_PHYPWRCMD_LDOON = 1,
|
||||
HDMI_PHYPWRCMD_TXON = 2
|
||||
};
|
||||
|
||||
enum hdmi_core_hdmi_dvi {
|
||||
HDMI_DVI = 0,
|
||||
HDMI_HDMI = 1
|
||||
};
|
||||
|
||||
enum hdmi_packing_mode {
|
||||
HDMI_PACK_10b_RGB_YUV444 = 0,
|
||||
HDMI_PACK_24b_RGB_YUV444_YUV422 = 1,
|
||||
HDMI_PACK_20b_YUV422 = 2,
|
||||
HDMI_PACK_ALREADYPACKED = 7
|
||||
};
|
||||
|
||||
enum hdmi_stereo_channels {
|
||||
HDMI_AUDIO_STEREO_NOCHANNELS = 0,
|
||||
HDMI_AUDIO_STEREO_ONECHANNEL = 1,
|
||||
HDMI_AUDIO_STEREO_TWOCHANNELS = 2,
|
||||
HDMI_AUDIO_STEREO_THREECHANNELS = 3,
|
||||
HDMI_AUDIO_STEREO_FOURCHANNELS = 4
|
||||
};
|
||||
|
||||
enum hdmi_audio_type {
|
||||
HDMI_AUDIO_TYPE_LPCM = 0,
|
||||
HDMI_AUDIO_TYPE_IEC = 1
|
||||
};
|
||||
|
||||
enum hdmi_audio_justify {
|
||||
HDMI_AUDIO_JUSTIFY_LEFT = 0,
|
||||
HDMI_AUDIO_JUSTIFY_RIGHT = 1
|
||||
};
|
||||
|
||||
enum hdmi_audio_sample_order {
|
||||
HDMI_AUDIO_SAMPLE_RIGHT_FIRST = 0,
|
||||
HDMI_AUDIO_SAMPLE_LEFT_FIRST = 1
|
||||
};
|
||||
|
||||
enum hdmi_audio_samples_perword {
|
||||
HDMI_AUDIO_ONEWORD_ONESAMPLE = 0,
|
||||
HDMI_AUDIO_ONEWORD_TWOSAMPLES = 1
|
||||
};
|
||||
|
||||
enum hdmi_audio_sample_size_omap {
|
||||
HDMI_AUDIO_SAMPLE_16BITS = 0,
|
||||
HDMI_AUDIO_SAMPLE_24BITS = 1
|
||||
};
|
||||
|
||||
enum hdmi_audio_transf_mode {
|
||||
HDMI_AUDIO_TRANSF_DMA = 0,
|
||||
HDMI_AUDIO_TRANSF_IRQ = 1
|
||||
};
|
||||
|
||||
enum hdmi_audio_blk_strt_end_sig {
|
||||
HDMI_AUDIO_BLOCK_SIG_STARTEND_ON = 0,
|
||||
HDMI_AUDIO_BLOCK_SIG_STARTEND_OFF = 1
|
||||
};
|
||||
|
||||
enum hdmi_core_audio_layout {
|
||||
HDMI_AUDIO_LAYOUT_2CH = 0,
|
||||
HDMI_AUDIO_LAYOUT_8CH = 1,
|
||||
HDMI_AUDIO_LAYOUT_6CH = 2
|
||||
};
|
||||
|
||||
enum hdmi_core_cts_mode {
|
||||
HDMI_AUDIO_CTS_MODE_HW = 0,
|
||||
HDMI_AUDIO_CTS_MODE_SW = 1
|
||||
};
|
||||
|
||||
enum hdmi_audio_mclk_mode {
|
||||
HDMI_AUDIO_MCLK_128FS = 0,
|
||||
HDMI_AUDIO_MCLK_256FS = 1,
|
||||
HDMI_AUDIO_MCLK_384FS = 2,
|
||||
HDMI_AUDIO_MCLK_512FS = 3,
|
||||
HDMI_AUDIO_MCLK_768FS = 4,
|
||||
HDMI_AUDIO_MCLK_1024FS = 5,
|
||||
HDMI_AUDIO_MCLK_1152FS = 6,
|
||||
HDMI_AUDIO_MCLK_192FS = 7
|
||||
};
|
||||
|
||||
struct hdmi_video_format {
|
||||
enum hdmi_packing_mode packing_mode;
|
||||
u32 y_res; /* Line per panel */
|
||||
u32 x_res; /* pixel per line */
|
||||
};
|
||||
|
||||
struct hdmi_config {
|
||||
struct omap_video_timings timings;
|
||||
struct hdmi_avi_infoframe infoframe;
|
||||
enum hdmi_core_hdmi_dvi hdmi_dvi_mode;
|
||||
};
|
||||
|
||||
struct hdmi_audio_format {
|
||||
enum hdmi_stereo_channels stereo_channels;
|
||||
u8 active_chnnls_msk;
|
||||
enum hdmi_audio_type type;
|
||||
enum hdmi_audio_justify justification;
|
||||
enum hdmi_audio_sample_order sample_order;
|
||||
enum hdmi_audio_samples_perword samples_per_word;
|
||||
enum hdmi_audio_sample_size_omap sample_size;
|
||||
enum hdmi_audio_blk_strt_end_sig en_sig_blk_strt_end;
|
||||
};
|
||||
|
||||
struct hdmi_audio_dma {
|
||||
u8 transfer_size;
|
||||
u8 block_size;
|
||||
enum hdmi_audio_transf_mode mode;
|
||||
u16 fifo_threshold;
|
||||
};
|
||||
|
||||
struct hdmi_core_audio_i2s_config {
|
||||
u8 in_length_bits;
|
||||
u8 justification;
|
||||
u8 sck_edge_mode;
|
||||
u8 vbit;
|
||||
u8 direction;
|
||||
u8 shift;
|
||||
u8 active_sds;
|
||||
};
|
||||
|
||||
struct hdmi_core_audio_config {
|
||||
struct hdmi_core_audio_i2s_config i2s_cfg;
|
||||
struct snd_aes_iec958 *iec60958_cfg;
|
||||
bool fs_override;
|
||||
u32 n;
|
||||
u32 cts;
|
||||
u32 aud_par_busclk;
|
||||
enum hdmi_core_audio_layout layout;
|
||||
enum hdmi_core_cts_mode cts_mode;
|
||||
bool use_mclk;
|
||||
enum hdmi_audio_mclk_mode mclk_mode;
|
||||
bool en_acr_pkt;
|
||||
bool en_dsd_audio;
|
||||
bool en_parallel_aud_input;
|
||||
bool en_spdif;
|
||||
};
|
||||
|
||||
struct hdmi_wp_data {
|
||||
void __iomem *base;
|
||||
phys_addr_t phys_base;
|
||||
};
|
||||
|
||||
struct hdmi_pll_data {
|
||||
struct dss_pll pll;
|
||||
|
||||
void __iomem *base;
|
||||
|
||||
struct hdmi_wp_data *wp;
|
||||
};
|
||||
|
||||
struct hdmi_phy_data {
|
||||
void __iomem *base;
|
||||
|
||||
u8 lane_function[4];
|
||||
u8 lane_polarity[4];
|
||||
};
|
||||
|
||||
struct hdmi_core_data {
|
||||
void __iomem *base;
|
||||
};
|
||||
|
||||
static inline void hdmi_write_reg(void __iomem *base_addr, const u32 idx,
|
||||
u32 val)
|
||||
{
|
||||
__raw_writel(val, base_addr + idx);
|
||||
}
|
||||
|
||||
static inline u32 hdmi_read_reg(void __iomem *base_addr, const u32 idx)
|
||||
{
|
||||
return __raw_readl(base_addr + idx);
|
||||
}
|
||||
|
||||
#define REG_FLD_MOD(base, idx, val, start, end) \
|
||||
hdmi_write_reg(base, idx, FLD_MOD(hdmi_read_reg(base, idx),\
|
||||
val, start, end))
|
||||
#define REG_GET(base, idx, start, end) \
|
||||
FLD_GET(hdmi_read_reg(base, idx), start, end)
|
||||
|
||||
static inline int hdmi_wait_for_bit_change(void __iomem *base_addr,
|
||||
const u32 idx, int b2, int b1, u32 val)
|
||||
{
|
||||
u32 t = 0, v;
|
||||
while (val != (v = REG_GET(base_addr, idx, b2, b1))) {
|
||||
if (t++ > 10000)
|
||||
return v;
|
||||
udelay(1);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/* HDMI wrapper funcs */
|
||||
int hdmi_wp_video_start(struct hdmi_wp_data *wp);
|
||||
void hdmi_wp_video_stop(struct hdmi_wp_data *wp);
|
||||
void hdmi_wp_dump(struct hdmi_wp_data *wp, struct seq_file *s);
|
||||
u32 hdmi_wp_get_irqstatus(struct hdmi_wp_data *wp);
|
||||
void hdmi_wp_set_irqstatus(struct hdmi_wp_data *wp, u32 irqstatus);
|
||||
void hdmi_wp_set_irqenable(struct hdmi_wp_data *wp, u32 mask);
|
||||
void hdmi_wp_clear_irqenable(struct hdmi_wp_data *wp, u32 mask);
|
||||
int hdmi_wp_set_phy_pwr(struct hdmi_wp_data *wp, enum hdmi_phy_pwr val);
|
||||
int hdmi_wp_set_pll_pwr(struct hdmi_wp_data *wp, enum hdmi_pll_pwr val);
|
||||
void hdmi_wp_video_config_format(struct hdmi_wp_data *wp,
|
||||
struct hdmi_video_format *video_fmt);
|
||||
void hdmi_wp_video_config_interface(struct hdmi_wp_data *wp,
|
||||
struct omap_video_timings *timings);
|
||||
void hdmi_wp_video_config_timing(struct hdmi_wp_data *wp,
|
||||
struct omap_video_timings *timings);
|
||||
void hdmi_wp_init_vid_fmt_timings(struct hdmi_video_format *video_fmt,
|
||||
struct omap_video_timings *timings, struct hdmi_config *param);
|
||||
int hdmi_wp_init(struct platform_device *pdev, struct hdmi_wp_data *wp);
|
||||
phys_addr_t hdmi_wp_get_audio_dma_addr(struct hdmi_wp_data *wp);
|
||||
|
||||
/* HDMI PLL funcs */
|
||||
void hdmi_pll_dump(struct hdmi_pll_data *pll, struct seq_file *s);
|
||||
void hdmi_pll_compute(struct hdmi_pll_data *pll,
|
||||
unsigned long target_tmds, struct dss_pll_clock_info *pi);
|
||||
int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll,
|
||||
struct hdmi_wp_data *wp);
|
||||
void hdmi_pll_uninit(struct hdmi_pll_data *hpll);
|
||||
|
||||
/* HDMI PHY funcs */
|
||||
int hdmi_phy_configure(struct hdmi_phy_data *phy, unsigned long hfbitclk,
|
||||
unsigned long lfbitclk);
|
||||
void hdmi_phy_dump(struct hdmi_phy_data *phy, struct seq_file *s);
|
||||
int hdmi_phy_init(struct platform_device *pdev, struct hdmi_phy_data *phy);
|
||||
int hdmi_phy_parse_lanes(struct hdmi_phy_data *phy, const u32 *lanes);
|
||||
|
||||
/* HDMI common funcs */
|
||||
int hdmi_parse_lanes_of(struct platform_device *pdev, struct device_node *ep,
|
||||
struct hdmi_phy_data *phy);
|
||||
|
||||
/* Audio funcs */
|
||||
int hdmi_compute_acr(u32 pclk, u32 sample_freq, u32 *n, u32 *cts);
|
||||
int hdmi_wp_audio_enable(struct hdmi_wp_data *wp, bool enable);
|
||||
int hdmi_wp_audio_core_req_enable(struct hdmi_wp_data *wp, bool enable);
|
||||
void hdmi_wp_audio_config_format(struct hdmi_wp_data *wp,
|
||||
struct hdmi_audio_format *aud_fmt);
|
||||
void hdmi_wp_audio_config_dma(struct hdmi_wp_data *wp,
|
||||
struct hdmi_audio_dma *aud_dma);
|
||||
static inline bool hdmi_mode_has_audio(struct hdmi_config *cfg)
|
||||
{
|
||||
return cfg->hdmi_dvi_mode == HDMI_HDMI ? true : false;
|
||||
}
|
||||
|
||||
/* HDMI DRV data */
|
||||
struct omap_hdmi {
|
||||
struct mutex lock;
|
||||
struct platform_device *pdev;
|
||||
|
||||
struct hdmi_wp_data wp;
|
||||
struct hdmi_pll_data pll;
|
||||
struct hdmi_phy_data phy;
|
||||
struct hdmi_core_data core;
|
||||
|
||||
struct hdmi_config cfg;
|
||||
|
||||
struct regulator *vdda_reg;
|
||||
|
||||
bool core_enabled;
|
||||
|
||||
struct omap_dss_device output;
|
||||
|
||||
struct platform_device *audio_pdev;
|
||||
void (*audio_abort_cb)(struct device *dev);
|
||||
int wp_idlemode;
|
||||
|
||||
bool audio_configured;
|
||||
struct omap_dss_audio audio_config;
|
||||
|
||||
/* This lock should be taken when booleans bellow are touched. */
|
||||
spinlock_t audio_playing_lock;
|
||||
bool audio_playing;
|
||||
bool display_enabled;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,839 @@
|
|||
/*
|
||||
* HDMI interface DSS driver for TI's OMAP4 family of SoCs.
|
||||
* Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Authors: Yong Zhi
|
||||
* Mythri pk <mythripk@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/>.
|
||||
*/
|
||||
|
||||
#define DSS_SUBSYS_NAME "HDMI"
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/component.h>
|
||||
#include <video/omapdss.h>
|
||||
#include <sound/omap-hdmi-audio.h>
|
||||
|
||||
#include "hdmi4_core.h"
|
||||
#include "dss.h"
|
||||
#include "dss_features.h"
|
||||
#include "hdmi.h"
|
||||
|
||||
static struct omap_hdmi hdmi;
|
||||
|
||||
static int hdmi_runtime_get(void)
|
||||
{
|
||||
int r;
|
||||
|
||||
DSSDBG("hdmi_runtime_get\n");
|
||||
|
||||
r = pm_runtime_get_sync(&hdmi.pdev->dev);
|
||||
WARN_ON(r < 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hdmi_runtime_put(void)
|
||||
{
|
||||
int r;
|
||||
|
||||
DSSDBG("hdmi_runtime_put\n");
|
||||
|
||||
r = pm_runtime_put_sync(&hdmi.pdev->dev);
|
||||
WARN_ON(r < 0 && r != -ENOSYS);
|
||||
}
|
||||
|
||||
static irqreturn_t hdmi_irq_handler(int irq, void *data)
|
||||
{
|
||||
struct hdmi_wp_data *wp = data;
|
||||
u32 irqstatus;
|
||||
|
||||
irqstatus = hdmi_wp_get_irqstatus(wp);
|
||||
hdmi_wp_set_irqstatus(wp, irqstatus);
|
||||
|
||||
if ((irqstatus & HDMI_IRQ_LINK_CONNECT) &&
|
||||
irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
|
||||
/*
|
||||
* If we get both connect and disconnect interrupts at the same
|
||||
* time, turn off the PHY, clear interrupts, and restart, which
|
||||
* raises connect interrupt if a cable is connected, or nothing
|
||||
* if cable is not connected.
|
||||
*/
|
||||
hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF);
|
||||
|
||||
hdmi_wp_set_irqstatus(wp, HDMI_IRQ_LINK_CONNECT |
|
||||
HDMI_IRQ_LINK_DISCONNECT);
|
||||
|
||||
hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
|
||||
} else if (irqstatus & HDMI_IRQ_LINK_CONNECT) {
|
||||
hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_TXON);
|
||||
} else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
|
||||
hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int hdmi_init_regulator(void)
|
||||
{
|
||||
int r;
|
||||
struct regulator *reg;
|
||||
|
||||
if (hdmi.vdda_reg != NULL)
|
||||
return 0;
|
||||
|
||||
reg = devm_regulator_get(&hdmi.pdev->dev, "vdda");
|
||||
|
||||
if (IS_ERR(reg)) {
|
||||
if (PTR_ERR(reg) != -EPROBE_DEFER)
|
||||
DSSERR("can't get VDDA regulator\n");
|
||||
return PTR_ERR(reg);
|
||||
}
|
||||
|
||||
if (regulator_can_change_voltage(reg)) {
|
||||
r = regulator_set_voltage(reg, 1800000, 1800000);
|
||||
if (r) {
|
||||
devm_regulator_put(reg);
|
||||
DSSWARN("can't set the regulator voltage\n");
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
hdmi.vdda_reg = reg;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdmi_power_on_core(struct omap_dss_device *dssdev)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = regulator_enable(hdmi.vdda_reg);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
r = hdmi_runtime_get();
|
||||
if (r)
|
||||
goto err_runtime_get;
|
||||
|
||||
/* Make selection of HDMI in DSS */
|
||||
dss_select_hdmi_venc_clk_source(DSS_HDMI_M_PCLK);
|
||||
|
||||
hdmi.core_enabled = true;
|
||||
|
||||
return 0;
|
||||
|
||||
err_runtime_get:
|
||||
regulator_disable(hdmi.vdda_reg);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void hdmi_power_off_core(struct omap_dss_device *dssdev)
|
||||
{
|
||||
hdmi.core_enabled = false;
|
||||
|
||||
hdmi_runtime_put();
|
||||
regulator_disable(hdmi.vdda_reg);
|
||||
}
|
||||
|
||||
static int hdmi_power_on_full(struct omap_dss_device *dssdev)
|
||||
{
|
||||
int r;
|
||||
struct omap_video_timings *p;
|
||||
struct omap_overlay_manager *mgr = hdmi.output.manager;
|
||||
struct hdmi_wp_data *wp = &hdmi.wp;
|
||||
struct dss_pll_clock_info hdmi_cinfo = { 0 };
|
||||
|
||||
r = hdmi_power_on_core(dssdev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
/* disable and clear irqs */
|
||||
hdmi_wp_clear_irqenable(wp, 0xffffffff);
|
||||
hdmi_wp_set_irqstatus(wp, 0xffffffff);
|
||||
|
||||
p = &hdmi.cfg.timings;
|
||||
|
||||
DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res);
|
||||
|
||||
hdmi_pll_compute(&hdmi.pll, p->pixelclock, &hdmi_cinfo);
|
||||
|
||||
r = dss_pll_enable(&hdmi.pll.pll);
|
||||
if (r) {
|
||||
DSSERR("Failed to enable PLL\n");
|
||||
goto err_pll_enable;
|
||||
}
|
||||
|
||||
r = dss_pll_set_config(&hdmi.pll.pll, &hdmi_cinfo);
|
||||
if (r) {
|
||||
DSSERR("Failed to configure PLL\n");
|
||||
goto err_pll_cfg;
|
||||
}
|
||||
|
||||
r = hdmi_phy_configure(&hdmi.phy, hdmi_cinfo.clkdco,
|
||||
hdmi_cinfo.clkout[0]);
|
||||
if (r) {
|
||||
DSSDBG("Failed to configure PHY\n");
|
||||
goto err_phy_cfg;
|
||||
}
|
||||
|
||||
r = hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
|
||||
if (r)
|
||||
goto err_phy_pwr;
|
||||
|
||||
hdmi4_configure(&hdmi.core, &hdmi.wp, &hdmi.cfg);
|
||||
|
||||
/* bypass TV gamma table */
|
||||
dispc_enable_gamma_table(0);
|
||||
|
||||
/* tv size */
|
||||
dss_mgr_set_timings(mgr, p);
|
||||
|
||||
r = hdmi_wp_video_start(&hdmi.wp);
|
||||
if (r)
|
||||
goto err_vid_enable;
|
||||
|
||||
r = dss_mgr_enable(mgr);
|
||||
if (r)
|
||||
goto err_mgr_enable;
|
||||
|
||||
hdmi_wp_set_irqenable(wp,
|
||||
HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT);
|
||||
|
||||
return 0;
|
||||
|
||||
err_mgr_enable:
|
||||
hdmi_wp_video_stop(&hdmi.wp);
|
||||
err_vid_enable:
|
||||
hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF);
|
||||
err_phy_pwr:
|
||||
err_phy_cfg:
|
||||
err_pll_cfg:
|
||||
dss_pll_disable(&hdmi.pll.pll);
|
||||
err_pll_enable:
|
||||
hdmi_power_off_core(dssdev);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static void hdmi_power_off_full(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct omap_overlay_manager *mgr = hdmi.output.manager;
|
||||
|
||||
hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff);
|
||||
|
||||
dss_mgr_disable(mgr);
|
||||
|
||||
hdmi_wp_video_stop(&hdmi.wp);
|
||||
|
||||
hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF);
|
||||
|
||||
dss_pll_disable(&hdmi.pll.pll);
|
||||
|
||||
hdmi_power_off_core(dssdev);
|
||||
}
|
||||
|
||||
static int hdmi_display_check_timing(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
struct omap_dss_device *out = &hdmi.output;
|
||||
|
||||
if (!dispc_mgr_timings_ok(out->dispc_channel, timings))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hdmi_display_set_timing(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
mutex_lock(&hdmi.lock);
|
||||
|
||||
hdmi.cfg.timings = *timings;
|
||||
|
||||
dispc_set_tv_pclk(timings->pixelclock);
|
||||
|
||||
mutex_unlock(&hdmi.lock);
|
||||
}
|
||||
|
||||
static void hdmi_display_get_timings(struct omap_dss_device *dssdev,
|
||||
struct omap_video_timings *timings)
|
||||
{
|
||||
*timings = hdmi.cfg.timings;
|
||||
}
|
||||
|
||||
static void hdmi_dump_regs(struct seq_file *s)
|
||||
{
|
||||
mutex_lock(&hdmi.lock);
|
||||
|
||||
if (hdmi_runtime_get()) {
|
||||
mutex_unlock(&hdmi.lock);
|
||||
return;
|
||||
}
|
||||
|
||||
hdmi_wp_dump(&hdmi.wp, s);
|
||||
hdmi_pll_dump(&hdmi.pll, s);
|
||||
hdmi_phy_dump(&hdmi.phy, s);
|
||||
hdmi4_core_dump(&hdmi.core, s);
|
||||
|
||||
hdmi_runtime_put();
|
||||
mutex_unlock(&hdmi.lock);
|
||||
}
|
||||
|
||||
static int read_edid(u8 *buf, int len)
|
||||
{
|
||||
int r;
|
||||
|
||||
mutex_lock(&hdmi.lock);
|
||||
|
||||
r = hdmi_runtime_get();
|
||||
BUG_ON(r);
|
||||
|
||||
r = hdmi4_read_edid(&hdmi.core, buf, len);
|
||||
|
||||
hdmi_runtime_put();
|
||||
mutex_unlock(&hdmi.lock);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void hdmi_start_audio_stream(struct omap_hdmi *hd)
|
||||
{
|
||||
hdmi_wp_audio_enable(&hd->wp, true);
|
||||
hdmi4_audio_start(&hd->core, &hd->wp);
|
||||
}
|
||||
|
||||
static void hdmi_stop_audio_stream(struct omap_hdmi *hd)
|
||||
{
|
||||
hdmi4_audio_stop(&hd->core, &hd->wp);
|
||||
hdmi_wp_audio_enable(&hd->wp, false);
|
||||
}
|
||||
|
||||
static int hdmi_display_enable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct omap_dss_device *out = &hdmi.output;
|
||||
unsigned long flags;
|
||||
int r = 0;
|
||||
|
||||
DSSDBG("ENTER hdmi_display_enable\n");
|
||||
|
||||
mutex_lock(&hdmi.lock);
|
||||
|
||||
if (out->manager == NULL) {
|
||||
DSSERR("failed to enable display: no output/manager\n");
|
||||
r = -ENODEV;
|
||||
goto err0;
|
||||
}
|
||||
|
||||
r = hdmi_power_on_full(dssdev);
|
||||
if (r) {
|
||||
DSSERR("failed to power on device\n");
|
||||
goto err0;
|
||||
}
|
||||
|
||||
if (hdmi.audio_configured) {
|
||||
r = hdmi4_audio_config(&hdmi.core, &hdmi.wp, &hdmi.audio_config,
|
||||
hdmi.cfg.timings.pixelclock);
|
||||
if (r) {
|
||||
DSSERR("Error restoring audio configuration: %d", r);
|
||||
hdmi.audio_abort_cb(&hdmi.pdev->dev);
|
||||
hdmi.audio_configured = false;
|
||||
}
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&hdmi.audio_playing_lock, flags);
|
||||
if (hdmi.audio_configured && hdmi.audio_playing)
|
||||
hdmi_start_audio_stream(&hdmi);
|
||||
hdmi.display_enabled = true;
|
||||
spin_unlock_irqrestore(&hdmi.audio_playing_lock, flags);
|
||||
|
||||
mutex_unlock(&hdmi.lock);
|
||||
return 0;
|
||||
|
||||
err0:
|
||||
mutex_unlock(&hdmi.lock);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void hdmi_display_disable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
DSSDBG("Enter hdmi_display_disable\n");
|
||||
|
||||
mutex_lock(&hdmi.lock);
|
||||
|
||||
spin_lock_irqsave(&hdmi.audio_playing_lock, flags);
|
||||
hdmi_stop_audio_stream(&hdmi);
|
||||
hdmi.display_enabled = false;
|
||||
spin_unlock_irqrestore(&hdmi.audio_playing_lock, flags);
|
||||
|
||||
hdmi_power_off_full(dssdev);
|
||||
|
||||
mutex_unlock(&hdmi.lock);
|
||||
}
|
||||
|
||||
static int hdmi_core_enable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
int r = 0;
|
||||
|
||||
DSSDBG("ENTER omapdss_hdmi_core_enable\n");
|
||||
|
||||
mutex_lock(&hdmi.lock);
|
||||
|
||||
r = hdmi_power_on_core(dssdev);
|
||||
if (r) {
|
||||
DSSERR("failed to power on device\n");
|
||||
goto err0;
|
||||
}
|
||||
|
||||
mutex_unlock(&hdmi.lock);
|
||||
return 0;
|
||||
|
||||
err0:
|
||||
mutex_unlock(&hdmi.lock);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void hdmi_core_disable(struct omap_dss_device *dssdev)
|
||||
{
|
||||
DSSDBG("Enter omapdss_hdmi_core_disable\n");
|
||||
|
||||
mutex_lock(&hdmi.lock);
|
||||
|
||||
hdmi_power_off_core(dssdev);
|
||||
|
||||
mutex_unlock(&hdmi.lock);
|
||||
}
|
||||
|
||||
static int hdmi_connect(struct omap_dss_device *dssdev,
|
||||
struct omap_dss_device *dst)
|
||||
{
|
||||
struct omap_overlay_manager *mgr;
|
||||
int r;
|
||||
|
||||
r = hdmi_init_regulator();
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel);
|
||||
if (!mgr)
|
||||
return -ENODEV;
|
||||
|
||||
r = dss_mgr_connect(mgr, dssdev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
r = omapdss_output_set_device(dssdev, dst);
|
||||
if (r) {
|
||||
DSSERR("failed to connect output to new device: %s\n",
|
||||
dst->name);
|
||||
dss_mgr_disconnect(mgr, dssdev);
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hdmi_disconnect(struct omap_dss_device *dssdev,
|
||||
struct omap_dss_device *dst)
|
||||
{
|
||||
WARN_ON(dst != dssdev->dst);
|
||||
|
||||
if (dst != dssdev->dst)
|
||||
return;
|
||||
|
||||
omapdss_output_unset_device(dssdev);
|
||||
|
||||
if (dssdev->manager)
|
||||
dss_mgr_disconnect(dssdev->manager, dssdev);
|
||||
}
|
||||
|
||||
static int hdmi_read_edid(struct omap_dss_device *dssdev,
|
||||
u8 *edid, int len)
|
||||
{
|
||||
bool need_enable;
|
||||
int r;
|
||||
|
||||
need_enable = hdmi.core_enabled == false;
|
||||
|
||||
if (need_enable) {
|
||||
r = hdmi_core_enable(dssdev);
|
||||
if (r)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = read_edid(edid, len);
|
||||
|
||||
if (need_enable)
|
||||
hdmi_core_disable(dssdev);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int hdmi_set_infoframe(struct omap_dss_device *dssdev,
|
||||
const struct hdmi_avi_infoframe *avi)
|
||||
{
|
||||
hdmi.cfg.infoframe = *avi;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdmi_set_hdmi_mode(struct omap_dss_device *dssdev,
|
||||
bool hdmi_mode)
|
||||
{
|
||||
hdmi.cfg.hdmi_dvi_mode = hdmi_mode ? HDMI_HDMI : HDMI_DVI;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct omapdss_hdmi_ops hdmi_ops = {
|
||||
.connect = hdmi_connect,
|
||||
.disconnect = hdmi_disconnect,
|
||||
|
||||
.enable = hdmi_display_enable,
|
||||
.disable = hdmi_display_disable,
|
||||
|
||||
.check_timings = hdmi_display_check_timing,
|
||||
.set_timings = hdmi_display_set_timing,
|
||||
.get_timings = hdmi_display_get_timings,
|
||||
|
||||
.read_edid = hdmi_read_edid,
|
||||
.set_infoframe = hdmi_set_infoframe,
|
||||
.set_hdmi_mode = hdmi_set_hdmi_mode,
|
||||
};
|
||||
|
||||
static void hdmi_init_output(struct platform_device *pdev)
|
||||
{
|
||||
struct omap_dss_device *out = &hdmi.output;
|
||||
|
||||
out->dev = &pdev->dev;
|
||||
out->id = OMAP_DSS_OUTPUT_HDMI;
|
||||
out->output_type = OMAP_DISPLAY_TYPE_HDMI;
|
||||
out->name = "hdmi.0";
|
||||
out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT;
|
||||
out->ops.hdmi = &hdmi_ops;
|
||||
out->owner = THIS_MODULE;
|
||||
|
||||
omapdss_register_output(out);
|
||||
}
|
||||
|
||||
static void hdmi_uninit_output(struct platform_device *pdev)
|
||||
{
|
||||
struct omap_dss_device *out = &hdmi.output;
|
||||
|
||||
omapdss_unregister_output(out);
|
||||
}
|
||||
|
||||
static int hdmi_probe_of(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct device_node *ep;
|
||||
int r;
|
||||
|
||||
ep = omapdss_of_get_first_endpoint(node);
|
||||
if (!ep)
|
||||
return 0;
|
||||
|
||||
r = hdmi_parse_lanes_of(pdev, ep, &hdmi.phy);
|
||||
if (r)
|
||||
goto err;
|
||||
|
||||
of_node_put(ep);
|
||||
return 0;
|
||||
|
||||
err:
|
||||
of_node_put(ep);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Audio callbacks */
|
||||
static int hdmi_audio_startup(struct device *dev,
|
||||
void (*abort_cb)(struct device *dev))
|
||||
{
|
||||
struct omap_hdmi *hd = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&hd->lock);
|
||||
|
||||
if (!hdmi_mode_has_audio(&hd->cfg) || !hd->display_enabled) {
|
||||
ret = -EPERM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
hd->audio_abort_cb = abort_cb;
|
||||
|
||||
out:
|
||||
mutex_unlock(&hd->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hdmi_audio_shutdown(struct device *dev)
|
||||
{
|
||||
struct omap_hdmi *hd = dev_get_drvdata(dev);
|
||||
|
||||
mutex_lock(&hd->lock);
|
||||
hd->audio_abort_cb = NULL;
|
||||
hd->audio_configured = false;
|
||||
hd->audio_playing = false;
|
||||
mutex_unlock(&hd->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdmi_audio_start(struct device *dev)
|
||||
{
|
||||
struct omap_hdmi *hd = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
|
||||
WARN_ON(!hdmi_mode_has_audio(&hd->cfg));
|
||||
|
||||
spin_lock_irqsave(&hd->audio_playing_lock, flags);
|
||||
|
||||
if (hd->display_enabled)
|
||||
hdmi_start_audio_stream(hd);
|
||||
hd->audio_playing = true;
|
||||
|
||||
spin_unlock_irqrestore(&hd->audio_playing_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hdmi_audio_stop(struct device *dev)
|
||||
{
|
||||
struct omap_hdmi *hd = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
|
||||
WARN_ON(!hdmi_mode_has_audio(&hd->cfg));
|
||||
|
||||
spin_lock_irqsave(&hd->audio_playing_lock, flags);
|
||||
|
||||
if (hd->display_enabled)
|
||||
hdmi_stop_audio_stream(hd);
|
||||
hd->audio_playing = false;
|
||||
|
||||
spin_unlock_irqrestore(&hd->audio_playing_lock, flags);
|
||||
}
|
||||
|
||||
static int hdmi_audio_config(struct device *dev,
|
||||
struct omap_dss_audio *dss_audio)
|
||||
{
|
||||
struct omap_hdmi *hd = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&hd->lock);
|
||||
|
||||
if (!hdmi_mode_has_audio(&hd->cfg) || !hd->display_enabled) {
|
||||
ret = -EPERM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = hdmi4_audio_config(&hd->core, &hd->wp, dss_audio,
|
||||
hd->cfg.timings.pixelclock);
|
||||
if (!ret) {
|
||||
hd->audio_configured = true;
|
||||
hd->audio_config = *dss_audio;
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&hd->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct omap_hdmi_audio_ops hdmi_audio_ops = {
|
||||
.audio_startup = hdmi_audio_startup,
|
||||
.audio_shutdown = hdmi_audio_shutdown,
|
||||
.audio_start = hdmi_audio_start,
|
||||
.audio_stop = hdmi_audio_stop,
|
||||
.audio_config = hdmi_audio_config,
|
||||
};
|
||||
|
||||
static int hdmi_audio_register(struct device *dev)
|
||||
{
|
||||
struct omap_hdmi_audio_pdata pdata = {
|
||||
.dev = dev,
|
||||
.dss_version = omapdss_get_version(),
|
||||
.audio_dma_addr = hdmi_wp_get_audio_dma_addr(&hdmi.wp),
|
||||
.ops = &hdmi_audio_ops,
|
||||
};
|
||||
|
||||
hdmi.audio_pdev = platform_device_register_data(
|
||||
dev, "omap-hdmi-audio", PLATFORM_DEVID_AUTO,
|
||||
&pdata, sizeof(pdata));
|
||||
|
||||
if (IS_ERR(hdmi.audio_pdev))
|
||||
return PTR_ERR(hdmi.audio_pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* HDMI HW IP initialisation */
|
||||
static int hdmi4_bind(struct device *dev, struct device *master, void *data)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
int r;
|
||||
int irq;
|
||||
|
||||
hdmi.pdev = pdev;
|
||||
dev_set_drvdata(&pdev->dev, &hdmi);
|
||||
|
||||
mutex_init(&hdmi.lock);
|
||||
spin_lock_init(&hdmi.audio_playing_lock);
|
||||
|
||||
if (pdev->dev.of_node) {
|
||||
r = hdmi_probe_of(pdev);
|
||||
if (r)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = hdmi_wp_init(pdev, &hdmi.wp);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
r = hdmi_pll_init(pdev, &hdmi.pll, &hdmi.wp);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
r = hdmi_phy_init(pdev, &hdmi.phy);
|
||||
if (r)
|
||||
goto err;
|
||||
|
||||
r = hdmi4_core_init(pdev, &hdmi.core);
|
||||
if (r)
|
||||
goto err;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
DSSERR("platform_get_irq failed\n");
|
||||
r = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
r = devm_request_threaded_irq(&pdev->dev, irq,
|
||||
NULL, hdmi_irq_handler,
|
||||
IRQF_ONESHOT, "OMAP HDMI", &hdmi.wp);
|
||||
if (r) {
|
||||
DSSERR("HDMI IRQ request failed\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
hdmi_init_output(pdev);
|
||||
|
||||
r = hdmi_audio_register(&pdev->dev);
|
||||
if (r) {
|
||||
DSSERR("Registering HDMI audio failed\n");
|
||||
hdmi_uninit_output(pdev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
return r;
|
||||
}
|
||||
|
||||
dss_debugfs_create_file("hdmi", hdmi_dump_regs);
|
||||
|
||||
return 0;
|
||||
err:
|
||||
hdmi_pll_uninit(&hdmi.pll);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void hdmi4_unbind(struct device *dev, struct device *master, void *data)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
|
||||
if (hdmi.audio_pdev)
|
||||
platform_device_unregister(hdmi.audio_pdev);
|
||||
|
||||
hdmi_uninit_output(pdev);
|
||||
|
||||
hdmi_pll_uninit(&hdmi.pll);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
}
|
||||
|
||||
static const struct component_ops hdmi4_component_ops = {
|
||||
.bind = hdmi4_bind,
|
||||
.unbind = hdmi4_unbind,
|
||||
};
|
||||
|
||||
static int hdmi4_probe(struct platform_device *pdev)
|
||||
{
|
||||
return component_add(&pdev->dev, &hdmi4_component_ops);
|
||||
}
|
||||
|
||||
static int hdmi4_remove(struct platform_device *pdev)
|
||||
{
|
||||
component_del(&pdev->dev, &hdmi4_component_ops);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdmi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
dispc_runtime_put();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdmi_runtime_resume(struct device *dev)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = dispc_runtime_get();
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops hdmi_pm_ops = {
|
||||
.runtime_suspend = hdmi_runtime_suspend,
|
||||
.runtime_resume = hdmi_runtime_resume,
|
||||
};
|
||||
|
||||
static const struct of_device_id hdmi_of_match[] = {
|
||||
{ .compatible = "ti,omap4-hdmi", },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver omapdss_hdmihw_driver = {
|
||||
.probe = hdmi4_probe,
|
||||
.remove = hdmi4_remove,
|
||||
.driver = {
|
||||
.name = "omapdss_hdmi",
|
||||
.pm = &hdmi_pm_ops,
|
||||
.of_match_table = hdmi_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
|
||||
int __init hdmi4_init_platform_driver(void)
|
||||
{
|
||||
return platform_driver_register(&omapdss_hdmihw_driver);
|
||||
}
|
||||
|
||||
void hdmi4_uninit_platform_driver(void)
|
||||
{
|
||||
platform_driver_unregister(&omapdss_hdmihw_driver);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue