Skip to content

Commit

Permalink
OMAP: DSS2: add panel-dvi driver
Browse files Browse the repository at this point in the history
We have currently panel-generic-dpi driver, which is a combined driver
for dummy panels and also for DVI output.

The aim is to split the panel-generic-dpi into two, one for fixed size
dummy panels connected via DPI, and the other (this) for variable
resolution output which supports DDC channel (in practice a DVI framer
chip connected to DPI output).

Original i2c code by: Ricardo Salveti de Araujo
<ricardo.salveti@canonical.com>

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
  • Loading branch information
Tomi Valkeinen committed Sep 30, 2011
1 parent 759593f commit ba2eac9
Show file tree
Hide file tree
Showing 4 changed files with 408 additions and 0 deletions.
7 changes: 7 additions & 0 deletions drivers/video/omap2/displays/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ config PANEL_GENERIC_DPI
Supports LCD Panel used in TI SDP3430 and EVM boards,
OMAP3517 EVM boards and CM-T35.

config PANEL_DVI
tristate "DVI output"
depends on OMAP2_DSS_DPI
help
Driver for external monitors, connected via DVI. The driver uses i2c
to read EDID information from the monitor.

config PANEL_LGPHILIPS_LB035Q02
tristate "LG.Philips LB035Q02 LCD Panel"
depends on OMAP2_DSS_DPI && SPI
Expand Down
1 change: 1 addition & 0 deletions drivers/video/omap2/displays/Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
obj-$(CONFIG_PANEL_GENERIC_DPI) += panel-generic-dpi.o
obj-$(CONFIG_PANEL_DVI) += panel-dvi.o
obj-$(CONFIG_PANEL_LGPHILIPS_LB035Q02) += panel-lgphilips-lb035q02.o
obj-$(CONFIG_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o
obj-$(CONFIG_PANEL_NEC_NL8048HL11_01B) += panel-nec-nl8048hl11-01b.o
Expand Down
363 changes: 363 additions & 0 deletions drivers/video/omap2/displays/panel-dvi.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,363 @@
/*
* DVI output support
*
* Copyright (C) 2011 Texas Instruments Inc
* 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/>.
*/

#include <linux/module.h>
#include <linux/slab.h>
#include <video/omapdss.h>
#include <linux/i2c.h>
#include <drm/drm_edid.h>

#include <video/omap-panel-dvi.h>

static const struct omap_video_timings panel_dvi_default_timings = {
.x_res = 640,
.y_res = 480,

.pixel_clock = 23500,

.hfp = 48,
.hsw = 32,
.hbp = 80,

.vfp = 3,
.vsw = 4,
.vbp = 7,
};

struct panel_drv_data {
struct omap_dss_device *dssdev;

struct mutex lock;
};

static inline struct panel_dvi_platform_data
*get_pdata(const struct omap_dss_device *dssdev)
{
return dssdev->data;
}

static int panel_dvi_power_on(struct omap_dss_device *dssdev)
{
struct panel_dvi_platform_data *pdata = get_pdata(dssdev);
int r;

if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
return 0;

r = omapdss_dpi_display_enable(dssdev);
if (r)
goto err0;

if (pdata->platform_enable) {
r = pdata->platform_enable(dssdev);
if (r)
goto err1;
}

return 0;
err1:
omapdss_dpi_display_disable(dssdev);
err0:
return r;
}

static void panel_dvi_power_off(struct omap_dss_device *dssdev)
{
struct panel_dvi_platform_data *pdata = get_pdata(dssdev);

if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
return;

if (pdata->platform_disable)
pdata->platform_disable(dssdev);

omapdss_dpi_display_disable(dssdev);
}

static int panel_dvi_probe(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata;

ddata = kzalloc(sizeof(*ddata), GFP_KERNEL);
if (!ddata)
return -ENOMEM;

dssdev->panel.timings = panel_dvi_default_timings;
dssdev->panel.config = OMAP_DSS_LCD_TFT;

ddata->dssdev = dssdev;
mutex_init(&ddata->lock);

dev_set_drvdata(&dssdev->dev, ddata);

return 0;
}

static void __exit panel_dvi_remove(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);

mutex_lock(&ddata->lock);

dev_set_drvdata(&dssdev->dev, NULL);

mutex_unlock(&ddata->lock);

kfree(ddata);
}

static int panel_dvi_enable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
int r;

mutex_lock(&ddata->lock);

r = panel_dvi_power_on(dssdev);
if (r == 0)
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;

mutex_unlock(&ddata->lock);

return r;
}

static void panel_dvi_disable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);

mutex_lock(&ddata->lock);

panel_dvi_power_off(dssdev);

dssdev->state = OMAP_DSS_DISPLAY_DISABLED;

mutex_unlock(&ddata->lock);
}

static int panel_dvi_suspend(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);

mutex_lock(&ddata->lock);

panel_dvi_power_off(dssdev);

dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;

mutex_unlock(&ddata->lock);

return 0;
}

static int panel_dvi_resume(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
int r;

mutex_lock(&ddata->lock);

r = panel_dvi_power_on(dssdev);
if (r == 0)
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;

mutex_unlock(&ddata->lock);

return r;
}

static void panel_dvi_set_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);

mutex_lock(&ddata->lock);
dpi_set_timings(dssdev, timings);
mutex_unlock(&ddata->lock);
}

static void panel_dvi_get_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);

mutex_lock(&ddata->lock);
*timings = dssdev->panel.timings;
mutex_unlock(&ddata->lock);
}

static int panel_dvi_check_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
int r;

mutex_lock(&ddata->lock);
r = dpi_check_timings(dssdev, timings);
mutex_unlock(&ddata->lock);

return r;
}


static int panel_dvi_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 panel_dvi_read_edid(struct omap_dss_device *dssdev,
u8 *edid, int len)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
struct panel_dvi_platform_data *pdata = get_pdata(dssdev);
struct i2c_adapter *adapter;
int r, l, bytes_read;

mutex_lock(&ddata->lock);

if (pdata->i2c_bus_num == 0) {
r = -ENODEV;
goto err;
}

adapter = i2c_get_adapter(pdata->i2c_bus_num);
if (!adapter) {
dev_err(&dssdev->dev, "Failed to get I2C adapter, bus %d\n",
pdata->i2c_bus_num);
r = -EINVAL;
goto err;
}

l = min(EDID_LENGTH, len);
r = panel_dvi_ddc_read(adapter, edid, l, 0);
if (r)
goto err;

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 = panel_dvi_ddc_read(adapter, edid + EDID_LENGTH,
l, EDID_LENGTH);
if (r)
goto err;

bytes_read += l;
}

mutex_unlock(&ddata->lock);

return bytes_read;

err:
mutex_unlock(&ddata->lock);
return r;
}

static bool panel_dvi_detect(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
struct panel_dvi_platform_data *pdata = get_pdata(dssdev);
struct i2c_adapter *adapter;
unsigned char out;
int r;

mutex_lock(&ddata->lock);

if (pdata->i2c_bus_num == 0)
goto out;

adapter = i2c_get_adapter(pdata->i2c_bus_num);
if (!adapter)
goto out;

r = panel_dvi_ddc_read(adapter, &out, 1, 0);

mutex_unlock(&ddata->lock);

return r == 0;

out:
mutex_unlock(&ddata->lock);
return true;
}

static struct omap_dss_driver panel_dvi_driver = {
.probe = panel_dvi_probe,
.remove = __exit_p(panel_dvi_remove),

.enable = panel_dvi_enable,
.disable = panel_dvi_disable,
.suspend = panel_dvi_suspend,
.resume = panel_dvi_resume,

.set_timings = panel_dvi_set_timings,
.get_timings = panel_dvi_get_timings,
.check_timings = panel_dvi_check_timings,

.read_edid = panel_dvi_read_edid,
.detect = panel_dvi_detect,

.driver = {
.name = "dvi",
.owner = THIS_MODULE,
},
};

static int __init panel_dvi_init(void)
{
return omap_dss_register_driver(&panel_dvi_driver);
}

static void __exit panel_dvi_exit(void)
{
omap_dss_unregister_driver(&panel_dvi_driver);
}

module_init(panel_dvi_init);
module_exit(panel_dvi_exit);
MODULE_LICENSE("GPL");
Loading

0 comments on commit ba2eac9

Please sign in to comment.