Commit b75bf98b authored by Jean-Christophe PLAGNIOL-VILLARD's avatar Jean-Christophe PLAGNIOL-VILLARD

Merge tag 'omapdss-for-3.11-2' of git://gitorious.org/linux-omap-dss2/linux into fbdev/for-next

OMAP display subsystem changes for 3.11 (part 2/2)

This is the second part of OMAP DSS changes for 3.11. This part contains the
new DSS device model support.

The current OMAP panel drivers use a custom DSS bus, and there's a hard limit
of one external display block per video pipeline. In the new DSS device model
the devices/drivers are made according to the control bus of the display block,
usually platform, i2c or spi. The display blocks can also be chained so that we
can have separate drivers for setups with both external encoder and panel.

To allow the current board files, which use the old style panels, to function,
the old display drivers are left in their current state, and new ones are added
to drivers/video/omap2/displays-new/. When the board files have been converted
to use the new style panels, we can remove the old code. This is planned to
happen in v3.12.

Having to support two very different DSS device models makes the driver
somewhat confusing in some parts, and prevents us from properly cleaning up
some other parts. These cleanups will be done when the old code is removed.

The new device model is designed with CDF (Common Display Framework) in mind.
While CDF is still under work, the new DSS device model should be much more
similar to CDF's model than the old device model, which should make the
eventual conversion to CDF much easier.
parents 8e980455 c545b595
......@@ -65,10 +65,8 @@ static int get_connector_type(struct omap_dss_device *dssdev)
switch (dssdev->type) {
case OMAP_DISPLAY_TYPE_HDMI:
return DRM_MODE_CONNECTOR_HDMIA;
case OMAP_DISPLAY_TYPE_DPI:
if (!strcmp(dssdev->name, "dvi"))
return DRM_MODE_CONNECTOR_DVID;
/* fallthrough */
case OMAP_DISPLAY_TYPE_DVI:
return DRM_MODE_CONNECTOR_DVID;
default:
return DRM_MODE_CONNECTOR_Unknown;
}
......
......@@ -6,5 +6,6 @@ if ARCH_OMAP2PLUS
source "drivers/video/omap2/dss/Kconfig"
source "drivers/video/omap2/omapfb/Kconfig"
source "drivers/video/omap2/displays/Kconfig"
source "drivers/video/omap2/displays-new/Kconfig"
endif
......@@ -2,4 +2,5 @@ obj-$(CONFIG_OMAP2_VRFB) += vrfb.o
obj-$(CONFIG_OMAP2_DSS) += dss/
obj-y += displays/
obj-y += displays-new/
obj-$(CONFIG_FB_OMAP2) += omapfb/
menu "OMAP Display Device Drivers (new device model)"
depends on OMAP2_DSS
config DISPLAY_ENCODER_TFP410
tristate "TFP410 DPI to DVI Encoder"
help
Driver for TFP410 DPI to DVI encoder.
config DISPLAY_ENCODER_TPD12S015
tristate "TPD12S015 HDMI ESD protection and level shifter"
help
Driver for TPD12S015, which offers HDMI ESD protection and level
shifting.
config DISPLAY_CONNECTOR_DVI
tristate "DVI Connector"
depends on I2C
help
Driver for a generic DVI connector.
config DISPLAY_CONNECTOR_HDMI
tristate "HDMI Connector"
help
Driver for a generic HDMI connector.
config DISPLAY_CONNECTOR_ANALOG_TV
tristate "Analog TV Connector"
help
Driver for a generic analog TV connector.
config DISPLAY_PANEL_DPI
tristate "Generic DPI panel"
help
Driver for generic DPI panels.
config DISPLAY_PANEL_DSI_CM
tristate "Generic DSI Command Mode Panel"
help
Driver for generic DSI command mode panels.
config DISPLAY_PANEL_SONY_ACX565AKM
tristate "ACX565AKM Panel"
depends on SPI && BACKLIGHT_CLASS_DEVICE
help
This is the LCD panel used on Nokia N900
config DISPLAY_PANEL_LGPHILIPS_LB035Q02
tristate "LG.Philips LB035Q02 LCD Panel"
depends on SPI
help
LCD Panel used on the Gumstix Overo Palo35
config DISPLAY_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 DISPLAY_PANEL_TPO_TD043MTEA1
tristate "TPO TD043MTEA1 LCD Panel"
depends on SPI
help
LCD Panel used in OMAP3 Pandora
config DISPLAY_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
obj-$(CONFIG_DISPLAY_ENCODER_TFP410) += encoder-tfp410.o
obj-$(CONFIG_DISPLAY_ENCODER_TPD12S015) += encoder-tpd12s015.o
obj-$(CONFIG_DISPLAY_CONNECTOR_DVI) += connector-dvi.o
obj-$(CONFIG_DISPLAY_CONNECTOR_HDMI) += connector-hdmi.o
obj-$(CONFIG_DISPLAY_CONNECTOR_ANALOG_TV) += connector-analog-tv.o
obj-$(CONFIG_DISPLAY_PANEL_DPI) += panel-dpi.o
obj-$(CONFIG_DISPLAY_PANEL_DSI_CM) += panel-dsi-cm.o
obj-$(CONFIG_DISPLAY_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o
obj-$(CONFIG_DISPLAY_PANEL_LGPHILIPS_LB035Q02) += panel-lgphilips-lb035q02.o
obj-$(CONFIG_DISPLAY_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o
obj-$(CONFIG_DISPLAY_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o
obj-$(CONFIG_DISPLAY_PANEL_NEC_NL8048HL11) += panel-nec-nl8048hl11.o
/*
* 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 <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;
};
#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);
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 -ENODEV;
}
ddata->in = in;
ddata->connector_type = pdata->connector_type;
ddata->invert_polarity = ddata->invert_polarity;
dssdev = &ddata->dssdev;
dssdev->name = pdata->name;
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 {
return -ENODEV;
}
ddata->timings = omap_dss_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 = omap_dss_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 struct platform_driver tvc_connector_driver = {
.probe = tvc_probe,
.remove = __exit_p(tvc_remove),
.driver = {
.name = "connector-analog-tv",
.owner = THIS_MODULE,
},
};
module_platform_driver(tvc_connector_driver);
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
MODULE_DESCRIPTION("Analog TV Connector driver");
MODULE_LICENSE("GPL");
/*
* 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,
.pixel_clock = 23500,
.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_OPPOSITE_EDGES,
};
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) {
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 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 {
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);
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);
if (ddata->i2c_adapter)
i2c_put_adapter(ddata->i2c_adapter);
return 0;
}
static struct platform_driver dvi_connector_driver = {
.probe = dvic_probe,
.remove = __exit_p(dvic_remove),
.driver = {
.name = "connector-dvi",
.owner = THIS_MODULE,
},
};
module_platform_driver(dvi_connector_driver);
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
MODULE_DESCRIPTION("Generic DVI Connector driver");
MODULE_LICENSE("GPL");
/*
* 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 <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,
.pixel_clock = 25175,
.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;
};
#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;
return in->ops.hdmi->detect(in);
}
static int hdmic_audio_enable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
int r;
/* enable audio only if the display is active */
if (!omapdss_device_is_enabled(dssdev))
return -EPERM;
r = in->ops.hdmi->audio_enable(in);
if (r)
return r;
dssdev->audio_state = OMAP_DSS_AUDIO_ENABLED;
return 0;
}
static void hdmic_audio_disable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
in->ops.hdmi->audio_disable(in);
dssdev->audio_state = OMAP_DSS_AUDIO_DISABLED;
}
static int hdmic_audio_start(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
int r;
/*
* No need to check the panel state. It was checked when trasitioning
* to AUDIO_ENABLED.
*/
if (dssdev->audio_state != OMAP_DSS_AUDIO_ENABLED)
return -EPERM;
r = in->ops.hdmi->audio_start(in);
if (r)
return r;
dssdev->audio_state = OMAP_DSS_AUDIO_PLAYING;
return 0;
}
static void hdmic_audio_stop(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
in->ops.hdmi->audio_stop(in);
dssdev->audio_state = OMAP_DSS_AUDIO_ENABLED;
}
static bool hdmic_audio_supported(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 false;
return in->ops.hdmi->audio_supported(in);
}
static int hdmic_audio_config(struct omap_dss_device *dssdev,
struct omap_dss_audio *audio)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
int r;
/* config audio only if the display is active */
if (!omapdss_device_is_enabled(dssdev))
return -EPERM;
r = in->ops.hdmi->audio_config(in, audio);
if (r)
return r;
dssdev->audio_state = OMAP_DSS_AUDIO_CONFIGURED;
return 0;
}
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,
.audio_enable = hdmic_audio_enable,
.audio_disable = hdmic_audio_disable,
.audio_start = hdmic_audio_start,
.audio_stop = hdmic_audio_stop,
.audio_supported = hdmic_audio_supported,
.audio_config = hdmic_audio_config,
};
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);
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 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 {
return -ENODEV;
}
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 struct platform_driver hdmi_connector_driver = {
.probe = hdmic_probe,
.remove = __exit_p(hdmic_remove),
.driver = {
.name = "connector-hdmi",
.owner = THIS_MODULE,
},
};
module_platform_driver(hdmi_connector_driver);
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
MODULE_DESCRIPTION("HDMI Connector driver");
MODULE_LICENSE("GPL");
/*
* 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 <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->output = dssdev;
dssdev->device = 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->device);
if (dst != dssdev->device)
return;
dst->output = NULL;
dssdev->device = 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);
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_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.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;
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(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 {
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;
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->device);
omap_dss_put_device(in);
return 0;
}
static struct platform_driver tfp410_driver = {
.probe = tfp410_probe,
.remove = __exit_p(tfp410_remove),
.driver = {
.name = "tfp410",
.owner = THIS_MODULE,
},
};
module_platform_driver(tfp410_driver);
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
MODULE_DESCRIPTION("TFP410 DPI to DVI encoder driver");
MODULE_LICENSE("GPL");
/*
* 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/gpio.h>
#include <linux/platform_device.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 ct_cp_hpd_gpio;
int ls_oe_gpio;
int hpd_gpio;
struct omap_video_timings timings;
struct completion hpd_completion;
};
#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
static irqreturn_t tpd_hpd_irq_handler(int irq, void *data)
{
struct panel_drv_data *ddata = data;
bool hpd;
hpd = gpio_get_value_cansleep(ddata->hpd_gpio);
dev_dbg(ddata->dssdev.dev, "hpd %d\n", hpd);
if (gpio_is_valid(ddata->ls_oe_gpio)) {
if (hpd)
gpio_set_value_cansleep(ddata->ls_oe_gpio, 1);
else
gpio_set_value_cansleep(ddata->ls_oe_gpio, 0);
}
complete_all(&ddata->hpd_completion);
return IRQ_HANDLED;
}
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->output = dssdev;
dssdev->device = dst;
INIT_COMPLETION(ddata->hpd_completion);
gpio_set_value_cansleep(ddata->ct_cp_hpd_gpio, 1);
/* DC-DC converter needs at max 300us to get to 90% of 5V */
udelay(300);
/*
* If there's a cable connected, wait for the hpd irq to trigger,
* which turns on the level shifters.
*/
if (gpio_get_value_cansleep(ddata->hpd_gpio)) {
unsigned long to;
to = wait_for_completion_timeout(&ddata->hpd_completion,
msecs_to_jiffies(250));
WARN_ON_ONCE(to == 0);
}
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->device);
if (dst != dssdev->device)
return;
gpio_set_value_cansleep(ddata->ct_cp_hpd_gpio, 0);
dst->output = NULL;
dssdev->device = 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;
if (!gpio_get_value_cansleep(ddata->hpd_gpio))
return -ENODEV;
return in->ops.hdmi->read_edid(in, edid, len);
}
static bool tpd_detect(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
return gpio_get_value_cansleep(ddata->hpd_gpio);
}
static int tpd_audio_enable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
return in->ops.hdmi->audio_enable(in);
}
static void tpd_audio_disable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
in->ops.hdmi->audio_disable(in);
}
static int tpd_audio_start(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
return in->ops.hdmi->audio_start(in);
}
static void tpd_audio_stop(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
in->ops.hdmi->audio_stop(in);
}
static bool tpd_audio_supported(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
return in->ops.hdmi->audio_supported(in);
}
static int tpd_audio_config(struct omap_dss_device *dssdev,
struct omap_dss_audio *audio)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
return in->ops.hdmi->audio_config(in, audio);
}
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,
.audio_enable = tpd_audio_enable,
.audio_disable = tpd_audio_disable,
.audio_start = tpd_audio_start,
.audio_stop = tpd_audio_stop,
.audio_supported = tpd_audio_supported,
.audio_config = tpd_audio_config,
};
static int tpd_probe_pdata(struct platform_device *pdev)
{
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
struct encoder_tpd12s015_platform_data *pdata;
struct omap_dss_device *dssdev, *in;
pdata = dev_get_platdata(&pdev->dev);
ddata->ct_cp_hpd_gpio = pdata->ct_cp_hpd_gpio;
ddata->ls_oe_gpio = pdata->ls_oe_gpio;
ddata->hpd_gpio = pdata->hpd_gpio;
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 tpd_probe(struct platform_device *pdev)
{
struct omap_dss_device *in, *dssdev;
struct panel_drv_data *ddata;
int r;
ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
if (!ddata)
return -ENOMEM;
platform_set_drvdata(pdev, ddata);
init_completion(&ddata->hpd_completion);
if (dev_get_platdata(&pdev->dev)) {
r = tpd_probe_pdata(pdev);
if (r)
return r;
} else {
return -ENODEV;
}
r = devm_gpio_request_one(&pdev->dev, ddata->ct_cp_hpd_gpio,
GPIOF_OUT_INIT_LOW, "hdmi_ct_cp_hpd");
if (r)
goto err_gpio;
if (gpio_is_valid(ddata->ls_oe_gpio)) {
r = devm_gpio_request_one(&pdev->dev, ddata->ls_oe_gpio,
GPIOF_OUT_INIT_LOW, "hdmi_ls_oe");
if (r)
goto err_gpio;
}
r = devm_gpio_request_one(&pdev->dev, ddata->hpd_gpio,
GPIOF_DIR_IN, "hdmi_hpd");
if (r)
goto err_gpio;
r = devm_request_threaded_irq(&pdev->dev, gpio_to_irq(ddata->hpd_gpio),
NULL, tpd_hpd_irq_handler,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
IRQF_ONESHOT, "hpd", ddata);
if (r)
goto err_irq;
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;
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_irq:
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->device);
omap_dss_put_device(in);
return 0;
}
static struct platform_driver tpd_driver = {
.probe = tpd_probe,
.remove = __exit_p(tpd_remove),
.driver = {
.name = "tpd12s015",
.owner = THIS_MODULE,
},
};
module_platform_driver(tpd_driver);
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
MODULE_DESCRIPTION("TPD12S015 driver");
MODULE_LICENSE("GPL");
/*
* 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 <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;
int backlight_gpio;
int 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;
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->enable_gpio))
gpio_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->enable_gpio))
gpio_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 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;
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;
ddata->enable_gpio = pdata->enable_gpio;
ddata->backlight_gpio = pdata->backlight_gpio;
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 {
return -ENODEV;
}
if (gpio_is_valid(ddata->enable_gpio)) {
r = devm_gpio_request_one(&pdev->dev, ddata->enable_gpio,
GPIOF_OUT_INIT_LOW, "panel enable");
if (r)
goto err_gpio;
}
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 struct platform_driver panel_dpi_driver = {
.probe = panel_dpi_probe,
.remove = __exit_p(panel_dpi_remove),
.driver = {
.name = "panel-dpi",
.owner = THIS_MODULE,
},
};
module_platform_driver(panel_dpi_driver);
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
MODULE_DESCRIPTION("Generic MIPI DPI Panel Driver");
MODULE_LICENSE("GPL");
This diff is collapsed.
/*
* 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,
.pixel_clock = 6500,
.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_OPPOSITE_EDGES,
};
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;
int reset_gpio;
int backlight_gpio;
int 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;
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->enable_gpio))
gpio_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 (gpio_is_valid(ddata->enable_gpio))
gpio_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;
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;
ddata->enable_gpio = pdata->enable_gpio;
ddata->backlight_gpio = pdata->backlight_gpio;
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 {
return -ENODEV;
}
if (gpio_is_valid(ddata->enable_gpio)) {
r = devm_gpio_request_one(&spi->dev, ddata->enable_gpio,
GPIOF_OUT_INIT_LOW, "panel enable");
if (r)
goto err_gpio;
}
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 struct spi_driver lb035q02_spi_driver = {
.probe = lb035q02_panel_spi_probe,
.remove = lb035q02_panel_spi_remove,
.driver = {
.name = "panel_lgphilips_lb035q02",
.owner = THIS_MODULE,
},
};
module_spi_driver(lb035q02_spi_driver);
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
MODULE_DESCRIPTION("LG.Philips LB035Q02 LCD Panel driver");
MODULE_LICENSE("GPL");
/*
* 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 <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 23800
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,
.pixel_clock = 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;
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(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 {
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 struct spi_driver nec_8048_driver = {
.driver = {
.name = "panel-nec-nl8048hl11",
.owner = THIS_MODULE,
.pm = NEC_8048_PM_OPS,
},
.probe = nec_8048_probe,
.remove = nec_8048_remove,
};
module_spi_driver(nec_8048_driver);
MODULE_AUTHOR("Erik Gilling <konkers@android.com>");
MODULE_DESCRIPTION("NEC-NL8048HL11 Driver");
MODULE_LICENSE("GPL");
/*
* 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/platform_device.h>
#include <linux/slab.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;
int resb_gpio;
int ini_gpio;
int mo_gpio;
int lr_gpio;
int ud_gpio;
};
static const struct omap_video_timings sharp_ls_timings = {
.x_res = 480,
.y_res = 640,
.pixel_clock = 19200,
.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_OPPOSITE_EDGES,
};
#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;
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;
/* wait couple of vsyncs until enabling the LCD */
msleep(50);
if (gpio_is_valid(ddata->resb_gpio))
gpio_set_value_cansleep(ddata->resb_gpio, 1);
if (gpio_is_valid(ddata->ini_gpio))
gpio_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 (gpio_is_valid(ddata->ini_gpio))
gpio_set_value_cansleep(ddata->ini_gpio, 0);
if (gpio_is_valid(ddata->resb_gpio))
gpio_set_value_cansleep(ddata->resb_gpio, 0);
/* wait at least 5 vsyncs after disabling the LCD */
msleep(100);
in->ops.dpi->disable(in);
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_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;
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;
ddata->resb_gpio = pdata->resb_gpio;
ddata->ini_gpio = pdata->ini_gpio;
ddata->mo_gpio = pdata->mo_gpio;
ddata->lr_gpio = pdata->lr_gpio;
ddata->ud_gpio = pdata->ud_gpio;
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 {
return -ENODEV;
}
if (gpio_is_valid(ddata->mo_gpio)) {
r = devm_gpio_request_one(&pdev->dev, ddata->mo_gpio,
GPIOF_OUT_INIT_LOW, "lcd MO");
if (r)
goto err_gpio;
}
if (gpio_is_valid(ddata->lr_gpio)) {
r = devm_gpio_request_one(&pdev->dev, ddata->lr_gpio,
GPIOF_OUT_INIT_HIGH, "lcd LR");
if (r)
goto err_gpio;
}
if (gpio_is_valid(ddata->ud_gpio)) {
r = devm_gpio_request_one(&pdev->dev, ddata->ud_gpio,
GPIOF_OUT_INIT_HIGH, "lcd UD");
if (r)
goto err_gpio;
}
if (gpio_is_valid(ddata->resb_gpio)) {
r = devm_gpio_request_one(&pdev->dev, ddata->resb_gpio,
GPIOF_OUT_INIT_LOW, "lcd RESB");
if (r)
goto err_gpio;
}
if (gpio_is_valid(ddata->ini_gpio)) {
r = devm_gpio_request_one(&pdev->dev, ddata->ini_gpio,
GPIOF_OUT_INIT_LOW, "lcd INI");
if (r)
goto err_gpio;
}
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:
err_gpio:
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 struct platform_driver sharp_ls_driver = {
.probe = sharp_ls_probe,
.remove = __exit_p(sharp_ls_remove),
.driver = {
.name = "panel-sharp-ls037v7dw01",
.owner = THIS_MODULE,
},
};
module_platform_driver(sharp_ls_driver);
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
MODULE_DESCRIPTION("Sharp LS037V7DW01 Panel Driver");
MODULE_LICENSE("GPL");
This diff is collapsed.
This diff is collapsed.
menu "OMAP2/3 Display Device Drivers"
menu "OMAP2/3 Display Device Drivers (old device model)"
depends on OMAP2_DSS
config PANEL_GENERIC_DPI
......
......@@ -422,7 +422,19 @@ static void wait_pending_extra_info_updates(void)
static struct omap_dss_device *dss_mgr_get_device(struct omap_overlay_manager *mgr)
{
return mgr->output ? mgr->output->device : NULL;
struct omap_dss_device *dssdev;
dssdev = mgr->output;
if (dssdev == NULL)
return NULL;
while (dssdev->device)
dssdev = dssdev->device;
if (dssdev->driver)
return dssdev;
else
return NULL;
}
static struct omap_dss_device *dss_ovl_get_device(struct omap_overlay *ovl)
......
......@@ -61,6 +61,7 @@ int omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev)
case OMAP_DISPLAY_TYPE_VENC:
case OMAP_DISPLAY_TYPE_SDI:
case OMAP_DISPLAY_TYPE_HDMI:
case OMAP_DISPLAY_TYPE_DVI:
return 24;
default:
BUG();
......
......@@ -461,6 +461,16 @@ void omapdss_dpi_set_timings(struct omap_dss_device *dssdev,
}
EXPORT_SYMBOL(omapdss_dpi_set_timings);
static void dpi_get_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
mutex_lock(&dpi.lock);
*timings = dpi.timings;
mutex_unlock(&dpi.lock);
}
int dpi_check_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
......@@ -678,6 +688,65 @@ static int dpi_probe_pdata(struct platform_device *dpidev)
return 0;
}
static int dpi_connect(struct omap_dss_device *dssdev,
struct omap_dss_device *dst)
{
struct omap_overlay_manager *mgr;
int r;
r = dpi_init_regulator();
if (r)
return r;
dpi_init_pll();
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->device);
if (dst != dssdev->device)
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 = omapdss_dpi_display_enable,
.disable = omapdss_dpi_display_disable,
.check_timings = dpi_check_timings,
.set_timings = omapdss_dpi_set_timings,
.get_timings = dpi_get_timings,
.set_data_lines = omapdss_dpi_set_data_lines,
};
static void dpi_init_output(struct platform_device *pdev)
{
struct omap_dss_device *out = &dpi.output;
......@@ -687,16 +756,17 @@ static void dpi_init_output(struct platform_device *pdev)
out->output_type = OMAP_DISPLAY_TYPE_DPI;
out->name = "dpi.0";
out->dispc_channel = dpi_get_channel();
out->ops.dpi = &dpi_ops;
out->owner = THIS_MODULE;
dss_register_output(out);
omapdss_register_output(out);
}
static void __exit dpi_uninit_output(struct platform_device *pdev)
{
struct omap_dss_device *out = &dpi.output;
dss_unregister_output(out);
omapdss_unregister_output(out);
}
static int omap_dpi_probe(struct platform_device *pdev)
......
......@@ -383,6 +383,15 @@ static inline struct dsi_data *dsi_get_dsidrv_data(struct platform_device *dside
static inline struct platform_device *dsi_get_dsidev_from_dssdev(struct omap_dss_device *dssdev)
{
/* HACK: dssdev can be either the panel device, when using old API, or
* the dsi device itself, when using the new API. So we solve this for
* now by checking the dssdev->id. This will be removed when the old API
* is removed.
*/
if (dssdev->id == OMAP_DSS_OUTPUT_DSI1 ||
dssdev->id == OMAP_DSS_OUTPUT_DSI2)
return to_platform_device(dssdev->dev);
return to_platform_device(dssdev->output->dev);
}
......@@ -5412,6 +5421,89 @@ static int dsi_probe_pdata(struct platform_device *dsidev)
return 0;
}
static int dsi_connect(struct omap_dss_device *dssdev,
struct omap_dss_device *dst)
{
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
struct omap_overlay_manager *mgr;
int r;
r = dsi_regulator_init(dsidev);
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",
dssdev->name);
dss_mgr_disconnect(mgr, dssdev);
return r;
}
return 0;
}
static void dsi_disconnect(struct omap_dss_device *dssdev,
struct omap_dss_device *dst)
{
WARN_ON(dst != dssdev->device);
if (dst != dssdev->device)
return;
omapdss_output_unset_device(dssdev);
if (dssdev->manager)
dss_mgr_disconnect(dssdev->manager, dssdev);
}
static const struct omapdss_dsi_ops dsi_ops = {
.connect = dsi_connect,
.disconnect = dsi_disconnect,
.bus_lock = dsi_bus_lock,
.bus_unlock = dsi_bus_unlock,
.enable = omapdss_dsi_display_enable,
.disable = omapdss_dsi_display_disable,
.enable_hs = omapdss_dsi_vc_enable_hs,
.configure_pins = omapdss_dsi_configure_pins,
.set_config = omapdss_dsi_set_config,
.enable_video_output = dsi_enable_video_output,
.disable_video_output = dsi_disable_video_output,
.update = omap_dsi_update,
.enable_te = omapdss_dsi_enable_te,
.request_vc = omap_dsi_request_vc,
.set_vc_id = omap_dsi_set_vc_id,
.release_vc = omap_dsi_release_vc,
.dcs_write = dsi_vc_dcs_write,
.dcs_write_nosync = dsi_vc_dcs_write_nosync,
.dcs_read = dsi_vc_dcs_read,
.gen_write = dsi_vc_generic_write,
.gen_write_nosync = dsi_vc_generic_write_nosync,
.gen_read = dsi_vc_generic_read,
.bta_sync = dsi_vc_send_bta_sync,
.set_max_rx_packet_size = dsi_vc_set_max_rx_packet_size,
};
static void dsi_init_output(struct platform_device *dsidev)
{
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
......@@ -5424,9 +5516,10 @@ static void dsi_init_output(struct platform_device *dsidev)
out->output_type = OMAP_DISPLAY_TYPE_DSI;
out->name = dsi->module_id == 0 ? "dsi.0" : "dsi.1";
out->dispc_channel = dsi_get_channel(dsi->module_id);
out->ops.dsi = &dsi_ops;
out->owner = THIS_MODULE;
dss_register_output(out);
omapdss_register_output(out);
}
static void dsi_uninit_output(struct platform_device *dsidev)
......@@ -5434,7 +5527,7 @@ static void dsi_uninit_output(struct platform_device *dsidev)
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
struct omap_dss_device *out = &dsi->output;
dss_unregister_output(out);
omapdss_unregister_output(out);
}
/* DSI1 HW IP initialisation */
......
......@@ -179,10 +179,6 @@ void dss_put_device(struct omap_dss_device *dssdev);
void dss_copy_device_pdata(struct omap_dss_device *dst,
const struct omap_dss_device *src);
/* output */
void dss_register_output(struct omap_dss_device *out);
void dss_unregister_output(struct omap_dss_device *out);
/* display */
int dss_suspend_all_devices(void);
int dss_resume_all_devices(void);
......
This diff is collapsed.
......@@ -92,15 +92,18 @@ int omapdss_output_unset_device(struct omap_dss_device *out)
}
EXPORT_SYMBOL(omapdss_output_unset_device);
void dss_register_output(struct omap_dss_device *out)
int omapdss_register_output(struct omap_dss_device *out)
{
list_add_tail(&out->list, &output_list);
return 0;
}
EXPORT_SYMBOL(omapdss_register_output);
void dss_unregister_output(struct omap_dss_device *out)
void omapdss_unregister_output(struct omap_dss_device *out)
{
list_del(&out->list);
}
EXPORT_SYMBOL(omapdss_unregister_output);
struct omap_dss_device *omap_dss_get_output(enum omap_dss_output_id id)
{
......@@ -143,7 +146,13 @@ EXPORT_SYMBOL(omap_dss_find_output_by_node);
struct omap_dss_device *omapdss_find_output_from_display(struct omap_dss_device *dssdev)
{
return omap_dss_get_device(dssdev->output);
while (dssdev->output)
dssdev = dssdev->output;
if (dssdev->id != 0)
return omap_dss_get_device(dssdev);
return NULL;
}
EXPORT_SYMBOL(omapdss_find_output_from_display);
......
......@@ -1022,14 +1022,14 @@ static void rfbi_init_output(struct platform_device *pdev)
out->dispc_channel = OMAP_DSS_CHANNEL_LCD;
out->owner = THIS_MODULE;
dss_register_output(out);
omapdss_register_output(out);
}
static void __exit rfbi_uninit_output(struct platform_device *pdev)
{
struct omap_dss_device *out = &rfbi.output;
dss_unregister_output(out);
omapdss_unregister_output(out);
}
/* RFBI HW IP initialisation */
......
......@@ -234,6 +234,26 @@ void omapdss_sdi_set_timings(struct omap_dss_device *dssdev,
}
EXPORT_SYMBOL(omapdss_sdi_set_timings);
static void sdi_get_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
*timings = sdi.timings;
}
static int sdi_check_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct omap_overlay_manager *mgr = sdi.output.manager;
if (mgr && !dispc_mgr_timings_ok(mgr->id, timings))
return -EINVAL;
if (timings->pixel_clock == 0)
return -EINVAL;
return 0;
}
void omapdss_sdi_set_datapairs(struct omap_dss_device *dssdev, int datapairs)
{
sdi.datapairs = datapairs;
......@@ -333,6 +353,63 @@ static int sdi_probe_pdata(struct platform_device *sdidev)
return 0;
}
static int sdi_connect(struct omap_dss_device *dssdev,
struct omap_dss_device *dst)
{
struct omap_overlay_manager *mgr;
int r;
r = sdi_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 sdi_disconnect(struct omap_dss_device *dssdev,
struct omap_dss_device *dst)
{
WARN_ON(dst != dssdev->device);
if (dst != dssdev->device)
return;
omapdss_output_unset_device(dssdev);
if (dssdev->manager)
dss_mgr_disconnect(dssdev->manager, dssdev);
}
static const struct omapdss_sdi_ops sdi_ops = {
.connect = sdi_connect,
.disconnect = sdi_disconnect,
.enable = omapdss_sdi_display_enable,
.disable = omapdss_sdi_display_disable,
.check_timings = sdi_check_timings,
.set_timings = omapdss_sdi_set_timings,
.get_timings = sdi_get_timings,
.set_datapairs = omapdss_sdi_set_datapairs,
};
static void sdi_init_output(struct platform_device *pdev)
{
struct omap_dss_device *out = &sdi.output;
......@@ -342,16 +419,17 @@ static void sdi_init_output(struct platform_device *pdev)
out->output_type = OMAP_DISPLAY_TYPE_SDI;
out->name = "sdi.0";
out->dispc_channel = OMAP_DSS_CHANNEL_LCD;
out->ops.sdi = &sdi_ops;
out->owner = THIS_MODULE;
dss_register_output(out);
omapdss_register_output(out);
}
static void __exit sdi_uninit_output(struct platform_device *pdev)
{
struct omap_dss_device *out = &sdi.output;
dss_unregister_output(out);
omapdss_unregister_output(out);
}
static int omap_sdi_probe(struct platform_device *pdev)
......
......@@ -564,6 +564,16 @@ int omapdss_venc_check_timings(struct omap_dss_device *dssdev,
return -EINVAL;
}
static void venc_get_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
mutex_lock(&venc.venc_lock);
*timings = venc.timings;
mutex_unlock(&venc.venc_lock);
}
u32 omapdss_venc_get_wss(struct omap_dss_device *dssdev)
{
/* Invert due to VENC_L21_WC_CTL:INV=1 */
......@@ -779,6 +789,67 @@ static int venc_probe_pdata(struct platform_device *vencdev)
return 0;
}
static int venc_connect(struct omap_dss_device *dssdev,
struct omap_dss_device *dst)
{
struct omap_overlay_manager *mgr;
int r;
r = venc_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 venc_disconnect(struct omap_dss_device *dssdev,
struct omap_dss_device *dst)
{
WARN_ON(dst != dssdev->device);
if (dst != dssdev->device)
return;
omapdss_output_unset_device(dssdev);
if (dssdev->manager)
dss_mgr_disconnect(dssdev->manager, dssdev);
}
static const struct omapdss_atv_ops venc_ops = {
.connect = venc_connect,
.disconnect = venc_disconnect,
.enable = omapdss_venc_display_enable,
.disable = omapdss_venc_display_disable,
.check_timings = omapdss_venc_check_timings,
.set_timings = omapdss_venc_set_timings,
.get_timings = venc_get_timings,
.set_type = omapdss_venc_set_type,
.invert_vid_out_polarity = omapdss_venc_invert_vid_out_polarity,
.set_wss = omapdss_venc_set_wss,
.get_wss = omapdss_venc_get_wss,
};
static void venc_init_output(struct platform_device *pdev)
{
struct omap_dss_device *out = &venc.output;
......@@ -788,16 +859,17 @@ static void venc_init_output(struct platform_device *pdev)
out->output_type = OMAP_DISPLAY_TYPE_VENC;
out->name = "venc.0";
out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT;
out->ops.atv = &venc_ops;
out->owner = THIS_MODULE;
dss_register_output(out);
omapdss_register_output(out);
}
static void __exit venc_uninit_output(struct platform_device *pdev)
{
struct omap_dss_device *out = &venc.output;
dss_unregister_output(out);
omapdss_unregister_output(out);
}
/* VENC HW IP initialisation */
......
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment