1
0
Fork 0

omapfb: copy omapdss & displays for omapfb

This patch makes a copy of the omapdss driver and the omap panel &
encoder drivers for omapfb. The purpose is to separate omapdrm and
omapfb drivers from each other.

Note that this patch only does a direct copy of the files without any
other modifications. The files are not yet used.

The original files are in:

drivers/video/fbdev/omap2/dss/
drivers/video/fbdev/omap2/displays-new/

Here's a more detailed explanation about this and the following patches,
from the introduction mail of the patch series:

A short background on the current status. We have the following
entities:

* omapdss, located in drivers/video/fbdev/omap2/dss/. This is a driver for the
  display subsystem IPs used on OMAP (and related) SoCs. It offers only a
  kernel internal API, and does not implement anything for fbdev or drm.

* omapdss panels and encoders, located in
  drivers/video/fbdev/omap2/displays-new/. These are panel and external encoder
  drivers, which use APIs offered by omapdss driver. These also don't implement
  anything for fbdev or drm.

* omapdrm, located in drivers/gpu/drm/omapdrm/. This is a drm driver, which
  uses omapdss and the panel/encoder drivers to operate the hardware.

* omapfb, located in drivers/video/fbdev/omap2/omapfb/. This is an fbdev
  driver, which uses omapdss and the panel/encoder drivers to operate the
  hardware.

* omap_vout, located in drivers/media/platform/omap/. This is a v4l2 driver,
  which uses omapdss and omapfb to implement a v4l2 API for the video overlays.

So, on the top level, we have either omapdrm, or omapfb+omap_vout. Both of
those use the same low level drivers. Without going to the historical details
why the architecture is like that, I think it's finally time to change that.

The situation with omapfb+omap_vout is that it still works, but no new features
have been added for a long time, and I want to keep it working as it's still
being used.  At some point in the future I'd like to remove omapfb and
omap_vout altogether.

Omapdrm, on the other hand, is being actively developed. Sharing the low level
parts with omapfb makes that development more difficult than it should be. It
also "hides" half of the development, as everything happening in the low level
parts resides under fbdev directory, not in the drm directory.

I've been wanting to clean this up for a long time, but I haven't figured out a
very good way to do it. I still haven't, but here's the best way I have come up
with.

This series makes a full copy of the low level parts, omapdss and panel/encoder
drivers. Both omapfb+omap_vout and omapdrm will have their own versions. The
copy omapfb+omap_vout get is a new copy, and the copy that omapdrm gets is just
the current files moved. This way git will associate the omapdrm version with
the old files.

The omapfb+omap_vout versions won't be touched unless there are some big issues
there.

The omapdrm versions can be refactored and cleaned up, as the omapfb support
code is no longer needed. We can perhaps also merge omapdss and omapdrm into
the same kernel module.

This series only does the copy, and the absolutely necessary parts. No further
cleanups are done yet.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Acked-by: Dave Airlie <airlied@gmail.com>
Acked-by: Rob Clark <robdclark@gmail.com>
hifive-unleashed-5.1
Tomi Valkeinen 2015-12-09 18:28:28 +02:00
parent 1c6aac92ee
commit f76ee892a9
56 changed files with 36276 additions and 0 deletions

View File

@ -0,0 +1,86 @@
menu "OMAP Display Device Drivers (new device model)"
depends on OMAP2_DSS
config DISPLAY_ENCODER_OPA362
tristate "OPA362 external analog amplifier"
help
Driver for OPA362 external analog TV amplifier controlled
through a GPIO.
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"
depends on BACKLIGHT_CLASS_DEVICE
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_TD028TTEC1
tristate "TPO TD028TTEC1 LCD Panel"
depends on SPI
help
LCD panel used in Openmoko.
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

View File

@ -0,0 +1,14 @@
obj-$(CONFIG_DISPLAY_ENCODER_OPA362) += encoder-opa362.o
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_TD028TTEC1) += panel-tpo-td028ttec1.o
obj-$(CONFIG_DISPLAY_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o
obj-$(CONFIG_DISPLAY_PANEL_NEC_NL8048HL11) += panel-nec-nl8048hl11.o

View File

@ -0,0 +1,320 @@
/*
* Analog TV Connector driver
*
* Copyright (C) 2013 Texas Instruments
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <video/omapdss.h>
#include <video/omap-panel-data.h>
struct panel_drv_data {
struct omap_dss_device dssdev;
struct omap_dss_device *in;
struct device *dev;
struct omap_video_timings timings;
enum omap_dss_venc_type connector_type;
bool invert_polarity;
};
static const struct omap_video_timings tvc_pal_timings = {
.x_res = 720,
.y_res = 574,
.pixelclock = 13500000,
.hsw = 64,
.hfp = 12,
.hbp = 68,
.vsw = 5,
.vfp = 5,
.vbp = 41,
.interlace = true,
};
static const struct of_device_id tvc_of_match[];
struct tvc_of_data {
enum omap_dss_venc_type connector_type;
};
#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
static int tvc_connect(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
int r;
dev_dbg(ddata->dev, "connect\n");
if (omapdss_device_is_connected(dssdev))
return 0;
r = in->ops.atv->connect(in, dssdev);
if (r)
return r;
return 0;
}
static void tvc_disconnect(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
dev_dbg(ddata->dev, "disconnect\n");
if (!omapdss_device_is_connected(dssdev))
return;
in->ops.atv->disconnect(in, dssdev);
}
static int tvc_enable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
int r;
dev_dbg(ddata->dev, "enable\n");
if (!omapdss_device_is_connected(dssdev))
return -ENODEV;
if (omapdss_device_is_enabled(dssdev))
return 0;
in->ops.atv->set_timings(in, &ddata->timings);
if (!ddata->dev->of_node) {
in->ops.atv->set_type(in, ddata->connector_type);
in->ops.atv->invert_vid_out_polarity(in,
ddata->invert_polarity);
}
r = in->ops.atv->enable(in);
if (r)
return r;
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return r;
}
static void tvc_disable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
dev_dbg(ddata->dev, "disable\n");
if (!omapdss_device_is_enabled(dssdev))
return;
in->ops.atv->disable(in);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
}
static void tvc_set_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
ddata->timings = *timings;
dssdev->panel.timings = *timings;
in->ops.atv->set_timings(in, timings);
}
static void tvc_get_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
*timings = ddata->timings;
}
static int tvc_check_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
return in->ops.atv->check_timings(in, timings);
}
static u32 tvc_get_wss(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
return in->ops.atv->get_wss(in);
}
static int tvc_set_wss(struct omap_dss_device *dssdev, u32 wss)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
return in->ops.atv->set_wss(in, wss);
}
static struct omap_dss_driver tvc_driver = {
.connect = tvc_connect,
.disconnect = tvc_disconnect,
.enable = tvc_enable,
.disable = tvc_disable,
.set_timings = tvc_set_timings,
.get_timings = tvc_get_timings,
.check_timings = tvc_check_timings,
.get_resolution = omapdss_default_get_resolution,
.get_wss = tvc_get_wss,
.set_wss = tvc_set_wss,
};
static int tvc_probe_pdata(struct platform_device *pdev)
{
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
struct connector_atv_platform_data *pdata;
struct omap_dss_device *in, *dssdev;
pdata = dev_get_platdata(&pdev->dev);
in = omap_dss_find_output(pdata->source);
if (in == NULL) {
dev_err(&pdev->dev, "Failed to find video source\n");
return -EPROBE_DEFER;
}
ddata->in = in;
ddata->connector_type = pdata->connector_type;
ddata->invert_polarity = pdata->invert_polarity;
dssdev = &ddata->dssdev;
dssdev->name = pdata->name;
return 0;
}
static int tvc_probe_of(struct platform_device *pdev)
{
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
struct device_node *node = pdev->dev.of_node;
struct omap_dss_device *in;
in = omapdss_of_find_source_for_first_ep(node);
if (IS_ERR(in)) {
dev_err(&pdev->dev, "failed to find video source\n");
return PTR_ERR(in);
}
ddata->in = in;
return 0;
}
static int tvc_probe(struct platform_device *pdev)
{
struct panel_drv_data *ddata;
struct omap_dss_device *dssdev;
int r;
ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
if (!ddata)
return -ENOMEM;
platform_set_drvdata(pdev, ddata);
ddata->dev = &pdev->dev;
if (dev_get_platdata(&pdev->dev)) {
r = tvc_probe_pdata(pdev);
if (r)
return r;
} else if (pdev->dev.of_node) {
r = tvc_probe_of(pdev);
if (r)
return r;
} else {
return -ENODEV;
}
ddata->timings = tvc_pal_timings;
dssdev = &ddata->dssdev;
dssdev->driver = &tvc_driver;
dssdev->dev = &pdev->dev;
dssdev->type = OMAP_DISPLAY_TYPE_VENC;
dssdev->owner = THIS_MODULE;
dssdev->panel.timings = tvc_pal_timings;
r = omapdss_register_display(dssdev);
if (r) {
dev_err(&pdev->dev, "Failed to register panel\n");
goto err_reg;
}
return 0;
err_reg:
omap_dss_put_device(ddata->in);
return r;
}
static int __exit tvc_remove(struct platform_device *pdev)
{
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
struct omap_dss_device *dssdev = &ddata->dssdev;
struct omap_dss_device *in = ddata->in;
omapdss_unregister_display(&ddata->dssdev);
tvc_disable(dssdev);
tvc_disconnect(dssdev);
omap_dss_put_device(in);
return 0;
}
static const struct of_device_id tvc_of_match[] = {
{ .compatible = "omapdss,svideo-connector", },
{ .compatible = "omapdss,composite-video-connector", },
{},
};
MODULE_DEVICE_TABLE(of, tvc_of_match);
static struct platform_driver tvc_connector_driver = {
.probe = tvc_probe,
.remove = __exit_p(tvc_remove),
.driver = {
.name = "connector-analog-tv",
.of_match_table = tvc_of_match,
.suppress_bind_attrs = true,
},
};
module_platform_driver(tvc_connector_driver);
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
MODULE_DESCRIPTION("Analog TV Connector driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,398 @@
/*
* Generic DVI Connector driver
*
* Copyright (C) 2013 Texas Instruments
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <drm/drm_edid.h>
#include <video/omapdss.h>
#include <video/omap-panel-data.h>
static const struct omap_video_timings dvic_default_timings = {
.x_res = 640,
.y_res = 480,
.pixelclock = 23500000,
.hfp = 48,
.hsw = 32,
.hbp = 80,
.vfp = 3,
.vsw = 4,
.vbp = 7,
.vsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
.hsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
.data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
.de_level = OMAPDSS_SIG_ACTIVE_HIGH,
.sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
};
struct panel_drv_data {
struct omap_dss_device dssdev;
struct omap_dss_device *in;
struct omap_video_timings timings;
struct i2c_adapter *i2c_adapter;
};
#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
static int dvic_connect(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
int r;
if (omapdss_device_is_connected(dssdev))
return 0;
r = in->ops.dvi->connect(in, dssdev);
if (r)
return r;
return 0;
}
static void dvic_disconnect(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
if (!omapdss_device_is_connected(dssdev))
return;
in->ops.dvi->disconnect(in, dssdev);
}
static int dvic_enable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
int r;
if (!omapdss_device_is_connected(dssdev))
return -ENODEV;
if (omapdss_device_is_enabled(dssdev))
return 0;
in->ops.dvi->set_timings(in, &ddata->timings);
r = in->ops.dvi->enable(in);
if (r)
return r;
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return 0;
}
static void dvic_disable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
if (!omapdss_device_is_enabled(dssdev))
return;
in->ops.dvi->disable(in);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
}
static void dvic_set_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
ddata->timings = *timings;
dssdev->panel.timings = *timings;
in->ops.dvi->set_timings(in, timings);
}
static void dvic_get_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
*timings = ddata->timings;
}
static int dvic_check_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
return in->ops.dvi->check_timings(in, timings);
}
static int dvic_ddc_read(struct i2c_adapter *adapter,
unsigned char *buf, u16 count, u8 offset)
{
int r, retries;
for (retries = 3; retries > 0; retries--) {
struct i2c_msg msgs[] = {
{
.addr = DDC_ADDR,
.flags = 0,
.len = 1,
.buf = &offset,
}, {
.addr = DDC_ADDR,
.flags = I2C_M_RD,
.len = count,
.buf = buf,
}
};
r = i2c_transfer(adapter, msgs, 2);
if (r == 2)
return 0;
if (r != -EAGAIN)
break;
}
return r < 0 ? r : -EIO;
}
static int dvic_read_edid(struct omap_dss_device *dssdev,
u8 *edid, int len)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
int r, l, bytes_read;
if (!ddata->i2c_adapter)
return -ENODEV;
l = min(EDID_LENGTH, len);
r = dvic_ddc_read(ddata->i2c_adapter, edid, l, 0);
if (r)
return r;
bytes_read = l;
/* if there are extensions, read second block */
if (len > EDID_LENGTH && edid[0x7e] > 0) {
l = min(EDID_LENGTH, len - EDID_LENGTH);
r = dvic_ddc_read(ddata->i2c_adapter, edid + EDID_LENGTH,
l, EDID_LENGTH);
if (r)
return r;
bytes_read += l;
}
return bytes_read;
}
static bool dvic_detect(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
unsigned char out;
int r;
if (!ddata->i2c_adapter)
return true;
r = dvic_ddc_read(ddata->i2c_adapter, &out, 1, 0);
return r == 0;
}
static struct omap_dss_driver dvic_driver = {
.connect = dvic_connect,
.disconnect = dvic_disconnect,
.enable = dvic_enable,
.disable = dvic_disable,
.set_timings = dvic_set_timings,
.get_timings = dvic_get_timings,
.check_timings = dvic_check_timings,
.get_resolution = omapdss_default_get_resolution,
.read_edid = dvic_read_edid,
.detect = dvic_detect,
};
static int dvic_probe_pdata(struct platform_device *pdev)
{
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
struct connector_dvi_platform_data *pdata;
struct omap_dss_device *in, *dssdev;
int i2c_bus_num;
pdata = dev_get_platdata(&pdev->dev);
i2c_bus_num = pdata->i2c_bus_num;
if (i2c_bus_num != -1) {
struct i2c_adapter *adapter;
adapter = i2c_get_adapter(i2c_bus_num);
if (!adapter) {
dev_err(&pdev->dev,
"Failed to get I2C adapter, bus %d\n",
i2c_bus_num);
return -EPROBE_DEFER;
}
ddata->i2c_adapter = adapter;
}
in = omap_dss_find_output(pdata->source);
if (in == NULL) {
i2c_put_adapter(ddata->i2c_adapter);
dev_err(&pdev->dev, "Failed to find video source\n");
return -EPROBE_DEFER;
}
ddata->in = in;
dssdev = &ddata->dssdev;
dssdev->name = pdata->name;
return 0;
}
static int dvic_probe_of(struct platform_device *pdev)
{
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
struct device_node *node = pdev->dev.of_node;
struct omap_dss_device *in;
struct device_node *adapter_node;
struct i2c_adapter *adapter;
in = omapdss_of_find_source_for_first_ep(node);
if (IS_ERR(in)) {
dev_err(&pdev->dev, "failed to find video source\n");
return PTR_ERR(in);
}
ddata->in = in;
adapter_node = of_parse_phandle(node, "ddc-i2c-bus", 0);
if (adapter_node) {
adapter = of_get_i2c_adapter_by_node(adapter_node);
if (adapter == NULL) {
dev_err(&pdev->dev, "failed to parse ddc-i2c-bus\n");
omap_dss_put_device(ddata->in);
return -EPROBE_DEFER;
}
ddata->i2c_adapter = adapter;
}
return 0;
}
static int dvic_probe(struct platform_device *pdev)
{
struct panel_drv_data *ddata;
struct omap_dss_device *dssdev;
int r;
ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
if (!ddata)
return -ENOMEM;
platform_set_drvdata(pdev, ddata);
if (dev_get_platdata(&pdev->dev)) {
r = dvic_probe_pdata(pdev);
if (r)
return r;
} else if (pdev->dev.of_node) {
r = dvic_probe_of(pdev);
if (r)
return r;
} else {
return -ENODEV;
}
ddata->timings = dvic_default_timings;
dssdev = &ddata->dssdev;
dssdev->driver = &dvic_driver;
dssdev->dev = &pdev->dev;
dssdev->type = OMAP_DISPLAY_TYPE_DVI;
dssdev->owner = THIS_MODULE;
dssdev->panel.timings = dvic_default_timings;
r = omapdss_register_display(dssdev);
if (r) {
dev_err(&pdev->dev, "Failed to register panel\n");
goto err_reg;
}
return 0;
err_reg:
omap_dss_put_device(ddata->in);
i2c_put_adapter(ddata->i2c_adapter);
return r;
}
static int __exit dvic_remove(struct platform_device *pdev)
{
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
struct omap_dss_device *dssdev = &ddata->dssdev;
struct omap_dss_device *in = ddata->in;
omapdss_unregister_display(&ddata->dssdev);
dvic_disable(dssdev);
dvic_disconnect(dssdev);
omap_dss_put_device(in);
i2c_put_adapter(ddata->i2c_adapter);
return 0;
}
static const struct of_device_id dvic_of_match[] = {
{ .compatible = "omapdss,dvi-connector", },
{},
};
MODULE_DEVICE_TABLE(of, dvic_of_match);
static struct platform_driver dvi_connector_driver = {
.probe = dvic_probe,
.remove = __exit_p(dvic_remove),
.driver = {
.name = "connector-dvi",
.of_match_table = dvic_of_match,
.suppress_bind_attrs = true,
},
};
module_platform_driver(dvi_connector_driver);
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
MODULE_DESCRIPTION("Generic DVI Connector driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,348 @@
/*
* HDMI Connector driver
*
* Copyright (C) 2013 Texas Instruments
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <drm/drm_edid.h>
#include <video/omapdss.h>
#include <video/omap-panel-data.h>
static const struct omap_video_timings hdmic_default_timings = {
.x_res = 640,
.y_res = 480,
.pixelclock = 25175000,
.hsw = 96,
.hfp = 16,
.hbp = 48,
.vsw = 2,
.vfp = 11,
.vbp = 31,
.vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
.hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
.interlace = false,
};
struct panel_drv_data {
struct omap_dss_device dssdev;
struct omap_dss_device *in;
struct device *dev;
struct omap_video_timings timings;
int hpd_gpio;
};
#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
static int hdmic_connect(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
int r;
dev_dbg(ddata->dev, "connect\n");
if (omapdss_device_is_connected(dssdev))
return 0;
r = in->ops.hdmi->connect(in, dssdev);
if (r)
return r;
return 0;
}
static void hdmic_disconnect(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
dev_dbg(ddata->dev, "disconnect\n");
if (!omapdss_device_is_connected(dssdev))
return;
in->ops.hdmi->disconnect(in, dssdev);
}
static int hdmic_enable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
int r;
dev_dbg(ddata->dev, "enable\n");
if (!omapdss_device_is_connected(dssdev))
return -ENODEV;
if (omapdss_device_is_enabled(dssdev))
return 0;
in->ops.hdmi->set_timings(in, &ddata->timings);
r = in->ops.hdmi->enable(in);
if (r)
return r;
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return r;
}
static void hdmic_disable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
dev_dbg(ddata->dev, "disable\n");
if (!omapdss_device_is_enabled(dssdev))
return;
in->ops.hdmi->disable(in);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
}
static void hdmic_set_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
ddata->timings = *timings;
dssdev->panel.timings = *timings;
in->ops.hdmi->set_timings(in, timings);
}
static void hdmic_get_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
*timings = ddata->timings;
}
static int hdmic_check_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
return in->ops.hdmi->check_timings(in, timings);
}
static int hdmic_read_edid(struct omap_dss_device *dssdev,
u8 *edid, int len)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
return in->ops.hdmi->read_edid(in, edid, len);
}
static bool hdmic_detect(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
if (gpio_is_valid(ddata->hpd_gpio))
return gpio_get_value_cansleep(ddata->hpd_gpio);
else
return in->ops.hdmi->detect(in);
}
static int hdmic_set_hdmi_mode(struct omap_dss_device *dssdev, bool hdmi_mode)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
return in->ops.hdmi->set_hdmi_mode(in, hdmi_mode);
}
static int hdmic_set_infoframe(struct omap_dss_device *dssdev,
const struct hdmi_avi_infoframe *avi)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
return in->ops.hdmi->set_infoframe(in, avi);
}
static struct omap_dss_driver hdmic_driver = {
.connect = hdmic_connect,
.disconnect = hdmic_disconnect,
.enable = hdmic_enable,
.disable = hdmic_disable,
.set_timings = hdmic_set_timings,
.get_timings = hdmic_get_timings,
.check_timings = hdmic_check_timings,
.get_resolution = omapdss_default_get_resolution,
.read_edid = hdmic_read_edid,
.detect = hdmic_detect,
.set_hdmi_mode = hdmic_set_hdmi_mode,
.set_hdmi_infoframe = hdmic_set_infoframe,
};
static int hdmic_probe_pdata(struct platform_device *pdev)
{
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
struct connector_hdmi_platform_data *pdata;
struct omap_dss_device *in, *dssdev;
pdata = dev_get_platdata(&pdev->dev);
ddata->hpd_gpio = -ENODEV;
in = omap_dss_find_output(pdata->source);
if (in == NULL) {
dev_err(&pdev->dev, "Failed to find video source\n");
return -EPROBE_DEFER;
}
ddata->in = in;
dssdev = &ddata->dssdev;
dssdev->name = pdata->name;
return 0;
}
static int hdmic_probe_of(struct platform_device *pdev)
{
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
struct device_node *node = pdev->dev.of_node;
struct omap_dss_device *in;
int gpio;
/* HPD GPIO */
gpio = of_get_named_gpio(node, "hpd-gpios", 0);
if (gpio_is_valid(gpio))
ddata->hpd_gpio = gpio;
else
ddata->hpd_gpio = -ENODEV;
in = omapdss_of_find_source_for_first_ep(node);
if (IS_ERR(in)) {
dev_err(&pdev->dev, "failed to find video source\n");
return PTR_ERR(in);
}
ddata->in = in;
return 0;
}
static int hdmic_probe(struct platform_device *pdev)
{
struct panel_drv_data *ddata;
struct omap_dss_device *dssdev;
int r;
ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
if (!ddata)
return -ENOMEM;
platform_set_drvdata(pdev, ddata);
ddata->dev = &pdev->dev;
if (dev_get_platdata(&pdev->dev)) {
r = hdmic_probe_pdata(pdev);
if (r)
return r;
} else if (pdev->dev.of_node) {
r = hdmic_probe_of(pdev);
if (r)
return r;
} else {
return -ENODEV;
}
if (gpio_is_valid(ddata->hpd_gpio)) {
r = devm_gpio_request_one(&pdev->dev, ddata->hpd_gpio,
GPIOF_DIR_IN, "hdmi_hpd");
if (r)
goto err_reg;
}
ddata->timings = hdmic_default_timings;
dssdev = &ddata->dssdev;
dssdev->driver = &hdmic_driver;
dssdev->dev = &pdev->dev;
dssdev->type = OMAP_DISPLAY_TYPE_HDMI;
dssdev->owner = THIS_MODULE;
dssdev->panel.timings = hdmic_default_timings;
r = omapdss_register_display(dssdev);
if (r) {
dev_err(&pdev->dev, "Failed to register panel\n");
goto err_reg;
}
return 0;
err_reg:
omap_dss_put_device(ddata->in);
return r;
}
static int __exit hdmic_remove(struct platform_device *pdev)
{
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
struct omap_dss_device *dssdev = &ddata->dssdev;
struct omap_dss_device *in = ddata->in;
omapdss_unregister_display(&ddata->dssdev);
hdmic_disable(dssdev);
hdmic_disconnect(dssdev);
omap_dss_put_device(in);
return 0;
}
static const struct of_device_id hdmic_of_match[] = {
{ .compatible = "omapdss,hdmi-connector", },
{},
};
MODULE_DEVICE_TABLE(of, hdmic_of_match);
static struct platform_driver hdmi_connector_driver = {
.probe = hdmic_probe,
.remove = __exit_p(hdmic_remove),
.driver = {
.name = "connector-hdmi",
.of_match_table = hdmic_of_match,
.suppress_bind_attrs = true,
},
};
module_platform_driver(hdmi_connector_driver);
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
MODULE_DESCRIPTION("HDMI Connector driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,278 @@
/*
* OPA362 analog video amplifier with output/power control
*
* Copyright (C) 2014 Golden Delicious Computers
* Author: H. Nikolaus Schaller <hns@goldelico.com>
*
* based on encoder-tfp410
*
* Copyright (C) 2013 Texas Instruments
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/of_gpio.h>
#include <video/omapdss.h>
struct panel_drv_data {
struct omap_dss_device dssdev;
struct omap_dss_device *in;
struct gpio_desc *enable_gpio;
struct omap_video_timings timings;
};
#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
static int opa362_connect(struct omap_dss_device *dssdev,
struct omap_dss_device *dst)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
int r;
dev_dbg(dssdev->dev, "connect\n");
if (omapdss_device_is_connected(dssdev))
return -EBUSY;
r = in->ops.atv->connect(in, dssdev);
if (r)
return r;
dst->src = dssdev;
dssdev->dst = dst;
return 0;
}
static void opa362_disconnect(struct omap_dss_device *dssdev,
struct omap_dss_device *dst)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
dev_dbg(dssdev->dev, "disconnect\n");
WARN_ON(!omapdss_device_is_connected(dssdev));
if (!omapdss_device_is_connected(dssdev))
return;
WARN_ON(dst != dssdev->dst);
if (dst != dssdev->dst)
return;
dst->src = NULL;
dssdev->dst = NULL;
in->ops.atv->disconnect(in, &ddata->dssdev);
}
static int opa362_enable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
int r;
dev_dbg(dssdev->dev, "enable\n");
if (!omapdss_device_is_connected(dssdev))
return -ENODEV;
if (omapdss_device_is_enabled(dssdev))
return 0;
in->ops.atv->set_timings(in, &ddata->timings);
r = in->ops.atv->enable(in);
if (r)
return r;
if (ddata->enable_gpio)
gpiod_set_value_cansleep(ddata->enable_gpio, 1);
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return 0;
}
static void opa362_disable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
dev_dbg(dssdev->dev, "disable\n");
if (!omapdss_device_is_enabled(dssdev))
return;
if (ddata->enable_gpio)
gpiod_set_value_cansleep(ddata->enable_gpio, 0);
in->ops.atv->disable(in);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
}
static void opa362_set_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
dev_dbg(dssdev->dev, "set_timings\n");
ddata->timings = *timings;
dssdev->panel.timings = *timings;
in->ops.atv->set_timings(in, timings);
}
static void opa362_get_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
dev_dbg(dssdev->dev, "get_timings\n");
*timings = ddata->timings;
}
static int opa362_check_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
dev_dbg(dssdev->dev, "check_timings\n");
return in->ops.atv->check_timings(in, timings);
}
static void opa362_set_type(struct omap_dss_device *dssdev,
enum omap_dss_venc_type type)
{
/* we can only drive a COMPOSITE output */
WARN_ON(type != OMAP_DSS_VENC_TYPE_COMPOSITE);
}
static const struct omapdss_atv_ops opa362_atv_ops = {
.connect = opa362_connect,
.disconnect = opa362_disconnect,
.enable = opa362_enable,
.disable = opa362_disable,
.check_timings = opa362_check_timings,
.set_timings = opa362_set_timings,
.get_timings = opa362_get_timings,
.set_type = opa362_set_type,
};
static int opa362_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct panel_drv_data *ddata;
struct omap_dss_device *dssdev, *in;
struct gpio_desc *gpio;
int r;
dev_dbg(&pdev->dev, "probe\n");
if (node == NULL) {
dev_err(&pdev->dev, "Unable to find device tree\n");
return -EINVAL;
}
ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
if (!ddata)
return -ENOMEM;
platform_set_drvdata(pdev, ddata);
gpio = devm_gpiod_get_optional(&pdev->dev, "enable", GPIOD_OUT_LOW);
if (IS_ERR(gpio))
return PTR_ERR(gpio);
ddata->enable_gpio = gpio;
in = omapdss_of_find_source_for_first_ep(node);
if (IS_ERR(in)) {
dev_err(&pdev->dev, "failed to find video source\n");
return PTR_ERR(in);
}
ddata->in = in;
dssdev = &ddata->dssdev;
dssdev->ops.atv = &opa362_atv_ops;
dssdev->dev = &pdev->dev;
dssdev->type = OMAP_DISPLAY_TYPE_VENC;
dssdev->output_type = OMAP_DISPLAY_TYPE_VENC;
dssdev->owner = THIS_MODULE;
r = omapdss_register_output(dssdev);
if (r) {
dev_err(&pdev->dev, "Failed to register output\n");
goto err_reg;
}
return 0;
err_reg:
omap_dss_put_device(ddata->in);
return r;
}
static int __exit opa362_remove(struct platform_device *pdev)
{
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
struct omap_dss_device *dssdev = &ddata->dssdev;
struct omap_dss_device *in = ddata->in;
omapdss_unregister_output(&ddata->dssdev);
WARN_ON(omapdss_device_is_enabled(dssdev));
if (omapdss_device_is_enabled(dssdev))
opa362_disable(dssdev);
WARN_ON(omapdss_device_is_connected(dssdev));
if (omapdss_device_is_connected(dssdev))
opa362_disconnect(dssdev, dssdev->dst);
omap_dss_put_device(in);
return 0;
}
static const struct of_device_id opa362_of_match[] = {
{ .compatible = "omapdss,ti,opa362", },
{},
};
MODULE_DEVICE_TABLE(of, opa362_of_match);
static struct platform_driver opa362_driver = {
.probe = opa362_probe,
.remove = __exit_p(opa362_remove),
.driver = {
.name = "amplifier-opa362",
.of_match_table = opa362_of_match,
.suppress_bind_attrs = true,
},
};
module_platform_driver(opa362_driver);
MODULE_AUTHOR("H. Nikolaus Schaller <hns@goldelico.com>");
MODULE_DESCRIPTION("OPA362 analog video amplifier with output/power control");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,320 @@
/*
* TFP410 DPI-to-DVI encoder driver
*
* Copyright (C) 2013 Texas Instruments
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/of_gpio.h>
#include <video/omapdss.h>
#include <video/omap-panel-data.h>
struct panel_drv_data {
struct omap_dss_device dssdev;
struct omap_dss_device *in;
int pd_gpio;
int data_lines;
struct omap_video_timings timings;
};
#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
static int tfp410_connect(struct omap_dss_device *dssdev,
struct omap_dss_device *dst)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
int r;
if (omapdss_device_is_connected(dssdev))
return -EBUSY;
r = in->ops.dpi->connect(in, dssdev);
if (r)
return r;
dst->src = dssdev;
dssdev->dst = dst;
return 0;
}
static void tfp410_disconnect(struct omap_dss_device *dssdev,
struct omap_dss_device *dst)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
WARN_ON(!omapdss_device_is_connected(dssdev));
if (!omapdss_device_is_connected(dssdev))
return;
WARN_ON(dst != dssdev->dst);
if (dst != dssdev->dst)
return;
dst->src = NULL;
dssdev->dst = NULL;
in->ops.dpi->disconnect(in, &ddata->dssdev);
}
static int tfp410_enable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
int r;
if (!omapdss_device_is_connected(dssdev))
return -ENODEV;
if (omapdss_device_is_enabled(dssdev))
return 0;
in->ops.dpi->set_timings(in, &ddata->timings);
if (ddata->data_lines)
in->ops.dpi->set_data_lines(in, ddata->data_lines);
r = in->ops.dpi->enable(in);
if (r)
return r;
if (gpio_is_valid(ddata->pd_gpio))
gpio_set_value_cansleep(ddata->pd_gpio, 1);
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return 0;
}
static void tfp410_disable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
if (!omapdss_device_is_enabled(dssdev))
return;
if (gpio_is_valid(ddata->pd_gpio))
gpio_set_value_cansleep(ddata->pd_gpio, 0);
in->ops.dpi->disable(in);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
}
static void tfp410_fix_timings(struct omap_video_timings *timings)
{
timings->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
timings->sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
timings->de_level = OMAPDSS_SIG_ACTIVE_HIGH;
}
static void tfp410_set_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
tfp410_fix_timings(timings);
ddata->timings = *timings;
dssdev->panel.timings = *timings;
in->ops.dpi->set_timings(in, timings);
}
static void tfp410_get_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
*timings = ddata->timings;
}
static int tfp410_check_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
tfp410_fix_timings(timings);
return in->ops.dpi->check_timings(in, timings);
}
static const struct omapdss_dvi_ops tfp410_dvi_ops = {
.connect = tfp410_connect,
.disconnect = tfp410_disconnect,
.enable = tfp410_enable,
.disable = tfp410_disable,
.check_timings = tfp410_check_timings,
.set_timings = tfp410_set_timings,
.get_timings = tfp410_get_timings,
};
static int tfp410_probe_pdata(struct platform_device *pdev)
{
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
struct encoder_tfp410_platform_data *pdata;
struct omap_dss_device *dssdev, *in;
pdata = dev_get_platdata(&pdev->dev);
ddata->pd_gpio = pdata->power_down_gpio;
ddata->data_lines = pdata->data_lines;
in = omap_dss_find_output(pdata->source);
if (in == NULL) {
dev_err(&pdev->dev, "Failed to find video source\n");
return -ENODEV;
}
ddata->in = in;
dssdev = &ddata->dssdev;
dssdev->name = pdata->name;
return 0;
}
static int tfp410_probe_of(struct platform_device *pdev)
{
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
struct device_node *node = pdev->dev.of_node;
struct omap_dss_device *in;
int gpio;
gpio = of_get_named_gpio(node, "powerdown-gpios", 0);
if (gpio_is_valid(gpio) || gpio == -ENOENT) {
ddata->pd_gpio = gpio;
} else {
dev_err(&pdev->dev, "failed to parse PD gpio\n");
return gpio;
}
in = omapdss_of_find_source_for_first_ep(node);
if (IS_ERR(in)) {
dev_err(&pdev->dev, "failed to find video source\n");
return PTR_ERR(in);
}
ddata->in = in;
return 0;
}
static int tfp410_probe(struct platform_device *pdev)
{
struct panel_drv_data *ddata;
struct omap_dss_device *dssdev;
int r;
ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
if (!ddata)
return -ENOMEM;
platform_set_drvdata(pdev, ddata);
if (dev_get_platdata(&pdev->dev)) {
r = tfp410_probe_pdata(pdev);
if (r)
return r;
} else if (pdev->dev.of_node) {
r = tfp410_probe_of(pdev);
if (r)
return r;
} else {
return -ENODEV;
}
if (gpio_is_valid(ddata->pd_gpio)) {
r = devm_gpio_request_one(&pdev->dev, ddata->pd_gpio,
GPIOF_OUT_INIT_LOW, "tfp410 PD");
if (r) {
dev_err(&pdev->dev, "Failed to request PD GPIO %d\n",
ddata->pd_gpio);
goto err_gpio;
}
}
dssdev = &ddata->dssdev;
dssdev->ops.dvi = &tfp410_dvi_ops;
dssdev->dev = &pdev->dev;
dssdev->type = OMAP_DISPLAY_TYPE_DPI;
dssdev->output_type = OMAP_DISPLAY_TYPE_DVI;
dssdev->owner = THIS_MODULE;
dssdev->phy.dpi.data_lines = ddata->data_lines;
dssdev->port_num = 1;
r = omapdss_register_output(dssdev);
if (r) {
dev_err(&pdev->dev, "Failed to register output\n");
goto err_reg;
}
return 0;
err_reg:
err_gpio:
omap_dss_put_device(ddata->in);
return r;
}
static int __exit tfp410_remove(struct platform_device *pdev)
{
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
struct omap_dss_device *dssdev = &ddata->dssdev;
struct omap_dss_device *in = ddata->in;
omapdss_unregister_output(&ddata->dssdev);
WARN_ON(omapdss_device_is_enabled(dssdev));
if (omapdss_device_is_enabled(dssdev))
tfp410_disable(dssdev);
WARN_ON(omapdss_device_is_connected(dssdev));
if (omapdss_device_is_connected(dssdev))
tfp410_disconnect(dssdev, dssdev->dst);
omap_dss_put_device(in);
return 0;
}
static const struct of_device_id tfp410_of_match[] = {
{ .compatible = "omapdss,ti,tfp410", },
{},
};
MODULE_DEVICE_TABLE(of, tfp410_of_match);
static struct platform_driver tfp410_driver = {
.probe = tfp410_probe,
.remove = __exit_p(tfp410_remove),
.driver = {
.name = "tfp410",
.of_match_table = tfp410_of_match,
.suppress_bind_attrs = true,
},
};
module_platform_driver(tfp410_driver);
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
MODULE_DESCRIPTION("TFP410 DPI to DVI encoder driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,328 @@
/*
* TPD12S015 HDMI ESD protection & level shifter chip driver
*
* Copyright (C) 2013 Texas Instruments
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/gpio/consumer.h>
#include <video/omapdss.h>
#include <video/omap-panel-data.h>
struct panel_drv_data {
struct omap_dss_device dssdev;
struct omap_dss_device *in;
struct gpio_desc *ct_cp_hpd_gpio;
struct gpio_desc *ls_oe_gpio;
struct gpio_desc *hpd_gpio;
struct omap_video_timings timings;
};
#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
static int tpd_connect(struct omap_dss_device *dssdev,
struct omap_dss_device *dst)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
int r;
r = in->ops.hdmi->connect(in, dssdev);
if (r)
return r;
dst->src = dssdev;
dssdev->dst = dst;
if (ddata->ct_cp_hpd_gpio) {
gpiod_set_value_cansleep(ddata->ct_cp_hpd_gpio, 1);
/* DC-DC converter needs at max 300us to get to 90% of 5V */
udelay(300);
}
return 0;
}
static void tpd_disconnect(struct omap_dss_device *dssdev,
struct omap_dss_device *dst)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
WARN_ON(dst != dssdev->dst);
if (dst != dssdev->dst)
return;
gpiod_set_value_cansleep(ddata->ct_cp_hpd_gpio, 0);
dst->src = NULL;
dssdev->dst = NULL;
in->ops.hdmi->disconnect(in, &ddata->dssdev);
}
static int tpd_enable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
int r;
if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
return 0;
in->ops.hdmi->set_timings(in, &ddata->timings);
r = in->ops.hdmi->enable(in);
if (r)
return r;
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return r;
}
static void tpd_disable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
return;
in->ops.hdmi->disable(in);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
}
static void tpd_set_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
ddata->timings = *timings;
dssdev->panel.timings = *timings;
in->ops.hdmi->set_timings(in, timings);
}
static void tpd_get_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
*timings = ddata->timings;
}
static int tpd_check_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
int r;
r = in->ops.hdmi->check_timings(in, timings);
return r;
}
static int tpd_read_edid(struct omap_dss_device *dssdev,
u8 *edid, int len)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
int r;
if (!gpiod_get_value_cansleep(ddata->hpd_gpio))
return -ENODEV;
gpiod_set_value_cansleep(ddata->ls_oe_gpio, 1);
r = in->ops.hdmi->read_edid(in, edid, len);
gpiod_set_value_cansleep(ddata->ls_oe_gpio, 0);
return r;
}
static bool tpd_detect(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
return gpiod_get_value_cansleep(ddata->hpd_gpio);
}
static int tpd_set_infoframe(struct omap_dss_device *dssdev,
const struct hdmi_avi_infoframe *avi)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
return in->ops.hdmi->set_infoframe(in, avi);
}
static int tpd_set_hdmi_mode(struct omap_dss_device *dssdev,
bool hdmi_mode)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
return in->ops.hdmi->set_hdmi_mode(in, hdmi_mode);
}
static const struct omapdss_hdmi_ops tpd_hdmi_ops = {
.connect = tpd_connect,
.disconnect = tpd_disconnect,
.enable = tpd_enable,
.disable = tpd_disable,
.check_timings = tpd_check_timings,
.set_timings = tpd_set_timings,
.get_timings = tpd_get_timings,
.read_edid = tpd_read_edid,
.detect = tpd_detect,
.set_infoframe = tpd_set_infoframe,
.set_hdmi_mode = tpd_set_hdmi_mode,
};
static int tpd_probe_of(struct platform_device *pdev)
{
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
struct device_node *node = pdev->dev.of_node;
struct omap_dss_device *in;
in = omapdss_of_find_source_for_first_ep(node);
if (IS_ERR(in)) {
dev_err(&pdev->dev, "failed to find video source\n");
return PTR_ERR(in);
}
ddata->in = in;
return 0;
}
static int tpd_probe(struct platform_device *pdev)
{
struct omap_dss_device *in, *dssdev;
struct panel_drv_data *ddata;
int r;
struct gpio_desc *gpio;
ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
if (!ddata)
return -ENOMEM;
platform_set_drvdata(pdev, ddata);
if (pdev->dev.of_node) {
r = tpd_probe_of(pdev);
if (r)
return r;
} else {
return -ENODEV;
}
gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 0,
GPIOD_OUT_LOW);
if (IS_ERR(gpio))
goto err_gpio;
ddata->ct_cp_hpd_gpio = gpio;
gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 1,
GPIOD_OUT_LOW);
if (IS_ERR(gpio))
goto err_gpio;
ddata->ls_oe_gpio = gpio;
gpio = devm_gpiod_get_index(&pdev->dev, NULL, 2,
GPIOD_IN);
if (IS_ERR(gpio))
goto err_gpio;
ddata->hpd_gpio = gpio;
dssdev = &ddata->dssdev;
dssdev->ops.hdmi = &tpd_hdmi_ops;
dssdev->dev = &pdev->dev;
dssdev->type = OMAP_DISPLAY_TYPE_HDMI;
dssdev->output_type = OMAP_DISPLAY_TYPE_HDMI;
dssdev->owner = THIS_MODULE;
dssdev->port_num = 1;
in = ddata->in;
r = omapdss_register_output(dssdev);
if (r) {
dev_err(&pdev->dev, "Failed to register output\n");
goto err_reg;
}
return 0;
err_reg:
err_gpio:
omap_dss_put_device(ddata->in);
return r;
}
static int __exit tpd_remove(struct platform_device *pdev)
{
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
struct omap_dss_device *dssdev = &ddata->dssdev;
struct omap_dss_device *in = ddata->in;
omapdss_unregister_output(&ddata->dssdev);
WARN_ON(omapdss_device_is_enabled(dssdev));
if (omapdss_device_is_enabled(dssdev))
tpd_disable(dssdev);
WARN_ON(omapdss_device_is_connected(dssdev));
if (omapdss_device_is_connected(dssdev))
tpd_disconnect(dssdev, dssdev->dst);
omap_dss_put_device(in);
return 0;
}
static const struct of_device_id tpd_of_match[] = {
{ .compatible = "omapdss,ti,tpd12s015", },
{},
};
MODULE_DEVICE_TABLE(of, tpd_of_match);
static struct platform_driver tpd_driver = {
.probe = tpd_probe,
.remove = __exit_p(tpd_remove),
.driver = {
.name = "tpd12s015",
.of_match_table = tpd_of_match,
.suppress_bind_attrs = true,
},
};
module_platform_driver(tpd_driver);
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
MODULE_DESCRIPTION("TPD12S015 driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,328 @@
/*
* Generic MIPI DPI Panel Driver
*
* Copyright (C) 2013 Texas Instruments
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <video/omapdss.h>
#include <video/omap-panel-data.h>
#include <video/of_display_timing.h>
struct panel_drv_data {
struct omap_dss_device dssdev;
struct omap_dss_device *in;
int data_lines;
struct omap_video_timings videomode;
/* used for non-DT boot, to be removed */
int backlight_gpio;
struct gpio_desc *enable_gpio;
};
#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
static int panel_dpi_connect(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
int r;
if (omapdss_device_is_connected(dssdev))
return 0;
r = in->ops.dpi->connect(in, dssdev);
if (r)
return r;
return 0;
}
static void panel_dpi_disconnect(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
if (!omapdss_device_is_connected(dssdev))
return;
in->ops.dpi->disconnect(in, dssdev);
}
static int panel_dpi_enable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
int r;
if (!omapdss_device_is_connected(dssdev))
return -ENODEV;
if (omapdss_device_is_enabled(dssdev))
return 0;
if (ddata->data_lines)
in->ops.dpi->set_data_lines(in, ddata->data_lines);
in->ops.dpi->set_timings(in, &ddata->videomode);
r = in->ops.dpi->enable(in);
if (r)
return r;
gpiod_set_value_cansleep(ddata->enable_gpio, 1);
if (gpio_is_valid(ddata->backlight_gpio))
gpio_set_value_cansleep(ddata->backlight_gpio, 1);
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return 0;
}
static void panel_dpi_disable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
if (!omapdss_device_is_enabled(dssdev))
return;
if (gpio_is_valid(ddata->backlight_gpio))
gpio_set_value_cansleep(ddata->backlight_gpio, 0);
gpiod_set_value_cansleep(ddata->enable_gpio, 0);
in->ops.dpi->disable(in);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
}
static void panel_dpi_set_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
ddata->videomode = *timings;
dssdev->panel.timings = *timings;
in->ops.dpi->set_timings(in, timings);
}
static void panel_dpi_get_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
*timings = ddata->videomode;
}
static int panel_dpi_check_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
return in->ops.dpi->check_timings(in, timings);
}
static struct omap_dss_driver panel_dpi_ops = {
.connect = panel_dpi_connect,
.disconnect = panel_dpi_disconnect,
.enable = panel_dpi_enable,
.disable = panel_dpi_disable,
.set_timings = panel_dpi_set_timings,
.get_timings = panel_dpi_get_timings,
.check_timings = panel_dpi_check_timings,
.get_resolution = omapdss_default_get_resolution,
};
static int panel_dpi_probe_pdata(struct platform_device *pdev)
{
const struct panel_dpi_platform_data *pdata;
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
struct omap_dss_device *dssdev, *in;
struct videomode vm;
int r;
pdata = dev_get_platdata(&pdev->dev);
in = omap_dss_find_output(pdata->source);
if (in == NULL) {
dev_err(&pdev->dev, "failed to find video source '%s'\n",
pdata->source);
return -EPROBE_DEFER;
}
ddata->in = in;
ddata->data_lines = pdata->data_lines;
videomode_from_timing(pdata->display_timing, &vm);
videomode_to_omap_video_timings(&vm, &ddata->videomode);
dssdev = &ddata->dssdev;
dssdev->name = pdata->name;
r = devm_gpio_request_one(&pdev->dev, pdata->enable_gpio,
GPIOF_OUT_INIT_LOW, "panel enable");
if (r)
goto err_gpio;
ddata->enable_gpio = gpio_to_desc(pdata->enable_gpio);
ddata->backlight_gpio = pdata->backlight_gpio;
return 0;
err_gpio:
omap_dss_put_device(ddata->in);
return r;
}
static int panel_dpi_probe_of(struct platform_device *pdev)
{
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
struct device_node *node = pdev->dev.of_node;
struct omap_dss_device *in;
int r;
struct display_timing timing;
struct videomode vm;
struct gpio_desc *gpio;
gpio = devm_gpiod_get_optional(&pdev->dev, "enable", GPIOD_OUT_LOW);
if (IS_ERR(gpio))
return PTR_ERR(gpio);
ddata->enable_gpio = gpio;
ddata->backlight_gpio = -ENOENT;
r = of_get_display_timing(node, "panel-timing", &timing);
if (r) {
dev_err(&pdev->dev, "failed to get video timing\n");
return r;
}
videomode_from_timing(&timing, &vm);
videomode_to_omap_video_timings(&vm, &ddata->videomode);
in = omapdss_of_find_source_for_first_ep(node);
if (IS_ERR(in)) {
dev_err(&pdev->dev, "failed to find video source\n");
return PTR_ERR(in);
}
ddata->in = in;
return 0;
}
static int panel_dpi_probe(struct platform_device *pdev)
{
struct panel_drv_data *ddata;
struct omap_dss_device *dssdev;
int r;
ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
if (ddata == NULL)
return -ENOMEM;
platform_set_drvdata(pdev, ddata);
if (dev_get_platdata(&pdev->dev)) {
r = panel_dpi_probe_pdata(pdev);
if (r)
return r;
} else if (pdev->dev.of_node) {
r = panel_dpi_probe_of(pdev);
if (r)
return r;
} else {
return -ENODEV;
}
if (gpio_is_valid(ddata->backlight_gpio)) {
r = devm_gpio_request_one(&pdev->dev, ddata->backlight_gpio,
GPIOF_OUT_INIT_LOW, "panel backlight");
if (r)
goto err_gpio;
}
dssdev = &ddata->dssdev;
dssdev->dev = &pdev->dev;
dssdev->driver = &panel_dpi_ops;
dssdev->type = OMAP_DISPLAY_TYPE_DPI;
dssdev->owner = THIS_MODULE;
dssdev->panel.timings = ddata->videomode;
dssdev->phy.dpi.data_lines = ddata->data_lines;
r = omapdss_register_display(dssdev);
if (r) {
dev_err(&pdev->dev, "Failed to register panel\n");
goto err_reg;
}
return 0;
err_reg:
err_gpio:
omap_dss_put_device(ddata->in);
return r;
}
static int __exit panel_dpi_remove(struct platform_device *pdev)
{
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
struct omap_dss_device *dssdev = &ddata->dssdev;
struct omap_dss_device *in = ddata->in;
omapdss_unregister_display(dssdev);
panel_dpi_disable(dssdev);
panel_dpi_disconnect(dssdev);
omap_dss_put_device(in);
return 0;
}
static const struct of_device_id panel_dpi_of_match[] = {
{ .compatible = "omapdss,panel-dpi", },
{},
};
MODULE_DEVICE_TABLE(of, panel_dpi_of_match);
static struct platform_driver panel_dpi_driver = {
.probe = panel_dpi_probe,
.remove = __exit_p(panel_dpi_remove),
.driver = {
.name = "panel-dpi",
.of_match_table = panel_dpi_of_match,
.suppress_bind_attrs = true,
},
};
module_platform_driver(panel_dpi_driver);
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
MODULE_DESCRIPTION("Generic MIPI DPI Panel Driver");
MODULE_LICENSE("GPL");

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,404 @@
/*
* LG.Philips LB035Q02 LCD Panel driver
*
* Copyright (C) 2013 Texas Instruments
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
* Based on a driver by: Steve Sakoman <steve@sakoman.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/spi/spi.h>
#include <linux/mutex.h>
#include <linux/gpio.h>
#include <video/omapdss.h>
#include <video/omap-panel-data.h>
static struct omap_video_timings lb035q02_timings = {
.x_res = 320,
.y_res = 240,
.pixelclock = 6500000,
.hsw = 2,
.hfp = 20,
.hbp = 68,
.vsw = 2,
.vfp = 4,
.vbp = 18,
.vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
.hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
.data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
.de_level = OMAPDSS_SIG_ACTIVE_HIGH,
.sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
};
struct panel_drv_data {
struct omap_dss_device dssdev;
struct omap_dss_device *in;
struct spi_device *spi;
int data_lines;
struct omap_video_timings videomode;
/* used for non-DT boot, to be removed */
int backlight_gpio;
struct gpio_desc *enable_gpio;
};
#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
static int lb035q02_write_reg(struct spi_device *spi, u8 reg, u16 val)
{
struct spi_message msg;
struct spi_transfer index_xfer = {
.len = 3,
.cs_change = 1,
};
struct spi_transfer value_xfer = {
.len = 3,
};
u8 buffer[16];
spi_message_init(&msg);
/* register index */
buffer[0] = 0x70;
buffer[1] = 0x00;
buffer[2] = reg & 0x7f;
index_xfer.tx_buf = buffer;
spi_message_add_tail(&index_xfer, &msg);
/* register value */
buffer[4] = 0x72;
buffer[5] = val >> 8;
buffer[6] = val;
value_xfer.tx_buf = buffer + 4;
spi_message_add_tail(&value_xfer, &msg);
return spi_sync(spi, &msg);
}
static void init_lb035q02_panel(struct spi_device *spi)
{
/* Init sequence from page 28 of the lb035q02 spec */
lb035q02_write_reg(spi, 0x01, 0x6300);
lb035q02_write_reg(spi, 0x02, 0x0200);
lb035q02_write_reg(spi, 0x03, 0x0177);
lb035q02_write_reg(spi, 0x04, 0x04c7);
lb035q02_write_reg(spi, 0x05, 0xffc0);
lb035q02_write_reg(spi, 0x06, 0xe806);
lb035q02_write_reg(spi, 0x0a, 0x4008);
lb035q02_write_reg(spi, 0x0b, 0x0000);
lb035q02_write_reg(spi, 0x0d, 0x0030);
lb035q02_write_reg(spi, 0x0e, 0x2800);
lb035q02_write_reg(spi, 0x0f, 0x0000);
lb035q02_write_reg(spi, 0x16, 0x9f80);
lb035q02_write_reg(spi, 0x17, 0x0a0f);
lb035q02_write_reg(spi, 0x1e, 0x00c1);
lb035q02_write_reg(spi, 0x30, 0x0300);
lb035q02_write_reg(spi, 0x31, 0x0007);
lb035q02_write_reg(spi, 0x32, 0x0000);
lb035q02_write_reg(spi, 0x33, 0x0000);
lb035q02_write_reg(spi, 0x34, 0x0707);
lb035q02_write_reg(spi, 0x35, 0x0004);
lb035q02_write_reg(spi, 0x36, 0x0302);
lb035q02_write_reg(spi, 0x37, 0x0202);
lb035q02_write_reg(spi, 0x3a, 0x0a0d);
lb035q02_write_reg(spi, 0x3b, 0x0806);
}
static int lb035q02_connect(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
int r;
if (omapdss_device_is_connected(dssdev))
return 0;
r = in->ops.dpi->connect(in, dssdev);
if (r)
return r;
init_lb035q02_panel(ddata->spi);
return 0;
}
static void lb035q02_disconnect(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
if (!omapdss_device_is_connected(dssdev))
return;
in->ops.dpi->disconnect(in, dssdev);
}
static int lb035q02_enable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
int r;
if (!omapdss_device_is_connected(dssdev))
return -ENODEV;
if (omapdss_device_is_enabled(dssdev))
return 0;
if (ddata->data_lines)
in->ops.dpi->set_data_lines(in, ddata->data_lines);
in->ops.dpi->set_timings(in, &ddata->videomode);
r = in->ops.dpi->enable(in);
if (r)
return r;
if (ddata->enable_gpio)
gpiod_set_value_cansleep(ddata->enable_gpio, 1);
if (gpio_is_valid(ddata->backlight_gpio))
gpio_set_value_cansleep(ddata->backlight_gpio, 1);
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return 0;
}
static void lb035q02_disable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
if (!omapdss_device_is_enabled(dssdev))
return;
if (ddata->enable_gpio)
gpiod_set_value_cansleep(ddata->enable_gpio, 0);
if (gpio_is_valid(ddata->backlight_gpio))
gpio_set_value_cansleep(ddata->backlight_gpio, 0);
in->ops.dpi->disable(in);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
}
static void lb035q02_set_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
ddata->videomode = *timings;
dssdev->panel.timings = *timings;
in->ops.dpi->set_timings(in, timings);
}
static void lb035q02_get_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
*timings = ddata->videomode;
}
static int lb035q02_check_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
return in->ops.dpi->check_timings(in, timings);
}
static struct omap_dss_driver lb035q02_ops = {
.connect = lb035q02_connect,
.disconnect = lb035q02_disconnect,
.enable = lb035q02_enable,
.disable = lb035q02_disable,
.set_timings = lb035q02_set_timings,
.get_timings = lb035q02_get_timings,
.check_timings = lb035q02_check_timings,
.get_resolution = omapdss_default_get_resolution,
};
static int lb035q02_probe_pdata(struct spi_device *spi)
{
const struct panel_lb035q02_platform_data *pdata;
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
struct omap_dss_device *dssdev, *in;
int r;
pdata = dev_get_platdata(&spi->dev);
in = omap_dss_find_output(pdata->source);
if (in == NULL) {
dev_err(&spi->dev, "failed to find video source '%s'\n",
pdata->source);
return -EPROBE_DEFER;
}
ddata->in = in;
ddata->data_lines = pdata->data_lines;
dssdev = &ddata->dssdev;
dssdev->name = pdata->name;
r = devm_gpio_request_one(&spi->dev, pdata->enable_gpio,
GPIOF_OUT_INIT_LOW, "panel enable");
if (r)
goto err_gpio;
ddata->enable_gpio = gpio_to_desc(pdata->enable_gpio);
ddata->backlight_gpio = pdata->backlight_gpio;
return 0;
err_gpio:
omap_dss_put_device(ddata->in);
return r;
}
static int lb035q02_probe_of(struct spi_device *spi)
{
struct device_node *node = spi->dev.of_node;
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
struct omap_dss_device *in;
struct gpio_desc *gpio;
gpio = devm_gpiod_get(&spi->dev, "enable", GPIOD_OUT_LOW);
if (IS_ERR(gpio)) {
dev_err(&spi->dev, "failed to parse enable gpio\n");
return PTR_ERR(gpio);
}
ddata->enable_gpio = gpio;
ddata->backlight_gpio = -ENOENT;
in = omapdss_of_find_source_for_first_ep(node);
if (IS_ERR(in)) {
dev_err(&spi->dev, "failed to find video source\n");
return PTR_ERR(in);
}
ddata->in = in;
return 0;
}
static int lb035q02_panel_spi_probe(struct spi_device *spi)
{
struct panel_drv_data *ddata;
struct omap_dss_device *dssdev;
int r;
ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL);
if (ddata == NULL)
return -ENOMEM;
dev_set_drvdata(&spi->dev, ddata);
ddata->spi = spi;
if (dev_get_platdata(&spi->dev)) {
r = lb035q02_probe_pdata(spi);
if (r)
return r;
} else if (spi->dev.of_node) {
r = lb035q02_probe_of(spi);
if (r)
return r;
} else {
return -ENODEV;
}
if (gpio_is_valid(ddata->backlight_gpio)) {
r = devm_gpio_request_one(&spi->dev, ddata->backlight_gpio,
GPIOF_OUT_INIT_LOW, "panel backlight");
if (r)
goto err_gpio;
}
ddata->videomode = lb035q02_timings;
dssdev = &ddata->dssdev;
dssdev->dev = &spi->dev;
dssdev->driver = &lb035q02_ops;
dssdev->type = OMAP_DISPLAY_TYPE_DPI;
dssdev->owner = THIS_MODULE;
dssdev->panel.timings = ddata->videomode;
dssdev->phy.dpi.data_lines = ddata->data_lines;
r = omapdss_register_display(dssdev);
if (r) {
dev_err(&spi->dev, "Failed to register panel\n");
goto err_reg;
}
return 0;
err_reg:
err_gpio:
omap_dss_put_device(ddata->in);
return r;
}
static int lb035q02_panel_spi_remove(struct spi_device *spi)
{
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
struct omap_dss_device *dssdev = &ddata->dssdev;
struct omap_dss_device *in = ddata->in;
omapdss_unregister_display(dssdev);
lb035q02_disable(dssdev);
lb035q02_disconnect(dssdev);
omap_dss_put_device(in);
return 0;
}
static const struct of_device_id lb035q02_of_match[] = {
{ .compatible = "omapdss,lgphilips,lb035q02", },
{},
};
MODULE_DEVICE_TABLE(of, lb035q02_of_match);
static struct spi_driver lb035q02_spi_driver = {
.probe = lb035q02_panel_spi_probe,
.remove = lb035q02_panel_spi_remove,
.driver = {
.name = "panel_lgphilips_lb035q02",
.of_match_table = lb035q02_of_match,
.suppress_bind_attrs = true,
},
};
module_spi_driver(lb035q02_spi_driver);
MODULE_ALIAS("spi:lgphilips,lb035q02");
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
MODULE_DESCRIPTION("LG.Philips LB035Q02 LCD Panel driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,437 @@
/*
* NEC NL8048HL11 Panel driver
*
* Copyright (C) 2010 Texas Instruments Inc.
* Author: Erik Gilling <konkers@android.com>
* Converted to new DSS device model: Tomi Valkeinen <tomi.valkeinen@ti.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/spi/spi.h>
#include <linux/fb.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <video/omapdss.h>
#include <video/omap-panel-data.h>
struct panel_drv_data {
struct omap_dss_device dssdev;
struct omap_dss_device *in;
struct omap_video_timings videomode;
int data_lines;
int res_gpio;
int qvga_gpio;
struct spi_device *spi;
};
#define LCD_XRES 800
#define LCD_YRES 480
/*
* NEC PIX Clock Ratings
* MIN:21.8MHz TYP:23.8MHz MAX:25.7MHz
*/
#define LCD_PIXEL_CLOCK 23800000
static const struct {
unsigned char addr;
unsigned char dat;
} nec_8048_init_seq[] = {
{ 3, 0x01 }, { 0, 0x00 }, { 1, 0x01 }, { 4, 0x00 }, { 5, 0x14 },
{ 6, 0x24 }, { 16, 0xD7 }, { 17, 0x00 }, { 18, 0x00 }, { 19, 0x55 },
{ 20, 0x01 }, { 21, 0x70 }, { 22, 0x1E }, { 23, 0x25 }, { 24, 0x25 },
{ 25, 0x02 }, { 26, 0x02 }, { 27, 0xA0 }, { 32, 0x2F }, { 33, 0x0F },
{ 34, 0x0F }, { 35, 0x0F }, { 36, 0x0F }, { 37, 0x0F }, { 38, 0x0F },
{ 39, 0x00 }, { 40, 0x02 }, { 41, 0x02 }, { 42, 0x02 }, { 43, 0x0F },
{ 44, 0x0F }, { 45, 0x0F }, { 46, 0x0F }, { 47, 0x0F }, { 48, 0x0F },
{ 49, 0x0F }, { 50, 0x00 }, { 51, 0x02 }, { 52, 0x02 }, { 53, 0x02 },
{ 80, 0x0C }, { 83, 0x42 }, { 84, 0x42 }, { 85, 0x41 }, { 86, 0x14 },
{ 89, 0x88 }, { 90, 0x01 }, { 91, 0x00 }, { 92, 0x02 }, { 93, 0x0C },
{ 94, 0x1C }, { 95, 0x27 }, { 98, 0x49 }, { 99, 0x27 }, { 102, 0x76 },
{ 103, 0x27 }, { 112, 0x01 }, { 113, 0x0E }, { 114, 0x02 },
{ 115, 0x0C }, { 118, 0x0C }, { 121, 0x30 }, { 130, 0x00 },
{ 131, 0x00 }, { 132, 0xFC }, { 134, 0x00 }, { 136, 0x00 },
{ 138, 0x00 }, { 139, 0x00 }, { 140, 0x00 }, { 141, 0xFC },
{ 143, 0x00 }, { 145, 0x00 }, { 147, 0x00 }, { 148, 0x00 },
{ 149, 0x00 }, { 150, 0xFC }, { 152, 0x00 }, { 154, 0x00 },
{ 156, 0x00 }, { 157, 0x00 }, { 2, 0x00 },
};
static const struct omap_video_timings nec_8048_panel_timings = {
.x_res = LCD_XRES,
.y_res = LCD_YRES,
.pixelclock = LCD_PIXEL_CLOCK,
.hfp = 6,
.hsw = 1,
.hbp = 4,
.vfp = 3,
.vsw = 1,
.vbp = 4,
.vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
.hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
.data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
.de_level = OMAPDSS_SIG_ACTIVE_HIGH,
.sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
};
#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
static int nec_8048_spi_send(struct spi_device *spi, unsigned char reg_addr,
unsigned char reg_data)
{
int ret = 0;
unsigned int cmd = 0, data = 0;
cmd = 0x0000 | reg_addr; /* register address write */
data = 0x0100 | reg_data; /* register data write */
data = (cmd << 16) | data;
ret = spi_write(spi, (unsigned char *)&data, 4);
if (ret)
pr_err("error in spi_write %x\n", data);
return ret;
}
static int init_nec_8048_wvga_lcd(struct spi_device *spi)
{
unsigned int i;
/* Initialization Sequence */
/* nec_8048_spi_send(spi, REG, VAL) */
for (i = 0; i < (ARRAY_SIZE(nec_8048_init_seq) - 1); i++)
nec_8048_spi_send(spi, nec_8048_init_seq[i].addr,
nec_8048_init_seq[i].dat);
udelay(20);
nec_8048_spi_send(spi, nec_8048_init_seq[i].addr,
nec_8048_init_seq[i].dat);
return 0;
}
static int nec_8048_connect(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
int r;
if (omapdss_device_is_connected(dssdev))
return 0;
r = in->ops.dpi->connect(in, dssdev);
if (r)
return r;
return 0;
}
static void nec_8048_disconnect(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
if (!omapdss_device_is_connected(dssdev))
return;
in->ops.dpi->disconnect(in, dssdev);
}
static int nec_8048_enable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
int r;
if (!omapdss_device_is_connected(dssdev))
return -ENODEV;
if (omapdss_device_is_enabled(dssdev))
return 0;
if (ddata->data_lines)
in->ops.dpi->set_data_lines(in, ddata->data_lines);
in->ops.dpi->set_timings(in, &ddata->videomode);
r = in->ops.dpi->enable(in);
if (r)
return r;
if (gpio_is_valid(ddata->res_gpio))
gpio_set_value_cansleep(ddata->res_gpio, 1);
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return 0;
}
static void nec_8048_disable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
if (!omapdss_device_is_enabled(dssdev))
return;
if (gpio_is_valid(ddata->res_gpio))
gpio_set_value_cansleep(ddata->res_gpio, 0);
in->ops.dpi->disable(in);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
}
static void nec_8048_set_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
ddata->videomode = *timings;
dssdev->panel.timings = *timings;
in->ops.dpi->set_timings(in, timings);
}
static void nec_8048_get_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
*timings = ddata->videomode;
}
static int nec_8048_check_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
return in->ops.dpi->check_timings(in, timings);
}
static struct omap_dss_driver nec_8048_ops = {
.connect = nec_8048_connect,
.disconnect = nec_8048_disconnect,
.enable = nec_8048_enable,
.disable = nec_8048_disable,
.set_timings = nec_8048_set_timings,
.get_timings = nec_8048_get_timings,
.check_timings = nec_8048_check_timings,
.get_resolution = omapdss_default_get_resolution,
};
static int nec_8048_probe_pdata(struct spi_device *spi)
{
const struct panel_nec_nl8048hl11_platform_data *pdata;
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
struct omap_dss_device *dssdev, *in;
pdata = dev_get_platdata(&spi->dev);
ddata->qvga_gpio = pdata->qvga_gpio;
ddata->res_gpio = pdata->res_gpio;
in = omap_dss_find_output(pdata->source);
if (in == NULL) {
dev_err(&spi->dev, "failed to find video source '%s'\n",
pdata->source);
return -EPROBE_DEFER;
}
ddata->in = in;
ddata->data_lines = pdata->data_lines;
dssdev = &ddata->dssdev;
dssdev->name = pdata->name;
return 0;
}
static int nec_8048_probe_of(struct spi_device *spi)
{
struct device_node *node = spi->dev.of_node;
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
struct omap_dss_device *in;
int gpio;
gpio = of_get_named_gpio(node, "reset-gpios", 0);
if (!gpio_is_valid(gpio)) {
dev_err(&spi->dev, "failed to parse enable gpio\n");
return gpio;
}
ddata->res_gpio = gpio;
/* XXX the panel spec doesn't mention any QVGA pin?? */
ddata->qvga_gpio = -ENOENT;
in = omapdss_of_find_source_for_first_ep(node);
if (IS_ERR(in)) {
dev_err(&spi->dev, "failed to find video source\n");
return PTR_ERR(in);
}
ddata->in = in;
return 0;
}
static int nec_8048_probe(struct spi_device *spi)
{
struct panel_drv_data *ddata;
struct omap_dss_device *dssdev;
int r;
dev_dbg(&spi->dev, "%s\n", __func__);
spi->mode = SPI_MODE_0;
spi->bits_per_word = 32;
r = spi_setup(spi);
if (r < 0) {
dev_err(&spi->dev, "spi_setup failed: %d\n", r);
return r;
}
init_nec_8048_wvga_lcd(spi);
ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL);
if (ddata == NULL)
return -ENOMEM;
dev_set_drvdata(&spi->dev, ddata);
ddata->spi = spi;
if (dev_get_platdata(&spi->dev)) {
r = nec_8048_probe_pdata(spi);
if (r)
return r;
} else if (spi->dev.of_node) {
r = nec_8048_probe_of(spi);
if (r)
return r;
} else {
return -ENODEV;
}
if (gpio_is_valid(ddata->qvga_gpio)) {
r = devm_gpio_request_one(&spi->dev, ddata->qvga_gpio,
GPIOF_OUT_INIT_HIGH, "lcd QVGA");
if (r)
goto err_gpio;
}
if (gpio_is_valid(ddata->res_gpio)) {
r = devm_gpio_request_one(&spi->dev, ddata->res_gpio,
GPIOF_OUT_INIT_LOW, "lcd RES");
if (r)
goto err_gpio;
}
ddata->videomode = nec_8048_panel_timings;
dssdev = &ddata->dssdev;
dssdev->dev = &spi->dev;
dssdev->driver = &nec_8048_ops;
dssdev->type = OMAP_DISPLAY_TYPE_DPI;
dssdev->owner = THIS_MODULE;
dssdev->panel.timings = ddata->videomode;
r = omapdss_register_display(dssdev);
if (r) {
dev_err(&spi->dev, "Failed to register panel\n");
goto err_reg;
}
return 0;
err_reg:
err_gpio:
omap_dss_put_device(ddata->in);
return r;
}
static int nec_8048_remove(struct spi_device *spi)
{
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
struct omap_dss_device *dssdev = &ddata->dssdev;
struct omap_dss_device *in = ddata->in;
dev_dbg(&ddata->spi->dev, "%s\n", __func__);
omapdss_unregister_display(dssdev);
nec_8048_disable(dssdev);
nec_8048_disconnect(dssdev);
omap_dss_put_device(in);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int nec_8048_suspend(struct device *dev)
{
struct spi_device *spi = to_spi_device(dev);
nec_8048_spi_send(spi, 2, 0x01);
mdelay(40);
return 0;
}
static int nec_8048_resume(struct device *dev)
{
struct spi_device *spi = to_spi_device(dev);
/* reinitialize the panel */
spi_setup(spi);
nec_8048_spi_send(spi, 2, 0x00);
init_nec_8048_wvga_lcd(spi);
return 0;
}
static SIMPLE_DEV_PM_OPS(nec_8048_pm_ops, nec_8048_suspend,
nec_8048_resume);
#define NEC_8048_PM_OPS (&nec_8048_pm_ops)
#else
#define NEC_8048_PM_OPS NULL
#endif
static const struct of_device_id nec_8048_of_match[] = {
{ .compatible = "omapdss,nec,nl8048hl11", },
{},
};
MODULE_DEVICE_TABLE(of, nec_8048_of_match);
static struct spi_driver nec_8048_driver = {
.driver = {
.name = "panel-nec-nl8048hl11",
.pm = NEC_8048_PM_OPS,
.of_match_table = nec_8048_of_match,
.suppress_bind_attrs = true,
},
.probe = nec_8048_probe,
.remove = nec_8048_remove,
};
module_spi_driver(nec_8048_driver);
MODULE_ALIAS("spi:nec,nl8048hl11");
MODULE_AUTHOR("Erik Gilling <konkers@android.com>");
MODULE_DESCRIPTION("NEC-NL8048HL11 Driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,415 @@
/*
* LCD panel driver for Sharp LS037V7DW01
*
* Copyright (C) 2013 Texas Instruments
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/regulator/consumer.h>
#include <video/omapdss.h>
#include <video/omap-panel-data.h>
struct panel_drv_data {
struct omap_dss_device dssdev;
struct omap_dss_device *in;
struct regulator *vcc;
int data_lines;
struct omap_video_timings videomode;
struct gpio_desc *resb_gpio; /* low = reset active min 20 us */
struct gpio_desc *ini_gpio; /* high = power on */
struct gpio_desc *mo_gpio; /* low = 480x640, high = 240x320 */
struct gpio_desc *lr_gpio; /* high = conventional horizontal scanning */
struct gpio_desc *ud_gpio; /* high = conventional vertical scanning */
};
static const struct omap_video_timings sharp_ls_timings = {
.x_res = 480,
.y_res = 640,
.pixelclock = 19200000,
.hsw = 2,
.hfp = 1,
.hbp = 28,
.vsw = 1,
.vfp = 1,
.vbp = 1,
.vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
.hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
.data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
.de_level = OMAPDSS_SIG_ACTIVE_HIGH,
.sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
};
#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
static int sharp_ls_connect(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
int r;
if (omapdss_device_is_connected(dssdev))
return 0;
r = in->ops.dpi->connect(in, dssdev);
if (r)
return r;
return 0;
}
static void sharp_ls_disconnect(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
if (!omapdss_device_is_connected(dssdev))
return;
in->ops.dpi->disconnect(in, dssdev);
}
static int sharp_ls_enable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
int r;
if (!omapdss_device_is_connected(dssdev))
return -ENODEV;
if (omapdss_device_is_enabled(dssdev))
return 0;
if (ddata->data_lines)
in->ops.dpi->set_data_lines(in, ddata->data_lines);
in->ops.dpi->set_timings(in, &ddata->videomode);
if (ddata->vcc) {
r = regulator_enable(ddata->vcc);
if (r != 0)
return r;
}
r = in->ops.dpi->enable(in);
if (r) {
regulator_disable(ddata->vcc);
return r;
}
/* wait couple of vsyncs until enabling the LCD */
msleep(50);
if (ddata->resb_gpio)
gpiod_set_value_cansleep(ddata->resb_gpio, 1);
if (ddata->ini_gpio)
gpiod_set_value_cansleep(ddata->ini_gpio, 1);
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return 0;
}
static void sharp_ls_disable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
if (!omapdss_device_is_enabled(dssdev))
return;
if (ddata->ini_gpio)
gpiod_set_value_cansleep(ddata->ini_gpio, 0);
if (ddata->resb_gpio)
gpiod_set_value_cansleep(ddata->resb_gpio, 0);
/* wait at least 5 vsyncs after disabling the LCD */
msleep(100);
in->ops.dpi->disable(in);
if (ddata->vcc)
regulator_disable(ddata->vcc);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
}
static void sharp_ls_set_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
ddata->videomode = *timings;
dssdev->panel.timings = *timings;
in->ops.dpi->set_timings(in, timings);
}
static void sharp_ls_get_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
*timings = ddata->videomode;
}
static int sharp_ls_check_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
return in->ops.dpi->check_timings(in, timings);
}
static struct omap_dss_driver sharp_ls_ops = {
.connect = sharp_ls_connect,
.disconnect = sharp_ls_disconnect,
.enable = sharp_ls_enable,
.disable = sharp_ls_disable,
.set_timings = sharp_ls_set_timings,
.get_timings = sharp_ls_get_timings,
.check_timings = sharp_ls_check_timings,
.get_resolution = omapdss_default_get_resolution,
};
static int sharp_ls_get_gpio(struct device *dev, int gpio, unsigned long flags,
char *desc, struct gpio_desc **gpiod)
{
struct gpio_desc *gd;
int r;
*gpiod = NULL;
r = devm_gpio_request_one(dev, gpio, flags, desc);
if (r)
return r == -ENOENT ? 0 : r;
gd = gpio_to_desc(gpio);
if (IS_ERR(gd))
return PTR_ERR(gd) == -ENOENT ? 0 : PTR_ERR(gd);
*gpiod = gd;
return 0;
}
static int sharp_ls_probe_pdata(struct platform_device *pdev)
{
const struct panel_sharp_ls037v7dw01_platform_data *pdata;
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
struct omap_dss_device *dssdev, *in;
int r;
pdata = dev_get_platdata(&pdev->dev);
in = omap_dss_find_output(pdata->source);
if (in == NULL) {
dev_err(&pdev->dev, "failed to find video source '%s'\n",
pdata->source);
return -EPROBE_DEFER;
}
ddata->in = in;
ddata->data_lines = pdata->data_lines;
dssdev = &ddata->dssdev;
dssdev->name = pdata->name;
r = sharp_ls_get_gpio(&pdev->dev, pdata->mo_gpio, GPIOF_OUT_INIT_LOW,
"lcd MO", &ddata->mo_gpio);
if (r)
return r;
r = sharp_ls_get_gpio(&pdev->dev, pdata->lr_gpio, GPIOF_OUT_INIT_HIGH,
"lcd LR", &ddata->lr_gpio);
if (r)
return r;
r = sharp_ls_get_gpio(&pdev->dev, pdata->ud_gpio, GPIOF_OUT_INIT_HIGH,
"lcd UD", &ddata->ud_gpio);
if (r)
return r;
r = sharp_ls_get_gpio(&pdev->dev, pdata->resb_gpio, GPIOF_OUT_INIT_LOW,
"lcd RESB", &ddata->resb_gpio);
if (r)
return r;
r = sharp_ls_get_gpio(&pdev->dev, pdata->ini_gpio, GPIOF_OUT_INIT_LOW,
"lcd INI", &ddata->ini_gpio);
if (r)
return r;
return 0;
}
static int sharp_ls_get_gpio_of(struct device *dev, int index, int val,
const char *desc, struct gpio_desc **gpiod)
{
struct gpio_desc *gd;
*gpiod = NULL;
gd = devm_gpiod_get_index(dev, desc, index, GPIOD_OUT_LOW);
if (IS_ERR(gd))
return PTR_ERR(gd);
*gpiod = gd;
return 0;
}
static int sharp_ls_probe_of(struct platform_device *pdev)
{
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
struct device_node *node = pdev->dev.of_node;
struct omap_dss_device *in;
int r;
ddata->vcc = devm_regulator_get(&pdev->dev, "envdd");
if (IS_ERR(ddata->vcc)) {
dev_err(&pdev->dev, "failed to get regulator\n");
return PTR_ERR(ddata->vcc);
}
/* lcd INI */
r = sharp_ls_get_gpio_of(&pdev->dev, 0, 0, "enable", &ddata->ini_gpio);
if (r)
return r;
/* lcd RESB */
r = sharp_ls_get_gpio_of(&pdev->dev, 0, 0, "reset", &ddata->resb_gpio);
if (r)
return r;
/* lcd MO */
r = sharp_ls_get_gpio_of(&pdev->dev, 0, 0, "mode", &ddata->mo_gpio);
if (r)
return r;
/* lcd LR */
r = sharp_ls_get_gpio_of(&pdev->dev, 1, 1, "mode", &ddata->lr_gpio);
if (r)
return r;
/* lcd UD */
r = sharp_ls_get_gpio_of(&pdev->dev, 2, 1, "mode", &ddata->ud_gpio);
if (r)
return r;
in = omapdss_of_find_source_for_first_ep(node);
if (IS_ERR(in)) {
dev_err(&pdev->dev, "failed to find video source\n");
return PTR_ERR(in);
}
ddata->in = in;
return 0;
}
static int sharp_ls_probe(struct platform_device *pdev)
{
struct panel_drv_data *ddata;
struct omap_dss_device *dssdev;
int r;
ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
if (ddata == NULL)
return -ENOMEM;
platform_set_drvdata(pdev, ddata);
if (dev_get_platdata(&pdev->dev)) {
r = sharp_ls_probe_pdata(pdev);
if (r)
return r;
} else if (pdev->dev.of_node) {
r = sharp_ls_probe_of(pdev);
if (r)
return r;
} else {
return -ENODEV;
}
ddata->videomode = sharp_ls_timings;
dssdev = &ddata->dssdev;
dssdev->dev = &pdev->dev;
dssdev->driver = &sharp_ls_ops;
dssdev->type = OMAP_DISPLAY_TYPE_DPI;
dssdev->owner = THIS_MODULE;
dssdev->panel.timings = ddata->videomode;
dssdev->phy.dpi.data_lines = ddata->data_lines;
r = omapdss_register_display(dssdev);
if (r) {
dev_err(&pdev->dev, "Failed to register panel\n");
goto err_reg;
}
return 0;
err_reg:
omap_dss_put_device(ddata->in);
return r;
}
static int __exit sharp_ls_remove(struct platform_device *pdev)
{
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
struct omap_dss_device *dssdev = &ddata->dssdev;
struct omap_dss_device *in = ddata->in;
omapdss_unregister_display(dssdev);
sharp_ls_disable(dssdev);
sharp_ls_disconnect(dssdev);
omap_dss_put_device(in);
return 0;
}
static const struct of_device_id sharp_ls_of_match[] = {
{ .compatible = "omapdss,sharp,ls037v7dw01", },
{},
};
MODULE_DEVICE_TABLE(of, sharp_ls_of_match);
static struct platform_driver sharp_ls_driver = {
.probe = sharp_ls_probe,
.remove = __exit_p(sharp_ls_remove),
.driver = {
.name = "panel-sharp-ls037v7dw01",
.of_match_table = sharp_ls_of_match,
.suppress_bind_attrs = true,
},
};
module_platform_driver(sharp_ls_driver);
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
MODULE_DESCRIPTION("Sharp LS037V7DW01 Panel Driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,917 @@
/*
* Sony ACX565AKM LCD Panel driver
*
* Copyright (C) 2010 Nokia Corporation
*
* Original Driver Author: Imre Deak <imre.deak@nokia.com>
* Based on panel-generic.c by Tomi Valkeinen <tomi.valkeinen@nokia.com>
* Adapted to new DSS2 framework: Roger Quadros <roger.quadros@nokia.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/spi/spi.h>
#include <linux/jiffies.h>
#include <linux/sched.h>
#include <linux/backlight.h>
#include <linux/fb.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <video/omapdss.h>
#include <video/omap-panel-data.h>
#define MIPID_CMD_READ_DISP_ID 0x04
#define MIPID_CMD_READ_RED 0x06
#define MIPID_CMD_READ_GREEN 0x07
#define MIPID_CMD_READ_BLUE 0x08
#define MIPID_CMD_READ_DISP_STATUS 0x09
#define MIPID_CMD_RDDSDR 0x0F
#define MIPID_CMD_SLEEP_IN 0x10
#define MIPID_CMD_SLEEP_OUT 0x11
#define MIPID_CMD_DISP_OFF 0x28
#define MIPID_CMD_DISP_ON 0x29
#define MIPID_CMD_WRITE_DISP_BRIGHTNESS 0x51
#define MIPID_CMD_READ_DISP_BRIGHTNESS 0x52
#define MIPID_CMD_WRITE_CTRL_DISP 0x53
#define CTRL_DISP_BRIGHTNESS_CTRL_ON (1 << 5)
#define CTRL_DISP_AMBIENT_LIGHT_CTRL_ON (1 << 4)
#define CTRL_DISP_BACKLIGHT_ON (1 << 2)
#define CTRL_DISP_AUTO_BRIGHTNESS_ON (1 << 1)
#define MIPID_CMD_READ_CTRL_DISP 0x54
#define MIPID_CMD_WRITE_CABC 0x55
#define MIPID_CMD_READ_CABC 0x56
#define MIPID_VER_LPH8923 3
#define MIPID_VER_LS041Y3 4
#define MIPID_VER_L4F00311 8
#define MIPID_VER_ACX565AKM 9
struct panel_drv_data {
struct omap_dss_device dssdev;
struct omap_dss_device *in;
int reset_gpio;
int datapairs;
struct omap_video_timings videomode;
char *name;
int enabled;
int model;
int revision;
u8 display_id[3];
unsigned has_bc:1;
unsigned has_cabc:1;
unsigned cabc_mode;
unsigned long hw_guard_end; /* next value of jiffies
when we can issue the
next sleep in/out command */
unsigned long hw_guard_wait; /* max guard time in jiffies */
struct spi_device *spi;
struct mutex mutex;
struct backlight_device *bl_dev;
};
static const struct omap_video_timings acx565akm_panel_timings = {
.x_res = 800,
.y_res = 480,
.pixelclock = 24000000,
.hfp = 28,
.hsw = 4,
.hbp = 24,
.vfp = 3,
.vsw = 3,
.vbp = 4,
.vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
.hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
.data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
.de_level = OMAPDSS_SIG_ACTIVE_HIGH,
.sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
};
#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
static void acx565akm_transfer(struct panel_drv_data *ddata, int cmd,
const u8 *wbuf, int wlen, u8 *rbuf, int rlen)
{
struct spi_message m;
struct spi_transfer *x, xfer[5];
int r;
BUG_ON(ddata->spi == NULL);
spi_message_init(&m);
memset(xfer, 0, sizeof(xfer));
x = &xfer[0];
cmd &= 0xff;
x->tx_buf = &cmd;
x->bits_per_word = 9;
x->len = 2;
if (rlen > 1 && wlen == 0) {
/*
* Between the command and the response data there is a
* dummy clock cycle. Add an extra bit after the command
* word to account for this.
*/
x->bits_per_word = 10;
cmd <<= 1;
}
spi_message_add_tail(x, &m);
if (wlen) {
x++;
x->tx_buf = wbuf;
x->len = wlen;
x->bits_per_word = 9;
spi_message_add_tail(x, &m);
}
if (rlen) {
x++;
x->rx_buf = rbuf;
x->len = rlen;
spi_message_add_tail(x, &m);
}
r = spi_sync(ddata->spi, &m);
if (r < 0)
dev_dbg(&ddata->spi->dev, "spi_sync %d\n", r);
}
static inline void acx565akm_cmd(struct panel_drv_data *ddata, int cmd)
{
acx565akm_transfer(ddata, cmd, NULL, 0, NULL, 0);
}
static inline void acx565akm_write(struct panel_drv_data *ddata,
int reg, const u8 *buf, int len)
{
acx565akm_transfer(ddata, reg, buf, len, NULL, 0);
}
static inline void acx565akm_read(struct panel_drv_data *ddata,
int reg, u8 *buf, int len)
{
acx565akm_transfer(ddata, reg, NULL, 0, buf, len);
}
static void hw_guard_start(struct panel_drv_data *ddata, int guard_msec)
{
ddata->hw_guard_wait = msecs_to_jiffies(guard_msec);
ddata->hw_guard_end = jiffies + ddata->hw_guard_wait;
}
static void hw_guard_wait(struct panel_drv_data *ddata)
{
unsigned long wait = ddata->hw_guard_end - jiffies;
if ((long)wait > 0 && wait <= ddata->hw_guard_wait) {
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(wait);
}
}
static void set_sleep_mode(struct panel_drv_data *ddata, int on)
{
int cmd;
if (on)
cmd = MIPID_CMD_SLEEP_IN;
else
cmd = MIPID_CMD_SLEEP_OUT;
/*
* We have to keep 120msec between sleep in/out commands.
* (8.2.15, 8.2.16).
*/
hw_guard_wait(ddata);
acx565akm_cmd(ddata, cmd);
hw_guard_start(ddata, 120);
}
static void set_display_state(struct panel_drv_data *ddata, int enabled)
{
int cmd = enabled ? MIPID_CMD_DISP_ON : MIPID_CMD_DISP_OFF;
acx565akm_cmd(ddata, cmd);
}
static int panel_enabled(struct panel_drv_data *ddata)
{
u32 disp_status;
int enabled;
acx565akm_read(ddata, MIPID_CMD_READ_DISP_STATUS,
(u8 *)&disp_status, 4);
disp_status = __be32_to_cpu(disp_status);
enabled = (disp_status & (1 << 17)) && (disp_status & (1 << 10));
dev_dbg(&ddata->spi->dev,
"LCD panel %senabled by bootloader (status 0x%04x)\n",
enabled ? "" : "not ", disp_status);
return enabled;
}
static int panel_detect(struct panel_drv_data *ddata)
{
acx565akm_read(ddata, MIPID_CMD_READ_DISP_ID, ddata->display_id, 3);
dev_dbg(&ddata->spi->dev, "MIPI display ID: %02x%02x%02x\n",
ddata->display_id[0],
ddata->display_id[1],
ddata->display_id[2]);
switch (ddata->display_id[0]) {
case 0x10:
ddata->model = MIPID_VER_ACX565AKM;
ddata->name = "acx565akm";
ddata->has_bc = 1;
ddata->has_cabc = 1;
break;
case 0x29:
ddata->model = MIPID_VER_L4F00311;
ddata->name = "l4f00311";
break;
case 0x45:
ddata->model = MIPID_VER_LPH8923;
ddata->name = "lph8923";
break;
case 0x83:
ddata->model = MIPID_VER_LS041Y3;
ddata->name = "ls041y3";
break;
default:
ddata->name = "unknown";
dev_err(&ddata->spi->dev, "invalid display ID\n");
return -ENODEV;
}
ddata->revision = ddata->display_id[1];
dev_info(&ddata->spi->dev, "omapfb: %s rev %02x LCD detected\n",
ddata->name, ddata->revision);
return 0;
}
/*----------------------Backlight Control-------------------------*/
static void enable_backlight_ctrl(struct panel_drv_data *ddata, int enable)
{
u16 ctrl;
acx565akm_read(ddata, MIPID_CMD_READ_CTRL_DISP, (u8 *)&ctrl, 1);
if (enable) {
ctrl |= CTRL_DISP_BRIGHTNESS_CTRL_ON |
CTRL_DISP_BACKLIGHT_ON;
} else {
ctrl &= ~(CTRL_DISP_BRIGHTNESS_CTRL_ON |
CTRL_DISP_BACKLIGHT_ON);
}
ctrl |= 1 << 8;
acx565akm_write(ddata, MIPID_CMD_WRITE_CTRL_DISP, (u8 *)&ctrl, 2);
}
static void set_cabc_mode(struct panel_drv_data *ddata, unsigned mode)
{
u16 cabc_ctrl;
ddata->cabc_mode = mode;
if (!ddata->enabled)
return;
cabc_ctrl = 0;
acx565akm_read(ddata, MIPID_CMD_READ_CABC, (u8 *)&cabc_ctrl, 1);
cabc_ctrl &= ~3;
cabc_ctrl |= (1 << 8) | (mode & 3);
acx565akm_write(ddata, MIPID_CMD_WRITE_CABC, (u8 *)&cabc_ctrl, 2);
}
static unsigned get_cabc_mode(struct panel_drv_data *ddata)
{
return ddata->cabc_mode;
}
static unsigned get_hw_cabc_mode(struct panel_drv_data *ddata)
{
u8 cabc_ctrl;
acx565akm_read(ddata, MIPID_CMD_READ_CABC, &cabc_ctrl, 1);
return cabc_ctrl & 3;
}
static void acx565akm_set_brightness(struct panel_drv_data *ddata, int level)
{
int bv;
bv = level | (1 << 8);
acx565akm_write(ddata, MIPID_CMD_WRITE_DISP_BRIGHTNESS, (u8 *)&bv, 2);
if (level)
enable_backlight_ctrl(ddata, 1);
else
enable_backlight_ctrl(ddata, 0);
}
static int acx565akm_get_actual_brightness(struct panel_drv_data *ddata)
{
u8 bv;
acx565akm_read(ddata, MIPID_CMD_READ_DISP_BRIGHTNESS, &bv, 1);
return bv;
}
static int acx565akm_bl_update_status(struct backlight_device *dev)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev);
int level;
dev_dbg(&ddata->spi->dev, "%s\n", __func__);
if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
dev->props.power == FB_BLANK_UNBLANK)
level = dev->props.brightness;
else
level = 0;
if (ddata->has_bc)
acx565akm_set_brightness(ddata, level);
else
return -ENODEV;
return 0;
}
static int acx565akm_bl_get_intensity(struct backlight_device *dev)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev);
dev_dbg(&dev->dev, "%s\n", __func__);
if (!ddata->has_bc)
return -ENODEV;
if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
dev->props.power == FB_BLANK_UNBLANK) {
if (ddata->has_bc)
return acx565akm_get_actual_brightness(ddata);
else
return dev->props.brightness;
}
return 0;
}
static int acx565akm_bl_update_status_locked(struct backlight_device *dev)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev);
int r;
mutex_lock(&ddata->mutex);
r = acx565akm_bl_update_status(dev);
mutex_unlock(&ddata->mutex);
return r;
}
static int acx565akm_bl_get_intensity_locked(struct backlight_device *dev)
{
struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev);
int r;
mutex_lock(&ddata->mutex);
r = acx565akm_bl_get_intensity(dev);
mutex_unlock(&ddata->mutex);
return r;
}
static const struct backlight_ops acx565akm_bl_ops = {
.get_brightness = acx565akm_bl_get_intensity_locked,
.update_status = acx565akm_bl_update_status_locked,
};
/*--------------------Auto Brightness control via Sysfs---------------------*/
static const char * const cabc_modes[] = {
"off", /* always used when CABC is not supported */
"ui",
"still-image",
"moving-image",
};
static ssize_t show_cabc_mode(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct panel_drv_data *ddata = dev_get_drvdata(dev);
const char *mode_str;
int mode;
int len;
if (!ddata->has_cabc)
mode = 0;
else
mode = get_cabc_mode(ddata);
mode_str = "unknown";
if (mode >= 0 && mode < ARRAY_SIZE(cabc_modes))
mode_str = cabc_modes[mode];
len = snprintf(buf, PAGE_SIZE, "%s\n", mode_str);
return len < PAGE_SIZE - 1 ? len : PAGE_SIZE - 1;
}
static ssize_t store_cabc_mode(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct panel_drv_data *ddata = dev_get_drvdata(dev);
int i;
for (i = 0; i < ARRAY_SIZE(cabc_modes); i++) {
const char *mode_str = cabc_modes[i];
int cmp_len = strlen(mode_str);
if (count > 0 && buf[count - 1] == '\n')
count--;
if (count != cmp_len)
continue;
if (strncmp(buf, mode_str, cmp_len) == 0)
break;
}
if (i == ARRAY_SIZE(cabc_modes))
return -EINVAL;
if (!ddata->has_cabc && i != 0)
return -EINVAL;
mutex_lock(&ddata->mutex);
set_cabc_mode(ddata, i);
mutex_unlock(&ddata->mutex);
return count;
}
static ssize_t show_cabc_available_modes(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct panel_drv_data *ddata = dev_get_drvdata(dev);
int len;
int i;
if (!ddata->has_cabc)
return snprintf(buf, PAGE_SIZE, "%s\n", cabc_modes[0]);
for (i = 0, len = 0;
len < PAGE_SIZE && i < ARRAY_SIZE(cabc_modes); i++)
len += snprintf(&buf[len], PAGE_SIZE - len, "%s%s%s",
i ? " " : "", cabc_modes[i],
i == ARRAY_SIZE(cabc_modes) - 1 ? "\n" : "");
return len < PAGE_SIZE ? len : PAGE_SIZE - 1;
}
static DEVICE_ATTR(cabc_mode, S_IRUGO | S_IWUSR,
show_cabc_mode, store_cabc_mode);
static DEVICE_ATTR(cabc_available_modes, S_IRUGO,
show_cabc_available_modes, NULL);
static struct attribute *bldev_attrs[] = {
&dev_attr_cabc_mode.attr,
&dev_attr_cabc_available_modes.attr,
NULL,
};
static struct attribute_group bldev_attr_group = {
.attrs = bldev_attrs,
};
static int acx565akm_connect(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
int r;
if (omapdss_device_is_connected(dssdev))
return 0;
r = in->ops.sdi->connect(in, dssdev);
if (r)
return r;
return 0;
}
static void acx565akm_disconnect(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
if (!omapdss_device_is_connected(dssdev))
return;
in->ops.sdi->disconnect(in, dssdev);
}
static int acx565akm_panel_power_on(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
int r;
dev_dbg(&ddata->spi->dev, "%s\n", __func__);
in->ops.sdi->set_timings(in, &ddata->videomode);
if (ddata->datapairs > 0)
in->ops.sdi->set_datapairs(in, ddata->datapairs);
r = in->ops.sdi->enable(in);
if (r) {
pr_err("%s sdi enable failed\n", __func__);
return r;
}
/*FIXME tweak me */
msleep(50);
if (gpio_is_valid(ddata->reset_gpio))
gpio_set_value(ddata->reset_gpio, 1);
if (ddata->enabled) {
dev_dbg(&ddata->spi->dev, "panel already enabled\n");
return 0;
}
/*
* We have to meet all the following delay requirements:
* 1. tRW: reset pulse width 10usec (7.12.1)
* 2. tRT: reset cancel time 5msec (7.12.1)
* 3. Providing PCLK,HS,VS signals for 2 frames = ~50msec worst
* case (7.6.2)
* 4. 120msec before the sleep out command (7.12.1)
*/
msleep(120);
set_sleep_mode(ddata, 0);
ddata->enabled = 1;
/* 5msec between sleep out and the next command. (8.2.16) */
usleep_range(5000, 10000);
set_display_state(ddata, 1);
set_cabc_mode(ddata, ddata->cabc_mode);
return acx565akm_bl_update_status(ddata->bl_dev);
}
static void acx565akm_panel_power_off(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
dev_dbg(dssdev->dev, "%s\n", __func__);
if (!ddata->enabled)
return;
set_display_state(ddata, 0);
set_sleep_mode(ddata, 1);
ddata->enabled = 0;
/*
* We have to provide PCLK,HS,VS signals for 2 frames (worst case
* ~50msec) after sending the sleep in command and asserting the
* reset signal. We probably could assert the reset w/o the delay
* but we still delay to avoid possible artifacts. (7.6.1)
*/
msleep(50);
if (gpio_is_valid(ddata->reset_gpio))
gpio_set_value(ddata->reset_gpio, 0);
/* FIXME need to tweak this delay */
msleep(100);
in->ops.sdi->disable(in);
}
static int acx565akm_enable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
int r;
dev_dbg(dssdev->dev, "%s\n", __func__);
if (!omapdss_device_is_connected(dssdev))
return -ENODEV;
if (omapdss_device_is_enabled(dssdev))
return 0;
mutex_lock(&ddata->mutex);
r = acx565akm_panel_power_on(dssdev);
mutex_unlock(&ddata->mutex);
if (r)
return r;
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return 0;
}
static void acx565akm_disable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
dev_dbg(dssdev->dev, "%s\n", __func__);
if (!omapdss_device_is_enabled(dssdev))
return;
mutex_lock(&ddata->mutex);
acx565akm_panel_power_off(dssdev);
mutex_unlock(&ddata->mutex);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
}
static void acx565akm_set_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
ddata->videomode = *timings;
dssdev->panel.timings = *timings;
in->ops.sdi->set_timings(in, timings);
}
static void acx565akm_get_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
*timings = ddata->videomode;
}
static int acx565akm_check_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
return in->ops.sdi->check_timings(in, timings);
}
static struct omap_dss_driver acx565akm_ops = {
.connect = acx565akm_connect,
.disconnect = acx565akm_disconnect,
.enable = acx565akm_enable,
.disable = acx565akm_disable,
.set_timings = acx565akm_set_timings,
.get_timings = acx565akm_get_timings,
.check_timings = acx565akm_check_timings,
.get_resolution = omapdss_default_get_resolution,
};
static int acx565akm_probe_pdata(struct spi_device *spi)
{
const struct panel_acx565akm_platform_data *pdata;
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
struct omap_dss_device *dssdev, *in;
pdata = dev_get_platdata(&spi->dev);
ddata->reset_gpio = pdata->reset_gpio;
in = omap_dss_find_output(pdata->source);
if (in == NULL) {
dev_err(&spi->dev, "failed to find video source '%s'\n",
pdata->source);
return -EPROBE_DEFER;
}
ddata->in = in;
ddata->datapairs = pdata->datapairs;
dssdev = &ddata->dssdev;
dssdev->name = pdata->name;
return 0;
}
static int acx565akm_probe_of(struct spi_device *spi)
{
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
struct device_node *np = spi->dev.of_node;
ddata->reset_gpio = of_get_named_gpio(np, "reset-gpios", 0);
ddata->in = omapdss_of_find_source_for_first_ep(np);
if (IS_ERR(ddata->in)) {
dev_err(&spi->dev, "failed to find video source\n");
return PTR_ERR(ddata->in);
}
return 0;
}
static int acx565akm_probe(struct spi_device *spi)
{
struct panel_drv_data *ddata;
struct omap_dss_device *dssdev;
struct backlight_device *bldev;
int max_brightness, brightness;
struct backlight_properties props;
int r;
dev_dbg(&spi->dev, "%s\n", __func__);
spi->mode = SPI_MODE_3;
ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL);
if (ddata == NULL)
return -ENOMEM;
dev_set_drvdata(&spi->dev, ddata);
ddata->spi = spi;
mutex_init(&ddata->mutex);
if (dev_get_platdata(&spi->dev)) {
r = acx565akm_probe_pdata(spi);
if (r)
return r;
} else if (spi->dev.of_node) {
r = acx565akm_probe_of(spi);
if (r)
return r;
} else {
dev_err(&spi->dev, "platform data missing!\n");
return -ENODEV;
}
if (gpio_is_valid(ddata->reset_gpio)) {
r = devm_gpio_request_one(&spi->dev, ddata->reset_gpio,
GPIOF_OUT_INIT_LOW, "lcd reset");
if (r)
goto err_gpio;
}
if (gpio_is_valid(ddata->reset_gpio))
gpio_set_value(ddata->reset_gpio, 1);
/*
* After reset we have to wait 5 msec before the first
* command can be sent.
*/
usleep_range(5000, 10000);
ddata->enabled = panel_enabled(ddata);
r = panel_detect(ddata);
if (!ddata->enabled && gpio_is_valid(ddata->reset_gpio))
gpio_set_value(ddata->reset_gpio, 0);
if (r) {
dev_err(&spi->dev, "%s panel detect error\n", __func__);
goto err_detect;
}
memset(&props, 0, sizeof(props));
props.fb_blank = FB_BLANK_UNBLANK;
props.power = FB_BLANK_UNBLANK;
props.type = BACKLIGHT_RAW;
bldev = backlight_device_register("acx565akm", &ddata->spi->dev,
ddata, &acx565akm_bl_ops, &props);
if (IS_ERR(bldev)) {
r = PTR_ERR(bldev);
goto err_reg_bl;
}
ddata->bl_dev = bldev;
if (ddata->has_cabc) {
r = sysfs_create_group(&bldev->dev.kobj, &bldev_attr_group);
if (r) {
dev_err(&bldev->dev,
"%s failed to create sysfs files\n", __func__);
goto err_sysfs;
}
ddata->cabc_mode = get_hw_cabc_mode(ddata);
}
max_brightness = 255;
if (ddata->has_bc)
brightness = acx565akm_get_actual_brightness(ddata);
else
brightness = 0;
bldev->props.max_brightness = max_brightness;
bldev->props.brightness = brightness;
acx565akm_bl_update_status(bldev);
ddata->videomode = acx565akm_panel_timings;
dssdev = &ddata->dssdev;
dssdev->dev = &spi->dev;
dssdev->driver = &acx565akm_ops;
dssdev->type = OMAP_DISPLAY_TYPE_SDI;
dssdev->owner = THIS_MODULE;
dssdev->panel.timings = ddata->videomode;
r = omapdss_register_display(dssdev);
if (r) {
dev_err(&spi->dev, "Failed to register panel\n");
goto err_reg;
}
return 0;
err_reg:
sysfs_remove_group(&bldev->dev.kobj, &bldev_attr_group);
err_sysfs:
backlight_device_unregister(bldev);
err_reg_bl:
err_detect:
err_gpio:
omap_dss_put_device(ddata->in);
return r;
}
static int acx565akm_remove(struct spi_device *spi)
{
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
struct omap_dss_device *dssdev = &ddata->dssdev;
struct omap_dss_device *in = ddata->in;
dev_dbg(&ddata->spi->dev, "%s\n", __func__);
sysfs_remove_group(&ddata->bl_dev->dev.kobj, &bldev_attr_group);
backlight_device_unregister(ddata->bl_dev);
omapdss_unregister_display(dssdev);
acx565akm_disable(dssdev);
acx565akm_disconnect(dssdev);
omap_dss_put_device(in);
return 0;
}
static const struct of_device_id acx565akm_of_match[] = {
{ .compatible = "omapdss,sony,acx565akm", },
{},
};
MODULE_DEVICE_TABLE(of, acx565akm_of_match);
static struct spi_driver acx565akm_driver = {
.driver = {
.name = "acx565akm",
.of_match_table = acx565akm_of_match,
.suppress_bind_attrs = true,
},
.probe = acx565akm_probe,
.remove = acx565akm_remove,
};
module_spi_driver(acx565akm_driver);
MODULE_AUTHOR("Nokia Corporation");
MODULE_DESCRIPTION("acx565akm LCD Driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,511 @@
/*
* Toppoly TD028TTEC1 panel support
*
* Copyright (C) 2008 Nokia Corporation
* Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
*
* Neo 1973 code (jbt6k74.c):
* Copyright (C) 2006-2007 by OpenMoko, Inc.
* Author: Harald Welte <laforge@openmoko.org>
*
* Ported and adapted from Neo 1973 U-Boot by:
* H. Nikolaus Schaller <hns@goldelico.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/spi/spi.h>
#include <linux/gpio.h>
#include <video/omapdss.h>
#include <video/omap-panel-data.h>
struct panel_drv_data {
struct omap_dss_device dssdev;
struct omap_dss_device *in;
int data_lines;
struct omap_video_timings videomode;
struct spi_device *spi_dev;
};
static struct omap_video_timings td028ttec1_panel_timings = {
.x_res = 480,
.y_res = 640,
.pixelclock = 22153000,
.hfp = 24,
.hsw = 8,
.hbp = 8,
.vfp = 4,
.vsw = 2,
.vbp = 2,
.vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
.hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
.data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
.de_level = OMAPDSS_SIG_ACTIVE_HIGH,
.sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
};
#define JBT_COMMAND 0x000
#define JBT_DATA 0x100
static int jbt_ret_write_0(struct panel_drv_data *ddata, u8 reg)
{
int rc;
u16 tx_buf = JBT_COMMAND | reg;
rc = spi_write(ddata->spi_dev, (u8 *)&tx_buf,
1*sizeof(u16));
if (rc != 0)
dev_err(&ddata->spi_dev->dev,
"jbt_ret_write_0 spi_write ret %d\n", rc);
return rc;
}
static int jbt_reg_write_1(struct panel_drv_data *ddata, u8 reg, u8 data)
{
int rc;
u16 tx_buf[2];
tx_buf[0] = JBT_COMMAND | reg;
tx_buf[1] = JBT_DATA | data;
rc = spi_write(ddata->spi_dev, (u8 *)tx_buf,
2*sizeof(u16));
if (rc != 0)
dev_err(&ddata->spi_dev->dev,
"jbt_reg_write_1 spi_write ret %d\n", rc);
return rc;
}
static int jbt_reg_write_2(struct panel_drv_data *ddata, u8 reg, u16 data)
{
int rc;
u16 tx_buf[3];
tx_buf[0] = JBT_COMMAND | reg;
tx_buf[1] = JBT_DATA | (data >> 8);
tx_buf[2] = JBT_DATA | (data & 0xff);
rc = spi_write(ddata->spi_dev, (u8 *)tx_buf,
3*sizeof(u16));
if (rc != 0)
dev_err(&ddata->spi_dev->dev,
"jbt_reg_write_2 spi_write ret %d\n", rc);
return rc;
}
enum jbt_register {
JBT_REG_SLEEP_IN = 0x10,
JBT_REG_SLEEP_OUT = 0x11,
JBT_REG_DISPLAY_OFF = 0x28,
JBT_REG_DISPLAY_ON = 0x29,
JBT_REG_RGB_FORMAT = 0x3a,
JBT_REG_QUAD_RATE = 0x3b,
JBT_REG_POWER_ON_OFF = 0xb0,
JBT_REG_BOOSTER_OP = 0xb1,
JBT_REG_BOOSTER_MODE = 0xb2,
JBT_REG_BOOSTER_FREQ = 0xb3,
JBT_REG_OPAMP_SYSCLK = 0xb4,
JBT_REG_VSC_VOLTAGE = 0xb5,
JBT_REG_VCOM_VOLTAGE = 0xb6,
JBT_REG_EXT_DISPL = 0xb7,
JBT_REG_OUTPUT_CONTROL = 0xb8,
JBT_REG_DCCLK_DCEV = 0xb9,
JBT_REG_DISPLAY_MODE1 = 0xba,
JBT_REG_DISPLAY_MODE2 = 0xbb,
JBT_REG_DISPLAY_MODE = 0xbc,
JBT_REG_ASW_SLEW = 0xbd,
JBT_REG_DUMMY_DISPLAY = 0xbe,
JBT_REG_DRIVE_SYSTEM = 0xbf,
JBT_REG_SLEEP_OUT_FR_A = 0xc0,
JBT_REG_SLEEP_OUT_FR_B = 0xc1,
JBT_REG_SLEEP_OUT_FR_C = 0xc2,
JBT_REG_SLEEP_IN_LCCNT_D = 0xc3,
JBT_REG_SLEEP_IN_LCCNT_E = 0xc4,
JBT_REG_SLEEP_IN_LCCNT_F = 0xc5,
JBT_REG_SLEEP_IN_LCCNT_G = 0xc6,
JBT_REG_GAMMA1_FINE_1 = 0xc7,
JBT_REG_GAMMA1_FINE_2 = 0xc8,
JBT_REG_GAMMA1_INCLINATION = 0xc9,
JBT_REG_GAMMA1_BLUE_OFFSET = 0xca,
JBT_REG_BLANK_CONTROL = 0xcf,
JBT_REG_BLANK_TH_TV = 0xd0,
JBT_REG_CKV_ON_OFF = 0xd1,
JBT_REG_CKV_1_2 = 0xd2,
JBT_REG_OEV_TIMING = 0xd3,
JBT_REG_ASW_TIMING_1 = 0xd4,
JBT_REG_ASW_TIMING_2 = 0xd5,
JBT_REG_HCLOCK_VGA = 0xec,
JBT_REG_HCLOCK_QVGA = 0xed,
};
#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
static int td028ttec1_panel_connect(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
int r;
if (omapdss_device_is_connected(dssdev))
return 0;
r = in->ops.dpi->connect(in, dssdev);
if (r)
return r;
return 0;
}
static void td028ttec1_panel_disconnect(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
if (!omapdss_device_is_connected(dssdev))
return;
in->ops.dpi->disconnect(in, dssdev);
}
static int td028ttec1_panel_enable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
int r;
if (!omapdss_device_is_connected(dssdev))
return -ENODEV;
if (omapdss_device_is_enabled(dssdev))
return 0;
if (ddata->data_lines)
in->ops.dpi->set_data_lines(in, ddata->data_lines);
in->ops.dpi->set_timings(in, &ddata->videomode);
r = in->ops.dpi->enable(in);
if (r)
return r;
dev_dbg(dssdev->dev, "td028ttec1_panel_enable() - state %d\n",
dssdev->state);
/* three times command zero */
r |= jbt_ret_write_0(ddata, 0x00);
usleep_range(1000, 2000);
r |= jbt_ret_write_0(ddata, 0x00);
usleep_range(1000, 2000);
r |= jbt_ret_write_0(ddata, 0x00);
usleep_range(1000, 2000);
if (r) {
dev_warn(dssdev->dev, "transfer error\n");
goto transfer_err;
}
/* deep standby out */
r |= jbt_reg_write_1(ddata, JBT_REG_POWER_ON_OFF, 0x17);
/* RGB I/F on, RAM write off, QVGA through, SIGCON enable */
r |= jbt_reg_write_1(ddata, JBT_REG_DISPLAY_MODE, 0x80);
/* Quad mode off */
r |= jbt_reg_write_1(ddata, JBT_REG_QUAD_RATE, 0x00);
/* AVDD on, XVDD on */
r |= jbt_reg_write_1(ddata, JBT_REG_POWER_ON_OFF, 0x16);
/* Output control */
r |= jbt_reg_write_2(ddata, JBT_REG_OUTPUT_CONTROL, 0xfff9);
/* Sleep mode off */
r |= jbt_ret_write_0(ddata, JBT_REG_SLEEP_OUT);
/* at this point we have like 50% grey */
/* initialize register set */
r |= jbt_reg_write_1(ddata, JBT_REG_DISPLAY_MODE1, 0x01);
r |= jbt_reg_write_1(ddata, JBT_REG_DISPLAY_MODE2, 0x00);
r |= jbt_reg_write_1(ddata, JBT_REG_RGB_FORMAT, 0x60);
r |= jbt_reg_write_1(ddata, JBT_REG_DRIVE_SYSTEM, 0x10);
r |= jbt_reg_write_1(ddata, JBT_REG_BOOSTER_OP, 0x56);
r |= jbt_reg_write_1(ddata, JBT_REG_BOOSTER_MODE, 0x33);
r |= jbt_reg_write_1(ddata, JBT_REG_BOOSTER_FREQ, 0x11);
r |= jbt_reg_write_1(ddata, JBT_REG_BOOSTER_FREQ, 0x11);
r |= jbt_reg_write_1(ddata, JBT_REG_OPAMP_SYSCLK, 0x02);
r |= jbt_reg_write_1(ddata, JBT_REG_VSC_VOLTAGE, 0x2b);
r |= jbt_reg_write_1(ddata, JBT_REG_VCOM_VOLTAGE, 0x40);
r |= jbt_reg_write_1(ddata, JBT_REG_EXT_DISPL, 0x03);
r |= jbt_reg_write_1(ddata, JBT_REG_DCCLK_DCEV, 0x04);
/*
* default of 0x02 in JBT_REG_ASW_SLEW responsible for 72Hz requirement
* to avoid red / blue flicker
*/
r |= jbt_reg_write_1(ddata, JBT_REG_ASW_SLEW, 0x04);
r |= jbt_reg_write_1(ddata, JBT_REG_DUMMY_DISPLAY, 0x00);
r |= jbt_reg_write_1(ddata, JBT_REG_SLEEP_OUT_FR_A, 0x11);
r |= jbt_reg_write_1(ddata, JBT_REG_SLEEP_OUT_FR_B, 0x11);
r |= jbt_reg_write_1(ddata, JBT_REG_SLEEP_OUT_FR_C, 0x11);
r |= jbt_reg_write_2(ddata, JBT_REG_SLEEP_IN_LCCNT_D, 0x2040);
r |= jbt_reg_write_2(ddata, JBT_REG_SLEEP_IN_LCCNT_E, 0x60c0);
r |= jbt_reg_write_2(ddata, JBT_REG_SLEEP_IN_LCCNT_F, 0x1020);
r |= jbt_reg_write_2(ddata, JBT_REG_SLEEP_IN_LCCNT_G, 0x60c0);
r |= jbt_reg_write_2(ddata, JBT_REG_GAMMA1_FINE_1, 0x5533);
r |= jbt_reg_write_1(ddata, JBT_REG_GAMMA1_FINE_2, 0x00);
r |= jbt_reg_write_1(ddata, JBT_REG_GAMMA1_INCLINATION, 0x00);
r |= jbt_reg_write_1(ddata, JBT_REG_GAMMA1_BLUE_OFFSET, 0x00);
r |= jbt_reg_write_2(ddata, JBT_REG_HCLOCK_VGA, 0x1f0);
r |= jbt_reg_write_1(ddata, JBT_REG_BLANK_CONTROL, 0x02);
r |= jbt_reg_write_2(ddata, JBT_REG_BLANK_TH_TV, 0x0804);
r |= jbt_reg_write_1(ddata, JBT_REG_CKV_ON_OFF, 0x01);
r |= jbt_reg_write_2(ddata, JBT_REG_CKV_1_2, 0x0000);
r |= jbt_reg_write_2(ddata, JBT_REG_OEV_TIMING, 0x0d0e);
r |= jbt_reg_write_2(ddata, JBT_REG_ASW_TIMING_1, 0x11a4);
r |= jbt_reg_write_1(ddata, JBT_REG_ASW_TIMING_2, 0x0e);
r |= jbt_ret_write_0(ddata, JBT_REG_DISPLAY_ON);
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
transfer_err:
return r ? -EIO : 0;
}
static void td028ttec1_panel_disable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
if (!omapdss_device_is_enabled(dssdev))
return;
dev_dbg(dssdev->dev, "td028ttec1_panel_disable()\n");
jbt_ret_write_0(ddata, JBT_REG_DISPLAY_OFF);
jbt_reg_write_2(ddata, JBT_REG_OUTPUT_CONTROL, 0x8002);
jbt_ret_write_0(ddata, JBT_REG_SLEEP_IN);
jbt_reg_write_1(ddata, JBT_REG_POWER_ON_OFF, 0x00);
in->ops.dpi->disable(in);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
}
static void td028ttec1_panel_set_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
ddata->videomode = *timings;
dssdev->panel.timings = *timings;
in->ops.dpi->set_timings(in, timings);
}
static void td028ttec1_panel_get_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
*timings = ddata->videomode;
}
static int td028ttec1_panel_check_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
return in->ops.dpi->check_timings(in, timings);
}
static struct omap_dss_driver td028ttec1_ops = {
.connect = td028ttec1_panel_connect,
.disconnect = td028ttec1_panel_disconnect,
.enable = td028ttec1_panel_enable,
.disable = td028ttec1_panel_disable,
.set_timings = td028ttec1_panel_set_timings,
.get_timings = td028ttec1_panel_get_timings,
.check_timings = td028ttec1_panel_check_timings,
};
static int td028ttec1_panel_probe_pdata(struct spi_device *spi)
{
const struct panel_tpo_td028ttec1_platform_data *pdata;
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
struct omap_dss_device *dssdev, *in;
pdata = dev_get_platdata(&spi->dev);
in = omap_dss_find_output(pdata->source);
if (in == NULL) {
dev_err(&spi->dev, "failed to find video source '%s'\n",
pdata->source);
return -EPROBE_DEFER;
}
ddata->in = in;
ddata->data_lines = pdata->data_lines;
dssdev = &ddata->dssdev;
dssdev->name = pdata->name;
return 0;
}
static int td028ttec1_probe_of(struct spi_device *spi)
{
struct device_node *node = spi->dev.of_node;
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
struct omap_dss_device *in;
in = omapdss_of_find_source_for_first_ep(node);
if (IS_ERR(in)) {
dev_err(&spi->dev, "failed to find video source\n");
return PTR_ERR(in);
}
ddata->in = in;
return 0;
}
static int td028ttec1_panel_probe(struct spi_device *spi)
{
struct panel_drv_data *ddata;
struct omap_dss_device *dssdev;
int r;
dev_dbg(&spi->dev, "%s\n", __func__);
spi->bits_per_word = 9;
spi->mode = SPI_MODE_3;
r = spi_setup(spi);
if (r < 0) {
dev_err(&spi->dev, "spi_setup failed: %d\n", r);
return r;
}
ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL);
if (ddata == NULL)
return -ENOMEM;
dev_set_drvdata(&spi->dev, ddata);
ddata->spi_dev = spi;
if (dev_get_platdata(&spi->dev)) {
r = td028ttec1_panel_probe_pdata(spi);
if (r)
return r;
} else if (spi->dev.of_node) {
r = td028ttec1_probe_of(spi);
if (r)
return r;
} else {
return -ENODEV;
}
ddata->videomode = td028ttec1_panel_timings;
dssdev = &ddata->dssdev;
dssdev->dev = &spi->dev;
dssdev->driver = &td028ttec1_ops;
dssdev->type = OMAP_DISPLAY_TYPE_DPI;
dssdev->owner = THIS_MODULE;
dssdev->panel.timings = ddata->videomode;
dssdev->phy.dpi.data_lines = ddata->data_lines;
r = omapdss_register_display(dssdev);
if (r) {
dev_err(&spi->dev, "Failed to register panel\n");
goto err_reg;
}
return 0;
err_reg:
omap_dss_put_device(ddata->in);
return r;
}
static int td028ttec1_panel_remove(struct spi_device *spi)
{
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
struct omap_dss_device *dssdev = &ddata->dssdev;
struct omap_dss_device *in = ddata->in;
dev_dbg(&ddata->spi_dev->dev, "%s\n", __func__);
omapdss_unregister_display(dssdev);
td028ttec1_panel_disable(dssdev);
td028ttec1_panel_disconnect(dssdev);
omap_dss_put_device(in);
return 0;
}
static const struct of_device_id td028ttec1_of_match[] = {
{ .compatible = "omapdss,toppoly,td028ttec1", },
{},
};
MODULE_DEVICE_TABLE(of, td028ttec1_of_match);
static struct spi_driver td028ttec1_spi_driver = {
.probe = td028ttec1_panel_probe,
.remove = td028ttec1_panel_remove,
.driver = {
.name = "panel-tpo-td028ttec1",
.of_match_table = td028ttec1_of_match,
.suppress_bind_attrs = true,
},
};
module_spi_driver(td028ttec1_spi_driver);
MODULE_ALIAS("spi:toppoly,td028ttec1");
MODULE_AUTHOR("H. Nikolaus Schaller <hns@goldelico.com>");
MODULE_DESCRIPTION("Toppoly TD028TTEC1 panel driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,686 @@
/*
* TPO TD043MTEA1 Panel driver
*
* Author: Gražvydas Ignotas <notasas@gmail.com>
* Converted to new DSS device model: Tomi Valkeinen <tomi.valkeinen@ti.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/spi/spi.h>
#include <linux/regulator/consumer.h>
#include <linux/gpio.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/of_gpio.h>
#include <video/omapdss.h>
#include <video/omap-panel-data.h>
#define TPO_R02_MODE(x) ((x) & 7)
#define TPO_R02_MODE_800x480 7
#define TPO_R02_NCLK_RISING BIT(3)
#define TPO_R02_HSYNC_HIGH BIT(4)
#define TPO_R02_VSYNC_HIGH BIT(5)
#define TPO_R03_NSTANDBY BIT(0)
#define TPO_R03_EN_CP_CLK BIT(1)
#define TPO_R03_EN_VGL_PUMP BIT(2)
#define TPO_R03_EN_PWM BIT(3)
#define TPO_R03_DRIVING_CAP_100 BIT(4)
#define TPO_R03_EN_PRE_CHARGE BIT(6)
#define TPO_R03_SOFTWARE_CTL BIT(7)
#define TPO_R04_NFLIP_H BIT(0)
#define TPO_R04_NFLIP_V BIT(1)
#define TPO_R04_CP_CLK_FREQ_1H BIT(2)
#define TPO_R04_VGL_FREQ_1H BIT(4)
#define TPO_R03_VAL_NORMAL (TPO_R03_NSTANDBY | TPO_R03_EN_CP_CLK | \
TPO_R03_EN_VGL_PUMP | TPO_R03_EN_PWM | \
TPO_R03_DRIVING_CAP_100 | TPO_R03_EN_PRE_CHARGE | \
TPO_R03_SOFTWARE_CTL)
#define TPO_R03_VAL_STANDBY (TPO_R03_DRIVING_CAP_100 | \
TPO_R03_EN_PRE_CHARGE | TPO_R03_SOFTWARE_CTL)
static const u16 tpo_td043_def_gamma[12] = {
105, 315, 381, 431, 490, 537, 579, 686, 780, 837, 880, 1023
};
struct panel_drv_data {
struct omap_dss_device dssdev;
struct omap_dss_device *in;
struct omap_video_timings videomode;
int data_lines;
struct spi_device *spi;
struct regulator *vcc_reg;
int nreset_gpio;
u16 gamma[12];
u32 mode;
u32 hmirror:1;
u32 vmirror:1;
u32 powered_on:1;
u32 spi_suspended:1;
u32 power_on_resume:1;
};
static const struct omap_video_timings tpo_td043_timings = {
.x_res = 800,
.y_res = 480,
.pixelclock = 36000000,
.hsw = 1,
.hfp = 68,
.hbp = 214,
.vsw = 1,
.vfp = 39,
.vbp = 34,
.vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
.hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
.data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
.de_level = OMAPDSS_SIG_ACTIVE_HIGH,
.sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
};
#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
static int tpo_td043_write(struct spi_device *spi, u8 addr, u8 data)
{
struct spi_message m;
struct spi_transfer xfer;
u16 w;
int r;
spi_message_init(&m);
memset(&xfer, 0, sizeof(xfer));
w = ((u16)addr << 10) | (1 << 8) | data;
xfer.tx_buf = &w;
xfer.bits_per_word = 16;
xfer.len = 2;
spi_message_add_tail(&xfer, &m);
r = spi_sync(spi, &m);
if (r < 0)
dev_warn(&spi->dev, "failed to write to LCD reg (%d)\n", r);
return r;
}
static void tpo_td043_write_gamma(struct spi_device *spi, u16 gamma[12])
{
u8 i, val;
/* gamma bits [9:8] */
for (val = i = 0; i < 4; i++)
val |= (gamma[i] & 0x300) >> ((i + 1) * 2);
tpo_td043_write(spi, 0x11, val);
for (val = i = 0; i < 4; i++)
val |= (gamma[i+4] & 0x300) >> ((i + 1) * 2);
tpo_td043_write(spi, 0x12, val);
for (val = i = 0; i < 4; i++)
val |= (gamma[i+8] & 0x300) >> ((i + 1) * 2);
tpo_td043_write(spi, 0x13, val);
/* gamma bits [7:0] */
for (val = i = 0; i < 12; i++)
tpo_td043_write(spi, 0x14 + i, gamma[i] & 0xff);
}
static int tpo_td043_write_mirror(struct spi_device *spi, bool h, bool v)
{
u8 reg4 = TPO_R04_NFLIP_H | TPO_R04_NFLIP_V |
TPO_R04_CP_CLK_FREQ_1H | TPO_R04_VGL_FREQ_1H;
if (h)
reg4 &= ~TPO_R04_NFLIP_H;
if (v)
reg4 &= ~TPO_R04_NFLIP_V;
return tpo_td043_write(spi, 4, reg4);
}
static int tpo_td043_set_hmirror(struct omap_dss_device *dssdev, bool enable)
{
struct panel_drv_data *ddata = dev_get_drvdata(dssdev->dev);
ddata->hmirror = enable;
return tpo_td043_write_mirror(ddata->spi, ddata->hmirror,
ddata->vmirror);
}
static bool tpo_td043_get_hmirror(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = dev_get_drvdata(dssdev->dev);
return ddata->hmirror;
}
static ssize_t tpo_td043_vmirror_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct panel_drv_data *ddata = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%d\n", ddata->vmirror);
}
static ssize_t tpo_td043_vmirror_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct panel_drv_data *ddata = dev_get_drvdata(dev);
int val;
int ret;
ret = kstrtoint(buf, 0, &val);
if (ret < 0)
return ret;
val = !!val;
ret = tpo_td043_write_mirror(ddata->spi, ddata->hmirror, val);
if (ret < 0)
return ret;
ddata->vmirror = val;
return count;
}
static ssize_t tpo_td043_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct panel_drv_data *ddata = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%d\n", ddata->mode);
}
static ssize_t tpo_td043_mode_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct panel_drv_data *ddata = dev_get_drvdata(dev);
long val;
int ret;
ret = kstrtol(buf, 0, &val);
if (ret != 0 || val & ~7)
return -EINVAL;
ddata->mode = val;
val |= TPO_R02_NCLK_RISING;
tpo_td043_write(ddata->spi, 2, val);
return count;
}
static ssize_t tpo_td043_gamma_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct panel_drv_data *ddata = dev_get_drvdata(dev);
ssize_t len = 0;
int ret;
int i;
for (i = 0; i < ARRAY_SIZE(ddata->gamma); i++) {
ret = snprintf(buf + len, PAGE_SIZE - len, "%u ",
ddata->gamma[i]);
if (ret < 0)
return ret;
len += ret;
}
buf[len - 1] = '\n';
return len;
}
static ssize_t tpo_td043_gamma_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct panel_drv_data *ddata = dev_get_drvdata(dev);
unsigned int g[12];
int ret;
int i;
ret = sscanf(buf, "%u %u %u %u %u %u %u %u %u %u %u %u",
&g[0], &g[1], &g[2], &g[3], &g[4], &g[5],
&g[6], &g[7], &g[8], &g[9], &g[10], &g[11]);
if (ret != 12)
return -EINVAL;
for (i = 0; i < 12; i++)
ddata->gamma[i] = g[i];
tpo_td043_write_gamma(ddata->spi, ddata->gamma);
return count;
}
static DEVICE_ATTR(vmirror, S_IRUGO | S_IWUSR,
tpo_td043_vmirror_show, tpo_td043_vmirror_store);
static DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
tpo_td043_mode_show, tpo_td043_mode_store);
static DEVICE_ATTR(gamma, S_IRUGO | S_IWUSR,
tpo_td043_gamma_show, tpo_td043_gamma_store);
static struct attribute *tpo_td043_attrs[] = {
&dev_attr_vmirror.attr,
&dev_attr_mode.attr,
&dev_attr_gamma.attr,
NULL,
};
static struct attribute_group tpo_td043_attr_group = {
.attrs = tpo_td043_attrs,
};
static int tpo_td043_power_on(struct panel_drv_data *ddata)
{
int r;
if (ddata->powered_on)
return 0;
r = regulator_enable(ddata->vcc_reg);
if (r != 0)
return r;
/* wait for panel to stabilize */
msleep(160);
if (gpio_is_valid(ddata->nreset_gpio))
gpio_set_value(ddata->nreset_gpio, 1);
tpo_td043_write(ddata->spi, 2,
TPO_R02_MODE(ddata->mode) | TPO_R02_NCLK_RISING);
tpo_td043_write(ddata->spi, 3, TPO_R03_VAL_NORMAL);
tpo_td043_write(ddata->spi, 0x20, 0xf0);
tpo_td043_write(ddata->spi, 0x21, 0xf0);
tpo_td043_write_mirror(ddata->spi, ddata->hmirror,
ddata->vmirror);
tpo_td043_write_gamma(ddata->spi, ddata->gamma);
ddata->powered_on = 1;
return 0;
}
static void tpo_td043_power_off(struct panel_drv_data *ddata)
{
if (!ddata->powered_on)
return;
tpo_td043_write(ddata->spi, 3,
TPO_R03_VAL_STANDBY | TPO_R03_EN_PWM);
if (gpio_is_valid(ddata->nreset_gpio))
gpio_set_value(ddata->nreset_gpio, 0);
/* wait for at least 2 vsyncs before cutting off power */
msleep(50);
tpo_td043_write(ddata->spi, 3, TPO_R03_VAL_STANDBY);
regulator_disable(ddata->vcc_reg);
ddata->powered_on = 0;
}
static int tpo_td043_connect(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
int r;
if (omapdss_device_is_connected(dssdev))
return 0;
r = in->ops.dpi->connect(in, dssdev);
if (r)
return r;
return 0;
}
static void tpo_td043_disconnect(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
if (!omapdss_device_is_connected(dssdev))
return;
in->ops.dpi->disconnect(in, dssdev);
}
static int tpo_td043_enable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
int r;
if (!omapdss_device_is_connected(dssdev))
return -ENODEV;
if (omapdss_device_is_enabled(dssdev))
return 0;
if (ddata->data_lines)
in->ops.dpi->set_data_lines(in, ddata->data_lines);
in->ops.dpi->set_timings(in, &ddata->videomode);
r = in->ops.dpi->enable(in);
if (r)
return r;
/*
* If we are resuming from system suspend, SPI clocks might not be
* enabled yet, so we'll program the LCD from SPI PM resume callback.
*/
if (!ddata->spi_suspended) {
r = tpo_td043_power_on(ddata);
if (r) {
in->ops.dpi->disable(in);
return r;
}
}
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return 0;
}
static void tpo_td043_disable(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
if (!omapdss_device_is_enabled(dssdev))
return;
in->ops.dpi->disable(in);
if (!ddata->spi_suspended)
tpo_td043_power_off(ddata);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
}
static void tpo_td043_set_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
ddata->videomode = *timings;
dssdev->panel.timings = *timings;
in->ops.dpi->set_timings(in, timings);
}
static void tpo_td043_get_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
*timings = ddata->videomode;
}
static int tpo_td043_check_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
struct omap_dss_device *in = ddata->in;
return in->ops.dpi->check_timings(in, timings);
}
static struct omap_dss_driver tpo_td043_ops = {
.connect = tpo_td043_connect,
.disconnect = tpo_td043_disconnect,
.enable = tpo_td043_enable,
.disable = tpo_td043_disable,
.set_timings = tpo_td043_set_timings,
.get_timings = tpo_td043_get_timings,
.check_timings = tpo_td043_check_timings,
.set_mirror = tpo_td043_set_hmirror,
.get_mirror = tpo_td043_get_hmirror,
.get_resolution = omapdss_default_get_resolution,
};
static int tpo_td043_probe_pdata(struct spi_device *spi)
{
const struct panel_tpo_td043mtea1_platform_data *pdata;
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
struct omap_dss_device *dssdev, *in;
pdata = dev_get_platdata(&spi->dev);
ddata->nreset_gpio = pdata->nreset_gpio;
in = omap_dss_find_output(pdata->source);
if (in == NULL) {
dev_err(&spi->dev, "failed to find video source '%s'\n",
pdata->source);
return -EPROBE_DEFER;
}
ddata->in = in;
ddata->data_lines = pdata->data_lines;
dssdev = &ddata->dssdev;
dssdev->name = pdata->name;
return 0;
}
static int tpo_td043_probe_of(struct spi_device *spi)
{
struct device_node *node = spi->dev.of_node;
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
struct omap_dss_device *in;
int gpio;
gpio = of_get_named_gpio(node, "reset-gpios", 0);
if (!gpio_is_valid(gpio)) {
dev_err(&spi->dev, "failed to parse enable gpio\n");
return gpio;
}
ddata->nreset_gpio = gpio;
in = omapdss_of_find_source_for_first_ep(node);
if (IS_ERR(in)) {
dev_err(&spi->dev, "failed to find video source\n");
return PTR_ERR(in);
}
ddata->in = in;
return 0;
}
static int tpo_td043_probe(struct spi_device *spi)
{
struct panel_drv_data *ddata;
struct omap_dss_device *dssdev;
int r;
dev_dbg(&spi->dev, "%s\n", __func__);
spi->bits_per_word = 16;
spi->mode = SPI_MODE_0;
r = spi_setup(spi);
if (r < 0) {
dev_err(&spi->dev, "spi_setup failed: %d\n", r);
return r;
}
ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL);
if (ddata == NULL)
return -ENOMEM;
dev_set_drvdata(&spi->dev, ddata);
ddata->spi = spi;
if (dev_get_platdata(&spi->dev)) {
r = tpo_td043_probe_pdata(spi);
if (r)
return r;
} else if (spi->dev.of_node) {
r = tpo_td043_probe_of(spi);
if (r)
return r;
} else {
return -ENODEV;
}
ddata->mode = TPO_R02_MODE_800x480;
memcpy(ddata->gamma, tpo_td043_def_gamma, sizeof(ddata->gamma));
ddata->vcc_reg = devm_regulator_get(&spi->dev, "vcc");
if (IS_ERR(ddata->vcc_reg)) {
dev_err(&spi->dev, "failed to get LCD VCC regulator\n");
r = PTR_ERR(ddata->vcc_reg);
goto err_regulator;
}
if (gpio_is_valid(ddata->nreset_gpio)) {
r = devm_gpio_request_one(&spi->dev,
ddata->nreset_gpio, GPIOF_OUT_INIT_LOW,
"lcd reset");
if (r < 0) {
dev_err(&spi->dev, "couldn't request reset GPIO\n");
goto err_gpio_req;
}
}
r = sysfs_create_group(&spi->dev.kobj, &tpo_td043_attr_group);
if (r) {
dev_err(&spi->dev, "failed to create sysfs files\n");
goto err_sysfs;
}
ddata->videomode = tpo_td043_timings;
dssdev = &ddata->dssdev;
dssdev->dev = &spi->dev;
dssdev->driver = &tpo_td043_ops;
dssdev->type = OMAP_DISPLAY_TYPE_DPI;
dssdev->owner = THIS_MODULE;
dssdev->panel.timings = ddata->videomode;
r = omapdss_register_display(dssdev);
if (r) {
dev_err(&spi->dev, "Failed to register panel\n");
goto err_reg;
}
return 0;
err_reg:
sysfs_remove_group(&spi->dev.kobj, &tpo_td043_attr_group);
err_sysfs:
err_gpio_req:
err_regulator:
omap_dss_put_device(ddata->in);
return r;
}
static int tpo_td043_remove(struct spi_device *spi)
{
struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
struct omap_dss_device *dssdev = &ddata->dssdev;
struct omap_dss_device *in = ddata->in;
dev_dbg(&ddata->spi->dev, "%s\n", __func__);
omapdss_unregister_display(dssdev);
tpo_td043_disable(dssdev);
tpo_td043_disconnect(dssdev);
omap_dss_put_device(in);
sysfs_remove_group(&spi->dev.kobj, &tpo_td043_attr_group);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int tpo_td043_spi_suspend(struct device *dev)
{
struct panel_drv_data *ddata = dev_get_drvdata(dev);
dev_dbg(dev, "tpo_td043_spi_suspend, tpo %p\n", ddata);
ddata->power_on_resume = ddata->powered_on;
tpo_td043_power_off(ddata);
ddata->spi_suspended = 1;
return 0;
}
static int tpo_td043_spi_resume(struct device *dev)
{
struct panel_drv_data *ddata = dev_get_drvdata(dev);
int ret;
dev_dbg(dev, "tpo_td043_spi_resume\n");
if (ddata->power_on_resume) {
ret = tpo_td043_power_on(ddata);
if (ret)
return ret;
}
ddata->spi_suspended = 0;
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(tpo_td043_spi_pm,
tpo_td043_spi_suspend, tpo_td043_spi_resume);
static const struct of_device_id tpo_td043_of_match[] = {
{ .compatible = "omapdss,tpo,td043mtea1", },
{},
};
MODULE_DEVICE_TABLE(of, tpo_td043_of_match);
static struct spi_driver tpo_td043_spi_driver = {
.driver = {
.name = "panel-tpo-td043mtea1",
.pm = &tpo_td043_spi_pm,
.of_match_table = tpo_td043_of_match,
.suppress_bind_attrs = true,
},
.probe = tpo_td043_probe,
.remove = tpo_td043_remove,
};
module_spi_driver(tpo_td043_spi_driver);
MODULE_ALIAS("spi:tpo,td043mtea1");
MODULE_AUTHOR("Gražvydas Ignotas <notasas@gmail.com>");
MODULE_DESCRIPTION("TPO TD043MTEA1 LCD Driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,135 @@
config OMAP2_DSS_INIT
bool
menuconfig OMAP2_DSS
tristate "OMAP2+ Display Subsystem support"
select VIDEOMODE_HELPERS
select OMAP2_DSS_INIT
select HDMI
help
OMAP2+ Display Subsystem support.
if OMAP2_DSS
config OMAP2_DSS_DEBUG
bool "Debug support"
default n
help
This enables printing of debug messages. Alternatively, debug messages
can also be enabled by setting CONFIG_DYNAMIC_DEBUG and then setting
appropriate flags in <debugfs>/dynamic_debug/control.
config OMAP2_DSS_DEBUGFS
bool "Debugfs filesystem support"
depends on DEBUG_FS
default n
help
This enables debugfs for OMAPDSS at <debugfs>/omapdss. This enables
querying about clock configuration and register configuration of dss,
dispc, dsi, hdmi and rfbi.
config OMAP2_DSS_COLLECT_IRQ_STATS
bool "Collect DSS IRQ statistics"
depends on OMAP2_DSS_DEBUGFS
default n
help
Collect DSS IRQ statistics, printable via debugfs.
The statistics can be found from
<debugfs>/omapdss/dispc_irq for DISPC interrupts, and
<debugfs>/omapdss/dsi_irq for DSI interrupts.
config OMAP2_DSS_DPI
bool "DPI support"
default y
help
DPI Interface. This is the Parallel Display Interface.
config OMAP2_DSS_RFBI
bool "RFBI support"
depends on BROKEN
default n
help
MIPI DBI support (RFBI, Remote Framebuffer Interface, in Texas
Instrument's terminology).
DBI is a bus between the host processor and a peripheral,
such as a display or a framebuffer chip.
See http://www.mipi.org/ for DBI specifications.
config OMAP2_DSS_VENC
bool "VENC support"
default y
help
OMAP Video Encoder support for S-Video and composite TV-out.
config OMAP2_DSS_HDMI_COMMON
bool
config OMAP4_DSS_HDMI
bool "HDMI support for OMAP4"
default y
select OMAP2_DSS_HDMI_COMMON
help
HDMI support for OMAP4 based SoCs.
config OMAP5_DSS_HDMI
bool "HDMI support for OMAP5"
default n
select OMAP2_DSS_HDMI_COMMON
help
HDMI Interface for OMAP5 and similar cores. This adds the High
Definition Multimedia Interface. See http://www.hdmi.org/ for HDMI
specification.
config OMAP2_DSS_SDI
bool "SDI support"
default n
help
SDI (Serial Display Interface) support.
SDI is a high speed one-way display serial bus between the host
processor and a display.
config OMAP2_DSS_DSI
bool "DSI support"
default n
help
MIPI DSI (Display Serial Interface) support.
DSI is a high speed half-duplex serial interface between the host
processor and a peripheral, such as a display or a framebuffer chip.
See http://www.mipi.org/ for DSI specifications.
config OMAP2_DSS_MIN_FCK_PER_PCK
int "Minimum FCK/PCK ratio (for scaling)"
range 0 32
default 0
help
This can be used to adjust the minimum FCK/PCK ratio.
With this you can make sure that DISPC FCK is at least
n x PCK. Video plane scaling requires higher FCK than
normally.
If this is set to 0, there's no extra constraint on the
DISPC FCK. However, the FCK will at minimum be
2xPCK (if active matrix) or 3xPCK (if passive matrix).
Max FCK is 173MHz, so this doesn't work if your PCK
is very high.
config OMAP2_DSS_SLEEP_AFTER_VENC_RESET
bool "Sleep 20ms after VENC reset"
default y
help
There is a 20ms sleep after VENC reset which seemed to fix the
reset. The reason for the bug is unclear, and it's also unclear
on what platforms this happens.
This option enables the sleep, and is enabled by default. You can
disable the sleep if it doesn't cause problems on your platform.
endif

View File

@ -0,0 +1,18 @@
obj-$(CONFIG_OMAP2_DSS_INIT) += omapdss-boot-init.o
obj-$(CONFIG_OMAP2_DSS) += omapdss.o
# Core DSS files
omapdss-y := core.o dss.o dss_features.o dispc.o dispc_coefs.o display.o \
output.o dss-of.o pll.o video-pll.o
# DSS compat layer files
omapdss-y += manager.o manager-sysfs.o overlay.o overlay-sysfs.o apply.o \
dispc-compat.o display-sysfs.o
omapdss-$(CONFIG_OMAP2_DSS_DPI) += dpi.o
omapdss-$(CONFIG_OMAP2_DSS_RFBI) += rfbi.o
omapdss-$(CONFIG_OMAP2_DSS_VENC) += venc.o
omapdss-$(CONFIG_OMAP2_DSS_SDI) += sdi.o
omapdss-$(CONFIG_OMAP2_DSS_DSI) += dsi.o
omapdss-$(CONFIG_OMAP2_DSS_HDMI_COMMON) += hdmi_common.o hdmi_wp.o hdmi_pll.o \
hdmi_phy.o
omapdss-$(CONFIG_OMAP4_DSS_HDMI) += hdmi4.o hdmi4_core.o
omapdss-$(CONFIG_OMAP5_DSS_HDMI) += hdmi5.o hdmi5_core.o
ccflags-$(CONFIG_OMAP2_DSS_DEBUG) += -DDEBUG

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,343 @@
/*
* linux/drivers/video/omap2/dss/core.c
*
* Copyright (C) 2009 Nokia Corporation
* Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
*
* Some code and ideas taken from drivers/video/omap/ driver
* by Imre Deak.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define DSS_SUBSYS_NAME "CORE"
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/seq_file.h>
#include <linux/debugfs.h>
#include <linux/io.h>
#include <linux/device.h>
#include <linux/regulator/consumer.h>
#include <linux/suspend.h>
#include <linux/slab.h>
#include <video/omapdss.h>
#include "dss.h"
#include "dss_features.h"
static struct {
struct platform_device *pdev;
const char *default_display_name;
} core;
static char *def_disp_name;
module_param_named(def_disp, def_disp_name, charp, 0);
MODULE_PARM_DESC(def_disp, "default display name");
const char *omapdss_get_default_display_name(void)
{
return core.default_display_name;
}
EXPORT_SYMBOL(omapdss_get_default_display_name);
enum omapdss_version omapdss_get_version(void)
{
struct omap_dss_board_info *pdata = core.pdev->dev.platform_data;
return pdata->version;
}
EXPORT_SYMBOL(omapdss_get_version);
struct platform_device *dss_get_core_pdev(void)
{
return core.pdev;
}
int dss_dsi_enable_pads(int dsi_id, unsigned lane_mask)
{
struct omap_dss_board_info *board_data = core.pdev->dev.platform_data;
if (!board_data->dsi_enable_pads)
return -ENOENT;
return board_data->dsi_enable_pads(dsi_id, lane_mask);
}
void dss_dsi_disable_pads(int dsi_id, unsigned lane_mask)
{
struct omap_dss_board_info *board_data = core.pdev->dev.platform_data;
if (!board_data->dsi_disable_pads)
return;
return board_data->dsi_disable_pads(dsi_id, lane_mask);
}
int dss_set_min_bus_tput(struct device *dev, unsigned long tput)
{
struct omap_dss_board_info *pdata = core.pdev->dev.platform_data;
if (pdata->set_min_bus_tput)
return pdata->set_min_bus_tput(dev, tput);
else
return 0;
}
#if defined(CONFIG_OMAP2_DSS_DEBUGFS)
static int dss_debug_show(struct seq_file *s, void *unused)
{
void (*func)(struct seq_file *) = s->private;
func(s);
return 0;
}
static int dss_debug_open(struct inode *inode, struct file *file)
{
return single_open(file, dss_debug_show, inode->i_private);
}
static const struct file_operations dss_debug_fops = {
.open = dss_debug_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static struct dentry *dss_debugfs_dir;
static int dss_initialize_debugfs(void)
{
dss_debugfs_dir = debugfs_create_dir("omapdss", NULL);
if (IS_ERR(dss_debugfs_dir)) {
int err = PTR_ERR(dss_debugfs_dir);
dss_debugfs_dir = NULL;
return err;
}
debugfs_create_file("clk", S_IRUGO, dss_debugfs_dir,
&dss_debug_dump_clocks, &dss_debug_fops);
return 0;
}
static void dss_uninitialize_debugfs(void)
{
if (dss_debugfs_dir)
debugfs_remove_recursive(dss_debugfs_dir);
}
int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *))
{
struct dentry *d;
d = debugfs_create_file(name, S_IRUGO, dss_debugfs_dir,
write, &dss_debug_fops);
return PTR_ERR_OR_ZERO(d);
}
#else /* CONFIG_OMAP2_DSS_DEBUGFS */
static inline int dss_initialize_debugfs(void)
{
return 0;
}
static inline void dss_uninitialize_debugfs(void)
{
}
int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *))
{
return 0;
}
#endif /* CONFIG_OMAP2_DSS_DEBUGFS */
/* PLATFORM DEVICE */
static int omap_dss_pm_notif(struct notifier_block *b, unsigned long v, void *d)
{
DSSDBG("pm notif %lu\n", v);
switch (v) {
case PM_SUSPEND_PREPARE:
case PM_HIBERNATION_PREPARE:
case PM_RESTORE_PREPARE:
DSSDBG("suspending displays\n");
return dss_suspend_all_devices();
case PM_POST_SUSPEND:
case PM_POST_HIBERNATION:
case PM_POST_RESTORE:
DSSDBG("resuming displays\n");
return dss_resume_all_devices();
default:
return 0;
}
}
static struct notifier_block omap_dss_pm_notif_block = {
.notifier_call = omap_dss_pm_notif,
};
static int __init omap_dss_probe(struct platform_device *pdev)
{
struct omap_dss_board_info *pdata = pdev->dev.platform_data;
int r;
core.pdev = pdev;
dss_features_init(omapdss_get_version());
r = dss_initialize_debugfs();
if (r)
goto err_debugfs;
if (def_disp_name)
core.default_display_name = def_disp_name;
else if (pdata->default_display_name)
core.default_display_name = pdata->default_display_name;
else if (pdata->default_device)
core.default_display_name = pdata->default_device->name;
register_pm_notifier(&omap_dss_pm_notif_block);
return 0;
err_debugfs:
return r;
}
static int omap_dss_remove(struct platform_device *pdev)
{
unregister_pm_notifier(&omap_dss_pm_notif_block);
dss_uninitialize_debugfs();
return 0;
}
static void omap_dss_shutdown(struct platform_device *pdev)
{
DSSDBG("shutdown\n");
dss_disable_all_devices();
}
static struct platform_driver omap_dss_driver = {
.remove = omap_dss_remove,
.shutdown = omap_dss_shutdown,
.driver = {
.name = "omapdss",
},
};
/* INIT */
static int (*dss_output_drv_reg_funcs[])(void) __initdata = {
dss_init_platform_driver,
dispc_init_platform_driver,
#ifdef CONFIG_OMAP2_DSS_DSI
dsi_init_platform_driver,
#endif
#ifdef CONFIG_OMAP2_DSS_DPI
dpi_init_platform_driver,
#endif
#ifdef CONFIG_OMAP2_DSS_SDI
sdi_init_platform_driver,
#endif
#ifdef CONFIG_OMAP2_DSS_RFBI
rfbi_init_platform_driver,
#endif
#ifdef CONFIG_OMAP2_DSS_VENC
venc_init_platform_driver,
#endif
#ifdef CONFIG_OMAP4_DSS_HDMI
hdmi4_init_platform_driver,
#endif
#ifdef CONFIG_OMAP5_DSS_HDMI
hdmi5_init_platform_driver,
#endif
};
static void (*dss_output_drv_unreg_funcs[])(void) = {
#ifdef CONFIG_OMAP5_DSS_HDMI
hdmi5_uninit_platform_driver,
#endif
#ifdef CONFIG_OMAP4_DSS_HDMI
hdmi4_uninit_platform_driver,
#endif
#ifdef CONFIG_OMAP2_DSS_VENC
venc_uninit_platform_driver,
#endif
#ifdef CONFIG_OMAP2_DSS_RFBI
rfbi_uninit_platform_driver,
#endif
#ifdef CONFIG_OMAP2_DSS_SDI
sdi_uninit_platform_driver,
#endif
#ifdef CONFIG_OMAP2_DSS_DPI
dpi_uninit_platform_driver,
#endif
#ifdef CONFIG_OMAP2_DSS_DSI
dsi_uninit_platform_driver,
#endif
dispc_uninit_platform_driver,
dss_uninit_platform_driver,
};
static int __init omap_dss_init(void)
{
int r;
int i;
r = platform_driver_probe(&omap_dss_driver, omap_dss_probe);
if (r)
return r;
for (i = 0; i < ARRAY_SIZE(dss_output_drv_reg_funcs); ++i) {
r = dss_output_drv_reg_funcs[i]();
if (r)
goto err_reg;
}
return 0;
err_reg:
for (i = ARRAY_SIZE(dss_output_drv_reg_funcs) - i;
i < ARRAY_SIZE(dss_output_drv_reg_funcs);
++i)
dss_output_drv_unreg_funcs[i]();
platform_driver_unregister(&omap_dss_driver);
return r;
}
static void __exit omap_dss_exit(void)
{
int i;
for (i = 0; i < ARRAY_SIZE(dss_output_drv_unreg_funcs); ++i)
dss_output_drv_unreg_funcs[i]();
platform_driver_unregister(&omap_dss_driver);
}
module_init(omap_dss_init);
module_exit(omap_dss_exit);
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>");
MODULE_DESCRIPTION("OMAP2/3 Display Subsystem");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,667 @@
/*
* Copyright (C) 2012 Texas Instruments
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define DSS_SUBSYS_NAME "APPLY"
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/jiffies.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/seq_file.h>
#include <video/omapdss.h>
#include "dss.h"
#include "dss_features.h"
#include "dispc-compat.h"
#define DISPC_IRQ_MASK_ERROR (DISPC_IRQ_GFX_FIFO_UNDERFLOW | \
DISPC_IRQ_OCP_ERR | \
DISPC_IRQ_VID1_FIFO_UNDERFLOW | \
DISPC_IRQ_VID2_FIFO_UNDERFLOW | \
DISPC_IRQ_SYNC_LOST | \
DISPC_IRQ_SYNC_LOST_DIGIT)
#define DISPC_MAX_NR_ISRS 8
struct omap_dispc_isr_data {
omap_dispc_isr_t isr;
void *arg;
u32 mask;
};
struct dispc_irq_stats {
unsigned long last_reset;
unsigned irq_count;
unsigned irqs[32];
};
static struct {
spinlock_t irq_lock;
u32 irq_error_mask;
struct omap_dispc_isr_data registered_isr[DISPC_MAX_NR_ISRS];
u32 error_irqs;
struct work_struct error_work;
#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
spinlock_t irq_stats_lock;
struct dispc_irq_stats irq_stats;
#endif
} dispc_compat;
#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
static void dispc_dump_irqs(struct seq_file *s)
{
unsigned long flags;
struct dispc_irq_stats stats;
spin_lock_irqsave(&dispc_compat.irq_stats_lock, flags);
stats = dispc_compat.irq_stats;
memset(&dispc_compat.irq_stats, 0, sizeof(dispc_compat.irq_stats));
dispc_compat.irq_stats.last_reset = jiffies;
spin_unlock_irqrestore(&dispc_compat.irq_stats_lock, flags);
seq_printf(s, "period %u ms\n",
jiffies_to_msecs(jiffies - stats.last_reset));
seq_printf(s, "irqs %d\n", stats.irq_count);
#define PIS(x) \
seq_printf(s, "%-20s %10d\n", #x, stats.irqs[ffs(DISPC_IRQ_##x)-1]);
PIS(FRAMEDONE);
PIS(VSYNC);
PIS(EVSYNC_EVEN);
PIS(EVSYNC_ODD);
PIS(ACBIAS_COUNT_STAT);
PIS(PROG_LINE_NUM);
PIS(GFX_FIFO_UNDERFLOW);
PIS(GFX_END_WIN);
PIS(PAL_GAMMA_MASK);
PIS(OCP_ERR);
PIS(VID1_FIFO_UNDERFLOW);
PIS(VID1_END_WIN);
PIS(VID2_FIFO_UNDERFLOW);
PIS(VID2_END_WIN);
if (dss_feat_get_num_ovls() > 3) {
PIS(VID3_FIFO_UNDERFLOW);
PIS(VID3_END_WIN);
}
PIS(SYNC_LOST);
PIS(SYNC_LOST_DIGIT);
PIS(WAKEUP);
if (dss_has_feature(FEAT_MGR_LCD2)) {
PIS(FRAMEDONE2);
PIS(VSYNC2);
PIS(ACBIAS_COUNT_STAT2);
PIS(SYNC_LOST2);
}
if (dss_has_feature(FEAT_MGR_LCD3)) {
PIS(FRAMEDONE3);
PIS(VSYNC3);
PIS(ACBIAS_COUNT_STAT3);
PIS(SYNC_LOST3);
}
#undef PIS
}
#endif
/* dispc.irq_lock has to be locked by the caller */
static void _omap_dispc_set_irqs(void)
{
u32 mask;
int i;
struct omap_dispc_isr_data *isr_data;
mask = dispc_compat.irq_error_mask;
for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
isr_data = &dispc_compat.registered_isr[i];
if (isr_data->isr == NULL)
continue;
mask |= isr_data->mask;
}
dispc_write_irqenable(mask);
}
int omap_dispc_register_isr(omap_dispc_isr_t isr, void *arg, u32 mask)
{
int i;
int ret;
unsigned long flags;
struct omap_dispc_isr_data *isr_data;
if (isr == NULL)
return -EINVAL;
spin_lock_irqsave(&dispc_compat.irq_lock, flags);
/* check for duplicate entry */
for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
isr_data = &dispc_compat.registered_isr[i];
if (isr_data->isr == isr && isr_data->arg == arg &&
isr_data->mask == mask) {
ret = -EINVAL;
goto err;
}
}
isr_data = NULL;
ret = -EBUSY;
for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
isr_data = &dispc_compat.registered_isr[i];
if (isr_data->isr != NULL)
continue;
isr_data->isr = isr;
isr_data->arg = arg;
isr_data->mask = mask;
ret = 0;
break;
}
if (ret)
goto err;
_omap_dispc_set_irqs();
spin_unlock_irqrestore(&dispc_compat.irq_lock, flags);
return 0;
err:
spin_unlock_irqrestore(&dispc_compat.irq_lock, flags);
return ret;
}
EXPORT_SYMBOL(omap_dispc_register_isr);
int omap_dispc_unregister_isr(omap_dispc_isr_t isr, void *arg, u32 mask)
{
int i;
unsigned long flags;
int ret = -EINVAL;
struct omap_dispc_isr_data *isr_data;
spin_lock_irqsave(&dispc_compat.irq_lock, flags);
for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
isr_data = &dispc_compat.registered_isr[i];
if (isr_data->isr != isr || isr_data->arg != arg ||
isr_data->mask != mask)
continue;
/* found the correct isr */
isr_data->isr = NULL;
isr_data->arg = NULL;
isr_data->mask = 0;
ret = 0;
break;
}
if (ret == 0)
_omap_dispc_set_irqs();
spin_unlock_irqrestore(&dispc_compat.irq_lock, flags);
return ret;
}
EXPORT_SYMBOL(omap_dispc_unregister_isr);
static void print_irq_status(u32 status)
{
if ((status & dispc_compat.irq_error_mask) == 0)
return;
#define PIS(x) (status & DISPC_IRQ_##x) ? (#x " ") : ""
pr_debug("DISPC IRQ: 0x%x: %s%s%s%s%s%s%s%s%s\n",
status,
PIS(OCP_ERR),
PIS(GFX_FIFO_UNDERFLOW),
PIS(VID1_FIFO_UNDERFLOW),
PIS(VID2_FIFO_UNDERFLOW),
dss_feat_get_num_ovls() > 3 ? PIS(VID3_FIFO_UNDERFLOW) : "",
PIS(SYNC_LOST),
PIS(SYNC_LOST_DIGIT),
dss_has_feature(FEAT_MGR_LCD2) ? PIS(SYNC_LOST2) : "",
dss_has_feature(FEAT_MGR_LCD3) ? PIS(SYNC_LOST3) : "");
#undef PIS
}
/* Called from dss.c. Note that we don't touch clocks here,
* but we presume they are on because we got an IRQ. However,
* an irq handler may turn the clocks off, so we may not have
* clock later in the function. */
static irqreturn_t omap_dispc_irq_handler(int irq, void *arg)
{
int i;
u32 irqstatus, irqenable;
u32 handledirqs = 0;
u32 unhandled_errors;
struct omap_dispc_isr_data *isr_data;
struct omap_dispc_isr_data registered_isr[DISPC_MAX_NR_ISRS];
spin_lock(&dispc_compat.irq_lock);
irqstatus = dispc_read_irqstatus();
irqenable = dispc_read_irqenable();
/* IRQ is not for us */
if (!(irqstatus & irqenable)) {
spin_unlock(&dispc_compat.irq_lock);
return IRQ_NONE;
}
#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
spin_lock(&dispc_compat.irq_stats_lock);
dispc_compat.irq_stats.irq_count++;
dss_collect_irq_stats(irqstatus, dispc_compat.irq_stats.irqs);
spin_unlock(&dispc_compat.irq_stats_lock);
#endif
print_irq_status(irqstatus);
/* Ack the interrupt. Do it here before clocks are possibly turned
* off */
dispc_clear_irqstatus(irqstatus);
/* flush posted write */
dispc_read_irqstatus();
/* make a copy and unlock, so that isrs can unregister
* themselves */
memcpy(registered_isr, dispc_compat.registered_isr,
sizeof(registered_isr));
spin_unlock(&dispc_compat.irq_lock);
for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
isr_data = &registered_isr[i];
if (!isr_data->isr)
continue;
if (isr_data->mask & irqstatus) {
isr_data->isr(isr_data->arg, irqstatus);
handledirqs |= isr_data->mask;
}
}
spin_lock(&dispc_compat.irq_lock);
unhandled_errors = irqstatus & ~handledirqs & dispc_compat.irq_error_mask;
if (unhandled_errors) {
dispc_compat.error_irqs |= unhandled_errors;
dispc_compat.irq_error_mask &= ~unhandled_errors;
_omap_dispc_set_irqs();
schedule_work(&dispc_compat.error_work);
}
spin_unlock(&dispc_compat.irq_lock);
return IRQ_HANDLED;
}
static void dispc_error_worker(struct work_struct *work)
{
int i;
u32 errors;
unsigned long flags;
static const unsigned fifo_underflow_bits[] = {
DISPC_IRQ_GFX_FIFO_UNDERFLOW,
DISPC_IRQ_VID1_FIFO_UNDERFLOW,
DISPC_IRQ_VID2_FIFO_UNDERFLOW,
DISPC_IRQ_VID3_FIFO_UNDERFLOW,
};
spin_lock_irqsave(&dispc_compat.irq_lock, flags);
errors = dispc_compat.error_irqs;
dispc_compat.error_irqs = 0;
spin_unlock_irqrestore(&dispc_compat.irq_lock, flags);
dispc_runtime_get();
for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
struct omap_overlay *ovl;
unsigned bit;
ovl = omap_dss_get_overlay(i);
bit = fifo_underflow_bits[i];
if (bit & errors) {
DSSERR("FIFO UNDERFLOW on %s, disabling the overlay\n",
ovl->name);
ovl->disable(ovl);
msleep(50);
}
}
for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
struct omap_overlay_manager *mgr;
unsigned bit;
mgr = omap_dss_get_overlay_manager(i);
bit = dispc_mgr_get_sync_lost_irq(i);
if (bit & errors) {
int j;
DSSERR("SYNC_LOST on channel %s, restarting the output "
"with video overlays disabled\n",
mgr->name);
dss_mgr_disable(mgr);
for (j = 0; j < omap_dss_get_num_overlays(); ++j) {
struct omap_overlay *ovl;
ovl = omap_dss_get_overlay(j);
if (ovl->id != OMAP_DSS_GFX &&
ovl->manager == mgr)
ovl->disable(ovl);
}
dss_mgr_enable(mgr);
}
}
if (errors & DISPC_IRQ_OCP_ERR) {
DSSERR("OCP_ERR\n");
for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
struct omap_overlay_manager *mgr;
mgr = omap_dss_get_overlay_manager(i);
dss_mgr_disable(mgr);
}
}
spin_lock_irqsave(&dispc_compat.irq_lock, flags);
dispc_compat.irq_error_mask |= errors;
_omap_dispc_set_irqs();
spin_unlock_irqrestore(&dispc_compat.irq_lock, flags);
dispc_runtime_put();
}
int dss_dispc_initialize_irq(void)
{
int r;
#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
spin_lock_init(&dispc_compat.irq_stats_lock);
dispc_compat.irq_stats.last_reset = jiffies;
dss_debugfs_create_file("dispc_irq", dispc_dump_irqs);
#endif
spin_lock_init(&dispc_compat.irq_lock);
memset(dispc_compat.registered_isr, 0,
sizeof(dispc_compat.registered_isr));
dispc_compat.irq_error_mask = DISPC_IRQ_MASK_ERROR;
if (dss_has_feature(FEAT_MGR_LCD2))
dispc_compat.irq_error_mask |= DISPC_IRQ_SYNC_LOST2;
if (dss_has_feature(FEAT_MGR_LCD3))
dispc_compat.irq_error_mask |= DISPC_IRQ_SYNC_LOST3;
if (dss_feat_get_num_ovls() > 3)
dispc_compat.irq_error_mask |= DISPC_IRQ_VID3_FIFO_UNDERFLOW;
/*
* there's SYNC_LOST_DIGIT waiting after enabling the DSS,
* so clear it
*/
dispc_clear_irqstatus(dispc_read_irqstatus());
INIT_WORK(&dispc_compat.error_work, dispc_error_worker);
_omap_dispc_set_irqs();
r = dispc_request_irq(omap_dispc_irq_handler, &dispc_compat);
if (r) {
DSSERR("dispc_request_irq failed\n");
return r;
}
return 0;
}
void dss_dispc_uninitialize_irq(void)
{
dispc_free_irq(&dispc_compat);
}
static void dispc_mgr_disable_isr(void *data, u32 mask)
{
struct completion *compl = data;
complete(compl);
}
static void dispc_mgr_enable_lcd_out(enum omap_channel channel)
{
dispc_mgr_enable(channel, true);
}
static void dispc_mgr_disable_lcd_out(enum omap_channel channel)
{
DECLARE_COMPLETION_ONSTACK(framedone_compl);
int r;
u32 irq;
if (!dispc_mgr_is_enabled(channel))
return;
/*
* When we disable LCD output, we need to wait for FRAMEDONE to know
* that DISPC has finished with the LCD output.
*/
irq = dispc_mgr_get_framedone_irq(channel);
r = omap_dispc_register_isr(dispc_mgr_disable_isr, &framedone_compl,
irq);
if (r)
DSSERR("failed to register FRAMEDONE isr\n");
dispc_mgr_enable(channel, false);
/* if we couldn't register for framedone, just sleep and exit */
if (r) {
msleep(100);
return;
}
if (!wait_for_completion_timeout(&framedone_compl,
msecs_to_jiffies(100)))
DSSERR("timeout waiting for FRAME DONE\n");
r = omap_dispc_unregister_isr(dispc_mgr_disable_isr, &framedone_compl,
irq);
if (r)
DSSERR("failed to unregister FRAMEDONE isr\n");
}
static void dispc_digit_out_enable_isr(void *data, u32 mask)
{
struct completion *compl = data;
/* ignore any sync lost interrupts */
if (mask & (DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD))
complete(compl);
}
static void dispc_mgr_enable_digit_out(void)
{
DECLARE_COMPLETION_ONSTACK(vsync_compl);
int r;
u32 irq_mask;
if (dispc_mgr_is_enabled(OMAP_DSS_CHANNEL_DIGIT))
return;
/*
* Digit output produces some sync lost interrupts during the first
* frame when enabling. Those need to be ignored, so we register for the
* sync lost irq to prevent the error handler from triggering.
*/
irq_mask = dispc_mgr_get_vsync_irq(OMAP_DSS_CHANNEL_DIGIT) |
dispc_mgr_get_sync_lost_irq(OMAP_DSS_CHANNEL_DIGIT);
r = omap_dispc_register_isr(dispc_digit_out_enable_isr, &vsync_compl,
irq_mask);
if (r) {
DSSERR("failed to register %x isr\n", irq_mask);
return;
}
dispc_mgr_enable(OMAP_DSS_CHANNEL_DIGIT, true);
/* wait for the first evsync */
if (!wait_for_completion_timeout(&vsync_compl, msecs_to_jiffies(100)))
DSSERR("timeout waiting for digit out to start\n");
r = omap_dispc_unregister_isr(dispc_digit_out_enable_isr, &vsync_compl,
irq_mask);
if (r)
DSSERR("failed to unregister %x isr\n", irq_mask);
}
static void dispc_mgr_disable_digit_out(void)
{
DECLARE_COMPLETION_ONSTACK(framedone_compl);
int r, i;
u32 irq_mask;
int num_irqs;
if (!dispc_mgr_is_enabled(OMAP_DSS_CHANNEL_DIGIT))
return;
/*
* When we disable the digit output, we need to wait for FRAMEDONE to
* know that DISPC has finished with the output.
*/
irq_mask = dispc_mgr_get_framedone_irq(OMAP_DSS_CHANNEL_DIGIT);
num_irqs = 1;
if (!irq_mask) {
/*
* omap 2/3 don't have framedone irq for TV, so we need to use
* vsyncs for this.
*/
irq_mask = dispc_mgr_get_vsync_irq(OMAP_DSS_CHANNEL_DIGIT);
/*
* We need to wait for both even and odd vsyncs. Note that this
* is not totally reliable, as we could get a vsync interrupt
* before we disable the output, which leads to timeout in the
* wait_for_completion.
*/
num_irqs = 2;
}
r = omap_dispc_register_isr(dispc_mgr_disable_isr, &framedone_compl,
irq_mask);
if (r)
DSSERR("failed to register %x isr\n", irq_mask);
dispc_mgr_enable(OMAP_DSS_CHANNEL_DIGIT, false);
/* if we couldn't register the irq, just sleep and exit */
if (r) {
msleep(100);
return;
}
for (i = 0; i < num_irqs; ++i) {
if (!wait_for_completion_timeout(&framedone_compl,
msecs_to_jiffies(100)))
DSSERR("timeout waiting for digit out to stop\n");
}
r = omap_dispc_unregister_isr(dispc_mgr_disable_isr, &framedone_compl,
irq_mask);
if (r)
DSSERR("failed to unregister %x isr\n", irq_mask);
}
void dispc_mgr_enable_sync(enum omap_channel channel)
{
if (dss_mgr_is_lcd(channel))
dispc_mgr_enable_lcd_out(channel);
else if (channel == OMAP_DSS_CHANNEL_DIGIT)
dispc_mgr_enable_digit_out();
else
WARN_ON(1);
}
void dispc_mgr_disable_sync(enum omap_channel channel)
{
if (dss_mgr_is_lcd(channel))
dispc_mgr_disable_lcd_out(channel);
else if (channel == OMAP_DSS_CHANNEL_DIGIT)
dispc_mgr_disable_digit_out();
else
WARN_ON(1);
}
static inline void dispc_irq_wait_handler(void *data, u32 mask)
{
complete((struct completion *)data);
}
int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask,
unsigned long timeout)
{
int r;
DECLARE_COMPLETION_ONSTACK(completion);
r = omap_dispc_register_isr(dispc_irq_wait_handler, &completion,
irqmask);
if (r)
return r;
timeout = wait_for_completion_interruptible_timeout(&completion,
timeout);
omap_dispc_unregister_isr(dispc_irq_wait_handler, &completion, irqmask);
if (timeout == 0)
return -ETIMEDOUT;
if (timeout == -ERESTARTSYS)
return -ERESTARTSYS;
return 0;
}

View File

@ -0,0 +1,30 @@
/*
* Copyright (C) 2012 Texas Instruments
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __OMAP2_DSS_DISPC_COMPAT_H
#define __OMAP2_DSS_DISPC_COMPAT_H
void dispc_mgr_enable_sync(enum omap_channel channel);
void dispc_mgr_disable_sync(enum omap_channel channel);
int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask,
unsigned long timeout);
int dss_dispc_initialize_irq(void);
void dss_dispc_uninitialize_irq(void);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,918 @@
/*
* linux/drivers/video/omap2/dss/dispc.h
*
* Copyright (C) 2011 Texas Instruments
* Author: Archit Taneja <archit@ti.com>
*
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __OMAP2_DISPC_REG_H
#define __OMAP2_DISPC_REG_H
/* DISPC common registers */
#define DISPC_REVISION 0x0000
#define DISPC_SYSCONFIG 0x0010
#define DISPC_SYSSTATUS 0x0014
#define DISPC_IRQSTATUS 0x0018
#define DISPC_IRQENABLE 0x001C
#define DISPC_CONTROL 0x0040
#define DISPC_CONFIG 0x0044
#define DISPC_CAPABLE 0x0048
#define DISPC_LINE_STATUS 0x005C
#define DISPC_LINE_NUMBER 0x0060
#define DISPC_GLOBAL_ALPHA 0x0074
#define DISPC_CONTROL2 0x0238
#define DISPC_CONFIG2 0x0620
#define DISPC_DIVISOR 0x0804
#define DISPC_GLOBAL_BUFFER 0x0800
#define DISPC_CONTROL3 0x0848
#define DISPC_CONFIG3 0x084C
#define DISPC_MSTANDBY_CTRL 0x0858
#define DISPC_GLOBAL_MFLAG_ATTRIBUTE 0x085C
/* DISPC overlay registers */
#define DISPC_OVL_BA0(n) (DISPC_OVL_BASE(n) + \
DISPC_BA0_OFFSET(n))
#define DISPC_OVL_BA1(n) (DISPC_OVL_BASE(n) + \
DISPC_BA1_OFFSET(n))
#define DISPC_OVL_BA0_UV(n) (DISPC_OVL_BASE(n) + \
DISPC_BA0_UV_OFFSET(n))
#define DISPC_OVL_BA1_UV(n) (DISPC_OVL_BASE(n) + \
DISPC_BA1_UV_OFFSET(n))
#define DISPC_OVL_POSITION(n) (DISPC_OVL_BASE(n) + \
DISPC_POS_OFFSET(n))
#define DISPC_OVL_SIZE(n) (DISPC_OVL_BASE(n) + \
DISPC_SIZE_OFFSET(n))
#define DISPC_OVL_ATTRIBUTES(n) (DISPC_OVL_BASE(n) + \
DISPC_ATTR_OFFSET(n))
#define DISPC_OVL_ATTRIBUTES2(n) (DISPC_OVL_BASE(n) + \
DISPC_ATTR2_OFFSET(n))
#define DISPC_OVL_FIFO_THRESHOLD(n) (DISPC_OVL_BASE(n) + \
DISPC_FIFO_THRESH_OFFSET(n))
#define DISPC_OVL_FIFO_SIZE_STATUS(n) (DISPC_OVL_BASE(n) + \
DISPC_FIFO_SIZE_STATUS_OFFSET(n))
#define DISPC_OVL_ROW_INC(n) (DISPC_OVL_BASE(n) + \
DISPC_ROW_INC_OFFSET(n))
#define DISPC_OVL_PIXEL_INC(n) (DISPC_OVL_BASE(n) + \
DISPC_PIX_INC_OFFSET(n))
#define DISPC_OVL_WINDOW_SKIP(n) (DISPC_OVL_BASE(n) + \
DISPC_WINDOW_SKIP_OFFSET(n))
#define DISPC_OVL_TABLE_BA(n) (DISPC_OVL_BASE(n) + \
DISPC_TABLE_BA_OFFSET(n))
#define DISPC_OVL_FIR(n) (DISPC_OVL_BASE(n) + \
DISPC_FIR_OFFSET(n))
#define DISPC_OVL_FIR2(n) (DISPC_OVL_BASE(n) + \
DISPC_FIR2_OFFSET(n))
#define DISPC_OVL_PICTURE_SIZE(n) (DISPC_OVL_BASE(n) + \
DISPC_PIC_SIZE_OFFSET(n))
#define DISPC_OVL_ACCU0(n) (DISPC_OVL_BASE(n) + \
DISPC_ACCU0_OFFSET(n))
#define DISPC_OVL_ACCU1(n) (DISPC_OVL_BASE(n) + \
DISPC_ACCU1_OFFSET(n))
#define DISPC_OVL_ACCU2_0(n) (DISPC_OVL_BASE(n) + \
DISPC_ACCU2_0_OFFSET(n))
#define DISPC_OVL_ACCU2_1(n) (DISPC_OVL_BASE(n) + \
DISPC_ACCU2_1_OFFSET(n))
#define DISPC_OVL_FIR_COEF_H(n, i) (DISPC_OVL_BASE(n) + \
DISPC_FIR_COEF_H_OFFSET(n, i))
#define DISPC_OVL_FIR_COEF_HV(n, i) (DISPC_OVL_BASE(n) + \
DISPC_FIR_COEF_HV_OFFSET(n, i))
#define DISPC_OVL_FIR_COEF_H2(n, i) (DISPC_OVL_BASE(n) + \
DISPC_FIR_COEF_H2_OFFSET(n, i))
#define DISPC_OVL_FIR_COEF_HV2(n, i) (DISPC_OVL_BASE(n) + \
DISPC_FIR_COEF_HV2_OFFSET(n, i))
#define DISPC_OVL_CONV_COEF(n, i) (DISPC_OVL_BASE(n) + \
DISPC_CONV_COEF_OFFSET(n, i))
#define DISPC_OVL_FIR_COEF_V(n, i) (DISPC_OVL_BASE(n) + \
DISPC_FIR_COEF_V_OFFSET(n, i))
#define DISPC_OVL_FIR_COEF_V2(n, i) (DISPC_OVL_BASE(n) + \
DISPC_FIR_COEF_V2_OFFSET(n, i))
#define DISPC_OVL_PRELOAD(n) (DISPC_OVL_BASE(n) + \
DISPC_PRELOAD_OFFSET(n))
#define DISPC_OVL_MFLAG_THRESHOLD(n) DISPC_MFLAG_THRESHOLD_OFFSET(n)
/* DISPC up/downsampling FIR filter coefficient structure */
struct dispc_coef {
s8 hc4_vc22;
s8 hc3_vc2;
u8 hc2_vc1;
s8 hc1_vc0;
s8 hc0_vc00;
};
const struct dispc_coef *dispc_ovl_get_scale_coef(int inc, int five_taps);
/* DISPC manager/channel specific registers */
static inline u16 DISPC_DEFAULT_COLOR(enum omap_channel channel)
{
switch (channel) {
case OMAP_DSS_CHANNEL_LCD:
return 0x004C;
case OMAP_DSS_CHANNEL_DIGIT:
return 0x0050;
case OMAP_DSS_CHANNEL_LCD2:
return 0x03AC;
case OMAP_DSS_CHANNEL_LCD3:
return 0x0814;
default:
BUG();
return 0;
}
}
static inline u16 DISPC_TRANS_COLOR(enum omap_channel channel)
{
switch (channel) {
case OMAP_DSS_CHANNEL_LCD:
return 0x0054;
case OMAP_DSS_CHANNEL_DIGIT:
return 0x0058;
case OMAP_DSS_CHANNEL_LCD2:
return 0x03B0;
case OMAP_DSS_CHANNEL_LCD3:
return 0x0818;
default:
BUG();
return 0;
}
}
static inline u16 DISPC_TIMING_H(enum omap_channel channel)
{
switch (channel) {
case OMAP_DSS_CHANNEL_LCD:
return 0x0064;
case OMAP_DSS_CHANNEL_DIGIT:
BUG();
return 0;
case OMAP_DSS_CHANNEL_LCD2:
return 0x0400;
case OMAP_DSS_CHANNEL_LCD3:
return 0x0840;
default:
BUG();
return 0;
}
}
static inline u16 DISPC_TIMING_V(enum omap_channel channel)
{
switch (channel) {
case OMAP_DSS_CHANNEL_LCD:
return 0x0068;
case OMAP_DSS_CHANNEL_DIGIT:
BUG();
return 0;
case OMAP_DSS_CHANNEL_LCD2:
return 0x0404;
case OMAP_DSS_CHANNEL_LCD3:
return 0x0844;
default:
BUG();
return 0;
}
}
static inline u16 DISPC_POL_FREQ(enum omap_channel channel)
{
switch (channel) {
case OMAP_DSS_CHANNEL_LCD:
return 0x006C;
case OMAP_DSS_CHANNEL_DIGIT:
BUG();
return 0;
case OMAP_DSS_CHANNEL_LCD2:
return 0x0408;
case OMAP_DSS_CHANNEL_LCD3:
return 0x083C;
default:
BUG();
return 0;
}
}
static inline u16 DISPC_DIVISORo(enum omap_channel channel)
{
switch (channel) {
case OMAP_DSS_CHANNEL_LCD:
return 0x0070;
case OMAP_DSS_CHANNEL_DIGIT:
BUG();
return 0;
case OMAP_DSS_CHANNEL_LCD2:
return 0x040C;
case OMAP_DSS_CHANNEL_LCD3:
return 0x0838;
default:
BUG();
return 0;
}
}
/* Named as DISPC_SIZE_LCD, DISPC_SIZE_DIGIT and DISPC_SIZE_LCD2 in TRM */
static inline u16 DISPC_SIZE_MGR(enum omap_channel channel)
{
switch (channel) {
case OMAP_DSS_CHANNEL_LCD:
return 0x007C;
case OMAP_DSS_CHANNEL_DIGIT:
return 0x0078;
case OMAP_DSS_CHANNEL_LCD2:
return 0x03CC;
case OMAP_DSS_CHANNEL_LCD3:
return 0x0834;
default:
BUG();
return 0;
}
}
static inline u16 DISPC_DATA_CYCLE1(enum omap_channel channel)
{
switch (channel) {
case OMAP_DSS_CHANNEL_LCD:
return 0x01D4;
case OMAP_DSS_CHANNEL_DIGIT:
BUG();
return 0;
case OMAP_DSS_CHANNEL_LCD2:
return 0x03C0;
case OMAP_DSS_CHANNEL_LCD3:
return 0x0828;
default:
BUG();
return 0;
}
}
static inline u16 DISPC_DATA_CYCLE2(enum omap_channel channel)
{
switch (channel) {
case OMAP_DSS_CHANNEL_LCD:
return 0x01D8;
case OMAP_DSS_CHANNEL_DIGIT:
BUG();
return 0;
case OMAP_DSS_CHANNEL_LCD2:
return 0x03C4;
case OMAP_DSS_CHANNEL_LCD3:
return 0x082C;
default:
BUG();
return 0;
}
}
static inline u16 DISPC_DATA_CYCLE3(enum omap_channel channel)
{
switch (channel) {
case OMAP_DSS_CHANNEL_LCD:
return 0x01DC;
case OMAP_DSS_CHANNEL_DIGIT:
BUG();
return 0;
case OMAP_DSS_CHANNEL_LCD2:
return 0x03C8;
case OMAP_DSS_CHANNEL_LCD3:
return 0x0830;
default:
BUG();
return 0;
}
}
static inline u16 DISPC_CPR_COEF_R(enum omap_channel channel)
{
switch (channel) {
case OMAP_DSS_CHANNEL_LCD:
return 0x0220;
case OMAP_DSS_CHANNEL_DIGIT:
BUG();
return 0;
case OMAP_DSS_CHANNEL_LCD2:
return 0x03BC;
case OMAP_DSS_CHANNEL_LCD3:
return 0x0824;
default:
BUG();
return 0;
}
}
static inline u16 DISPC_CPR_COEF_G(enum omap_channel channel)
{
switch (channel) {
case OMAP_DSS_CHANNEL_LCD:
return 0x0224;
case OMAP_DSS_CHANNEL_DIGIT:
BUG();
return 0;
case OMAP_DSS_CHANNEL_LCD2:
return 0x03B8;
case OMAP_DSS_CHANNEL_LCD3:
return 0x0820;
default:
BUG();
return 0;
}
}
static inline u16 DISPC_CPR_COEF_B(enum omap_channel channel)
{
switch (channel) {
case OMAP_DSS_CHANNEL_LCD:
return 0x0228;
case OMAP_DSS_CHANNEL_DIGIT:
BUG();
return 0;
case OMAP_DSS_CHANNEL_LCD2:
return 0x03B4;
case OMAP_DSS_CHANNEL_LCD3:
return 0x081C;
default:
BUG();
return 0;
}
}
/* DISPC overlay register base addresses */
static inline u16 DISPC_OVL_BASE(enum omap_plane plane)
{
switch (plane) {
case OMAP_DSS_GFX:
return 0x0080;
case OMAP_DSS_VIDEO1:
return 0x00BC;
case OMAP_DSS_VIDEO2:
return 0x014C;
case OMAP_DSS_VIDEO3:
return 0x0300;
case OMAP_DSS_WB:
return 0x0500;
default:
BUG();
return 0;
}
}
/* DISPC overlay register offsets */
static inline u16 DISPC_BA0_OFFSET(enum omap_plane plane)
{
switch (plane) {
case OMAP_DSS_GFX:
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x0000;
case OMAP_DSS_VIDEO3:
case OMAP_DSS_WB:
return 0x0008;
default:
BUG();
return 0;
}
}
static inline u16 DISPC_BA1_OFFSET(enum omap_plane plane)
{
switch (plane) {
case OMAP_DSS_GFX:
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x0004;
case OMAP_DSS_VIDEO3:
case OMAP_DSS_WB:
return 0x000C;
default:
BUG();
return 0;
}
}
static inline u16 DISPC_BA0_UV_OFFSET(enum omap_plane plane)
{
switch (plane) {
case OMAP_DSS_GFX:
BUG();
return 0;
case OMAP_DSS_VIDEO1:
return 0x0544;
case OMAP_DSS_VIDEO2:
return 0x04BC;
case OMAP_DSS_VIDEO3:
return 0x0310;
case OMAP_DSS_WB:
return 0x0118;
default:
BUG();
return 0;
}
}
static inline u16 DISPC_BA1_UV_OFFSET(enum omap_plane plane)
{
switch (plane) {
case OMAP_DSS_GFX:
BUG();
return 0;
case OMAP_DSS_VIDEO1:
return 0x0548;
case OMAP_DSS_VIDEO2:
return 0x04C0;
case OMAP_DSS_VIDEO3:
return 0x0314;
case OMAP_DSS_WB:
return 0x011C;
default:
BUG();
return 0;
}
}
static inline u16 DISPC_POS_OFFSET(enum omap_plane plane)
{
switch (plane) {
case OMAP_DSS_GFX:
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x0008;
case OMAP_DSS_VIDEO3:
return 0x009C;
default:
BUG();
return 0;
}
}
static inline u16 DISPC_SIZE_OFFSET(enum omap_plane plane)
{
switch (plane) {
case OMAP_DSS_GFX:
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x000C;
case OMAP_DSS_VIDEO3:
case OMAP_DSS_WB:
return 0x00A8;
default:
BUG();
return 0;
}
}
static inline u16 DISPC_ATTR_OFFSET(enum omap_plane plane)
{
switch (plane) {
case OMAP_DSS_GFX:
return 0x0020;
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x0010;
case OMAP_DSS_VIDEO3:
case OMAP_DSS_WB:
return 0x0070;
default:
BUG();
return 0;
}
}
static inline u16 DISPC_ATTR2_OFFSET(enum omap_plane plane)
{
switch (plane) {
case OMAP_DSS_GFX:
BUG();
return 0;
case OMAP_DSS_VIDEO1:
return 0x0568;
case OMAP_DSS_VIDEO2:
return 0x04DC;
case OMAP_DSS_VIDEO3:
return 0x032C;
case OMAP_DSS_WB:
return 0x0310;
default:
BUG();
return 0;
}
}
static inline u16 DISPC_FIFO_THRESH_OFFSET(enum omap_plane plane)
{
switch (plane) {
case OMAP_DSS_GFX:
return 0x0024;
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x0014;
case OMAP_DSS_VIDEO3:
case OMAP_DSS_WB:
return 0x008C;
default:
BUG();
return 0;
}
}
static inline u16 DISPC_FIFO_SIZE_STATUS_OFFSET(enum omap_plane plane)
{
switch (plane) {
case OMAP_DSS_GFX:
return 0x0028;
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x0018;
case OMAP_DSS_VIDEO3:
case OMAP_DSS_WB:
return 0x0088;
default:
BUG();
return 0;
}
}
static inline u16 DISPC_ROW_INC_OFFSET(enum omap_plane plane)
{
switch (plane) {
case OMAP_DSS_GFX:
return 0x002C;
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x001C;
case OMAP_DSS_VIDEO3:
case OMAP_DSS_WB:
return 0x00A4;
default:
BUG();
return 0;
}
}
static inline u16 DISPC_PIX_INC_OFFSET(enum omap_plane plane)
{
switch (plane) {
case OMAP_DSS_GFX:
return 0x0030;
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x0020;
case OMAP_DSS_VIDEO3:
case OMAP_DSS_WB:
return 0x0098;
default:
BUG();
return 0;
}
}
static inline u16 DISPC_WINDOW_SKIP_OFFSET(enum omap_plane plane)
{
switch (plane) {
case OMAP_DSS_GFX:
return 0x0034;
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
case OMAP_DSS_VIDEO3:
BUG();
return 0;
default:
BUG();
return 0;
}
}
static inline u16 DISPC_TABLE_BA_OFFSET(enum omap_plane plane)
{
switch (plane) {
case OMAP_DSS_GFX:
return 0x0038;
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
case OMAP_DSS_VIDEO3:
BUG();
return 0;
default:
BUG();
return 0;
}
}
static inline u16 DISPC_FIR_OFFSET(enum omap_plane plane)
{
switch (plane) {
case OMAP_DSS_GFX:
BUG();
return 0;
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x0024;
case OMAP_DSS_VIDEO3:
case OMAP_DSS_WB:
return 0x0090;
default:
BUG();
return 0;
}
}
static inline u16 DISPC_FIR2_OFFSET(enum omap_plane plane)
{
switch (plane) {
case OMAP_DSS_GFX:
BUG();
return 0;
case OMAP_DSS_VIDEO1:
return 0x0580;
case OMAP_DSS_VIDEO2:
return 0x055C;
case OMAP_DSS_VIDEO3:
return 0x0424;
case OMAP_DSS_WB:
return 0x290;
default:
BUG();
return 0;
}
}
static inline u16 DISPC_PIC_SIZE_OFFSET(enum omap_plane plane)
{
switch (plane) {
case OMAP_DSS_GFX:
BUG();
return 0;
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x0028;
case OMAP_DSS_VIDEO3:
case OMAP_DSS_WB:
return 0x0094;
default:
BUG();
return 0;
}
}
static inline u16 DISPC_ACCU0_OFFSET(enum omap_plane plane)
{
switch (plane) {
case OMAP_DSS_GFX:
BUG();
return 0;
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x002C;
case OMAP_DSS_VIDEO3:
case OMAP_DSS_WB:
return 0x0000;
default:
BUG();
return 0;
}
}
static inline u16 DISPC_ACCU2_0_OFFSET(enum omap_plane plane)
{
switch (plane) {
case OMAP_DSS_GFX:
BUG();
return 0;
case OMAP_DSS_VIDEO1:
return 0x0584;
case OMAP_DSS_VIDEO2:
return 0x0560;
case OMAP_DSS_VIDEO3:
return 0x0428;
case OMAP_DSS_WB:
return 0x0294;
default:
BUG();
return 0;
}
}
static inline u16 DISPC_ACCU1_OFFSET(enum omap_plane plane)
{
switch (plane) {
case OMAP_DSS_GFX:
BUG();
return 0;
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x0030;
case OMAP_DSS_VIDEO3:
case OMAP_DSS_WB:
return 0x0004;
default:
BUG();
return 0;
}
}
static inline u16 DISPC_ACCU2_1_OFFSET(enum omap_plane plane)
{
switch (plane) {
case OMAP_DSS_GFX:
BUG();
return 0;
case OMAP_DSS_VIDEO1:
return 0x0588;
case OMAP_DSS_VIDEO2:
return 0x0564;
case OMAP_DSS_VIDEO3:
return 0x042C;
case OMAP_DSS_WB:
return 0x0298;
default:
BUG();
return 0;
}
}
/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */
static inline u16 DISPC_FIR_COEF_H_OFFSET(enum omap_plane plane, u16 i)
{
switch (plane) {
case OMAP_DSS_GFX:
BUG();
return 0;
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x0034 + i * 0x8;
case OMAP_DSS_VIDEO3:
case OMAP_DSS_WB:
return 0x0010 + i * 0x8;
default:
BUG();
return 0;
}
}
/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */
static inline u16 DISPC_FIR_COEF_H2_OFFSET(enum omap_plane plane, u16 i)
{
switch (plane) {
case OMAP_DSS_GFX:
BUG();
return 0;
case OMAP_DSS_VIDEO1:
return 0x058C + i * 0x8;
case OMAP_DSS_VIDEO2:
return 0x0568 + i * 0x8;
case OMAP_DSS_VIDEO3:
return 0x0430 + i * 0x8;
case OMAP_DSS_WB:
return 0x02A0 + i * 0x8;
default:
BUG();
return 0;
}
}
/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */
static inline u16 DISPC_FIR_COEF_HV_OFFSET(enum omap_plane plane, u16 i)
{
switch (plane) {
case OMAP_DSS_GFX:
BUG();
return 0;
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x0038 + i * 0x8;
case OMAP_DSS_VIDEO3:
case OMAP_DSS_WB:
return 0x0014 + i * 0x8;
default:
BUG();
return 0;
}
}
/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */
static inline u16 DISPC_FIR_COEF_HV2_OFFSET(enum omap_plane plane, u16 i)
{
switch (plane) {
case OMAP_DSS_GFX:
BUG();
return 0;
case OMAP_DSS_VIDEO1:
return 0x0590 + i * 8;
case OMAP_DSS_VIDEO2:
return 0x056C + i * 0x8;
case OMAP_DSS_VIDEO3:
return 0x0434 + i * 0x8;
case OMAP_DSS_WB:
return 0x02A4 + i * 0x8;
default:
BUG();
return 0;
}
}
/* coef index i = {0, 1, 2, 3, 4,} */
static inline u16 DISPC_CONV_COEF_OFFSET(enum omap_plane plane, u16 i)
{
switch (plane) {
case OMAP_DSS_GFX:
BUG();
return 0;
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
case OMAP_DSS_VIDEO3:
case OMAP_DSS_WB:
return 0x0074 + i * 0x4;
default:
BUG();
return 0;
}
}
/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */
static inline u16 DISPC_FIR_COEF_V_OFFSET(enum omap_plane plane, u16 i)
{
switch (plane) {
case OMAP_DSS_GFX:
BUG();
return 0;
case OMAP_DSS_VIDEO1:
return 0x0124 + i * 0x4;
case OMAP_DSS_VIDEO2:
return 0x00B4 + i * 0x4;
case OMAP_DSS_VIDEO3:
case OMAP_DSS_WB:
return 0x0050 + i * 0x4;
default:
BUG();
return 0;
}
}
/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */
static inline u16 DISPC_FIR_COEF_V2_OFFSET(enum omap_plane plane, u16 i)
{
switch (plane) {
case OMAP_DSS_GFX:
BUG();
return 0;
case OMAP_DSS_VIDEO1:
return 0x05CC + i * 0x4;
case OMAP_DSS_VIDEO2:
return 0x05A8 + i * 0x4;
case OMAP_DSS_VIDEO3:
return 0x0470 + i * 0x4;
case OMAP_DSS_WB:
return 0x02E0 + i * 0x4;
default:
BUG();
return 0;
}
}
static inline u16 DISPC_PRELOAD_OFFSET(enum omap_plane plane)
{
switch (plane) {
case OMAP_DSS_GFX:
return 0x01AC;
case OMAP_DSS_VIDEO1:
return 0x0174;
case OMAP_DSS_VIDEO2:
return 0x00E8;
case OMAP_DSS_VIDEO3:
return 0x00A0;
default:
BUG();
return 0;
}
}
static inline u16 DISPC_MFLAG_THRESHOLD_OFFSET(enum omap_plane plane)
{
switch (plane) {
case OMAP_DSS_GFX:
return 0x0860;
case OMAP_DSS_VIDEO1:
return 0x0864;
case OMAP_DSS_VIDEO2:
return 0x0868;
case OMAP_DSS_VIDEO3:
return 0x086c;
case OMAP_DSS_WB:
return 0x0870;
default:
BUG();
return 0;
}
}
#endif

View File

@ -0,0 +1,325 @@
/*
* linux/drivers/video/omap2/dss/dispc_coefs.c
*
* Copyright (C) 2011 Texas Instruments
* Author: Chandrabhanu Mahapatra <cmahapatra@ti.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/kernel.h>
#include <video/omapdss.h>
#include "dispc.h"
static const struct dispc_coef coef3_M8[8] = {
{ 0, 0, 128, 0, 0 },
{ 0, -4, 123, 9, 0 },
{ 0, -4, 108, 24, 0 },
{ 0, -2, 87, 43, 0 },
{ 0, 64, 64, 0, 0 },
{ 0, 43, 87, -2, 0 },
{ 0, 24, 108, -4, 0 },
{ 0, 9, 123, -4, 0 },
};
static const struct dispc_coef coef3_M9[8] = {
{ 0, 6, 116, 6, 0 },
{ 0, 0, 112, 16, 0 },
{ 0, -2, 100, 30, 0 },
{ 0, -2, 83, 47, 0 },
{ 0, 64, 64, 0, 0 },
{ 0, 47, 83, -2, 0 },
{ 0, 30, 100, -2, 0 },
{ 0, 16, 112, 0, 0 },
};
static const struct dispc_coef coef3_M10[8] = {
{ 0, 10, 108, 10, 0 },
{ 0, 3, 104, 21, 0 },
{ 0, 0, 94, 34, 0 },
{ 0, -1, 80, 49, 0 },
{ 0, 64, 64, 0, 0 },
{ 0, 49, 80, -1, 0 },
{ 0, 34, 94, 0, 0 },
{ 0, 21, 104, 3, 0 },
};
static const struct dispc_coef coef3_M11[8] = {
{ 0, 14, 100, 14, 0 },
{ 0, 6, 98, 24, 0 },
{ 0, 2, 90, 36, 0 },
{ 0, 0, 78, 50, 0 },
{ 0, 64, 64, 0, 0 },
{ 0, 50, 78, 0, 0 },
{ 0, 36, 90, 2, 0 },
{ 0, 24, 98, 6, 0 },
};
static const struct dispc_coef coef3_M12[8] = {
{ 0, 16, 96, 16, 0 },
{ 0, 9, 93, 26, 0 },
{ 0, 4, 86, 38, 0 },
{ 0, 1, 76, 51, 0 },
{ 0, 64, 64, 0, 0 },
{ 0, 51, 76, 1, 0 },
{ 0, 38, 86, 4, 0 },
{ 0, 26, 93, 9, 0 },
};
static const struct dispc_coef coef3_M13[8] = {
{ 0, 18, 92, 18, 0 },
{ 0, 10, 90, 28, 0 },
{ 0, 5, 83, 40, 0 },
{ 0, 1, 75, 52, 0 },
{ 0, 64, 64, 0, 0 },
{ 0, 52, 75, 1, 0 },
{ 0, 40, 83, 5, 0 },
{ 0, 28, 90, 10, 0 },
};
static const struct dispc_coef coef3_M14[8] = {
{ 0, 20, 88, 20, 0 },
{ 0, 12, 86, 30, 0 },
{ 0, 6, 81, 41, 0 },
{ 0, 2, 74, 52, 0 },
{ 0, 64, 64, 0, 0 },
{ 0, 52, 74, 2, 0 },
{ 0, 41, 81, 6, 0 },
{ 0, 30, 86, 12, 0 },
};
static const struct dispc_coef coef3_M16[8] = {
{ 0, 22, 84, 22, 0 },
{ 0, 14, 82, 32, 0 },
{ 0, 8, 78, 42, 0 },
{ 0, 3, 72, 53, 0 },
{ 0, 64, 64, 0, 0 },
{ 0, 53, 72, 3, 0 },
{ 0, 42, 78, 8, 0 },
{ 0, 32, 82, 14, 0 },
};
static const struct dispc_coef coef3_M19[8] = {
{ 0, 24, 80, 24, 0 },
{ 0, 16, 79, 33, 0 },
{ 0, 9, 76, 43, 0 },
{ 0, 4, 70, 54, 0 },
{ 0, 64, 64, 0, 0 },
{ 0, 54, 70, 4, 0 },
{ 0, 43, 76, 9, 0 },
{ 0, 33, 79, 16, 0 },
};
static const struct dispc_coef coef3_M22[8] = {
{ 0, 25, 78, 25, 0 },
{ 0, 17, 77, 34, 0 },
{ 0, 10, 74, 44, 0 },
{ 0, 5, 69, 54, 0 },
{ 0, 64, 64, 0, 0 },
{ 0, 54, 69, 5, 0 },
{ 0, 44, 74, 10, 0 },
{ 0, 34, 77, 17, 0 },
};
static const struct dispc_coef coef3_M26[8] = {
{ 0, 26, 76, 26, 0 },
{ 0, 19, 74, 35, 0 },
{ 0, 11, 72, 45, 0 },
{ 0, 5, 69, 54, 0 },
{ 0, 64, 64, 0, 0 },
{ 0, 54, 69, 5, 0 },
{ 0, 45, 72, 11, 0 },
{ 0, 35, 74, 19, 0 },
};
static const struct dispc_coef coef3_M32[8] = {
{ 0, 27, 74, 27, 0 },
{ 0, 19, 73, 36, 0 },
{ 0, 12, 71, 45, 0 },
{ 0, 6, 68, 54, 0 },
{ 0, 64, 64, 0, 0 },
{ 0, 54, 68, 6, 0 },
{ 0, 45, 71, 12, 0 },
{ 0, 36, 73, 19, 0 },
};
static const struct dispc_coef coef5_M8[8] = {
{ 0, 0, 128, 0, 0 },
{ -2, 14, 125, -10, 1 },
{ -6, 33, 114, -15, 2 },
{ -10, 55, 98, -16, 1 },
{ 0, -14, 78, 78, -14 },
{ 1, -16, 98, 55, -10 },
{ 2, -15, 114, 33, -6 },
{ 1, -10, 125, 14, -2 },
};
static const struct dispc_coef coef5_M9[8] = {
{ -3, 10, 114, 10, -3 },
{ -6, 24, 111, 0, -1 },
{ -8, 40, 103, -7, 0 },
{ -11, 58, 91, -11, 1 },
{ 0, -12, 76, 76, -12 },
{ 1, -11, 91, 58, -11 },
{ 0, -7, 103, 40, -8 },
{ -1, 0, 111, 24, -6 },
};
static const struct dispc_coef coef5_M10[8] = {
{ -4, 18, 100, 18, -4 },
{ -6, 30, 99, 8, -3 },
{ -8, 44, 93, 0, -1 },
{ -9, 58, 84, -5, 0 },
{ 0, -8, 72, 72, -8 },
{ 0, -5, 84, 58, -9 },
{ -1, 0, 93, 44, -8 },
{ -3, 8, 99, 30, -6 },
};
static const struct dispc_coef coef5_M11[8] = {
{ -5, 23, 92, 23, -5 },
{ -6, 34, 90, 13, -3 },
{ -6, 45, 85, 6, -2 },
{ -6, 57, 78, 0, -1 },
{ 0, -4, 68, 68, -4 },
{ -1, 0, 78, 57, -6 },
{ -2, 6, 85, 45, -6 },
{ -3, 13, 90, 34, -6 },
};
static const struct dispc_coef coef5_M12[8] = {
{ -4, 26, 84, 26, -4 },
{ -5, 36, 82, 18, -3 },
{ -4, 46, 78, 10, -2 },
{ -3, 55, 72, 5, -1 },
{ 0, 0, 64, 64, 0 },
{ -1, 5, 72, 55, -3 },
{ -2, 10, 78, 46, -4 },
{ -3, 18, 82, 36, -5 },
};
static const struct dispc_coef coef5_M13[8] = {
{ -3, 28, 78, 28, -3 },
{ -3, 37, 76, 21, -3 },
{ -2, 45, 73, 14, -2 },
{ 0, 53, 68, 8, -1 },
{ 0, 3, 61, 61, 3 },
{ -1, 8, 68, 53, 0 },
{ -2, 14, 73, 45, -2 },
{ -3, 21, 76, 37, -3 },
};
static const struct dispc_coef coef5_M14[8] = {
{ -2, 30, 72, 30, -2 },
{ -1, 37, 71, 23, -2 },
{ 0, 45, 69, 16, -2 },
{ 3, 52, 64, 10, -1 },
{ 0, 6, 58, 58, 6 },
{ -1, 10, 64, 52, 3 },
{ -2, 16, 69, 45, 0 },
{ -2, 23, 71, 37, -1 },
};
static const struct dispc_coef coef5_M16[8] = {
{ 0, 31, 66, 31, 0 },
{ 1, 38, 65, 25, -1 },
{ 3, 44, 62, 20, -1 },
{ 6, 49, 59, 14, 0 },
{ 0, 10, 54, 54, 10 },
{ 0, 14, 59, 49, 6 },
{ -1, 20, 62, 44, 3 },
{ -1, 25, 65, 38, 1 },
};
static const struct dispc_coef coef5_M19[8] = {
{ 3, 32, 58, 32, 3 },
{ 4, 38, 58, 27, 1 },
{ 7, 42, 55, 23, 1 },
{ 10, 46, 54, 18, 0 },
{ 0, 14, 50, 50, 14 },
{ 0, 18, 54, 46, 10 },
{ 1, 23, 55, 42, 7 },
{ 1, 27, 58, 38, 4 },
};
static const struct dispc_coef coef5_M22[8] = {
{ 4, 33, 54, 33, 4 },
{ 6, 37, 54, 28, 3 },
{ 9, 41, 53, 24, 1 },
{ 12, 45, 51, 20, 0 },
{ 0, 16, 48, 48, 16 },
{ 0, 20, 51, 45, 12 },
{ 1, 24, 53, 41, 9 },
{ 3, 28, 54, 37, 6 },
};
static const struct dispc_coef coef5_M26[8] = {
{ 6, 33, 50, 33, 6 },
{ 8, 36, 51, 29, 4 },
{ 11, 40, 50, 25, 2 },
{ 14, 43, 48, 22, 1 },
{ 0, 18, 46, 46, 18 },
{ 1, 22, 48, 43, 14 },
{ 2, 25, 50, 40, 11 },
{ 4, 29, 51, 36, 8 },
};
static const struct dispc_coef coef5_M32[8] = {
{ 7, 33, 48, 33, 7 },
{ 10, 36, 48, 29, 5 },
{ 13, 39, 47, 26, 3 },
{ 16, 42, 46, 23, 1 },
{ 0, 19, 45, 45, 19 },
{ 1, 23, 46, 42, 16 },
{ 3, 26, 47, 39, 13 },
{ 5, 29, 48, 36, 10 },
};
const struct dispc_coef *dispc_ovl_get_scale_coef(int inc, int five_taps)
{
int i;
static const struct {
int Mmin;
int Mmax;
const struct dispc_coef *coef_3;
const struct dispc_coef *coef_5;
} coefs[] = {
{ 27, 32, coef3_M32, coef5_M32 },
{ 23, 26, coef3_M26, coef5_M26 },
{ 20, 22, coef3_M22, coef5_M22 },
{ 17, 19, coef3_M19, coef5_M19 },
{ 15, 16, coef3_M16, coef5_M16 },
{ 14, 14, coef3_M14, coef5_M14 },
{ 13, 13, coef3_M13, coef5_M13 },
{ 12, 12, coef3_M12, coef5_M12 },
{ 11, 11, coef3_M11, coef5_M11 },
{ 10, 10, coef3_M10, coef5_M10 },
{ 9, 9, coef3_M9, coef5_M9 },
{ 4, 8, coef3_M8, coef5_M8 },
/*
* When upscaling more than two times, blockiness and outlines
* around the image are observed when M8 tables are used. M11,
* M16 and M19 tables are used to prevent this.
*/
{ 3, 3, coef3_M11, coef5_M11 },
{ 2, 2, coef3_M16, coef5_M16 },
{ 0, 1, coef3_M19, coef5_M19 },
};
inc /= 128;
for (i = 0; i < ARRAY_SIZE(coefs); ++i)
if (inc >= coefs[i].Mmin && inc <= coefs[i].Mmax)
return five_taps ? coefs[i].coef_5 : coefs[i].coef_3;
return NULL;
}

View File

@ -0,0 +1,356 @@
/*
* Copyright (C) 2009 Nokia Corporation
* Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
*
* Some code and ideas taken from drivers/video/omap/ driver
* by Imre Deak.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define DSS_SUBSYS_NAME "DISPLAY"
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/sysfs.h>
#include <video/omapdss.h>
#include "dss.h"
static ssize_t display_name_show(struct omap_dss_device *dssdev, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%s\n",
dssdev->name ?
dssdev->name : "");
}
static ssize_t display_enabled_show(struct omap_dss_device *dssdev, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%d\n",
omapdss_device_is_enabled(dssdev));
}
static ssize_t display_enabled_store(struct omap_dss_device *dssdev,
const char *buf, size_t size)
{
int r;
bool enable;
r = strtobool(buf, &enable);
if (r)
return r;
if (enable == omapdss_device_is_enabled(dssdev))
return size;
if (omapdss_device_is_connected(dssdev) == false)
return -ENODEV;
if (enable) {
r = dssdev->driver->enable(dssdev);
if (r)
return r;
} else {
dssdev->driver->disable(dssdev);
}
return size;
}
static ssize_t display_tear_show(struct omap_dss_device *dssdev, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%d\n",
dssdev->driver->get_te ?
dssdev->driver->get_te(dssdev) : 0);
}
static ssize_t display_tear_store(struct omap_dss_device *dssdev,
const char *buf, size_t size)
{
int r;
bool te;
if (!dssdev->driver->enable_te || !dssdev->driver->get_te)
return -ENOENT;
r = strtobool(buf, &te);
if (r)
return r;
r = dssdev->driver->enable_te(dssdev, te);
if (r)
return r;
return size;
}
static ssize_t display_timings_show(struct omap_dss_device *dssdev, char *buf)
{
struct omap_video_timings t;
if (!dssdev->driver->get_timings)
return -ENOENT;
dssdev->driver->get_timings(dssdev, &t);
return snprintf(buf, PAGE_SIZE, "%u,%u/%u/%u/%u,%u/%u/%u/%u\n",
t.pixelclock,
t.x_res, t.hfp, t.hbp, t.hsw,
t.y_res, t.vfp, t.vbp, t.vsw);
}
static ssize_t display_timings_store(struct omap_dss_device *dssdev,
const char *buf, size_t size)
{
struct omap_video_timings t = dssdev->panel.timings;
int r, found;
if (!dssdev->driver->set_timings || !dssdev->driver->check_timings)
return -ENOENT;
found = 0;
#ifdef CONFIG_OMAP2_DSS_VENC
if (strncmp("pal", buf, 3) == 0) {
t = omap_dss_pal_timings;
found = 1;
} else if (strncmp("ntsc", buf, 4) == 0) {
t = omap_dss_ntsc_timings;
found = 1;
}
#endif
if (!found && sscanf(buf, "%u,%hu/%hu/%hu/%hu,%hu/%hu/%hu/%hu",
&t.pixelclock,
&t.x_res, &t.hfp, &t.hbp, &t.hsw,
&t.y_res, &t.vfp, &t.vbp, &t.vsw) != 9)
return -EINVAL;
r = dssdev->driver->check_timings(dssdev, &t);
if (r)
return r;
dssdev->driver->disable(dssdev);
dssdev->driver->set_timings(dssdev, &t);
r = dssdev->driver->enable(dssdev);
if (r)
return r;
return size;
}
static ssize_t display_rotate_show(struct omap_dss_device *dssdev, char *buf)
{
int rotate;
if (!dssdev->driver->get_rotate)
return -ENOENT;
rotate = dssdev->driver->get_rotate(dssdev);
return snprintf(buf, PAGE_SIZE, "%u\n", rotate);
}
static ssize_t display_rotate_store(struct omap_dss_device *dssdev,
const char *buf, size_t size)
{
int rot, r;
if (!dssdev->driver->set_rotate || !dssdev->driver->get_rotate)
return -ENOENT;
r = kstrtoint(buf, 0, &rot);
if (r)
return r;
r = dssdev->driver->set_rotate(dssdev, rot);
if (r)
return r;
return size;
}
static ssize_t display_mirror_show(struct omap_dss_device *dssdev, char *buf)
{
int mirror;
if (!dssdev->driver->get_mirror)
return -ENOENT;
mirror = dssdev->driver->get_mirror(dssdev);
return snprintf(buf, PAGE_SIZE, "%u\n", mirror);
}
static ssize_t display_mirror_store(struct omap_dss_device *dssdev,
const char *buf, size_t size)
{
int r;
bool mirror;
if (!dssdev->driver->set_mirror || !dssdev->driver->get_mirror)
return -ENOENT;
r = strtobool(buf, &mirror);
if (r)
return r;
r = dssdev->driver->set_mirror(dssdev, mirror);
if (r)
return r;
return size;
}
static ssize_t display_wss_show(struct omap_dss_device *dssdev, char *buf)
{
unsigned int wss;
if (!dssdev->driver->get_wss)
return -ENOENT;
wss = dssdev->driver->get_wss(dssdev);
return snprintf(buf, PAGE_SIZE, "0x%05x\n", wss);
}
static ssize_t display_wss_store(struct omap_dss_device *dssdev,
const char *buf, size_t size)
{
u32 wss;
int r;
if (!dssdev->driver->get_wss || !dssdev->driver->set_wss)
return -ENOENT;
r = kstrtou32(buf, 0, &wss);
if (r)
return r;
if (wss > 0xfffff)
return -EINVAL;
r = dssdev->driver->set_wss(dssdev, wss);
if (r)
return r;
return size;
}
struct display_attribute {
struct attribute attr;
ssize_t (*show)(struct omap_dss_device *, char *);
ssize_t (*store)(struct omap_dss_device *, const char *, size_t);
};
#define DISPLAY_ATTR(_name, _mode, _show, _store) \
struct display_attribute display_attr_##_name = \
__ATTR(_name, _mode, _show, _store)
static DISPLAY_ATTR(name, S_IRUGO, display_name_show, NULL);
static DISPLAY_ATTR(display_name, S_IRUGO, display_name_show, NULL);
static DISPLAY_ATTR(enabled, S_IRUGO|S_IWUSR,
display_enabled_show, display_enabled_store);
static DISPLAY_ATTR(tear_elim, S_IRUGO|S_IWUSR,
display_tear_show, display_tear_store);
static DISPLAY_ATTR(timings, S_IRUGO|S_IWUSR,
display_timings_show, display_timings_store);
static DISPLAY_ATTR(rotate, S_IRUGO|S_IWUSR,
display_rotate_show, display_rotate_store);
static DISPLAY_ATTR(mirror, S_IRUGO|S_IWUSR,
display_mirror_show, display_mirror_store);
static DISPLAY_ATTR(wss, S_IRUGO|S_IWUSR,
display_wss_show, display_wss_store);
static struct attribute *display_sysfs_attrs[] = {
&display_attr_name.attr,
&display_attr_display_name.attr,
&display_attr_enabled.attr,
&display_attr_tear_elim.attr,
&display_attr_timings.attr,
&display_attr_rotate.attr,
&display_attr_mirror.attr,
&display_attr_wss.attr,
NULL
};
static ssize_t display_attr_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
struct omap_dss_device *dssdev;
struct display_attribute *display_attr;
dssdev = container_of(kobj, struct omap_dss_device, kobj);
display_attr = container_of(attr, struct display_attribute, attr);
if (!display_attr->show)
return -ENOENT;
return display_attr->show(dssdev, buf);
}
static ssize_t display_attr_store(struct kobject *kobj, struct attribute *attr,
const char *buf, size_t size)
{
struct omap_dss_device *dssdev;
struct display_attribute *display_attr;
dssdev = container_of(kobj, struct omap_dss_device, kobj);
display_attr = container_of(attr, struct display_attribute, attr);
if (!display_attr->store)
return -ENOENT;
return display_attr->store(dssdev, buf, size);
}
static const struct sysfs_ops display_sysfs_ops = {
.show = display_attr_show,
.store = display_attr_store,
};
static struct kobj_type display_ktype = {
.sysfs_ops = &display_sysfs_ops,
.default_attrs = display_sysfs_attrs,
};
int display_init_sysfs(struct platform_device *pdev)
{
struct omap_dss_device *dssdev = NULL;
int r;
for_each_dss_dev(dssdev) {
r = kobject_init_and_add(&dssdev->kobj, &display_ktype,
&pdev->dev.kobj, "%s", dssdev->alias);
if (r) {
DSSERR("failed to create sysfs files\n");
omap_dss_put_device(dssdev);
goto err;
}
}
return 0;
err:
display_uninit_sysfs(pdev);
return r;
}
void display_uninit_sysfs(struct platform_device *pdev)
{
struct omap_dss_device *dssdev = NULL;
for_each_dss_dev(dssdev) {
if (kobject_name(&dssdev->kobj) == NULL)
continue;
kobject_del(&dssdev->kobj);
kobject_put(&dssdev->kobj);
memset(&dssdev->kobj, 0, sizeof(dssdev->kobj));
}
}

View File

@ -0,0 +1,338 @@
/*
* linux/drivers/video/omap2/dss/display.c
*
* Copyright (C) 2009 Nokia Corporation
* Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
*
* Some code and ideas taken from drivers/video/omap/ driver
* by Imre Deak.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define DSS_SUBSYS_NAME "DISPLAY"
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/jiffies.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <video/omapdss.h>
#include "dss.h"
#include "dss_features.h"
void omapdss_default_get_resolution(struct omap_dss_device *dssdev,
u16 *xres, u16 *yres)
{
*xres = dssdev->panel.timings.x_res;
*yres = dssdev->panel.timings.y_res;
}
EXPORT_SYMBOL(omapdss_default_get_resolution);
int omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev)
{
switch (dssdev->type) {
case OMAP_DISPLAY_TYPE_DPI:
if (dssdev->phy.dpi.data_lines == 24)
return 24;
else
return 16;
case OMAP_DISPLAY_TYPE_DBI:
if (dssdev->ctrl.pixel_size == 24)
return 24;
else
return 16;
case OMAP_DISPLAY_TYPE_DSI:
if (dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt) > 16)
return 24;
else
return 16;
case OMAP_DISPLAY_TYPE_VENC:
case OMAP_DISPLAY_TYPE_SDI:
case OMAP_DISPLAY_TYPE_HDMI:
case OMAP_DISPLAY_TYPE_DVI:
return 24;
default:
BUG();
return 0;
}
}
EXPORT_SYMBOL(omapdss_default_get_recommended_bpp);
void omapdss_default_get_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
*timings = dssdev->panel.timings;
}
EXPORT_SYMBOL(omapdss_default_get_timings);
int dss_suspend_all_devices(void)
{
struct omap_dss_device *dssdev = NULL;
for_each_dss_dev(dssdev) {
if (!dssdev->driver)
continue;
if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
dssdev->driver->disable(dssdev);
dssdev->activate_after_resume = true;
} else {
dssdev->activate_after_resume = false;
}
}
return 0;
}
int dss_resume_all_devices(void)
{
struct omap_dss_device *dssdev = NULL;
for_each_dss_dev(dssdev) {
if (!dssdev->driver)
continue;
if (dssdev->activate_after_resume) {
dssdev->driver->enable(dssdev);
dssdev->activate_after_resume = false;
}
}
return 0;
}
void dss_disable_all_devices(void)
{
struct omap_dss_device *dssdev = NULL;
for_each_dss_dev(dssdev) {
if (!dssdev->driver)
continue;
if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
dssdev->driver->disable(dssdev);
}
}
static LIST_HEAD(panel_list);
static DEFINE_MUTEX(panel_list_mutex);
static int disp_num_counter;
int omapdss_register_display(struct omap_dss_device *dssdev)
{
struct omap_dss_driver *drv = dssdev->driver;
int id;
/*
* Note: this presumes all the displays are either using DT or non-DT,
* which normally should be the case. This also presumes that all
* displays either have an DT alias, or none has.
*/
if (dssdev->dev->of_node) {
id = of_alias_get_id(dssdev->dev->of_node, "display");
if (id < 0)
id = disp_num_counter++;
} else {
id = disp_num_counter++;
}
snprintf(dssdev->alias, sizeof(dssdev->alias), "display%d", id);
/* Use 'label' property for name, if it exists */
if (dssdev->dev->of_node)
of_property_read_string(dssdev->dev->of_node, "label",
&dssdev->name);
if (dssdev->name == NULL)
dssdev->name = dssdev->alias;
if (drv && drv->get_resolution == NULL)
drv->get_resolution = omapdss_default_get_resolution;
if (drv && drv->get_recommended_bpp == NULL)
drv->get_recommended_bpp = omapdss_default_get_recommended_bpp;
if (drv && drv->get_timings == NULL)
drv->get_timings = omapdss_default_get_timings;
mutex_lock(&panel_list_mutex);
list_add_tail(&dssdev->panel_list, &panel_list);
mutex_unlock(&panel_list_mutex);
return 0;
}
EXPORT_SYMBOL(omapdss_register_display);
void omapdss_unregister_display(struct omap_dss_device *dssdev)
{
mutex_lock(&panel_list_mutex);
list_del(&dssdev->panel_list);
mutex_unlock(&panel_list_mutex);
}
EXPORT_SYMBOL(omapdss_unregister_display);
struct omap_dss_device *omap_dss_get_device(struct omap_dss_device *dssdev)
{
if (!try_module_get(dssdev->owner))
return NULL;
if (get_device(dssdev->dev) == NULL) {
module_put(dssdev->owner);
return NULL;
}
return dssdev;
}
EXPORT_SYMBOL(omap_dss_get_device);
void omap_dss_put_device(struct omap_dss_device *dssdev)
{
put_device(dssdev->dev);
module_put(dssdev->owner);
}
EXPORT_SYMBOL(omap_dss_put_device);
/*
* ref count of the found device is incremented.
* ref count of from-device is decremented.
*/
struct omap_dss_device *omap_dss_get_next_device(struct omap_dss_device *from)
{
struct list_head *l;
struct omap_dss_device *dssdev;
mutex_lock(&panel_list_mutex);
if (list_empty(&panel_list)) {
dssdev = NULL;
goto out;
}
if (from == NULL) {
dssdev = list_first_entry(&panel_list, struct omap_dss_device,
panel_list);
omap_dss_get_device(dssdev);
goto out;
}
omap_dss_put_device(from);
list_for_each(l, &panel_list) {
dssdev = list_entry(l, struct omap_dss_device, panel_list);
if (dssdev == from) {
if (list_is_last(l, &panel_list)) {
dssdev = NULL;
goto out;
}
dssdev = list_entry(l->next, struct omap_dss_device,
panel_list);
omap_dss_get_device(dssdev);
goto out;
}
}
WARN(1, "'from' dssdev not found\n");
dssdev = NULL;
out:
mutex_unlock(&panel_list_mutex);
return dssdev;
}
EXPORT_SYMBOL(omap_dss_get_next_device);
struct omap_dss_device *omap_dss_find_device(void *data,
int (*match)(struct omap_dss_device *dssdev, void *data))
{
struct omap_dss_device *dssdev = NULL;
while ((dssdev = omap_dss_get_next_device(dssdev)) != NULL) {
if (match(dssdev, data))
return dssdev;
}
return NULL;
}
EXPORT_SYMBOL(omap_dss_find_device);
void videomode_to_omap_video_timings(const struct videomode *vm,
struct omap_video_timings *ovt)
{
memset(ovt, 0, sizeof(*ovt));
ovt->pixelclock = vm->pixelclock;
ovt->x_res = vm->hactive;
ovt->hbp = vm->hback_porch;
ovt->hfp = vm->hfront_porch;
ovt->hsw = vm->hsync_len;
ovt->y_res = vm->vactive;
ovt->vbp = vm->vback_porch;
ovt->vfp = vm->vfront_porch;
ovt->vsw = vm->vsync_len;
ovt->vsync_level = vm->flags & DISPLAY_FLAGS_VSYNC_HIGH ?
OMAPDSS_SIG_ACTIVE_HIGH :
OMAPDSS_SIG_ACTIVE_LOW;
ovt->hsync_level = vm->flags & DISPLAY_FLAGS_HSYNC_HIGH ?
OMAPDSS_SIG_ACTIVE_HIGH :
OMAPDSS_SIG_ACTIVE_LOW;
ovt->de_level = vm->flags & DISPLAY_FLAGS_DE_HIGH ?
OMAPDSS_SIG_ACTIVE_HIGH :
OMAPDSS_SIG_ACTIVE_LOW;
ovt->data_pclk_edge = vm->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE ?
OMAPDSS_DRIVE_SIG_RISING_EDGE :
OMAPDSS_DRIVE_SIG_FALLING_EDGE;
ovt->sync_pclk_edge = ovt->data_pclk_edge;
}
EXPORT_SYMBOL(videomode_to_omap_video_timings);
void omap_video_timings_to_videomode(const struct omap_video_timings *ovt,
struct videomode *vm)
{
memset(vm, 0, sizeof(*vm));
vm->pixelclock = ovt->pixelclock;
vm->hactive = ovt->x_res;
vm->hback_porch = ovt->hbp;
vm->hfront_porch = ovt->hfp;
vm->hsync_len = ovt->hsw;
vm->vactive = ovt->y_res;
vm->vback_porch = ovt->vbp;
vm->vfront_porch = ovt->vfp;
vm->vsync_len = ovt->vsw;
if (ovt->hsync_level == OMAPDSS_SIG_ACTIVE_HIGH)
vm->flags |= DISPLAY_FLAGS_HSYNC_HIGH;
else
vm->flags |= DISPLAY_FLAGS_HSYNC_LOW;
if (ovt->vsync_level == OMAPDSS_SIG_ACTIVE_HIGH)
vm->flags |= DISPLAY_FLAGS_VSYNC_HIGH;
else
vm->flags |= DISPLAY_FLAGS_VSYNC_LOW;
if (ovt->de_level == OMAPDSS_SIG_ACTIVE_HIGH)
vm->flags |= DISPLAY_FLAGS_DE_HIGH;
else
vm->flags |= DISPLAY_FLAGS_DE_LOW;
if (ovt->data_pclk_edge == OMAPDSS_DRIVE_SIG_RISING_EDGE)
vm->flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE;
else
vm->flags |= DISPLAY_FLAGS_PIXDATA_NEGEDGE;
}
EXPORT_SYMBOL(omap_video_timings_to_videomode);

View File

@ -0,0 +1,899 @@
/*
* linux/drivers/video/omap2/dss/dpi.c
*
* Copyright (C) 2009 Nokia Corporation
* Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
*
* Some code and ideas taken from drivers/video/omap/ driver
* by Imre Deak.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define DSS_SUBSYS_NAME "DPI"
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/string.h>
#include <linux/of.h>
#include <linux/clk.h>
#include <linux/component.h>
#include <video/omapdss.h>
#include "dss.h"
#include "dss_features.h"
#define HSDIV_DISPC 0
struct dpi_data {
struct platform_device *pdev;
struct regulator *vdds_dsi_reg;
struct dss_pll *pll;
struct mutex lock;
struct omap_video_timings timings;
struct dss_lcd_mgr_config mgr_config;
int data_lines;
struct omap_dss_device output;
bool port_initialized;
};
static struct dpi_data *dpi_get_data_from_dssdev(struct omap_dss_device *dssdev)
{
return container_of(dssdev, struct dpi_data, output);
}
/* only used in non-DT mode */
static struct dpi_data *dpi_get_data_from_pdev(struct platform_device *pdev)
{
return dev_get_drvdata(&pdev->dev);
}
static struct dss_pll *dpi_get_pll(enum omap_channel channel)
{
/*
* XXX we can't currently use DSI PLL for DPI with OMAP3, as the DSI PLL
* would also be used for DISPC fclk. Meaning, when the DPI output is
* disabled, DISPC clock will be disabled, and TV out will stop.
*/
switch (omapdss_get_version()) {
case OMAPDSS_VER_OMAP24xx:
case OMAPDSS_VER_OMAP34xx_ES1:
case OMAPDSS_VER_OMAP34xx_ES3:
case OMAPDSS_VER_OMAP3630:
case OMAPDSS_VER_AM35xx:
case OMAPDSS_VER_AM43xx:
return NULL;
case OMAPDSS_VER_OMAP4430_ES1:
case OMAPDSS_VER_OMAP4430_ES2:
case OMAPDSS_VER_OMAP4:
switch (channel) {
case OMAP_DSS_CHANNEL_LCD:
return dss_pll_find("dsi0");
case OMAP_DSS_CHANNEL_LCD2:
return dss_pll_find("dsi1");
default:
return NULL;
}
case OMAPDSS_VER_OMAP5:
switch (channel) {
case OMAP_DSS_CHANNEL_LCD:
return dss_pll_find("dsi0");
case OMAP_DSS_CHANNEL_LCD3:
return dss_pll_find("dsi1");
default:
return NULL;
}
case OMAPDSS_VER_DRA7xx:
switch (channel) {
case OMAP_DSS_CHANNEL_LCD:
case OMAP_DSS_CHANNEL_LCD2:
return dss_pll_find("video0");
case OMAP_DSS_CHANNEL_LCD3:
return dss_pll_find("video1");
default:
return NULL;
}
default:
return NULL;
}
}
static enum omap_dss_clk_source dpi_get_alt_clk_src(enum omap_channel channel)
{
switch (channel) {
case OMAP_DSS_CHANNEL_LCD:
return OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC;
case OMAP_DSS_CHANNEL_LCD2:
return OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC;
case OMAP_DSS_CHANNEL_LCD3:
return OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC;
default:
/* this shouldn't happen */
WARN_ON(1);
return OMAP_DSS_CLK_SRC_FCK;
}
}
struct dpi_clk_calc_ctx {
struct dss_pll *pll;
/* inputs */
unsigned long pck_min, pck_max;
/* outputs */
struct dss_pll_clock_info dsi_cinfo;
unsigned long fck;
struct dispc_clock_info dispc_cinfo;
};
static bool dpi_calc_dispc_cb(int lckd, int pckd, unsigned long lck,
unsigned long pck, void *data)
{
struct dpi_clk_calc_ctx *ctx = data;
/*
* Odd dividers give us uneven duty cycle, causing problem when level
* shifted. So skip all odd dividers when the pixel clock is on the
* higher side.
*/
if (ctx->pck_min >= 100000000) {
if (lckd > 1 && lckd % 2 != 0)
return false;
if (pckd > 1 && pckd % 2 != 0)
return false;
}
ctx->dispc_cinfo.lck_div = lckd;
ctx->dispc_cinfo.pck_div = pckd;
ctx->dispc_cinfo.lck = lck;
ctx->dispc_cinfo.pck = pck;
return true;
}
static bool dpi_calc_hsdiv_cb(int m_dispc, unsigned long dispc,
void *data)
{
struct dpi_clk_calc_ctx *ctx = data;
/*
* Odd dividers give us uneven duty cycle, causing problem when level
* shifted. So skip all odd dividers when the pixel clock is on the
* higher side.
*/
if (m_dispc > 1 && m_dispc % 2 != 0 && ctx->pck_min >= 100000000)
return false;
ctx->dsi_cinfo.mX[HSDIV_DISPC] = m_dispc;
ctx->dsi_cinfo.clkout[HSDIV_DISPC] = dispc;
return dispc_div_calc(dispc, ctx->pck_min, ctx->pck_max,
dpi_calc_dispc_cb, ctx);
}
static bool dpi_calc_pll_cb(int n, int m, unsigned long fint,
unsigned long clkdco,
void *data)
{
struct dpi_clk_calc_ctx *ctx = data;
ctx->dsi_cinfo.n = n;
ctx->dsi_cinfo.m = m;
ctx->dsi_cinfo.fint = fint;
ctx->dsi_cinfo.clkdco = clkdco;
return dss_pll_hsdiv_calc(ctx->pll, clkdco,
ctx->pck_min, dss_feat_get_param_max(FEAT_PARAM_DSS_FCK),
dpi_calc_hsdiv_cb, ctx);
}
static bool dpi_calc_dss_cb(unsigned long fck, void *data)
{
struct dpi_clk_calc_ctx *ctx = data;
ctx->fck = fck;
return dispc_div_calc(fck, ctx->pck_min, ctx->pck_max,
dpi_calc_dispc_cb, ctx);
}
static bool dpi_dsi_clk_calc(struct dpi_data *dpi, unsigned long pck,
struct dpi_clk_calc_ctx *ctx)
{
unsigned long clkin;
unsigned long pll_min, pll_max;
memset(ctx, 0, sizeof(*ctx));
ctx->pll = dpi->pll;
ctx->pck_min = pck - 1000;
ctx->pck_max = pck + 1000;
pll_min = 0;
pll_max = 0;
clkin = clk_get_rate(ctx->pll->clkin);
return dss_pll_calc(ctx->pll, clkin,
pll_min, pll_max,
dpi_calc_pll_cb, ctx);
}
static bool dpi_dss_clk_calc(unsigned long pck, struct dpi_clk_calc_ctx *ctx)
{
int i;
/*
* DSS fck gives us very few possibilities, so finding a good pixel
* clock may not be possible. We try multiple times to find the clock,
* each time widening the pixel clock range we look for, up to
* +/- ~15MHz.
*/
for (i = 0; i < 25; ++i) {
bool ok;
memset(ctx, 0, sizeof(*ctx));
if (pck > 1000 * i * i * i)
ctx->pck_min = max(pck - 1000 * i * i * i, 0lu);
else
ctx->pck_min = 0;
ctx->pck_max = pck + 1000 * i * i * i;
ok = dss_div_calc(pck, ctx->pck_min, dpi_calc_dss_cb, ctx);
if (ok)
return ok;
}
return false;
}
static int dpi_set_dsi_clk(struct dpi_data *dpi, enum omap_channel channel,
unsigned long pck_req, unsigned long *fck, int *lck_div,
int *pck_div)
{
struct dpi_clk_calc_ctx ctx;
int r;
bool ok;
ok = dpi_dsi_clk_calc(dpi, pck_req, &ctx);
if (!ok)
return -EINVAL;
r = dss_pll_set_config(dpi->pll, &ctx.dsi_cinfo);
if (r)
return r;
dss_select_lcd_clk_source(channel,
dpi_get_alt_clk_src(channel));
dpi->mgr_config.clock_info = ctx.dispc_cinfo;
*fck = ctx.dsi_cinfo.clkout[HSDIV_DISPC];
*lck_div = ctx.dispc_cinfo.lck_div;
*pck_div = ctx.dispc_cinfo.pck_div;
return 0;
}
static int dpi_set_dispc_clk(struct dpi_data *dpi, unsigned long pck_req,
unsigned long *fck, int *lck_div, int *pck_div)
{
struct dpi_clk_calc_ctx ctx;
int r;
bool ok;
ok = dpi_dss_clk_calc(pck_req, &ctx);
if (!ok)
return -EINVAL;
r = dss_set_fck_rate(ctx.fck);
if (r)
return r;
dpi->mgr_config.clock_info = ctx.dispc_cinfo;
*fck = ctx.fck;
*lck_div = ctx.dispc_cinfo.lck_div;
*pck_div = ctx.dispc_cinfo.pck_div;
return 0;
}
static int dpi_set_mode(struct dpi_data *dpi)
{
struct omap_dss_device *out = &dpi->output;
struct omap_overlay_manager *mgr = out->manager;
struct omap_video_timings *t = &dpi->timings;
int lck_div = 0, pck_div = 0;
unsigned long fck = 0;
unsigned long pck;
int r = 0;
if (dpi->pll)
r = dpi_set_dsi_clk(dpi, mgr->id, t->pixelclock, &fck,
&lck_div, &pck_div);
else
r = dpi_set_dispc_clk(dpi, t->pixelclock, &fck,
&lck_div, &pck_div);
if (r)
return r;
pck = fck / lck_div / pck_div;
if (pck != t->pixelclock) {
DSSWARN("Could not find exact pixel clock. Requested %d Hz, got %lu Hz\n",
t->pixelclock, pck);
t->pixelclock = pck;
}
dss_mgr_set_timings(mgr, t);
return 0;
}
static void dpi_config_lcd_manager(struct dpi_data *dpi)
{
struct omap_dss_device *out = &dpi->output;
struct omap_overlay_manager *mgr = out->manager;
dpi->mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS;
dpi->mgr_config.stallmode = false;
dpi->mgr_config.fifohandcheck = false;
dpi->mgr_config.video_port_width = dpi->data_lines;
dpi->mgr_config.lcden_sig_polarity = 0;
dss_mgr_set_lcd_config(mgr, &dpi->mgr_config);
}
static int dpi_display_enable(struct omap_dss_device *dssdev)
{
struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
struct omap_dss_device *out = &dpi->output;
int r;
mutex_lock(&dpi->lock);
if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI) && !dpi->vdds_dsi_reg) {
DSSERR("no VDSS_DSI regulator\n");
r = -ENODEV;
goto err_no_reg;
}
if (out->manager == NULL) {
DSSERR("failed to enable display: no output/manager\n");
r = -ENODEV;
goto err_no_out_mgr;
}
if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI)) {
r = regulator_enable(dpi->vdds_dsi_reg);
if (r)
goto err_reg_enable;
}
r = dispc_runtime_get();
if (r)
goto err_get_dispc;
r = dss_dpi_select_source(out->port_num, out->manager->id);
if (r)
goto err_src_sel;
if (dpi->pll) {
r = dss_pll_enable(dpi->pll);
if (r)
goto err_dsi_pll_init;
}
r = dpi_set_mode(dpi);
if (r)
goto err_set_mode;
dpi_config_lcd_manager(dpi);
mdelay(2);
r = dss_mgr_enable(out->manager);
if (r)
goto err_mgr_enable;
mutex_unlock(&dpi->lock);
return 0;
err_mgr_enable:
err_set_mode:
if (dpi->pll)
dss_pll_disable(dpi->pll);
err_dsi_pll_init:
err_src_sel:
dispc_runtime_put();
err_get_dispc:
if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI))
regulator_disable(dpi->vdds_dsi_reg);
err_reg_enable:
err_no_out_mgr:
err_no_reg:
mutex_unlock(&dpi->lock);
return r;
}
static void dpi_display_disable(struct omap_dss_device *dssdev)
{
struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
struct omap_overlay_manager *mgr = dpi->output.manager;
mutex_lock(&dpi->lock);
dss_mgr_disable(mgr);
if (dpi->pll) {
dss_select_lcd_clk_source(mgr->id, OMAP_DSS_CLK_SRC_FCK);
dss_pll_disable(dpi->pll);
}
dispc_runtime_put();
if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI))
regulator_disable(dpi->vdds_dsi_reg);
mutex_unlock(&dpi->lock);
}
static void dpi_set_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
DSSDBG("dpi_set_timings\n");
mutex_lock(&dpi->lock);
dpi->timings = *timings;
mutex_unlock(&dpi->lock);
}
static void dpi_get_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
mutex_lock(&dpi->lock);
*timings = dpi->timings;
mutex_unlock(&dpi->lock);
}
static int dpi_check_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
struct omap_overlay_manager *mgr = dpi->output.manager;
int lck_div, pck_div;
unsigned long fck;
unsigned long pck;
struct dpi_clk_calc_ctx ctx;
bool ok;
if (mgr && !dispc_mgr_timings_ok(mgr->id, timings))
return -EINVAL;
if (timings->pixelclock == 0)
return -EINVAL;
if (dpi->pll) {
ok = dpi_dsi_clk_calc(dpi, timings->pixelclock, &ctx);
if (!ok)
return -EINVAL;
fck = ctx.dsi_cinfo.clkout[HSDIV_DISPC];
} else {
ok = dpi_dss_clk_calc(timings->pixelclock, &ctx);
if (!ok)
return -EINVAL;
fck = ctx.fck;
}
lck_div = ctx.dispc_cinfo.lck_div;
pck_div = ctx.dispc_cinfo.pck_div;
pck = fck / lck_div / pck_div;
timings->pixelclock = pck;
return 0;
}
static void dpi_set_data_lines(struct omap_dss_device *dssdev, int data_lines)
{
struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
mutex_lock(&dpi->lock);
dpi->data_lines = data_lines;
mutex_unlock(&dpi->lock);
}
static int dpi_verify_dsi_pll(struct dss_pll *pll)
{
int r;
/* do initial setup with the PLL to see if it is operational */
r = dss_pll_enable(pll);
if (r)
return r;
dss_pll_disable(pll);
return 0;
}
static int dpi_init_regulator(struct dpi_data *dpi)
{
struct regulator *vdds_dsi;
if (!dss_has_feature(FEAT_DPI_USES_VDDS_DSI))
return 0;
if (dpi->vdds_dsi_reg)
return 0;
vdds_dsi = devm_regulator_get(&dpi->pdev->dev, "vdds_dsi");
if (IS_ERR(vdds_dsi)) {
if (PTR_ERR(vdds_dsi) != -EPROBE_DEFER)
DSSERR("can't get VDDS_DSI regulator\n");
return PTR_ERR(vdds_dsi);
}
dpi->vdds_dsi_reg = vdds_dsi;
return 0;
}
static void dpi_init_pll(struct dpi_data *dpi)
{
struct dss_pll *pll;
if (dpi->pll)
return;
pll = dpi_get_pll(dpi->output.dispc_channel);
if (!pll)
return;
/* On DRA7 we need to set a mux to use the PLL */
if (omapdss_get_version() == OMAPDSS_VER_DRA7xx)
dss_ctrl_pll_set_control_mux(pll->id, dpi->output.dispc_channel);
if (dpi_verify_dsi_pll(pll)) {
DSSWARN("DSI PLL not operational\n");
return;
}
dpi->pll = pll;
}
/*
* Return a hardcoded channel for the DPI output. This should work for
* current use cases, but this can be later expanded to either resolve
* the channel in some more dynamic manner, or get the channel as a user
* parameter.
*/
static enum omap_channel dpi_get_channel(int port_num)
{
switch (omapdss_get_version()) {
case OMAPDSS_VER_OMAP24xx:
case OMAPDSS_VER_OMAP34xx_ES1:
case OMAPDSS_VER_OMAP34xx_ES3:
case OMAPDSS_VER_OMAP3630:
case OMAPDSS_VER_AM35xx:
case OMAPDSS_VER_AM43xx:
return OMAP_DSS_CHANNEL_LCD;
case OMAPDSS_VER_DRA7xx:
switch (port_num) {
case 2:
return OMAP_DSS_CHANNEL_LCD3;
case 1:
return OMAP_DSS_CHANNEL_LCD2;
case 0:
default:
return OMAP_DSS_CHANNEL_LCD;
}
case OMAPDSS_VER_OMAP4430_ES1:
case OMAPDSS_VER_OMAP4430_ES2:
case OMAPDSS_VER_OMAP4:
return OMAP_DSS_CHANNEL_LCD2;
case OMAPDSS_VER_OMAP5:
return OMAP_DSS_CHANNEL_LCD3;
default:
DSSWARN("unsupported DSS version\n");
return OMAP_DSS_CHANNEL_LCD;
}
}
static int dpi_connect(struct omap_dss_device *dssdev,
struct omap_dss_device *dst)
{
struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
struct omap_overlay_manager *mgr;
int r;
r = dpi_init_regulator(dpi);
if (r)
return r;
dpi_init_pll(dpi);
mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel);
if (!mgr)
return -ENODEV;
r = dss_mgr_connect(mgr, dssdev);
if (r)
return r;
r = omapdss_output_set_device(dssdev, dst);
if (r) {
DSSERR("failed to connect output to new device: %s\n",
dst->name);
dss_mgr_disconnect(mgr, dssdev);
return r;
}
return 0;
}
static void dpi_disconnect(struct omap_dss_device *dssdev,
struct omap_dss_device *dst)
{
WARN_ON(dst != dssdev->dst);
if (dst != dssdev->dst)
return;
omapdss_output_unset_device(dssdev);
if (dssdev->manager)
dss_mgr_disconnect(dssdev->manager, dssdev);
}
static const struct omapdss_dpi_ops dpi_ops = {
.connect = dpi_connect,
.disconnect = dpi_disconnect,
.enable = dpi_display_enable,
.disable = dpi_display_disable,
.check_timings = dpi_check_timings,
.set_timings = dpi_set_timings,
.get_timings = dpi_get_timings,
.set_data_lines = dpi_set_data_lines,
};
static void dpi_init_output(struct platform_device *pdev)
{
struct dpi_data *dpi = dpi_get_data_from_pdev(pdev);
struct omap_dss_device *out = &dpi->output;
out->dev = &pdev->dev;
out->id = OMAP_DSS_OUTPUT_DPI;
out->output_type = OMAP_DISPLAY_TYPE_DPI;
out->name = "dpi.0";
out->dispc_channel = dpi_get_channel(0);
out->ops.dpi = &dpi_ops;
out->owner = THIS_MODULE;
omapdss_register_output(out);
}
static void dpi_uninit_output(struct platform_device *pdev)
{
struct dpi_data *dpi = dpi_get_data_from_pdev(pdev);
struct omap_dss_device *out = &dpi->output;
omapdss_unregister_output(out);
}
static void dpi_init_output_port(struct platform_device *pdev,
struct device_node *port)
{
struct dpi_data *dpi = port->data;
struct omap_dss_device *out = &dpi->output;
int r;
u32 port_num;
r = of_property_read_u32(port, "reg", &port_num);
if (r)
port_num = 0;
switch (port_num) {
case 2:
out->name = "dpi.2";
break;
case 1:
out->name = "dpi.1";
break;
case 0:
default:
out->name = "dpi.0";
break;
}
out->dev = &pdev->dev;
out->id = OMAP_DSS_OUTPUT_DPI;
out->output_type = OMAP_DISPLAY_TYPE_DPI;
out->dispc_channel = dpi_get_channel(port_num);
out->port_num = port_num;
out->ops.dpi = &dpi_ops;
out->owner = THIS_MODULE;
omapdss_register_output(out);
}
static void dpi_uninit_output_port(struct device_node *port)
{
struct dpi_data *dpi = port->data;
struct omap_dss_device *out = &dpi->output;
omapdss_unregister_output(out);
}
static int dpi_bind(struct device *dev, struct device *master, void *data)
{
struct platform_device *pdev = to_platform_device(dev);
struct dpi_data *dpi;
dpi = devm_kzalloc(&pdev->dev, sizeof(*dpi), GFP_KERNEL);
if (!dpi)
return -ENOMEM;
dpi->pdev = pdev;
dev_set_drvdata(&pdev->dev, dpi);
mutex_init(&dpi->lock);
dpi_init_output(pdev);
return 0;
}
static void dpi_unbind(struct device *dev, struct device *master, void *data)
{
struct platform_device *pdev = to_platform_device(dev);
dpi_uninit_output(pdev);
}
static const struct component_ops dpi_component_ops = {
.bind = dpi_bind,
.unbind = dpi_unbind,
};
static int dpi_probe(struct platform_device *pdev)
{
return component_add(&pdev->dev, &dpi_component_ops);
}
static int dpi_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &dpi_component_ops);
return 0;
}
static struct platform_driver omap_dpi_driver = {
.probe = dpi_probe,
.remove = dpi_remove,
.driver = {
.name = "omapdss_dpi",
.suppress_bind_attrs = true,
},
};
int __init dpi_init_platform_driver(void)
{
return platform_driver_register(&omap_dpi_driver);
}
void dpi_uninit_platform_driver(void)
{
platform_driver_unregister(&omap_dpi_driver);
}
int dpi_init_port(struct platform_device *pdev, struct device_node *port)
{
struct dpi_data *dpi;
struct device_node *ep;
u32 datalines;
int r;
dpi = devm_kzalloc(&pdev->dev, sizeof(*dpi), GFP_KERNEL);
if (!dpi)
return -ENOMEM;
ep = omapdss_of_get_next_endpoint(port, NULL);
if (!ep)
return 0;
r = of_property_read_u32(ep, "data-lines", &datalines);
if (r) {
DSSERR("failed to parse datalines\n");
goto err_datalines;
}
dpi->data_lines = datalines;
of_node_put(ep);
dpi->pdev = pdev;
port->data = dpi;
mutex_init(&dpi->lock);
dpi_init_output_port(pdev, port);
dpi->port_initialized = true;
return 0;
err_datalines:
of_node_put(ep);
return r;
}
void dpi_uninit_port(struct device_node *port)
{
struct dpi_data *dpi = port->data;
if (!dpi->port_initialized)
return;
dpi_uninit_output_port(port);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,183 @@
/*
* Copyright (C) 2013 Texas Instruments
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#include <linux/device.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/seq_file.h>
#include <video/omapdss.h>
#include "dss.h"
struct device_node *
omapdss_of_get_next_port(const struct device_node *parent,
struct device_node *prev)
{
struct device_node *port = NULL;
if (!parent)
return NULL;
if (!prev) {
struct device_node *ports;
/*
* It's the first call, we have to find a port subnode
* within this node or within an optional 'ports' node.
*/
ports = of_get_child_by_name(parent, "ports");
if (ports)
parent = ports;
port = of_get_child_by_name(parent, "port");
/* release the 'ports' node */
of_node_put(ports);
} else {
struct device_node *ports;
ports = of_get_parent(prev);
if (!ports)
return NULL;
do {
port = of_get_next_child(ports, prev);
if (!port) {
of_node_put(ports);
return NULL;
}
prev = port;
} while (of_node_cmp(port->name, "port") != 0);
of_node_put(ports);
}
return port;
}
EXPORT_SYMBOL_GPL(omapdss_of_get_next_port);
struct device_node *
omapdss_of_get_next_endpoint(const struct device_node *parent,
struct device_node *prev)
{
struct device_node *ep = NULL;
if (!parent)
return NULL;
do {
ep = of_get_next_child(parent, prev);
if (!ep)
return NULL;
prev = ep;
} while (of_node_cmp(ep->name, "endpoint") != 0);
return ep;
}
EXPORT_SYMBOL_GPL(omapdss_of_get_next_endpoint);
struct device_node *dss_of_port_get_parent_device(struct device_node *port)
{
struct device_node *np;
int i;
if (!port)
return NULL;
np = of_get_parent(port);
for (i = 0; i < 2 && np; ++i) {
struct property *prop;
prop = of_find_property(np, "compatible", NULL);
if (prop)
return np;
np = of_get_next_parent(np);
}
return NULL;
}
u32 dss_of_port_get_port_number(struct device_node *port)
{
int r;
u32 reg;
r = of_property_read_u32(port, "reg", &reg);
if (r)
reg = 0;
return reg;
}
static struct device_node *omapdss_of_get_remote_port(const struct device_node *node)
{
struct device_node *np;
np = of_parse_phandle(node, "remote-endpoint", 0);
if (!np)
return NULL;
np = of_get_next_parent(np);
return np;
}
struct device_node *
omapdss_of_get_first_endpoint(const struct device_node *parent)
{
struct device_node *port, *ep;
port = omapdss_of_get_next_port(parent, NULL);
if (!port)
return NULL;
ep = omapdss_of_get_next_endpoint(port, NULL);
of_node_put(port);
return ep;
}
EXPORT_SYMBOL_GPL(omapdss_of_get_first_endpoint);
struct omap_dss_device *
omapdss_of_find_source_for_first_ep(struct device_node *node)
{
struct device_node *ep;
struct device_node *src_port;
struct omap_dss_device *src;
ep = omapdss_of_get_first_endpoint(node);
if (!ep)
return ERR_PTR(-EINVAL);
src_port = omapdss_of_get_remote_port(ep);
if (!src_port) {
of_node_put(ep);
return ERR_PTR(-EINVAL);
}
of_node_put(ep);
src = omap_dss_find_output_by_port_node(src_port);
of_node_put(src_port);
return src ? src : ERR_PTR(-EPROBE_DEFER);
}
EXPORT_SYMBOL_GPL(omapdss_of_find_source_for_first_ep);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,468 @@
/*
* linux/drivers/video/omap2/dss/dss.h
*
* Copyright (C) 2009 Nokia Corporation
* Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
*
* Some code and ideas taken from drivers/video/omap/ driver
* by Imre Deak.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __OMAP2_DSS_H
#define __OMAP2_DSS_H
#include <linux/interrupt.h>
#ifdef pr_fmt
#undef pr_fmt
#endif
#ifdef DSS_SUBSYS_NAME
#define pr_fmt(fmt) DSS_SUBSYS_NAME ": " fmt
#else
#define pr_fmt(fmt) fmt
#endif
#define DSSDBG(format, ...) \
pr_debug(format, ## __VA_ARGS__)
#ifdef DSS_SUBSYS_NAME
#define DSSERR(format, ...) \
printk(KERN_ERR "omapdss " DSS_SUBSYS_NAME " error: " format, \
## __VA_ARGS__)
#else
#define DSSERR(format, ...) \
printk(KERN_ERR "omapdss error: " format, ## __VA_ARGS__)
#endif
#ifdef DSS_SUBSYS_NAME
#define DSSINFO(format, ...) \
printk(KERN_INFO "omapdss " DSS_SUBSYS_NAME ": " format, \
## __VA_ARGS__)
#else
#define DSSINFO(format, ...) \
printk(KERN_INFO "omapdss: " format, ## __VA_ARGS__)
#endif
#ifdef DSS_SUBSYS_NAME
#define DSSWARN(format, ...) \
printk(KERN_WARNING "omapdss " DSS_SUBSYS_NAME ": " format, \
## __VA_ARGS__)
#else
#define DSSWARN(format, ...) \
printk(KERN_WARNING "omapdss: " format, ## __VA_ARGS__)
#endif
/* OMAP TRM gives bitfields as start:end, where start is the higher bit
number. For example 7:0 */
#define FLD_MASK(start, end) (((1 << ((start) - (end) + 1)) - 1) << (end))
#define FLD_VAL(val, start, end) (((val) << (end)) & FLD_MASK(start, end))
#define FLD_GET(val, start, end) (((val) & FLD_MASK(start, end)) >> (end))
#define FLD_MOD(orig, val, start, end) \
(((orig) & ~FLD_MASK(start, end)) | FLD_VAL(val, start, end))
enum dss_io_pad_mode {
DSS_IO_PAD_MODE_RESET,
DSS_IO_PAD_MODE_RFBI,
DSS_IO_PAD_MODE_BYPASS,
};
enum dss_hdmi_venc_clk_source_select {
DSS_VENC_TV_CLK = 0,
DSS_HDMI_M_PCLK = 1,
};
enum dss_dsi_content_type {
DSS_DSI_CONTENT_DCS,
DSS_DSI_CONTENT_GENERIC,
};
enum dss_writeback_channel {
DSS_WB_LCD1_MGR = 0,
DSS_WB_LCD2_MGR = 1,
DSS_WB_TV_MGR = 2,
DSS_WB_OVL0 = 3,
DSS_WB_OVL1 = 4,
DSS_WB_OVL2 = 5,
DSS_WB_OVL3 = 6,
DSS_WB_LCD3_MGR = 7,
};
enum dss_pll_id {
DSS_PLL_DSI1,
DSS_PLL_DSI2,
DSS_PLL_HDMI,
DSS_PLL_VIDEO1,
DSS_PLL_VIDEO2,
};
struct dss_pll;
#define DSS_PLL_MAX_HSDIVS 4
/*
* Type-A PLLs: clkout[]/mX[] refer to hsdiv outputs m4, m5, m6, m7.
* Type-B PLLs: clkout[0] refers to m2.
*/
struct dss_pll_clock_info {
/* rates that we get with dividers below */
unsigned long fint;
unsigned long clkdco;
unsigned long clkout[DSS_PLL_MAX_HSDIVS];
/* dividers */
u16 n;
u16 m;
u32 mf;
u16 mX[DSS_PLL_MAX_HSDIVS];
u16 sd;
};
struct dss_pll_ops {
int (*enable)(struct dss_pll *pll);
void (*disable)(struct dss_pll *pll);
int (*set_config)(struct dss_pll *pll,
const struct dss_pll_clock_info *cinfo);
};
struct dss_pll_hw {
unsigned n_max;
unsigned m_min;
unsigned m_max;
unsigned mX_max;
unsigned long fint_min, fint_max;
unsigned long clkdco_min, clkdco_low, clkdco_max;
u8 n_msb, n_lsb;
u8 m_msb, m_lsb;
u8 mX_msb[DSS_PLL_MAX_HSDIVS], mX_lsb[DSS_PLL_MAX_HSDIVS];
bool has_stopmode;
bool has_freqsel;
bool has_selfreqdco;
bool has_refsel;
};
struct dss_pll {
const char *name;
enum dss_pll_id id;
struct clk *clkin;
struct regulator *regulator;
void __iomem *base;
const struct dss_pll_hw *hw;
const struct dss_pll_ops *ops;
struct dss_pll_clock_info cinfo;
};
struct dispc_clock_info {
/* rates that we get with dividers below */
unsigned long lck;
unsigned long pck;
/* dividers */
u16 lck_div;
u16 pck_div;
};
struct dss_lcd_mgr_config {
enum dss_io_pad_mode io_pad_mode;
bool stallmode;
bool fifohandcheck;
struct dispc_clock_info clock_info;
int video_port_width;
int lcden_sig_polarity;
};
struct seq_file;
struct platform_device;
/* core */
struct platform_device *dss_get_core_pdev(void);
int dss_dsi_enable_pads(int dsi_id, unsigned lane_mask);
void dss_dsi_disable_pads(int dsi_id, unsigned lane_mask);
int dss_set_min_bus_tput(struct device *dev, unsigned long tput);
int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *));
/* display */
int dss_suspend_all_devices(void);
int dss_resume_all_devices(void);
void dss_disable_all_devices(void);
int display_init_sysfs(struct platform_device *pdev);
void display_uninit_sysfs(struct platform_device *pdev);
/* manager */
int dss_init_overlay_managers(void);
void dss_uninit_overlay_managers(void);
int dss_init_overlay_managers_sysfs(struct platform_device *pdev);
void dss_uninit_overlay_managers_sysfs(struct platform_device *pdev);
int dss_mgr_simple_check(struct omap_overlay_manager *mgr,
const struct omap_overlay_manager_info *info);
int dss_mgr_check_timings(struct omap_overlay_manager *mgr,
const struct omap_video_timings *timings);
int dss_mgr_check(struct omap_overlay_manager *mgr,
struct omap_overlay_manager_info *info,
const struct omap_video_timings *mgr_timings,
const struct dss_lcd_mgr_config *config,
struct omap_overlay_info **overlay_infos);
static inline bool dss_mgr_is_lcd(enum omap_channel id)
{
if (id == OMAP_DSS_CHANNEL_LCD || id == OMAP_DSS_CHANNEL_LCD2 ||
id == OMAP_DSS_CHANNEL_LCD3)
return true;
else
return false;
}
int dss_manager_kobj_init(struct omap_overlay_manager *mgr,
struct platform_device *pdev);
void dss_manager_kobj_uninit(struct omap_overlay_manager *mgr);
/* overlay */
void dss_init_overlays(struct platform_device *pdev);
void dss_uninit_overlays(struct platform_device *pdev);
void dss_overlay_setup_dispc_manager(struct omap_overlay_manager *mgr);
int dss_ovl_simple_check(struct omap_overlay *ovl,
const struct omap_overlay_info *info);
int dss_ovl_check(struct omap_overlay *ovl, struct omap_overlay_info *info,
const struct omap_video_timings *mgr_timings);
bool dss_ovl_use_replication(struct dss_lcd_mgr_config config,
enum omap_color_mode mode);
int dss_overlay_kobj_init(struct omap_overlay *ovl,
struct platform_device *pdev);
void dss_overlay_kobj_uninit(struct omap_overlay *ovl);
/* DSS */
int dss_init_platform_driver(void) __init;
void dss_uninit_platform_driver(void);
int dss_runtime_get(void);
void dss_runtime_put(void);
unsigned long dss_get_dispc_clk_rate(void);
int dss_dpi_select_source(int port, enum omap_channel channel);
void dss_select_hdmi_venc_clk_source(enum dss_hdmi_venc_clk_source_select);
enum dss_hdmi_venc_clk_source_select dss_get_hdmi_venc_clk_source(void);
const char *dss_get_generic_clk_source_name(enum omap_dss_clk_source clk_src);
void dss_dump_clocks(struct seq_file *s);
/* DSS VIDEO PLL */
struct dss_pll *dss_video_pll_init(struct platform_device *pdev, int id,
struct regulator *regulator);
void dss_video_pll_uninit(struct dss_pll *pll);
/* dss-of */
struct device_node *dss_of_port_get_parent_device(struct device_node *port);
u32 dss_of_port_get_port_number(struct device_node *port);
#if defined(CONFIG_OMAP2_DSS_DEBUGFS)
void dss_debug_dump_clocks(struct seq_file *s);
#endif
void dss_ctrl_pll_enable(enum dss_pll_id pll_id, bool enable);
void dss_ctrl_pll_set_control_mux(enum dss_pll_id pll_id,
enum omap_channel channel);
void dss_sdi_init(int datapairs);
int dss_sdi_enable(void);
void dss_sdi_disable(void);
void dss_select_dsi_clk_source(int dsi_module,
enum omap_dss_clk_source clk_src);
void dss_select_lcd_clk_source(enum omap_channel channel,
enum omap_dss_clk_source clk_src);
enum omap_dss_clk_source dss_get_dispc_clk_source(void);
enum omap_dss_clk_source dss_get_dsi_clk_source(int dsi_module);
enum omap_dss_clk_source dss_get_lcd_clk_source(enum omap_channel channel);
void dss_set_venc_output(enum omap_dss_venc_type type);
void dss_set_dac_pwrdn_bgz(bool enable);
int dss_set_fck_rate(unsigned long rate);
typedef bool (*dss_div_calc_func)(unsigned long fck, void *data);
bool dss_div_calc(unsigned long pck, unsigned long fck_min,
dss_div_calc_func func, void *data);
/* SDI */
int sdi_init_platform_driver(void) __init;
void sdi_uninit_platform_driver(void);
#ifdef CONFIG_OMAP2_DSS_SDI
int sdi_init_port(struct platform_device *pdev, struct device_node *port);
void sdi_uninit_port(struct device_node *port);
#else
static inline int sdi_init_port(struct platform_device *pdev,
struct device_node *port)
{
return 0;
}
static inline void sdi_uninit_port(struct device_node *port)
{
}
#endif
/* DSI */
#ifdef CONFIG_OMAP2_DSS_DSI
struct dentry;
struct file_operations;
int dsi_init_platform_driver(void) __init;
void dsi_uninit_platform_driver(void);
void dsi_dump_clocks(struct seq_file *s);
void dsi_irq_handler(void);
u8 dsi_get_pixel_size(enum omap_dss_dsi_pixel_format fmt);
#else
static inline u8 dsi_get_pixel_size(enum omap_dss_dsi_pixel_format fmt)
{
WARN(1, "%s: DSI not compiled in, returning pixel_size as 0\n",
__func__);
return 0;
}
#endif
/* DPI */
int dpi_init_platform_driver(void) __init;
void dpi_uninit_platform_driver(void);
#ifdef CONFIG_OMAP2_DSS_DPI
int dpi_init_port(struct platform_device *pdev, struct device_node *port);
void dpi_uninit_port(struct device_node *port);
#else
static inline int dpi_init_port(struct platform_device *pdev,
struct device_node *port)
{
return 0;
}
static inline void dpi_uninit_port(struct device_node *port)
{
}
#endif
/* DISPC */
int dispc_init_platform_driver(void) __init;
void dispc_uninit_platform_driver(void);
void dispc_dump_clocks(struct seq_file *s);
void dispc_enable_sidle(void);
void dispc_disable_sidle(void);
void dispc_lcd_enable_signal(bool enable);
void dispc_pck_free_enable(bool enable);
void dispc_enable_fifomerge(bool enable);
void dispc_enable_gamma_table(bool enable);
typedef bool (*dispc_div_calc_func)(int lckd, int pckd, unsigned long lck,
unsigned long pck, void *data);
bool dispc_div_calc(unsigned long dispc,
unsigned long pck_min, unsigned long pck_max,
dispc_div_calc_func func, void *data);
bool dispc_mgr_timings_ok(enum omap_channel channel,
const struct omap_video_timings *timings);
int dispc_calc_clock_rates(unsigned long dispc_fclk_rate,
struct dispc_clock_info *cinfo);
void dispc_ovl_set_fifo_threshold(enum omap_plane plane, u32 low, u32 high);
void dispc_ovl_compute_fifo_thresholds(enum omap_plane plane,
u32 *fifo_low, u32 *fifo_high, bool use_fifomerge,
bool manual_update);
void dispc_mgr_set_clock_div(enum omap_channel channel,
const struct dispc_clock_info *cinfo);
int dispc_mgr_get_clock_div(enum omap_channel channel,
struct dispc_clock_info *cinfo);
void dispc_set_tv_pclk(unsigned long pclk);
u32 dispc_wb_get_framedone_irq(void);
bool dispc_wb_go_busy(void);
void dispc_wb_go(void);
void dispc_wb_enable(bool enable);
bool dispc_wb_is_enabled(void);
void dispc_wb_set_channel_in(enum dss_writeback_channel channel);
int dispc_wb_setup(const struct omap_dss_writeback_info *wi,
bool mem_to_mem, const struct omap_video_timings *timings);
/* VENC */
int venc_init_platform_driver(void) __init;
void venc_uninit_platform_driver(void);
/* HDMI */
int hdmi4_init_platform_driver(void) __init;
void hdmi4_uninit_platform_driver(void);
int hdmi5_init_platform_driver(void) __init;
void hdmi5_uninit_platform_driver(void);
/* RFBI */
int rfbi_init_platform_driver(void) __init;
void rfbi_uninit_platform_driver(void);
#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
static inline void dss_collect_irq_stats(u32 irqstatus, unsigned *irq_arr)
{
int b;
for (b = 0; b < 32; ++b) {
if (irqstatus & (1 << b))
irq_arr[b]++;
}
}
#endif
/* PLL */
typedef bool (*dss_pll_calc_func)(int n, int m, unsigned long fint,
unsigned long clkdco, void *data);
typedef bool (*dss_hsdiv_calc_func)(int m_dispc, unsigned long dispc,
void *data);
int dss_pll_register(struct dss_pll *pll);
void dss_pll_unregister(struct dss_pll *pll);
struct dss_pll *dss_pll_find(const char *name);
int dss_pll_enable(struct dss_pll *pll);
void dss_pll_disable(struct dss_pll *pll);
int dss_pll_set_config(struct dss_pll *pll,
const struct dss_pll_clock_info *cinfo);
bool dss_pll_hsdiv_calc(const struct dss_pll *pll, unsigned long clkdco,
unsigned long out_min, unsigned long out_max,
dss_hsdiv_calc_func func, void *data);
bool dss_pll_calc(const struct dss_pll *pll, unsigned long clkin,
unsigned long pll_min, unsigned long pll_max,
dss_pll_calc_func func, void *data);
int dss_pll_write_config_type_a(struct dss_pll *pll,
const struct dss_pll_clock_info *cinfo);
int dss_pll_write_config_type_b(struct dss_pll *pll,
const struct dss_pll_clock_info *cinfo);
int dss_pll_wait_reset_done(struct dss_pll *pll);
#endif

View File

@ -0,0 +1,951 @@
/*
* linux/drivers/video/omap2/dss/dss_features.c
*
* Copyright (C) 2010 Texas Instruments
* Author: Archit Taneja <archit@ti.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <video/omapdss.h>
#include "dss.h"
#include "dss_features.h"
/* Defines a generic omap register field */
struct dss_reg_field {
u8 start, end;
};
struct dss_param_range {
int min, max;
};
struct omap_dss_features {
const struct dss_reg_field *reg_fields;
const int num_reg_fields;
const enum dss_feat_id *features;
const int num_features;
const int num_mgrs;
const int num_ovls;
const enum omap_display_type *supported_displays;
const enum omap_dss_output_id *supported_outputs;
const enum omap_color_mode *supported_color_modes;
const enum omap_overlay_caps *overlay_caps;
const char * const *clksrc_names;
const struct dss_param_range *dss_params;
const enum omap_dss_rotation_type supported_rotation_types;
const u32 buffer_size_unit;
const u32 burst_size_unit;
};
/* This struct is assigned to one of the below during initialization */
static const struct omap_dss_features *omap_current_dss_features;
static const struct dss_reg_field omap2_dss_reg_fields[] = {
[FEAT_REG_FIRHINC] = { 11, 0 },
[FEAT_REG_FIRVINC] = { 27, 16 },
[FEAT_REG_FIFOLOWTHRESHOLD] = { 8, 0 },
[FEAT_REG_FIFOHIGHTHRESHOLD] = { 24, 16 },
[FEAT_REG_FIFOSIZE] = { 8, 0 },
[FEAT_REG_HORIZONTALACCU] = { 9, 0 },
[FEAT_REG_VERTICALACCU] = { 25, 16 },
[FEAT_REG_DISPC_CLK_SWITCH] = { 0, 0 },
};
static const struct dss_reg_field omap3_dss_reg_fields[] = {
[FEAT_REG_FIRHINC] = { 12, 0 },
[FEAT_REG_FIRVINC] = { 28, 16 },
[FEAT_REG_FIFOLOWTHRESHOLD] = { 11, 0 },
[FEAT_REG_FIFOHIGHTHRESHOLD] = { 27, 16 },
[FEAT_REG_FIFOSIZE] = { 10, 0 },
[FEAT_REG_HORIZONTALACCU] = { 9, 0 },
[FEAT_REG_VERTICALACCU] = { 25, 16 },
[FEAT_REG_DISPC_CLK_SWITCH] = { 0, 0 },
};
static const struct dss_reg_field am43xx_dss_reg_fields[] = {
[FEAT_REG_FIRHINC] = { 12, 0 },
[FEAT_REG_FIRVINC] = { 28, 16 },
[FEAT_REG_FIFOLOWTHRESHOLD] = { 11, 0 },
[FEAT_REG_FIFOHIGHTHRESHOLD] = { 27, 16 },
[FEAT_REG_FIFOSIZE] = { 10, 0 },
[FEAT_REG_HORIZONTALACCU] = { 9, 0 },
[FEAT_REG_VERTICALACCU] = { 25, 16 },
[FEAT_REG_DISPC_CLK_SWITCH] = { 0, 0 },
};
static const struct dss_reg_field omap4_dss_reg_fields[] = {
[FEAT_REG_FIRHINC] = { 12, 0 },
[FEAT_REG_FIRVINC] = { 28, 16 },
[FEAT_REG_FIFOLOWTHRESHOLD] = { 15, 0 },
[FEAT_REG_FIFOHIGHTHRESHOLD] = { 31, 16 },
[FEAT_REG_FIFOSIZE] = { 15, 0 },
[FEAT_REG_HORIZONTALACCU] = { 10, 0 },
[FEAT_REG_VERTICALACCU] = { 26, 16 },
[FEAT_REG_DISPC_CLK_SWITCH] = { 9, 8 },
};
static const struct dss_reg_field omap5_dss_reg_fields[] = {
[FEAT_REG_FIRHINC] = { 12, 0 },
[FEAT_REG_FIRVINC] = { 28, 16 },
[FEAT_REG_FIFOLOWTHRESHOLD] = { 15, 0 },
[FEAT_REG_FIFOHIGHTHRESHOLD] = { 31, 16 },
[FEAT_REG_FIFOSIZE] = { 15, 0 },
[FEAT_REG_HORIZONTALACCU] = { 10, 0 },
[FEAT_REG_VERTICALACCU] = { 26, 16 },
[FEAT_REG_DISPC_CLK_SWITCH] = { 9, 7 },
};
static const enum omap_display_type omap2_dss_supported_displays[] = {
/* OMAP_DSS_CHANNEL_LCD */
OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI,
/* OMAP_DSS_CHANNEL_DIGIT */
OMAP_DISPLAY_TYPE_VENC,
};
static const enum omap_display_type omap3430_dss_supported_displays[] = {
/* OMAP_DSS_CHANNEL_LCD */
OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI |
OMAP_DISPLAY_TYPE_SDI | OMAP_DISPLAY_TYPE_DSI,
/* OMAP_DSS_CHANNEL_DIGIT */
OMAP_DISPLAY_TYPE_VENC,
};
static const enum omap_display_type omap3630_dss_supported_displays[] = {
/* OMAP_DSS_CHANNEL_LCD */
OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI |
OMAP_DISPLAY_TYPE_DSI,
/* OMAP_DSS_CHANNEL_DIGIT */
OMAP_DISPLAY_TYPE_VENC,
};
static const enum omap_display_type am43xx_dss_supported_displays[] = {
/* OMAP_DSS_CHANNEL_LCD */
OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI,
};
static const enum omap_display_type omap4_dss_supported_displays[] = {
/* OMAP_DSS_CHANNEL_LCD */
OMAP_DISPLAY_TYPE_DBI | OMAP_DISPLAY_TYPE_DSI,
/* OMAP_DSS_CHANNEL_DIGIT */
OMAP_DISPLAY_TYPE_VENC | OMAP_DISPLAY_TYPE_HDMI,
/* OMAP_DSS_CHANNEL_LCD2 */
OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI |
OMAP_DISPLAY_TYPE_DSI,
};
static const enum omap_display_type omap5_dss_supported_displays[] = {
/* OMAP_DSS_CHANNEL_LCD */
OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI |
OMAP_DISPLAY_TYPE_DSI,
/* OMAP_DSS_CHANNEL_DIGIT */
OMAP_DISPLAY_TYPE_HDMI | OMAP_DISPLAY_TYPE_DPI,
/* OMAP_DSS_CHANNEL_LCD2 */
OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI |
OMAP_DISPLAY_TYPE_DSI,
};
static const enum omap_dss_output_id omap2_dss_supported_outputs[] = {
/* OMAP_DSS_CHANNEL_LCD */
OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI,
/* OMAP_DSS_CHANNEL_DIGIT */
OMAP_DSS_OUTPUT_VENC,
};
static const enum omap_dss_output_id omap3430_dss_supported_outputs[] = {
/* OMAP_DSS_CHANNEL_LCD */
OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI |
OMAP_DSS_OUTPUT_SDI | OMAP_DSS_OUTPUT_DSI1,
/* OMAP_DSS_CHANNEL_DIGIT */
OMAP_DSS_OUTPUT_VENC,
};
static const enum omap_dss_output_id omap3630_dss_supported_outputs[] = {
/* OMAP_DSS_CHANNEL_LCD */
OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI |
OMAP_DSS_OUTPUT_DSI1,
/* OMAP_DSS_CHANNEL_DIGIT */
OMAP_DSS_OUTPUT_VENC,
};
static const enum omap_dss_output_id am43xx_dss_supported_outputs[] = {
/* OMAP_DSS_CHANNEL_LCD */
OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI,
};
static const enum omap_dss_output_id omap4_dss_supported_outputs[] = {
/* OMAP_DSS_CHANNEL_LCD */
OMAP_DSS_OUTPUT_DBI | OMAP_DSS_OUTPUT_DSI1,
/* OMAP_DSS_CHANNEL_DIGIT */
OMAP_DSS_OUTPUT_VENC | OMAP_DSS_OUTPUT_HDMI,
/* OMAP_DSS_CHANNEL_LCD2 */
OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI |
OMAP_DSS_OUTPUT_DSI2,
};
static const enum omap_dss_output_id omap5_dss_supported_outputs[] = {
/* OMAP_DSS_CHANNEL_LCD */
OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI |
OMAP_DSS_OUTPUT_DSI1 | OMAP_DSS_OUTPUT_DSI2,
/* OMAP_DSS_CHANNEL_DIGIT */
OMAP_DSS_OUTPUT_HDMI,
/* OMAP_DSS_CHANNEL_LCD2 */
OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI |
OMAP_DSS_OUTPUT_DSI1,
/* OMAP_DSS_CHANNEL_LCD3 */
OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI |
OMAP_DSS_OUTPUT_DSI2,
};
static const enum omap_color_mode omap2_dss_supported_color_modes[] = {
/* OMAP_DSS_GFX */
OMAP_DSS_COLOR_CLUT1 | OMAP_DSS_COLOR_CLUT2 |
OMAP_DSS_COLOR_CLUT4 | OMAP_DSS_COLOR_CLUT8 |
OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_RGB16 |
OMAP_DSS_COLOR_RGB24U | OMAP_DSS_COLOR_RGB24P,
/* OMAP_DSS_VIDEO1 */
OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U |
OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_YUV2 |
OMAP_DSS_COLOR_UYVY,
/* OMAP_DSS_VIDEO2 */
OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U |
OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_YUV2 |
OMAP_DSS_COLOR_UYVY,
};
static const enum omap_color_mode omap3_dss_supported_color_modes[] = {
/* OMAP_DSS_GFX */
OMAP_DSS_COLOR_CLUT1 | OMAP_DSS_COLOR_CLUT2 |
OMAP_DSS_COLOR_CLUT4 | OMAP_DSS_COLOR_CLUT8 |
OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_ARGB16 |
OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U |
OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_ARGB32 |
OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_RGBX32,
/* OMAP_DSS_VIDEO1 */
OMAP_DSS_COLOR_RGB24U | OMAP_DSS_COLOR_RGB24P |
OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_RGB16 |
OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_UYVY,
/* OMAP_DSS_VIDEO2 */
OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_ARGB16 |
OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U |
OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_YUV2 |
OMAP_DSS_COLOR_UYVY | OMAP_DSS_COLOR_ARGB32 |
OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_RGBX32,
};
static const enum omap_color_mode omap4_dss_supported_color_modes[] = {
/* OMAP_DSS_GFX */
OMAP_DSS_COLOR_CLUT1 | OMAP_DSS_COLOR_CLUT2 |
OMAP_DSS_COLOR_CLUT4 | OMAP_DSS_COLOR_CLUT8 |
OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_ARGB16 |
OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U |
OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_ARGB32 |
OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_RGBX32 |
OMAP_DSS_COLOR_ARGB16_1555 | OMAP_DSS_COLOR_RGBX16 |
OMAP_DSS_COLOR_RGBA16 | OMAP_DSS_COLOR_XRGB16_1555,
/* OMAP_DSS_VIDEO1 */
OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB12U |
OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_ARGB16_1555 |
OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_NV12 |
OMAP_DSS_COLOR_RGBA16 | OMAP_DSS_COLOR_RGB24U |
OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_UYVY |
OMAP_DSS_COLOR_ARGB16 | OMAP_DSS_COLOR_XRGB16_1555 |
OMAP_DSS_COLOR_ARGB32 | OMAP_DSS_COLOR_RGBX16 |
OMAP_DSS_COLOR_RGBX32,
/* OMAP_DSS_VIDEO2 */
OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB12U |
OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_ARGB16_1555 |
OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_NV12 |
OMAP_DSS_COLOR_RGBA16 | OMAP_DSS_COLOR_RGB24U |
OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_UYVY |
OMAP_DSS_COLOR_ARGB16 | OMAP_DSS_COLOR_XRGB16_1555 |
OMAP_DSS_COLOR_ARGB32 | OMAP_DSS_COLOR_RGBX16 |
OMAP_DSS_COLOR_RGBX32,
/* OMAP_DSS_VIDEO3 */
OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB12U |
OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_ARGB16_1555 |
OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_NV12 |
OMAP_DSS_COLOR_RGBA16 | OMAP_DSS_COLOR_RGB24U |
OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_UYVY |
OMAP_DSS_COLOR_ARGB16 | OMAP_DSS_COLOR_XRGB16_1555 |
OMAP_DSS_COLOR_ARGB32 | OMAP_DSS_COLOR_RGBX16 |
OMAP_DSS_COLOR_RGBX32,
/* OMAP_DSS_WB */
OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB12U |
OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_ARGB16_1555 |
OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_NV12 |
OMAP_DSS_COLOR_RGBA16 | OMAP_DSS_COLOR_RGB24U |
OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_UYVY |
OMAP_DSS_COLOR_ARGB16 | OMAP_DSS_COLOR_XRGB16_1555 |
OMAP_DSS_COLOR_ARGB32 | OMAP_DSS_COLOR_RGBX16 |
OMAP_DSS_COLOR_RGBX32,
};
static const enum omap_overlay_caps omap2_dss_overlay_caps[] = {
/* OMAP_DSS_GFX */
OMAP_DSS_OVL_CAP_POS | OMAP_DSS_OVL_CAP_REPLICATION,
/* OMAP_DSS_VIDEO1 */
OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_POS |
OMAP_DSS_OVL_CAP_REPLICATION,
/* OMAP_DSS_VIDEO2 */
OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_POS |
OMAP_DSS_OVL_CAP_REPLICATION,
};
static const enum omap_overlay_caps omap3430_dss_overlay_caps[] = {
/* OMAP_DSS_GFX */
OMAP_DSS_OVL_CAP_GLOBAL_ALPHA | OMAP_DSS_OVL_CAP_POS |
OMAP_DSS_OVL_CAP_REPLICATION,
/* OMAP_DSS_VIDEO1 */
OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_POS |
OMAP_DSS_OVL_CAP_REPLICATION,
/* OMAP_DSS_VIDEO2 */
OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_GLOBAL_ALPHA |
OMAP_DSS_OVL_CAP_POS | OMAP_DSS_OVL_CAP_REPLICATION,
};
static const enum omap_overlay_caps omap3630_dss_overlay_caps[] = {
/* OMAP_DSS_GFX */
OMAP_DSS_OVL_CAP_GLOBAL_ALPHA | OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA |
OMAP_DSS_OVL_CAP_POS | OMAP_DSS_OVL_CAP_REPLICATION,
/* OMAP_DSS_VIDEO1 */
OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_POS |
OMAP_DSS_OVL_CAP_REPLICATION,
/* OMAP_DSS_VIDEO2 */
OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_GLOBAL_ALPHA |
OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA | OMAP_DSS_OVL_CAP_POS |
OMAP_DSS_OVL_CAP_REPLICATION,
};
static const enum omap_overlay_caps omap4_dss_overlay_caps[] = {
/* OMAP_DSS_GFX */
OMAP_DSS_OVL_CAP_GLOBAL_ALPHA | OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA |
OMAP_DSS_OVL_CAP_ZORDER | OMAP_DSS_OVL_CAP_POS |
OMAP_DSS_OVL_CAP_REPLICATION,
/* OMAP_DSS_VIDEO1 */
OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_GLOBAL_ALPHA |
OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA | OMAP_DSS_OVL_CAP_ZORDER |
OMAP_DSS_OVL_CAP_POS | OMAP_DSS_OVL_CAP_REPLICATION,
/* OMAP_DSS_VIDEO2 */
OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_GLOBAL_ALPHA |
OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA | OMAP_DSS_OVL_CAP_ZORDER |
OMAP_DSS_OVL_CAP_POS | OMAP_DSS_OVL_CAP_REPLICATION,
/* OMAP_DSS_VIDEO3 */
OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_GLOBAL_ALPHA |
OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA | OMAP_DSS_OVL_CAP_ZORDER |
OMAP_DSS_OVL_CAP_POS | OMAP_DSS_OVL_CAP_REPLICATION,
};
static const char * const omap2_dss_clk_source_names[] = {
[OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC] = "N/A",
[OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI] = "N/A",
[OMAP_DSS_CLK_SRC_FCK] = "DSS_FCLK1",
};
static const char * const omap3_dss_clk_source_names[] = {
[OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC] = "DSI1_PLL_FCLK",
[OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI] = "DSI2_PLL_FCLK",
[OMAP_DSS_CLK_SRC_FCK] = "DSS1_ALWON_FCLK",
};
static const char * const omap4_dss_clk_source_names[] = {
[OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC] = "PLL1_CLK1",
[OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI] = "PLL1_CLK2",
[OMAP_DSS_CLK_SRC_FCK] = "DSS_FCLK",
[OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC] = "PLL2_CLK1",
[OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DSI] = "PLL2_CLK2",
};
static const char * const omap5_dss_clk_source_names[] = {
[OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC] = "DPLL_DSI1_A_CLK1",
[OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI] = "DPLL_DSI1_A_CLK2",
[OMAP_DSS_CLK_SRC_FCK] = "DSS_CLK",
[OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC] = "DPLL_DSI1_C_CLK1",
[OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DSI] = "DPLL_DSI1_C_CLK2",
};
static const struct dss_param_range omap2_dss_param_range[] = {
[FEAT_PARAM_DSS_FCK] = { 0, 133000000 },
[FEAT_PARAM_DSS_PCD] = { 2, 255 },
[FEAT_PARAM_DOWNSCALE] = { 1, 2 },
/*
* Assuming the line width buffer to be 768 pixels as OMAP2 DISPC
* scaler cannot scale a image with width more than 768.
*/
[FEAT_PARAM_LINEWIDTH] = { 1, 768 },
};
static const struct dss_param_range omap3_dss_param_range[] = {
[FEAT_PARAM_DSS_FCK] = { 0, 173000000 },
[FEAT_PARAM_DSS_PCD] = { 1, 255 },
[FEAT_PARAM_DSIPLL_LPDIV] = { 1, (1 << 13) - 1},
[FEAT_PARAM_DSI_FCK] = { 0, 173000000 },
[FEAT_PARAM_DOWNSCALE] = { 1, 4 },
[FEAT_PARAM_LINEWIDTH] = { 1, 1024 },
};
static const struct dss_param_range am43xx_dss_param_range[] = {
[FEAT_PARAM_DSS_FCK] = { 0, 200000000 },
[FEAT_PARAM_DSS_PCD] = { 1, 255 },
[FEAT_PARAM_DOWNSCALE] = { 1, 4 },
[FEAT_PARAM_LINEWIDTH] = { 1, 1024 },
};
static const struct dss_param_range omap4_dss_param_range[] = {
[FEAT_PARAM_DSS_FCK] = { 0, 186000000 },
[FEAT_PARAM_DSS_PCD] = { 1, 255 },
[FEAT_PARAM_DSIPLL_LPDIV] = { 0, (1 << 13) - 1 },
[FEAT_PARAM_DSI_FCK] = { 0, 170000000 },
[FEAT_PARAM_DOWNSCALE] = { 1, 4 },
[FEAT_PARAM_LINEWIDTH] = { 1, 2048 },
};
static const struct dss_param_range omap5_dss_param_range[] = {
[FEAT_PARAM_DSS_FCK] = { 0, 209250000 },
[FEAT_PARAM_DSS_PCD] = { 1, 255 },
[FEAT_PARAM_DSIPLL_LPDIV] = { 0, (1 << 13) - 1 },
[FEAT_PARAM_DSI_FCK] = { 0, 209250000 },
[FEAT_PARAM_DOWNSCALE] = { 1, 4 },
[FEAT_PARAM_LINEWIDTH] = { 1, 2048 },
};
static const enum dss_feat_id omap2_dss_feat_list[] = {
FEAT_LCDENABLEPOL,
FEAT_LCDENABLESIGNAL,
FEAT_PCKFREEENABLE,
FEAT_FUNCGATED,
FEAT_ROWREPEATENABLE,
FEAT_RESIZECONF,
};
static const enum dss_feat_id omap3430_dss_feat_list[] = {
FEAT_LCDENABLEPOL,
FEAT_LCDENABLESIGNAL,
FEAT_PCKFREEENABLE,
FEAT_FUNCGATED,
FEAT_LINEBUFFERSPLIT,
FEAT_ROWREPEATENABLE,
FEAT_RESIZECONF,
FEAT_DSI_REVERSE_TXCLKESC,
FEAT_VENC_REQUIRES_TV_DAC_CLK,
FEAT_CPR,
FEAT_PRELOAD,
FEAT_FIR_COEF_V,
FEAT_ALPHA_FIXED_ZORDER,
FEAT_FIFO_MERGE,
FEAT_OMAP3_DSI_FIFO_BUG,
FEAT_DPI_USES_VDDS_DSI,
};
static const enum dss_feat_id am35xx_dss_feat_list[] = {
FEAT_LCDENABLEPOL,
FEAT_LCDENABLESIGNAL,
FEAT_PCKFREEENABLE,
FEAT_FUNCGATED,
FEAT_LINEBUFFERSPLIT,
FEAT_ROWREPEATENABLE,
FEAT_RESIZECONF,
FEAT_DSI_REVERSE_TXCLKESC,
FEAT_VENC_REQUIRES_TV_DAC_CLK,
FEAT_CPR,
FEAT_PRELOAD,
FEAT_FIR_COEF_V,
FEAT_ALPHA_FIXED_ZORDER,
FEAT_FIFO_MERGE,
FEAT_OMAP3_DSI_FIFO_BUG,
};
static const enum dss_feat_id am43xx_dss_feat_list[] = {
FEAT_LCDENABLEPOL,
FEAT_LCDENABLESIGNAL,
FEAT_PCKFREEENABLE,
FEAT_FUNCGATED,
FEAT_LINEBUFFERSPLIT,
FEAT_ROWREPEATENABLE,
FEAT_RESIZECONF,
FEAT_CPR,
FEAT_PRELOAD,
FEAT_FIR_COEF_V,
FEAT_ALPHA_FIXED_ZORDER,
FEAT_FIFO_MERGE,
};
static const enum dss_feat_id omap3630_dss_feat_list[] = {
FEAT_LCDENABLEPOL,
FEAT_LCDENABLESIGNAL,
FEAT_PCKFREEENABLE,
FEAT_FUNCGATED,
FEAT_LINEBUFFERSPLIT,
FEAT_ROWREPEATENABLE,
FEAT_RESIZECONF,
FEAT_DSI_PLL_PWR_BUG,
FEAT_CPR,
FEAT_PRELOAD,
FEAT_FIR_COEF_V,
FEAT_ALPHA_FIXED_ZORDER,
FEAT_FIFO_MERGE,
FEAT_OMAP3_DSI_FIFO_BUG,
FEAT_DPI_USES_VDDS_DSI,
};
static const enum dss_feat_id omap4430_es1_0_dss_feat_list[] = {
FEAT_MGR_LCD2,
FEAT_CORE_CLK_DIV,
FEAT_LCD_CLK_SRC,
FEAT_DSI_DCS_CMD_CONFIG_VC,
FEAT_DSI_VC_OCP_WIDTH,
FEAT_DSI_GNQ,
FEAT_HANDLE_UV_SEPARATE,
FEAT_ATTR2,
FEAT_CPR,
FEAT_PRELOAD,
FEAT_FIR_COEF_V,
FEAT_ALPHA_FREE_ZORDER,
FEAT_FIFO_MERGE,
FEAT_BURST_2D,
};
static const enum dss_feat_id omap4430_es2_0_1_2_dss_feat_list[] = {
FEAT_MGR_LCD2,
FEAT_CORE_CLK_DIV,
FEAT_LCD_CLK_SRC,
FEAT_DSI_DCS_CMD_CONFIG_VC,
FEAT_DSI_VC_OCP_WIDTH,
FEAT_DSI_GNQ,
FEAT_HDMI_CTS_SWMODE,
FEAT_HANDLE_UV_SEPARATE,
FEAT_ATTR2,
FEAT_CPR,
FEAT_PRELOAD,
FEAT_FIR_COEF_V,
FEAT_ALPHA_FREE_ZORDER,
FEAT_FIFO_MERGE,
FEAT_BURST_2D,
};
static const enum dss_feat_id omap4_dss_feat_list[] = {
FEAT_MGR_LCD2,
FEAT_CORE_CLK_DIV,
FEAT_LCD_CLK_SRC,
FEAT_DSI_DCS_CMD_CONFIG_VC,
FEAT_DSI_VC_OCP_WIDTH,
FEAT_DSI_GNQ,
FEAT_HDMI_CTS_SWMODE,
FEAT_HDMI_AUDIO_USE_MCLK,
FEAT_HANDLE_UV_SEPARATE,
FEAT_ATTR2,
FEAT_CPR,
FEAT_PRELOAD,
FEAT_FIR_COEF_V,
FEAT_ALPHA_FREE_ZORDER,
FEAT_FIFO_MERGE,
FEAT_BURST_2D,
};
static const enum dss_feat_id omap5_dss_feat_list[] = {
FEAT_MGR_LCD2,
FEAT_MGR_LCD3,
FEAT_CORE_CLK_DIV,
FEAT_LCD_CLK_SRC,
FEAT_DSI_DCS_CMD_CONFIG_VC,
FEAT_DSI_VC_OCP_WIDTH,
FEAT_DSI_GNQ,
FEAT_HDMI_CTS_SWMODE,
FEAT_HDMI_AUDIO_USE_MCLK,
FEAT_HANDLE_UV_SEPARATE,
FEAT_ATTR2,
FEAT_CPR,
FEAT_PRELOAD,
FEAT_FIR_COEF_V,
FEAT_ALPHA_FREE_ZORDER,
FEAT_FIFO_MERGE,
FEAT_BURST_2D,
FEAT_DSI_PHY_DCC,
FEAT_MFLAG,
};
/* OMAP2 DSS Features */
static const struct omap_dss_features omap2_dss_features = {
.reg_fields = omap2_dss_reg_fields,
.num_reg_fields = ARRAY_SIZE(omap2_dss_reg_fields),
.features = omap2_dss_feat_list,
.num_features = ARRAY_SIZE(omap2_dss_feat_list),
.num_mgrs = 2,
.num_ovls = 3,
.supported_displays = omap2_dss_supported_displays,
.supported_outputs = omap2_dss_supported_outputs,
.supported_color_modes = omap2_dss_supported_color_modes,
.overlay_caps = omap2_dss_overlay_caps,
.clksrc_names = omap2_dss_clk_source_names,
.dss_params = omap2_dss_param_range,
.supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_VRFB,
.buffer_size_unit = 1,
.burst_size_unit = 8,
};
/* OMAP3 DSS Features */
static const struct omap_dss_features omap3430_dss_features = {
.reg_fields = omap3_dss_reg_fields,
.num_reg_fields = ARRAY_SIZE(omap3_dss_reg_fields),
.features = omap3430_dss_feat_list,
.num_features = ARRAY_SIZE(omap3430_dss_feat_list),
.num_mgrs = 2,
.num_ovls = 3,
.supported_displays = omap3430_dss_supported_displays,
.supported_outputs = omap3430_dss_supported_outputs,
.supported_color_modes = omap3_dss_supported_color_modes,
.overlay_caps = omap3430_dss_overlay_caps,
.clksrc_names = omap3_dss_clk_source_names,
.dss_params = omap3_dss_param_range,
.supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_VRFB,
.buffer_size_unit = 1,
.burst_size_unit = 8,
};
/*
* AM35xx DSS Features. This is basically OMAP3 DSS Features without the
* vdds_dsi regulator.
*/
static const struct omap_dss_features am35xx_dss_features = {
.reg_fields = omap3_dss_reg_fields,
.num_reg_fields = ARRAY_SIZE(omap3_dss_reg_fields),
.features = am35xx_dss_feat_list,
.num_features = ARRAY_SIZE(am35xx_dss_feat_list),
.num_mgrs = 2,
.num_ovls = 3,
.supported_displays = omap3430_dss_supported_displays,
.supported_outputs = omap3430_dss_supported_outputs,
.supported_color_modes = omap3_dss_supported_color_modes,
.overlay_caps = omap3430_dss_overlay_caps,
.clksrc_names = omap3_dss_clk_source_names,
.dss_params = omap3_dss_param_range,
.supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_VRFB,
.buffer_size_unit = 1,
.burst_size_unit = 8,
};
static const struct omap_dss_features am43xx_dss_features = {
.reg_fields = am43xx_dss_reg_fields,
.num_reg_fields = ARRAY_SIZE(am43xx_dss_reg_fields),
.features = am43xx_dss_feat_list,
.num_features = ARRAY_SIZE(am43xx_dss_feat_list),
.num_mgrs = 1,
.num_ovls = 3,
.supported_displays = am43xx_dss_supported_displays,
.supported_outputs = am43xx_dss_supported_outputs,
.supported_color_modes = omap3_dss_supported_color_modes,
.overlay_caps = omap3430_dss_overlay_caps,
.clksrc_names = omap2_dss_clk_source_names,
.dss_params = am43xx_dss_param_range,
.supported_rotation_types = OMAP_DSS_ROT_DMA,
.buffer_size_unit = 1,
.burst_size_unit = 8,
};
static const struct omap_dss_features omap3630_dss_features = {
.reg_fields = omap3_dss_reg_fields,
.num_reg_fields = ARRAY_SIZE(omap3_dss_reg_fields),
.features = omap3630_dss_feat_list,
.num_features = ARRAY_SIZE(omap3630_dss_feat_list),
.num_mgrs = 2,
.num_ovls = 3,
.supported_displays = omap3630_dss_supported_displays,
.supported_outputs = omap3630_dss_supported_outputs,
.supported_color_modes = omap3_dss_supported_color_modes,
.overlay_caps = omap3630_dss_overlay_caps,
.clksrc_names = omap3_dss_clk_source_names,
.dss_params = omap3_dss_param_range,
.supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_VRFB,
.buffer_size_unit = 1,
.burst_size_unit = 8,
};
/* OMAP4 DSS Features */
/* For OMAP4430 ES 1.0 revision */
static const struct omap_dss_features omap4430_es1_0_dss_features = {
.reg_fields = omap4_dss_reg_fields,
.num_reg_fields = ARRAY_SIZE(omap4_dss_reg_fields),
.features = omap4430_es1_0_dss_feat_list,
.num_features = ARRAY_SIZE(omap4430_es1_0_dss_feat_list),
.num_mgrs = 3,
.num_ovls = 4,
.supported_displays = omap4_dss_supported_displays,
.supported_outputs = omap4_dss_supported_outputs,
.supported_color_modes = omap4_dss_supported_color_modes,
.overlay_caps = omap4_dss_overlay_caps,
.clksrc_names = omap4_dss_clk_source_names,
.dss_params = omap4_dss_param_range,
.supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_TILER,
.buffer_size_unit = 16,
.burst_size_unit = 16,
};
/* For OMAP4430 ES 2.0, 2.1 and 2.2 revisions */
static const struct omap_dss_features omap4430_es2_0_1_2_dss_features = {
.reg_fields = omap4_dss_reg_fields,
.num_reg_fields = ARRAY_SIZE(omap4_dss_reg_fields),
.features = omap4430_es2_0_1_2_dss_feat_list,
.num_features = ARRAY_SIZE(omap4430_es2_0_1_2_dss_feat_list),
.num_mgrs = 3,
.num_ovls = 4,
.supported_displays = omap4_dss_supported_displays,
.supported_outputs = omap4_dss_supported_outputs,
.supported_color_modes = omap4_dss_supported_color_modes,
.overlay_caps = omap4_dss_overlay_caps,
.clksrc_names = omap4_dss_clk_source_names,
.dss_params = omap4_dss_param_range,
.supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_TILER,
.buffer_size_unit = 16,
.burst_size_unit = 16,
};
/* For all the other OMAP4 versions */
static const struct omap_dss_features omap4_dss_features = {
.reg_fields = omap4_dss_reg_fields,
.num_reg_fields = ARRAY_SIZE(omap4_dss_reg_fields),
.features = omap4_dss_feat_list,
.num_features = ARRAY_SIZE(omap4_dss_feat_list),
.num_mgrs = 3,
.num_ovls = 4,
.supported_displays = omap4_dss_supported_displays,
.supported_outputs = omap4_dss_supported_outputs,
.supported_color_modes = omap4_dss_supported_color_modes,
.overlay_caps = omap4_dss_overlay_caps,
.clksrc_names = omap4_dss_clk_source_names,
.dss_params = omap4_dss_param_range,
.supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_TILER,
.buffer_size_unit = 16,
.burst_size_unit = 16,
};
/* OMAP5 DSS Features */
static const struct omap_dss_features omap5_dss_features = {
.reg_fields = omap5_dss_reg_fields,
.num_reg_fields = ARRAY_SIZE(omap5_dss_reg_fields),
.features = omap5_dss_feat_list,
.num_features = ARRAY_SIZE(omap5_dss_feat_list),
.num_mgrs = 4,
.num_ovls = 4,
.supported_displays = omap5_dss_supported_displays,
.supported_outputs = omap5_dss_supported_outputs,
.supported_color_modes = omap4_dss_supported_color_modes,
.overlay_caps = omap4_dss_overlay_caps,
.clksrc_names = omap5_dss_clk_source_names,
.dss_params = omap5_dss_param_range,
.supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_TILER,
.buffer_size_unit = 16,
.burst_size_unit = 16,
};
/* Functions returning values related to a DSS feature */
int dss_feat_get_num_mgrs(void)
{
return omap_current_dss_features->num_mgrs;
}
EXPORT_SYMBOL(dss_feat_get_num_mgrs);
int dss_feat_get_num_ovls(void)
{
return omap_current_dss_features->num_ovls;
}
EXPORT_SYMBOL(dss_feat_get_num_ovls);
unsigned long dss_feat_get_param_min(enum dss_range_param param)
{
return omap_current_dss_features->dss_params[param].min;
}
unsigned long dss_feat_get_param_max(enum dss_range_param param)
{
return omap_current_dss_features->dss_params[param].max;
}
enum omap_display_type dss_feat_get_supported_displays(enum omap_channel channel)
{
return omap_current_dss_features->supported_displays[channel];
}
enum omap_dss_output_id dss_feat_get_supported_outputs(enum omap_channel channel)
{
return omap_current_dss_features->supported_outputs[channel];
}
enum omap_color_mode dss_feat_get_supported_color_modes(enum omap_plane plane)
{
return omap_current_dss_features->supported_color_modes[plane];
}
EXPORT_SYMBOL(dss_feat_get_supported_color_modes);
enum omap_overlay_caps dss_feat_get_overlay_caps(enum omap_plane plane)
{
return omap_current_dss_features->overlay_caps[plane];
}
bool dss_feat_color_mode_supported(enum omap_plane plane,
enum omap_color_mode color_mode)
{
return omap_current_dss_features->supported_color_modes[plane] &
color_mode;
}
const char *dss_feat_get_clk_source_name(enum omap_dss_clk_source id)
{
return omap_current_dss_features->clksrc_names[id];
}
u32 dss_feat_get_buffer_size_unit(void)
{
return omap_current_dss_features->buffer_size_unit;
}
u32 dss_feat_get_burst_size_unit(void)
{
return omap_current_dss_features->burst_size_unit;
}
/* DSS has_feature check */
bool dss_has_feature(enum dss_feat_id id)
{
int i;
const enum dss_feat_id *features = omap_current_dss_features->features;
const int num_features = omap_current_dss_features->num_features;
for (i = 0; i < num_features; i++) {
if (features[i] == id)
return true;
}
return false;
}
void dss_feat_get_reg_field(enum dss_feat_reg_field id, u8 *start, u8 *end)
{
if (id >= omap_current_dss_features->num_reg_fields)
BUG();
*start = omap_current_dss_features->reg_fields[id].start;
*end = omap_current_dss_features->reg_fields[id].end;
}
bool dss_feat_rotation_type_supported(enum omap_dss_rotation_type rot_type)
{
return omap_current_dss_features->supported_rotation_types & rot_type;
}
void dss_features_init(enum omapdss_version version)
{
switch (version) {
case OMAPDSS_VER_OMAP24xx:
omap_current_dss_features = &omap2_dss_features;
break;
case OMAPDSS_VER_OMAP34xx_ES1:
case OMAPDSS_VER_OMAP34xx_ES3:
omap_current_dss_features = &omap3430_dss_features;
break;
case OMAPDSS_VER_OMAP3630:
omap_current_dss_features = &omap3630_dss_features;
break;
case OMAPDSS_VER_OMAP4430_ES1:
omap_current_dss_features = &omap4430_es1_0_dss_features;
break;
case OMAPDSS_VER_OMAP4430_ES2:
omap_current_dss_features = &omap4430_es2_0_1_2_dss_features;
break;
case OMAPDSS_VER_OMAP4:
omap_current_dss_features = &omap4_dss_features;
break;
case OMAPDSS_VER_OMAP5:
case OMAPDSS_VER_DRA7xx:
omap_current_dss_features = &omap5_dss_features;
break;
case OMAPDSS_VER_AM35xx:
omap_current_dss_features = &am35xx_dss_features;
break;
case OMAPDSS_VER_AM43xx:
omap_current_dss_features = &am43xx_dss_features;
break;
default:
DSSWARN("Unsupported OMAP version");
break;
}
}

View File

@ -0,0 +1,108 @@
/*
* linux/drivers/video/omap2/dss/dss_features.h
*
* Copyright (C) 2010 Texas Instruments
* Author: Archit Taneja <archit@ti.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __OMAP2_DSS_FEATURES_H
#define __OMAP2_DSS_FEATURES_H
#define MAX_DSS_MANAGERS 4
#define MAX_DSS_OVERLAYS 4
#define MAX_DSS_LCD_MANAGERS 3
#define MAX_NUM_DSI 2
/* DSS has feature id */
enum dss_feat_id {
FEAT_LCDENABLEPOL,
FEAT_LCDENABLESIGNAL,
FEAT_PCKFREEENABLE,
FEAT_FUNCGATED,
FEAT_MGR_LCD2,
FEAT_MGR_LCD3,
FEAT_LINEBUFFERSPLIT,
FEAT_ROWREPEATENABLE,
FEAT_RESIZECONF,
/* Independent core clk divider */
FEAT_CORE_CLK_DIV,
FEAT_LCD_CLK_SRC,
/* DSI-PLL power command 0x3 is not working */
FEAT_DSI_PLL_PWR_BUG,
FEAT_DSI_DCS_CMD_CONFIG_VC,
FEAT_DSI_VC_OCP_WIDTH,
FEAT_DSI_REVERSE_TXCLKESC,
FEAT_DSI_GNQ,
FEAT_DPI_USES_VDDS_DSI,
FEAT_HDMI_CTS_SWMODE,
FEAT_HDMI_AUDIO_USE_MCLK,
FEAT_HANDLE_UV_SEPARATE,
FEAT_ATTR2,
FEAT_VENC_REQUIRES_TV_DAC_CLK,
FEAT_CPR,
FEAT_PRELOAD,
FEAT_FIR_COEF_V,
FEAT_ALPHA_FIXED_ZORDER,
FEAT_ALPHA_FREE_ZORDER,
FEAT_FIFO_MERGE,
/* An unknown HW bug causing the normal FIFO thresholds not to work */
FEAT_OMAP3_DSI_FIFO_BUG,
FEAT_BURST_2D,
FEAT_DSI_PHY_DCC,
FEAT_MFLAG,
};
/* DSS register field id */
enum dss_feat_reg_field {
FEAT_REG_FIRHINC,
FEAT_REG_FIRVINC,
FEAT_REG_FIFOHIGHTHRESHOLD,
FEAT_REG_FIFOLOWTHRESHOLD,
FEAT_REG_FIFOSIZE,
FEAT_REG_HORIZONTALACCU,
FEAT_REG_VERTICALACCU,
FEAT_REG_DISPC_CLK_SWITCH,
};
enum dss_range_param {
FEAT_PARAM_DSS_FCK,
FEAT_PARAM_DSS_PCD,
FEAT_PARAM_DSIPLL_LPDIV,
FEAT_PARAM_DSI_FCK,
FEAT_PARAM_DOWNSCALE,
FEAT_PARAM_LINEWIDTH,
};
/* DSS Feature Functions */
unsigned long dss_feat_get_param_min(enum dss_range_param param);
unsigned long dss_feat_get_param_max(enum dss_range_param param);
enum omap_overlay_caps dss_feat_get_overlay_caps(enum omap_plane plane);
bool dss_feat_color_mode_supported(enum omap_plane plane,
enum omap_color_mode color_mode);
const char *dss_feat_get_clk_source_name(enum omap_dss_clk_source id);
u32 dss_feat_get_buffer_size_unit(void); /* in bytes */
u32 dss_feat_get_burst_size_unit(void); /* in bytes */
bool dss_feat_rotation_type_supported(enum omap_dss_rotation_type rot_type);
bool dss_has_feature(enum dss_feat_id id);
void dss_feat_get_reg_field(enum dss_feat_reg_field id, u8 *start, u8 *end);
void dss_features_init(enum omapdss_version version);
enum omap_display_type dss_feat_get_supported_displays(enum omap_channel channel);
enum omap_dss_output_id dss_feat_get_supported_outputs(enum omap_channel channel);
#endif

View File

@ -0,0 +1,370 @@
/*
* HDMI driver definition for TI OMAP4 Processor.
*
* Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _HDMI_H
#define _HDMI_H
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/hdmi.h>
#include <video/omapdss.h>
#include "dss.h"
/* HDMI Wrapper */
#define HDMI_WP_REVISION 0x0
#define HDMI_WP_SYSCONFIG 0x10
#define HDMI_WP_IRQSTATUS_RAW 0x24
#define HDMI_WP_IRQSTATUS 0x28
#define HDMI_WP_IRQENABLE_SET 0x2C
#define HDMI_WP_IRQENABLE_CLR 0x30
#define HDMI_WP_IRQWAKEEN 0x34
#define HDMI_WP_PWR_CTRL 0x40
#define HDMI_WP_DEBOUNCE 0x44
#define HDMI_WP_VIDEO_CFG 0x50
#define HDMI_WP_VIDEO_SIZE 0x60
#define HDMI_WP_VIDEO_TIMING_H 0x68
#define HDMI_WP_VIDEO_TIMING_V 0x6C
#define HDMI_WP_CLK 0x70
#define HDMI_WP_AUDIO_CFG 0x80
#define HDMI_WP_AUDIO_CFG2 0x84
#define HDMI_WP_AUDIO_CTRL 0x88
#define HDMI_WP_AUDIO_DATA 0x8C
/* HDMI WP IRQ flags */
#define HDMI_IRQ_CORE (1 << 0)
#define HDMI_IRQ_OCP_TIMEOUT (1 << 4)
#define HDMI_IRQ_AUDIO_FIFO_UNDERFLOW (1 << 8)
#define HDMI_IRQ_AUDIO_FIFO_OVERFLOW (1 << 9)
#define HDMI_IRQ_AUDIO_FIFO_SAMPLE_REQ (1 << 10)
#define HDMI_IRQ_VIDEO_VSYNC (1 << 16)
#define HDMI_IRQ_VIDEO_FRAME_DONE (1 << 17)
#define HDMI_IRQ_PHY_LINE5V_ASSERT (1 << 24)
#define HDMI_IRQ_LINK_CONNECT (1 << 25)
#define HDMI_IRQ_LINK_DISCONNECT (1 << 26)
#define HDMI_IRQ_PLL_LOCK (1 << 29)
#define HDMI_IRQ_PLL_UNLOCK (1 << 30)
#define HDMI_IRQ_PLL_RECAL (1 << 31)
/* HDMI PLL */
#define PLLCTRL_PLL_CONTROL 0x0
#define PLLCTRL_PLL_STATUS 0x4
#define PLLCTRL_PLL_GO 0x8
#define PLLCTRL_CFG1 0xC
#define PLLCTRL_CFG2 0x10
#define PLLCTRL_CFG3 0x14
#define PLLCTRL_SSC_CFG1 0x18
#define PLLCTRL_SSC_CFG2 0x1C
#define PLLCTRL_CFG4 0x20
/* HDMI PHY */
#define HDMI_TXPHY_TX_CTRL 0x0
#define HDMI_TXPHY_DIGITAL_CTRL 0x4
#define HDMI_TXPHY_POWER_CTRL 0x8
#define HDMI_TXPHY_PAD_CFG_CTRL 0xC
#define HDMI_TXPHY_BIST_CONTROL 0x1C
enum hdmi_pll_pwr {
HDMI_PLLPWRCMD_ALLOFF = 0,
HDMI_PLLPWRCMD_PLLONLY = 1,
HDMI_PLLPWRCMD_BOTHON_ALLCLKS = 2,
HDMI_PLLPWRCMD_BOTHON_NOPHYCLK = 3
};
enum hdmi_phy_pwr {
HDMI_PHYPWRCMD_OFF = 0,
HDMI_PHYPWRCMD_LDOON = 1,
HDMI_PHYPWRCMD_TXON = 2
};
enum hdmi_core_hdmi_dvi {
HDMI_DVI = 0,
HDMI_HDMI = 1
};
enum hdmi_packing_mode {
HDMI_PACK_10b_RGB_YUV444 = 0,
HDMI_PACK_24b_RGB_YUV444_YUV422 = 1,
HDMI_PACK_20b_YUV422 = 2,
HDMI_PACK_ALREADYPACKED = 7
};
enum hdmi_stereo_channels {
HDMI_AUDIO_STEREO_NOCHANNELS = 0,
HDMI_AUDIO_STEREO_ONECHANNEL = 1,
HDMI_AUDIO_STEREO_TWOCHANNELS = 2,
HDMI_AUDIO_STEREO_THREECHANNELS = 3,
HDMI_AUDIO_STEREO_FOURCHANNELS = 4
};
enum hdmi_audio_type {
HDMI_AUDIO_TYPE_LPCM = 0,
HDMI_AUDIO_TYPE_IEC = 1
};
enum hdmi_audio_justify {
HDMI_AUDIO_JUSTIFY_LEFT = 0,
HDMI_AUDIO_JUSTIFY_RIGHT = 1
};
enum hdmi_audio_sample_order {
HDMI_AUDIO_SAMPLE_RIGHT_FIRST = 0,
HDMI_AUDIO_SAMPLE_LEFT_FIRST = 1
};
enum hdmi_audio_samples_perword {
HDMI_AUDIO_ONEWORD_ONESAMPLE = 0,
HDMI_AUDIO_ONEWORD_TWOSAMPLES = 1
};
enum hdmi_audio_sample_size_omap {
HDMI_AUDIO_SAMPLE_16BITS = 0,
HDMI_AUDIO_SAMPLE_24BITS = 1
};
enum hdmi_audio_transf_mode {
HDMI_AUDIO_TRANSF_DMA = 0,
HDMI_AUDIO_TRANSF_IRQ = 1
};
enum hdmi_audio_blk_strt_end_sig {
HDMI_AUDIO_BLOCK_SIG_STARTEND_ON = 0,
HDMI_AUDIO_BLOCK_SIG_STARTEND_OFF = 1
};
enum hdmi_core_audio_layout {
HDMI_AUDIO_LAYOUT_2CH = 0,
HDMI_AUDIO_LAYOUT_8CH = 1,
HDMI_AUDIO_LAYOUT_6CH = 2
};
enum hdmi_core_cts_mode {
HDMI_AUDIO_CTS_MODE_HW = 0,
HDMI_AUDIO_CTS_MODE_SW = 1
};
enum hdmi_audio_mclk_mode {
HDMI_AUDIO_MCLK_128FS = 0,
HDMI_AUDIO_MCLK_256FS = 1,
HDMI_AUDIO_MCLK_384FS = 2,
HDMI_AUDIO_MCLK_512FS = 3,
HDMI_AUDIO_MCLK_768FS = 4,
HDMI_AUDIO_MCLK_1024FS = 5,
HDMI_AUDIO_MCLK_1152FS = 6,
HDMI_AUDIO_MCLK_192FS = 7
};
struct hdmi_video_format {
enum hdmi_packing_mode packing_mode;
u32 y_res; /* Line per panel */
u32 x_res; /* pixel per line */
};
struct hdmi_config {
struct omap_video_timings timings;
struct hdmi_avi_infoframe infoframe;
enum hdmi_core_hdmi_dvi hdmi_dvi_mode;
};
struct hdmi_audio_format {
enum hdmi_stereo_channels stereo_channels;
u8 active_chnnls_msk;
enum hdmi_audio_type type;
enum hdmi_audio_justify justification;
enum hdmi_audio_sample_order sample_order;
enum hdmi_audio_samples_perword samples_per_word;
enum hdmi_audio_sample_size_omap sample_size;
enum hdmi_audio_blk_strt_end_sig en_sig_blk_strt_end;
};
struct hdmi_audio_dma {
u8 transfer_size;
u8 block_size;
enum hdmi_audio_transf_mode mode;
u16 fifo_threshold;
};
struct hdmi_core_audio_i2s_config {
u8 in_length_bits;
u8 justification;
u8 sck_edge_mode;
u8 vbit;
u8 direction;
u8 shift;
u8 active_sds;
};
struct hdmi_core_audio_config {
struct hdmi_core_audio_i2s_config i2s_cfg;
struct snd_aes_iec958 *iec60958_cfg;
bool fs_override;
u32 n;
u32 cts;
u32 aud_par_busclk;
enum hdmi_core_audio_layout layout;
enum hdmi_core_cts_mode cts_mode;
bool use_mclk;
enum hdmi_audio_mclk_mode mclk_mode;
bool en_acr_pkt;
bool en_dsd_audio;
bool en_parallel_aud_input;
bool en_spdif;
};
struct hdmi_wp_data {
void __iomem *base;
phys_addr_t phys_base;
};
struct hdmi_pll_data {
struct dss_pll pll;
void __iomem *base;
struct hdmi_wp_data *wp;
};
struct hdmi_phy_data {
void __iomem *base;
u8 lane_function[4];
u8 lane_polarity[4];
};
struct hdmi_core_data {
void __iomem *base;
};
static inline void hdmi_write_reg(void __iomem *base_addr, const u32 idx,
u32 val)
{
__raw_writel(val, base_addr + idx);
}
static inline u32 hdmi_read_reg(void __iomem *base_addr, const u32 idx)
{
return __raw_readl(base_addr + idx);
}
#define REG_FLD_MOD(base, idx, val, start, end) \
hdmi_write_reg(base, idx, FLD_MOD(hdmi_read_reg(base, idx),\
val, start, end))
#define REG_GET(base, idx, start, end) \
FLD_GET(hdmi_read_reg(base, idx), start, end)
static inline int hdmi_wait_for_bit_change(void __iomem *base_addr,
const u32 idx, int b2, int b1, u32 val)
{
u32 t = 0, v;
while (val != (v = REG_GET(base_addr, idx, b2, b1))) {
if (t++ > 10000)
return v;
udelay(1);
}
return v;
}
/* HDMI wrapper funcs */
int hdmi_wp_video_start(struct hdmi_wp_data *wp);
void hdmi_wp_video_stop(struct hdmi_wp_data *wp);
void hdmi_wp_dump(struct hdmi_wp_data *wp, struct seq_file *s);
u32 hdmi_wp_get_irqstatus(struct hdmi_wp_data *wp);
void hdmi_wp_set_irqstatus(struct hdmi_wp_data *wp, u32 irqstatus);
void hdmi_wp_set_irqenable(struct hdmi_wp_data *wp, u32 mask);
void hdmi_wp_clear_irqenable(struct hdmi_wp_data *wp, u32 mask);
int hdmi_wp_set_phy_pwr(struct hdmi_wp_data *wp, enum hdmi_phy_pwr val);
int hdmi_wp_set_pll_pwr(struct hdmi_wp_data *wp, enum hdmi_pll_pwr val);
void hdmi_wp_video_config_format(struct hdmi_wp_data *wp,
struct hdmi_video_format *video_fmt);
void hdmi_wp_video_config_interface(struct hdmi_wp_data *wp,
struct omap_video_timings *timings);
void hdmi_wp_video_config_timing(struct hdmi_wp_data *wp,
struct omap_video_timings *timings);
void hdmi_wp_init_vid_fmt_timings(struct hdmi_video_format *video_fmt,
struct omap_video_timings *timings, struct hdmi_config *param);
int hdmi_wp_init(struct platform_device *pdev, struct hdmi_wp_data *wp);
phys_addr_t hdmi_wp_get_audio_dma_addr(struct hdmi_wp_data *wp);
/* HDMI PLL funcs */
void hdmi_pll_dump(struct hdmi_pll_data *pll, struct seq_file *s);
void hdmi_pll_compute(struct hdmi_pll_data *pll,
unsigned long target_tmds, struct dss_pll_clock_info *pi);
int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll,
struct hdmi_wp_data *wp);
void hdmi_pll_uninit(struct hdmi_pll_data *hpll);
/* HDMI PHY funcs */
int hdmi_phy_configure(struct hdmi_phy_data *phy, unsigned long hfbitclk,
unsigned long lfbitclk);
void hdmi_phy_dump(struct hdmi_phy_data *phy, struct seq_file *s);
int hdmi_phy_init(struct platform_device *pdev, struct hdmi_phy_data *phy);
int hdmi_phy_parse_lanes(struct hdmi_phy_data *phy, const u32 *lanes);
/* HDMI common funcs */
int hdmi_parse_lanes_of(struct platform_device *pdev, struct device_node *ep,
struct hdmi_phy_data *phy);
/* Audio funcs */
int hdmi_compute_acr(u32 pclk, u32 sample_freq, u32 *n, u32 *cts);
int hdmi_wp_audio_enable(struct hdmi_wp_data *wp, bool enable);
int hdmi_wp_audio_core_req_enable(struct hdmi_wp_data *wp, bool enable);
void hdmi_wp_audio_config_format(struct hdmi_wp_data *wp,
struct hdmi_audio_format *aud_fmt);
void hdmi_wp_audio_config_dma(struct hdmi_wp_data *wp,
struct hdmi_audio_dma *aud_dma);
static inline bool hdmi_mode_has_audio(struct hdmi_config *cfg)
{
return cfg->hdmi_dvi_mode == HDMI_HDMI ? true : false;
}
/* HDMI DRV data */
struct omap_hdmi {
struct mutex lock;
struct platform_device *pdev;
struct hdmi_wp_data wp;
struct hdmi_pll_data pll;
struct hdmi_phy_data phy;
struct hdmi_core_data core;
struct hdmi_config cfg;
struct regulator *vdda_reg;
bool core_enabled;
struct omap_dss_device output;
struct platform_device *audio_pdev;
void (*audio_abort_cb)(struct device *dev);
int wp_idlemode;
bool audio_configured;
struct omap_dss_audio audio_config;
/* This lock should be taken when booleans bellow are touched. */
spinlock_t audio_playing_lock;
bool audio_playing;
bool display_enabled;
};
#endif

View File

@ -0,0 +1,839 @@
/*
* HDMI interface DSS driver for TI's OMAP4 family of SoCs.
* Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/
* Authors: Yong Zhi
* Mythri pk <mythripk@ti.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define DSS_SUBSYS_NAME "HDMI"
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/clk.h>
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/component.h>
#include <video/omapdss.h>
#include <sound/omap-hdmi-audio.h>
#include "hdmi4_core.h"
#include "dss.h"
#include "dss_features.h"
#include "hdmi.h"
static struct omap_hdmi hdmi;
static int hdmi_runtime_get(void)
{
int r;
DSSDBG("hdmi_runtime_get\n");
r = pm_runtime_get_sync(&hdmi.pdev->dev);
WARN_ON(r < 0);
if (r < 0)
return r;
return 0;
}
static void hdmi_runtime_put(void)
{
int r;
DSSDBG("hdmi_runtime_put\n");
r = pm_runtime_put_sync(&hdmi.pdev->dev);
WARN_ON(r < 0 && r != -ENOSYS);
}
static irqreturn_t hdmi_irq_handler(int irq, void *data)
{
struct hdmi_wp_data *wp = data;
u32 irqstatus;
irqstatus = hdmi_wp_get_irqstatus(wp);
hdmi_wp_set_irqstatus(wp, irqstatus);
if ((irqstatus & HDMI_IRQ_LINK_CONNECT) &&
irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
/*
* If we get both connect and disconnect interrupts at the same
* time, turn off the PHY, clear interrupts, and restart, which
* raises connect interrupt if a cable is connected, or nothing
* if cable is not connected.
*/
hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF);
hdmi_wp_set_irqstatus(wp, HDMI_IRQ_LINK_CONNECT |
HDMI_IRQ_LINK_DISCONNECT);
hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
} else if (irqstatus & HDMI_IRQ_LINK_CONNECT) {
hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_TXON);
} else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
}
return IRQ_HANDLED;
}
static int hdmi_init_regulator(void)
{
int r;
struct regulator *reg;
if (hdmi.vdda_reg != NULL)
return 0;
reg = devm_regulator_get(&hdmi.pdev->dev, "vdda");
if (IS_ERR(reg)) {
if (PTR_ERR(reg) != -EPROBE_DEFER)
DSSERR("can't get VDDA regulator\n");
return PTR_ERR(reg);
}
if (regulator_can_change_voltage(reg)) {
r = regulator_set_voltage(reg, 1800000, 1800000);
if (r) {
devm_regulator_put(reg);
DSSWARN("can't set the regulator voltage\n");
return r;
}
}
hdmi.vdda_reg = reg;
return 0;
}
static int hdmi_power_on_core(struct omap_dss_device *dssdev)
{
int r;
r = regulator_enable(hdmi.vdda_reg);
if (r)
return r;
r = hdmi_runtime_get();
if (r)
goto err_runtime_get;
/* Make selection of HDMI in DSS */
dss_select_hdmi_venc_clk_source(DSS_HDMI_M_PCLK);
hdmi.core_enabled = true;
return 0;
err_runtime_get:
regulator_disable(hdmi.vdda_reg);
return r;
}
static void hdmi_power_off_core(struct omap_dss_device *dssdev)
{
hdmi.core_enabled = false;
hdmi_runtime_put();
regulator_disable(hdmi.vdda_reg);
}
static int hdmi_power_on_full(struct omap_dss_device *dssdev)
{
int r;
struct omap_video_timings *p;
struct omap_overlay_manager *mgr = hdmi.output.manager;
struct hdmi_wp_data *wp = &hdmi.wp;
struct dss_pll_clock_info hdmi_cinfo = { 0 };
r = hdmi_power_on_core(dssdev);
if (r)
return r;
/* disable and clear irqs */
hdmi_wp_clear_irqenable(wp, 0xffffffff);
hdmi_wp_set_irqstatus(wp, 0xffffffff);
p = &hdmi.cfg.timings;
DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res);
hdmi_pll_compute(&hdmi.pll, p->pixelclock, &hdmi_cinfo);
r = dss_pll_enable(&hdmi.pll.pll);
if (r) {
DSSERR("Failed to enable PLL\n");
goto err_pll_enable;
}
r = dss_pll_set_config(&hdmi.pll.pll, &hdmi_cinfo);
if (r) {
DSSERR("Failed to configure PLL\n");
goto err_pll_cfg;
}
r = hdmi_phy_configure(&hdmi.phy, hdmi_cinfo.clkdco,
hdmi_cinfo.clkout[0]);
if (r) {
DSSDBG("Failed to configure PHY\n");
goto err_phy_cfg;
}
r = hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
if (r)
goto err_phy_pwr;
hdmi4_configure(&hdmi.core, &hdmi.wp, &hdmi.cfg);
/* bypass TV gamma table */
dispc_enable_gamma_table(0);
/* tv size */
dss_mgr_set_timings(mgr, p);
r = hdmi_wp_video_start(&hdmi.wp);
if (r)
goto err_vid_enable;
r = dss_mgr_enable(mgr);
if (r)
goto err_mgr_enable;
hdmi_wp_set_irqenable(wp,
HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT);
return 0;
err_mgr_enable:
hdmi_wp_video_stop(&hdmi.wp);
err_vid_enable:
hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF);
err_phy_pwr:
err_phy_cfg:
err_pll_cfg:
dss_pll_disable(&hdmi.pll.pll);
err_pll_enable:
hdmi_power_off_core(dssdev);
return -EIO;
}
static void hdmi_power_off_full(struct omap_dss_device *dssdev)
{
struct omap_overlay_manager *mgr = hdmi.output.manager;
hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff);
dss_mgr_disable(mgr);
hdmi_wp_video_stop(&hdmi.wp);
hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF);
dss_pll_disable(&hdmi.pll.pll);
hdmi_power_off_core(dssdev);
}
static int hdmi_display_check_timing(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct omap_dss_device *out = &hdmi.output;
if (!dispc_mgr_timings_ok(out->dispc_channel, timings))
return -EINVAL;
return 0;
}
static void hdmi_display_set_timing(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
mutex_lock(&hdmi.lock);
hdmi.cfg.timings = *timings;
dispc_set_tv_pclk(timings->pixelclock);
mutex_unlock(&hdmi.lock);
}
static void hdmi_display_get_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
*timings = hdmi.cfg.timings;
}
static void hdmi_dump_regs(struct seq_file *s)
{
mutex_lock(&hdmi.lock);
if (hdmi_runtime_get()) {
mutex_unlock(&hdmi.lock);
return;
}
hdmi_wp_dump(&hdmi.wp, s);
hdmi_pll_dump(&hdmi.pll, s);
hdmi_phy_dump(&hdmi.phy, s);
hdmi4_core_dump(&hdmi.core, s);
hdmi_runtime_put();
mutex_unlock(&hdmi.lock);
}
static int read_edid(u8 *buf, int len)
{
int r;
mutex_lock(&hdmi.lock);
r = hdmi_runtime_get();
BUG_ON(r);
r = hdmi4_read_edid(&hdmi.core, buf, len);
hdmi_runtime_put();
mutex_unlock(&hdmi.lock);
return r;
}
static void hdmi_start_audio_stream(struct omap_hdmi *hd)
{
hdmi_wp_audio_enable(&hd->wp, true);
hdmi4_audio_start(&hd->core, &hd->wp);
}
static void hdmi_stop_audio_stream(struct omap_hdmi *hd)
{
hdmi4_audio_stop(&hd->core, &hd->wp);
hdmi_wp_audio_enable(&hd->wp, false);
}
static int hdmi_display_enable(struct omap_dss_device *dssdev)
{
struct omap_dss_device *out = &hdmi.output;
unsigned long flags;
int r = 0;
DSSDBG("ENTER hdmi_display_enable\n");
mutex_lock(&hdmi.lock);
if (out->manager == NULL) {
DSSERR("failed to enable display: no output/manager\n");
r = -ENODEV;
goto err0;
}
r = hdmi_power_on_full(dssdev);
if (r) {
DSSERR("failed to power on device\n");
goto err0;
}
if (hdmi.audio_configured) {
r = hdmi4_audio_config(&hdmi.core, &hdmi.wp, &hdmi.audio_config,
hdmi.cfg.timings.pixelclock);
if (r) {
DSSERR("Error restoring audio configuration: %d", r);
hdmi.audio_abort_cb(&hdmi.pdev->dev);
hdmi.audio_configured = false;
}
}
spin_lock_irqsave(&hdmi.audio_playing_lock, flags);
if (hdmi.audio_configured && hdmi.audio_playing)
hdmi_start_audio_stream(&hdmi);
hdmi.display_enabled = true;
spin_unlock_irqrestore(&hdmi.audio_playing_lock, flags);
mutex_unlock(&hdmi.lock);
return 0;
err0:
mutex_unlock(&hdmi.lock);
return r;
}
static void hdmi_display_disable(struct omap_dss_device *dssdev)
{
unsigned long flags;
DSSDBG("Enter hdmi_display_disable\n");
mutex_lock(&hdmi.lock);
spin_lock_irqsave(&hdmi.audio_playing_lock, flags);
hdmi_stop_audio_stream(&hdmi);
hdmi.display_enabled = false;
spin_unlock_irqrestore(&hdmi.audio_playing_lock, flags);
hdmi_power_off_full(dssdev);
mutex_unlock(&hdmi.lock);
}
static int hdmi_core_enable(struct omap_dss_device *dssdev)
{
int r = 0;
DSSDBG("ENTER omapdss_hdmi_core_enable\n");
mutex_lock(&hdmi.lock);
r = hdmi_power_on_core(dssdev);
if (r) {
DSSERR("failed to power on device\n");
goto err0;
}
mutex_unlock(&hdmi.lock);
return 0;
err0:
mutex_unlock(&hdmi.lock);
return r;
}
static void hdmi_core_disable(struct omap_dss_device *dssdev)
{
DSSDBG("Enter omapdss_hdmi_core_disable\n");
mutex_lock(&hdmi.lock);
hdmi_power_off_core(dssdev);
mutex_unlock(&hdmi.lock);
}
static int hdmi_connect(struct omap_dss_device *dssdev,
struct omap_dss_device *dst)
{
struct omap_overlay_manager *mgr;
int r;
r = hdmi_init_regulator();
if (r)
return r;
mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel);
if (!mgr)
return -ENODEV;
r = dss_mgr_connect(mgr, dssdev);
if (r)
return r;
r = omapdss_output_set_device(dssdev, dst);
if (r) {
DSSERR("failed to connect output to new device: %s\n",
dst->name);
dss_mgr_disconnect(mgr, dssdev);
return r;
}
return 0;
}
static void hdmi_disconnect(struct omap_dss_device *dssdev,
struct omap_dss_device *dst)
{
WARN_ON(dst != dssdev->dst);
if (dst != dssdev->dst)
return;
omapdss_output_unset_device(dssdev);
if (dssdev->manager)
dss_mgr_disconnect(dssdev->manager, dssdev);
}
static int hdmi_read_edid(struct omap_dss_device *dssdev,
u8 *edid, int len)
{
bool need_enable;
int r;
need_enable = hdmi.core_enabled == false;
if (need_enable) {
r = hdmi_core_enable(dssdev);
if (r)
return r;
}
r = read_edid(edid, len);
if (need_enable)
hdmi_core_disable(dssdev);
return r;
}
static int hdmi_set_infoframe(struct omap_dss_device *dssdev,
const struct hdmi_avi_infoframe *avi)
{
hdmi.cfg.infoframe = *avi;
return 0;
}
static int hdmi_set_hdmi_mode(struct omap_dss_device *dssdev,
bool hdmi_mode)
{
hdmi.cfg.hdmi_dvi_mode = hdmi_mode ? HDMI_HDMI : HDMI_DVI;
return 0;
}
static const struct omapdss_hdmi_ops hdmi_ops = {
.connect = hdmi_connect,
.disconnect = hdmi_disconnect,
.enable = hdmi_display_enable,
.disable = hdmi_display_disable,
.check_timings = hdmi_display_check_timing,
.set_timings = hdmi_display_set_timing,
.get_timings = hdmi_display_get_timings,
.read_edid = hdmi_read_edid,
.set_infoframe = hdmi_set_infoframe,
.set_hdmi_mode = hdmi_set_hdmi_mode,
};
static void hdmi_init_output(struct platform_device *pdev)
{
struct omap_dss_device *out = &hdmi.output;
out->dev = &pdev->dev;
out->id = OMAP_DSS_OUTPUT_HDMI;
out->output_type = OMAP_DISPLAY_TYPE_HDMI;
out->name = "hdmi.0";
out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT;
out->ops.hdmi = &hdmi_ops;
out->owner = THIS_MODULE;
omapdss_register_output(out);
}
static void hdmi_uninit_output(struct platform_device *pdev)
{
struct omap_dss_device *out = &hdmi.output;
omapdss_unregister_output(out);
}
static int hdmi_probe_of(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct device_node *ep;
int r;
ep = omapdss_of_get_first_endpoint(node);
if (!ep)
return 0;
r = hdmi_parse_lanes_of(pdev, ep, &hdmi.phy);
if (r)
goto err;
of_node_put(ep);
return 0;
err:
of_node_put(ep);
return r;
}
/* Audio callbacks */
static int hdmi_audio_startup(struct device *dev,
void (*abort_cb)(struct device *dev))
{
struct omap_hdmi *hd = dev_get_drvdata(dev);
int ret = 0;
mutex_lock(&hd->lock);
if (!hdmi_mode_has_audio(&hd->cfg) || !hd->display_enabled) {
ret = -EPERM;
goto out;
}
hd->audio_abort_cb = abort_cb;
out:
mutex_unlock(&hd->lock);
return ret;
}
static int hdmi_audio_shutdown(struct device *dev)
{
struct omap_hdmi *hd = dev_get_drvdata(dev);
mutex_lock(&hd->lock);
hd->audio_abort_cb = NULL;
hd->audio_configured = false;
hd->audio_playing = false;
mutex_unlock(&hd->lock);
return 0;
}
static int hdmi_audio_start(struct device *dev)
{
struct omap_hdmi *hd = dev_get_drvdata(dev);
unsigned long flags;
WARN_ON(!hdmi_mode_has_audio(&hd->cfg));
spin_lock_irqsave(&hd->audio_playing_lock, flags);
if (hd->display_enabled)
hdmi_start_audio_stream(hd);
hd->audio_playing = true;
spin_unlock_irqrestore(&hd->audio_playing_lock, flags);
return 0;
}
static void hdmi_audio_stop(struct device *dev)
{
struct omap_hdmi *hd = dev_get_drvdata(dev);
unsigned long flags;
WARN_ON(!hdmi_mode_has_audio(&hd->cfg));
spin_lock_irqsave(&hd->audio_playing_lock, flags);
if (hd->display_enabled)
hdmi_stop_audio_stream(hd);
hd->audio_playing = false;
spin_unlock_irqrestore(&hd->audio_playing_lock, flags);
}
static int hdmi_audio_config(struct device *dev,
struct omap_dss_audio *dss_audio)
{
struct omap_hdmi *hd = dev_get_drvdata(dev);
int ret;
mutex_lock(&hd->lock);
if (!hdmi_mode_has_audio(&hd->cfg) || !hd->display_enabled) {
ret = -EPERM;
goto out;
}
ret = hdmi4_audio_config(&hd->core, &hd->wp, dss_audio,
hd->cfg.timings.pixelclock);
if (!ret) {
hd->audio_configured = true;
hd->audio_config = *dss_audio;
}
out:
mutex_unlock(&hd->lock);
return ret;
}
static const struct omap_hdmi_audio_ops hdmi_audio_ops = {
.audio_startup = hdmi_audio_startup,
.audio_shutdown = hdmi_audio_shutdown,
.audio_start = hdmi_audio_start,
.audio_stop = hdmi_audio_stop,
.audio_config = hdmi_audio_config,
};
static int hdmi_audio_register(struct device *dev)
{
struct omap_hdmi_audio_pdata pdata = {
.dev = dev,
.dss_version = omapdss_get_version(),
.audio_dma_addr = hdmi_wp_get_audio_dma_addr(&hdmi.wp),
.ops = &hdmi_audio_ops,
};
hdmi.audio_pdev = platform_device_register_data(
dev, "omap-hdmi-audio", PLATFORM_DEVID_AUTO,
&pdata, sizeof(pdata));
if (IS_ERR(hdmi.audio_pdev))
return PTR_ERR(hdmi.audio_pdev);
return 0;
}
/* HDMI HW IP initialisation */
static int hdmi4_bind(struct device *dev, struct device *master, void *data)
{
struct platform_device *pdev = to_platform_device(dev);
int r;
int irq;
hdmi.pdev = pdev;
dev_set_drvdata(&pdev->dev, &hdmi);
mutex_init(&hdmi.lock);
spin_lock_init(&hdmi.audio_playing_lock);
if (pdev->dev.of_node) {
r = hdmi_probe_of(pdev);
if (r)
return r;
}
r = hdmi_wp_init(pdev, &hdmi.wp);
if (r)
return r;
r = hdmi_pll_init(pdev, &hdmi.pll, &hdmi.wp);
if (r)
return r;
r = hdmi_phy_init(pdev, &hdmi.phy);
if (r)
goto err;
r = hdmi4_core_init(pdev, &hdmi.core);
if (r)
goto err;
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
DSSERR("platform_get_irq failed\n");
r = -ENODEV;
goto err;
}
r = devm_request_threaded_irq(&pdev->dev, irq,
NULL, hdmi_irq_handler,
IRQF_ONESHOT, "OMAP HDMI", &hdmi.wp);
if (r) {
DSSERR("HDMI IRQ request failed\n");
goto err;
}
pm_runtime_enable(&pdev->dev);
hdmi_init_output(pdev);
r = hdmi_audio_register(&pdev->dev);
if (r) {
DSSERR("Registering HDMI audio failed\n");
hdmi_uninit_output(pdev);
pm_runtime_disable(&pdev->dev);
return r;
}
dss_debugfs_create_file("hdmi", hdmi_dump_regs);
return 0;
err:
hdmi_pll_uninit(&hdmi.pll);
return r;
}
static void hdmi4_unbind(struct device *dev, struct device *master, void *data)
{
struct platform_device *pdev = to_platform_device(dev);
if (hdmi.audio_pdev)
platform_device_unregister(hdmi.audio_pdev);
hdmi_uninit_output(pdev);
hdmi_pll_uninit(&hdmi.pll);
pm_runtime_disable(&pdev->dev);
}
static const struct component_ops hdmi4_component_ops = {
.bind = hdmi4_bind,
.unbind = hdmi4_unbind,
};
static int hdmi4_probe(struct platform_device *pdev)
{
return component_add(&pdev->dev, &hdmi4_component_ops);
}
static int hdmi4_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &hdmi4_component_ops);
return 0;
}
static int hdmi_runtime_suspend(struct device *dev)
{
dispc_runtime_put();
return 0;
}
static int hdmi_runtime_resume(struct device *dev)
{
int r;
r = dispc_runtime_get();
if (r < 0)
return r;
return 0;
}
static const struct dev_pm_ops hdmi_pm_ops = {
.runtime_suspend = hdmi_runtime_suspend,
.runtime_resume = hdmi_runtime_resume,
};
static const struct of_device_id hdmi_of_match[] = {
{ .compatible = "ti,omap4-hdmi", },
{},
};
static struct platform_driver omapdss_hdmihw_driver = {
.probe = hdmi4_probe,
.remove = hdmi4_remove,
.driver = {
.name = "omapdss_hdmi",
.pm = &hdmi_pm_ops,
.of_match_table = hdmi_of_match,
.suppress_bind_attrs = true,
},
};
int __init hdmi4_init_platform_driver(void)
{
return platform_driver_register(&omapdss_hdmihw_driver);
}
void hdmi4_uninit_platform_driver(void)
{
platform_driver_unregister(&omapdss_hdmihw_driver);
}

View File

@ -0,0 +1,904 @@
/*
* ti_hdmi_4xxx_ip.c
*
* HDMI TI81xx, TI38xx, TI OMAP4 etc IP driver Library
* Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/
* Authors: Yong Zhi
* Mythri pk <mythripk@ti.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define DSS_SUBSYS_NAME "HDMICORE"
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/string.h>
#include <linux/seq_file.h>
#include <sound/asound.h>
#include <sound/asoundef.h>
#include "hdmi4_core.h"
#include "dss_features.h"
#define HDMI_CORE_AV 0x500
static inline void __iomem *hdmi_av_base(struct hdmi_core_data *core)
{
return core->base + HDMI_CORE_AV;
}
static int hdmi_core_ddc_init(struct hdmi_core_data *core)
{
void __iomem *base = core->base;
/* Turn on CLK for DDC */
REG_FLD_MOD(base, HDMI_CORE_AV_DPD, 0x7, 2, 0);
/* IN_PROG */
if (REG_GET(base, HDMI_CORE_DDC_STATUS, 4, 4) == 1) {
/* Abort transaction */
REG_FLD_MOD(base, HDMI_CORE_DDC_CMD, 0xf, 3, 0);
/* IN_PROG */
if (hdmi_wait_for_bit_change(base, HDMI_CORE_DDC_STATUS,
4, 4, 0) != 0) {
DSSERR("Timeout aborting DDC transaction\n");
return -ETIMEDOUT;
}
}
/* Clk SCL Devices */
REG_FLD_MOD(base, HDMI_CORE_DDC_CMD, 0xA, 3, 0);
/* HDMI_CORE_DDC_STATUS_IN_PROG */
if (hdmi_wait_for_bit_change(base, HDMI_CORE_DDC_STATUS,
4, 4, 0) != 0) {
DSSERR("Timeout starting SCL clock\n");
return -ETIMEDOUT;
}
/* Clear FIFO */
REG_FLD_MOD(base, HDMI_CORE_DDC_CMD, 0x9, 3, 0);
/* HDMI_CORE_DDC_STATUS_IN_PROG */
if (hdmi_wait_for_bit_change(base, HDMI_CORE_DDC_STATUS,
4, 4, 0) != 0) {
DSSERR("Timeout clearing DDC fifo\n");
return -ETIMEDOUT;
}
return 0;
}
static int hdmi_core_ddc_edid(struct hdmi_core_data *core,
u8 *pedid, int ext)
{
void __iomem *base = core->base;
u32 i;
char checksum;
u32 offset = 0;
/* HDMI_CORE_DDC_STATUS_IN_PROG */
if (hdmi_wait_for_bit_change(base, HDMI_CORE_DDC_STATUS,
4, 4, 0) != 0) {
DSSERR("Timeout waiting DDC to be ready\n");
return -ETIMEDOUT;
}
if (ext % 2 != 0)
offset = 0x80;
/* Load Segment Address Register */
REG_FLD_MOD(base, HDMI_CORE_DDC_SEGM, ext / 2, 7, 0);
/* Load Slave Address Register */
REG_FLD_MOD(base, HDMI_CORE_DDC_ADDR, 0xA0 >> 1, 7, 1);
/* Load Offset Address Register */
REG_FLD_MOD(base, HDMI_CORE_DDC_OFFSET, offset, 7, 0);
/* Load Byte Count */
REG_FLD_MOD(base, HDMI_CORE_DDC_COUNT1, 0x80, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_DDC_COUNT2, 0x0, 1, 0);
/* Set DDC_CMD */
if (ext)
REG_FLD_MOD(base, HDMI_CORE_DDC_CMD, 0x4, 3, 0);
else
REG_FLD_MOD(base, HDMI_CORE_DDC_CMD, 0x2, 3, 0);
/* HDMI_CORE_DDC_STATUS_BUS_LOW */
if (REG_GET(base, HDMI_CORE_DDC_STATUS, 6, 6) == 1) {
DSSERR("I2C Bus Low?\n");
return -EIO;
}
/* HDMI_CORE_DDC_STATUS_NO_ACK */
if (REG_GET(base, HDMI_CORE_DDC_STATUS, 5, 5) == 1) {
DSSERR("I2C No Ack\n");
return -EIO;
}
for (i = 0; i < 0x80; ++i) {
int t;
/* IN_PROG */
if (REG_GET(base, HDMI_CORE_DDC_STATUS, 4, 4) == 0) {
DSSERR("operation stopped when reading edid\n");
return -EIO;
}
t = 0;
/* FIFO_EMPTY */
while (REG_GET(base, HDMI_CORE_DDC_STATUS, 2, 2) == 1) {
if (t++ > 10000) {
DSSERR("timeout reading edid\n");
return -ETIMEDOUT;
}
udelay(1);
}
pedid[i] = REG_GET(base, HDMI_CORE_DDC_DATA, 7, 0);
}
checksum = 0;
for (i = 0; i < 0x80; ++i)
checksum += pedid[i];
if (checksum != 0) {
DSSERR("E-EDID checksum failed!!\n");
return -EIO;
}
return 0;
}
int hdmi4_read_edid(struct hdmi_core_data *core, u8 *edid, int len)
{
int r, l;
if (len < 128)
return -EINVAL;
r = hdmi_core_ddc_init(core);
if (r)
return r;
r = hdmi_core_ddc_edid(core, edid, 0);
if (r)
return r;
l = 128;
if (len >= 128 * 2 && edid[0x7e] > 0) {
r = hdmi_core_ddc_edid(core, edid + 0x80, 1);
if (r)
return r;
l += 128;
}
return l;
}
static void hdmi_core_init(struct hdmi_core_video_config *video_cfg)
{
DSSDBG("Enter hdmi_core_init\n");
/* video core */
video_cfg->ip_bus_width = HDMI_INPUT_8BIT;
video_cfg->op_dither_truc = HDMI_OUTPUTTRUNCATION_8BIT;
video_cfg->deep_color_pkt = HDMI_DEEPCOLORPACKECTDISABLE;
video_cfg->pkt_mode = HDMI_PACKETMODERESERVEDVALUE;
video_cfg->hdmi_dvi = HDMI_DVI;
video_cfg->tclk_sel_clkmult = HDMI_FPLL10IDCK;
}
static void hdmi_core_powerdown_disable(struct hdmi_core_data *core)
{
DSSDBG("Enter hdmi_core_powerdown_disable\n");
REG_FLD_MOD(core->base, HDMI_CORE_SYS_SYS_CTRL1, 0x0, 0, 0);
}
static void hdmi_core_swreset_release(struct hdmi_core_data *core)
{
DSSDBG("Enter hdmi_core_swreset_release\n");
REG_FLD_MOD(core->base, HDMI_CORE_SYS_SRST, 0x0, 0, 0);
}
static void hdmi_core_swreset_assert(struct hdmi_core_data *core)
{
DSSDBG("Enter hdmi_core_swreset_assert\n");
REG_FLD_MOD(core->base, HDMI_CORE_SYS_SRST, 0x1, 0, 0);
}
/* HDMI_CORE_VIDEO_CONFIG */
static void hdmi_core_video_config(struct hdmi_core_data *core,
struct hdmi_core_video_config *cfg)
{
u32 r = 0;
void __iomem *core_sys_base = core->base;
void __iomem *core_av_base = hdmi_av_base(core);
/* sys_ctrl1 default configuration not tunable */
r = hdmi_read_reg(core_sys_base, HDMI_CORE_SYS_SYS_CTRL1);
r = FLD_MOD(r, HDMI_CORE_SYS_SYS_CTRL1_VEN_FOLLOWVSYNC, 5, 5);
r = FLD_MOD(r, HDMI_CORE_SYS_SYS_CTRL1_HEN_FOLLOWHSYNC, 4, 4);
r = FLD_MOD(r, HDMI_CORE_SYS_SYS_CTRL1_BSEL_24BITBUS, 2, 2);
r = FLD_MOD(r, HDMI_CORE_SYS_SYS_CTRL1_EDGE_RISINGEDGE, 1, 1);
hdmi_write_reg(core_sys_base, HDMI_CORE_SYS_SYS_CTRL1, r);
REG_FLD_MOD(core_sys_base,
HDMI_CORE_SYS_VID_ACEN, cfg->ip_bus_width, 7, 6);
/* Vid_Mode */
r = hdmi_read_reg(core_sys_base, HDMI_CORE_SYS_VID_MODE);
/* dither truncation configuration */
if (cfg->op_dither_truc > HDMI_OUTPUTTRUNCATION_12BIT) {
r = FLD_MOD(r, cfg->op_dither_truc - 3, 7, 6);
r = FLD_MOD(r, 1, 5, 5);
} else {
r = FLD_MOD(r, cfg->op_dither_truc, 7, 6);
r = FLD_MOD(r, 0, 5, 5);
}
hdmi_write_reg(core_sys_base, HDMI_CORE_SYS_VID_MODE, r);
/* HDMI_Ctrl */
r = hdmi_read_reg(core_av_base, HDMI_CORE_AV_HDMI_CTRL);
r = FLD_MOD(r, cfg->deep_color_pkt, 6, 6);
r = FLD_MOD(r, cfg->pkt_mode, 5, 3);
r = FLD_MOD(r, cfg->hdmi_dvi, 0, 0);
hdmi_write_reg(core_av_base, HDMI_CORE_AV_HDMI_CTRL, r);
/* TMDS_CTRL */
REG_FLD_MOD(core_sys_base,
HDMI_CORE_SYS_TMDS_CTRL, cfg->tclk_sel_clkmult, 6, 5);
}
static void hdmi_core_write_avi_infoframe(struct hdmi_core_data *core,
struct hdmi_avi_infoframe *frame)
{
void __iomem *av_base = hdmi_av_base(core);
u8 data[HDMI_INFOFRAME_SIZE(AVI)];
int i;
hdmi_avi_infoframe_pack(frame, data, sizeof(data));
print_hex_dump_debug("AVI: ", DUMP_PREFIX_NONE, 16, 1, data,
HDMI_INFOFRAME_SIZE(AVI), false);
for (i = 0; i < sizeof(data); ++i) {
hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_BASE + i * 4,
data[i]);
}
}
static void hdmi_core_av_packet_config(struct hdmi_core_data *core,
struct hdmi_core_packet_enable_repeat repeat_cfg)
{
/* enable/repeat the infoframe */
hdmi_write_reg(hdmi_av_base(core), HDMI_CORE_AV_PB_CTRL1,
(repeat_cfg.audio_pkt << 5) |
(repeat_cfg.audio_pkt_repeat << 4) |
(repeat_cfg.avi_infoframe << 1) |
(repeat_cfg.avi_infoframe_repeat));
/* enable/repeat the packet */
hdmi_write_reg(hdmi_av_base(core), HDMI_CORE_AV_PB_CTRL2,
(repeat_cfg.gen_cntrl_pkt << 3) |
(repeat_cfg.gen_cntrl_pkt_repeat << 2) |
(repeat_cfg.generic_pkt << 1) |
(repeat_cfg.generic_pkt_repeat));
}
void hdmi4_configure(struct hdmi_core_data *core,
struct hdmi_wp_data *wp, struct hdmi_config *cfg)
{
/* HDMI */
struct omap_video_timings video_timing;
struct hdmi_video_format video_format;
/* HDMI core */
struct hdmi_core_video_config v_core_cfg;
struct hdmi_core_packet_enable_repeat repeat_cfg = { 0 };
hdmi_core_init(&v_core_cfg);
hdmi_wp_init_vid_fmt_timings(&video_format, &video_timing, cfg);
hdmi_wp_video_config_timing(wp, &video_timing);
/* video config */
video_format.packing_mode = HDMI_PACK_24b_RGB_YUV444_YUV422;
hdmi_wp_video_config_format(wp, &video_format);
hdmi_wp_video_config_interface(wp, &video_timing);
/*
* configure core video part
* set software reset in the core
*/
hdmi_core_swreset_assert(core);
/* power down off */
hdmi_core_powerdown_disable(core);
v_core_cfg.pkt_mode = HDMI_PACKETMODE24BITPERPIXEL;
v_core_cfg.hdmi_dvi = cfg->hdmi_dvi_mode;
hdmi_core_video_config(core, &v_core_cfg);
/* release software reset in the core */
hdmi_core_swreset_release(core);
if (cfg->hdmi_dvi_mode == HDMI_HDMI) {
hdmi_core_write_avi_infoframe(core, &cfg->infoframe);
/* enable/repeat the infoframe */
repeat_cfg.avi_infoframe = HDMI_PACKETENABLE;
repeat_cfg.avi_infoframe_repeat = HDMI_PACKETREPEATON;
/* wakeup */
repeat_cfg.audio_pkt = HDMI_PACKETENABLE;
repeat_cfg.audio_pkt_repeat = HDMI_PACKETREPEATON;
}
hdmi_core_av_packet_config(core, repeat_cfg);
}
void hdmi4_core_dump(struct hdmi_core_data *core, struct seq_file *s)
{
int i;
#define CORE_REG(i, name) name(i)
#define DUMPCORE(r) seq_printf(s, "%-35s %08x\n", #r,\
hdmi_read_reg(core->base, r))
#define DUMPCOREAV(r) seq_printf(s, "%-35s %08x\n", #r,\
hdmi_read_reg(hdmi_av_base(core), r))
#define DUMPCOREAV2(i, r) seq_printf(s, "%s[%d]%*s %08x\n", #r, i, \
(i < 10) ? 32 - (int)strlen(#r) : 31 - (int)strlen(#r), " ", \
hdmi_read_reg(hdmi_av_base(core), CORE_REG(i, r)))
DUMPCORE(HDMI_CORE_SYS_VND_IDL);
DUMPCORE(HDMI_CORE_SYS_DEV_IDL);
DUMPCORE(HDMI_CORE_SYS_DEV_IDH);
DUMPCORE(HDMI_CORE_SYS_DEV_REV);
DUMPCORE(HDMI_CORE_SYS_SRST);
DUMPCORE(HDMI_CORE_SYS_SYS_CTRL1);
DUMPCORE(HDMI_CORE_SYS_SYS_STAT);
DUMPCORE(HDMI_CORE_SYS_SYS_CTRL3);
DUMPCORE(HDMI_CORE_SYS_DE_DLY);
DUMPCORE(HDMI_CORE_SYS_DE_CTRL);
DUMPCORE(HDMI_CORE_SYS_DE_TOP);
DUMPCORE(HDMI_CORE_SYS_DE_CNTL);
DUMPCORE(HDMI_CORE_SYS_DE_CNTH);
DUMPCORE(HDMI_CORE_SYS_DE_LINL);
DUMPCORE(HDMI_CORE_SYS_DE_LINH_1);
DUMPCORE(HDMI_CORE_SYS_HRES_L);
DUMPCORE(HDMI_CORE_SYS_HRES_H);
DUMPCORE(HDMI_CORE_SYS_VRES_L);
DUMPCORE(HDMI_CORE_SYS_VRES_H);
DUMPCORE(HDMI_CORE_SYS_IADJUST);
DUMPCORE(HDMI_CORE_SYS_POLDETECT);
DUMPCORE(HDMI_CORE_SYS_HWIDTH1);
DUMPCORE(HDMI_CORE_SYS_HWIDTH2);
DUMPCORE(HDMI_CORE_SYS_VWIDTH);
DUMPCORE(HDMI_CORE_SYS_VID_CTRL);
DUMPCORE(HDMI_CORE_SYS_VID_ACEN);
DUMPCORE(HDMI_CORE_SYS_VID_MODE);
DUMPCORE(HDMI_CORE_SYS_VID_BLANK1);
DUMPCORE(HDMI_CORE_SYS_VID_BLANK3);
DUMPCORE(HDMI_CORE_SYS_VID_BLANK1);
DUMPCORE(HDMI_CORE_SYS_DC_HEADER);
DUMPCORE(HDMI_CORE_SYS_VID_DITHER);
DUMPCORE(HDMI_CORE_SYS_RGB2XVYCC_CT);
DUMPCORE(HDMI_CORE_SYS_R2Y_COEFF_LOW);
DUMPCORE(HDMI_CORE_SYS_R2Y_COEFF_UP);
DUMPCORE(HDMI_CORE_SYS_G2Y_COEFF_LOW);
DUMPCORE(HDMI_CORE_SYS_G2Y_COEFF_UP);
DUMPCORE(HDMI_CORE_SYS_B2Y_COEFF_LOW);
DUMPCORE(HDMI_CORE_SYS_B2Y_COEFF_UP);
DUMPCORE(HDMI_CORE_SYS_R2CB_COEFF_LOW);
DUMPCORE(HDMI_CORE_SYS_R2CB_COEFF_UP);
DUMPCORE(HDMI_CORE_SYS_G2CB_COEFF_LOW);
DUMPCORE(HDMI_CORE_SYS_G2CB_COEFF_UP);
DUMPCORE(HDMI_CORE_SYS_B2CB_COEFF_LOW);
DUMPCORE(HDMI_CORE_SYS_B2CB_COEFF_UP);
DUMPCORE(HDMI_CORE_SYS_R2CR_COEFF_LOW);
DUMPCORE(HDMI_CORE_SYS_R2CR_COEFF_UP);
DUMPCORE(HDMI_CORE_SYS_G2CR_COEFF_LOW);
DUMPCORE(HDMI_CORE_SYS_G2CR_COEFF_UP);
DUMPCORE(HDMI_CORE_SYS_B2CR_COEFF_LOW);
DUMPCORE(HDMI_CORE_SYS_B2CR_COEFF_UP);
DUMPCORE(HDMI_CORE_SYS_RGB_OFFSET_LOW);
DUMPCORE(HDMI_CORE_SYS_RGB_OFFSET_UP);
DUMPCORE(HDMI_CORE_SYS_Y_OFFSET_LOW);
DUMPCORE(HDMI_CORE_SYS_Y_OFFSET_UP);
DUMPCORE(HDMI_CORE_SYS_CBCR_OFFSET_LOW);
DUMPCORE(HDMI_CORE_SYS_CBCR_OFFSET_UP);
DUMPCORE(HDMI_CORE_SYS_INTR_STATE);
DUMPCORE(HDMI_CORE_SYS_INTR1);
DUMPCORE(HDMI_CORE_SYS_INTR2);
DUMPCORE(HDMI_CORE_SYS_INTR3);
DUMPCORE(HDMI_CORE_SYS_INTR4);
DUMPCORE(HDMI_CORE_SYS_INTR_UNMASK1);
DUMPCORE(HDMI_CORE_SYS_INTR_UNMASK2);
DUMPCORE(HDMI_CORE_SYS_INTR_UNMASK3);
DUMPCORE(HDMI_CORE_SYS_INTR_UNMASK4);
DUMPCORE(HDMI_CORE_SYS_INTR_CTRL);
DUMPCORE(HDMI_CORE_SYS_TMDS_CTRL);
DUMPCORE(HDMI_CORE_DDC_ADDR);
DUMPCORE(HDMI_CORE_DDC_SEGM);
DUMPCORE(HDMI_CORE_DDC_OFFSET);
DUMPCORE(HDMI_CORE_DDC_COUNT1);
DUMPCORE(HDMI_CORE_DDC_COUNT2);
DUMPCORE(HDMI_CORE_DDC_STATUS);
DUMPCORE(HDMI_CORE_DDC_CMD);
DUMPCORE(HDMI_CORE_DDC_DATA);
DUMPCOREAV(HDMI_CORE_AV_ACR_CTRL);
DUMPCOREAV(HDMI_CORE_AV_FREQ_SVAL);
DUMPCOREAV(HDMI_CORE_AV_N_SVAL1);
DUMPCOREAV(HDMI_CORE_AV_N_SVAL2);
DUMPCOREAV(HDMI_CORE_AV_N_SVAL3);
DUMPCOREAV(HDMI_CORE_AV_CTS_SVAL1);
DUMPCOREAV(HDMI_CORE_AV_CTS_SVAL2);
DUMPCOREAV(HDMI_CORE_AV_CTS_SVAL3);
DUMPCOREAV(HDMI_CORE_AV_CTS_HVAL1);
DUMPCOREAV(HDMI_CORE_AV_CTS_HVAL2);
DUMPCOREAV(HDMI_CORE_AV_CTS_HVAL3);
DUMPCOREAV(HDMI_CORE_AV_AUD_MODE);
DUMPCOREAV(HDMI_CORE_AV_SPDIF_CTRL);
DUMPCOREAV(HDMI_CORE_AV_HW_SPDIF_FS);
DUMPCOREAV(HDMI_CORE_AV_SWAP_I2S);
DUMPCOREAV(HDMI_CORE_AV_SPDIF_ERTH);
DUMPCOREAV(HDMI_CORE_AV_I2S_IN_MAP);
DUMPCOREAV(HDMI_CORE_AV_I2S_IN_CTRL);
DUMPCOREAV(HDMI_CORE_AV_I2S_CHST0);
DUMPCOREAV(HDMI_CORE_AV_I2S_CHST1);
DUMPCOREAV(HDMI_CORE_AV_I2S_CHST2);
DUMPCOREAV(HDMI_CORE_AV_I2S_CHST4);
DUMPCOREAV(HDMI_CORE_AV_I2S_CHST5);
DUMPCOREAV(HDMI_CORE_AV_ASRC);
DUMPCOREAV(HDMI_CORE_AV_I2S_IN_LEN);
DUMPCOREAV(HDMI_CORE_AV_HDMI_CTRL);
DUMPCOREAV(HDMI_CORE_AV_AUDO_TXSTAT);
DUMPCOREAV(HDMI_CORE_AV_AUD_PAR_BUSCLK_1);
DUMPCOREAV(HDMI_CORE_AV_AUD_PAR_BUSCLK_2);
DUMPCOREAV(HDMI_CORE_AV_AUD_PAR_BUSCLK_3);
DUMPCOREAV(HDMI_CORE_AV_TEST_TXCTRL);
DUMPCOREAV(HDMI_CORE_AV_DPD);
DUMPCOREAV(HDMI_CORE_AV_PB_CTRL1);
DUMPCOREAV(HDMI_CORE_AV_PB_CTRL2);
DUMPCOREAV(HDMI_CORE_AV_AVI_TYPE);
DUMPCOREAV(HDMI_CORE_AV_AVI_VERS);
DUMPCOREAV(HDMI_CORE_AV_AVI_LEN);
DUMPCOREAV(HDMI_CORE_AV_AVI_CHSUM);
for (i = 0; i < HDMI_CORE_AV_AVI_DBYTE_NELEMS; i++)
DUMPCOREAV2(i, HDMI_CORE_AV_AVI_DBYTE);
DUMPCOREAV(HDMI_CORE_AV_SPD_TYPE);
DUMPCOREAV(HDMI_CORE_AV_SPD_VERS);
DUMPCOREAV(HDMI_CORE_AV_SPD_LEN);
DUMPCOREAV(HDMI_CORE_AV_SPD_CHSUM);
for (i = 0; i < HDMI_CORE_AV_SPD_DBYTE_NELEMS; i++)
DUMPCOREAV2(i, HDMI_CORE_AV_SPD_DBYTE);
DUMPCOREAV(HDMI_CORE_AV_AUDIO_TYPE);
DUMPCOREAV(HDMI_CORE_AV_AUDIO_VERS);
DUMPCOREAV(HDMI_CORE_AV_AUDIO_LEN);
DUMPCOREAV(HDMI_CORE_AV_AUDIO_CHSUM);
for (i = 0; i < HDMI_CORE_AV_AUD_DBYTE_NELEMS; i++)
DUMPCOREAV2(i, HDMI_CORE_AV_AUD_DBYTE);
DUMPCOREAV(HDMI_CORE_AV_MPEG_TYPE);
DUMPCOREAV(HDMI_CORE_AV_MPEG_VERS);
DUMPCOREAV(HDMI_CORE_AV_MPEG_LEN);
DUMPCOREAV(HDMI_CORE_AV_MPEG_CHSUM);
for (i = 0; i < HDMI_CORE_AV_MPEG_DBYTE_NELEMS; i++)
DUMPCOREAV2(i, HDMI_CORE_AV_MPEG_DBYTE);
for (i = 0; i < HDMI_CORE_AV_GEN_DBYTE_NELEMS; i++)
DUMPCOREAV2(i, HDMI_CORE_AV_GEN_DBYTE);
DUMPCOREAV(HDMI_CORE_AV_CP_BYTE1);
for (i = 0; i < HDMI_CORE_AV_GEN2_DBYTE_NELEMS; i++)
DUMPCOREAV2(i, HDMI_CORE_AV_GEN2_DBYTE);
DUMPCOREAV(HDMI_CORE_AV_CEC_ADDR_ID);
}
static void hdmi_core_audio_config(struct hdmi_core_data *core,
struct hdmi_core_audio_config *cfg)
{
u32 r;
void __iomem *av_base = hdmi_av_base(core);
/*
* Parameters for generation of Audio Clock Recovery packets
*/
REG_FLD_MOD(av_base, HDMI_CORE_AV_N_SVAL1, cfg->n, 7, 0);
REG_FLD_MOD(av_base, HDMI_CORE_AV_N_SVAL2, cfg->n >> 8, 7, 0);
REG_FLD_MOD(av_base, HDMI_CORE_AV_N_SVAL3, cfg->n >> 16, 7, 0);
if (cfg->cts_mode == HDMI_AUDIO_CTS_MODE_SW) {
REG_FLD_MOD(av_base, HDMI_CORE_AV_CTS_SVAL1, cfg->cts, 7, 0);
REG_FLD_MOD(av_base,
HDMI_CORE_AV_CTS_SVAL2, cfg->cts >> 8, 7, 0);
REG_FLD_MOD(av_base,
HDMI_CORE_AV_CTS_SVAL3, cfg->cts >> 16, 7, 0);
} else {
REG_FLD_MOD(av_base, HDMI_CORE_AV_AUD_PAR_BUSCLK_1,
cfg->aud_par_busclk, 7, 0);
REG_FLD_MOD(av_base, HDMI_CORE_AV_AUD_PAR_BUSCLK_2,
(cfg->aud_par_busclk >> 8), 7, 0);
REG_FLD_MOD(av_base, HDMI_CORE_AV_AUD_PAR_BUSCLK_3,
(cfg->aud_par_busclk >> 16), 7, 0);
}
/* Set ACR clock divisor */
REG_FLD_MOD(av_base,
HDMI_CORE_AV_FREQ_SVAL, cfg->mclk_mode, 2, 0);
r = hdmi_read_reg(av_base, HDMI_CORE_AV_ACR_CTRL);
/*
* Use TMDS clock for ACR packets. For devices that use
* the MCLK, this is the first part of the MCLK initialization.
*/
r = FLD_MOD(r, 0, 2, 2);
r = FLD_MOD(r, cfg->en_acr_pkt, 1, 1);
r = FLD_MOD(r, cfg->cts_mode, 0, 0);
hdmi_write_reg(av_base, HDMI_CORE_AV_ACR_CTRL, r);
/* For devices using MCLK, this completes its initialization. */
if (cfg->use_mclk)
REG_FLD_MOD(av_base, HDMI_CORE_AV_ACR_CTRL, 1, 2, 2);
/* Override of SPDIF sample frequency with value in I2S_CHST4 */
REG_FLD_MOD(av_base, HDMI_CORE_AV_SPDIF_CTRL,
cfg->fs_override, 1, 1);
/*
* Set IEC-60958-3 channel status word. It is passed to the IP
* just as it is received. The user of the driver is responsible
* for its contents.
*/
hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_CHST0,
cfg->iec60958_cfg->status[0]);
hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_CHST1,
cfg->iec60958_cfg->status[1]);
hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_CHST2,
cfg->iec60958_cfg->status[2]);
/* yes, this is correct: status[3] goes to CHST4 register */
hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_CHST4,
cfg->iec60958_cfg->status[3]);
/* yes, this is correct: status[4] goes to CHST5 register */
hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_CHST5,
cfg->iec60958_cfg->status[4]);
/* set I2S parameters */
r = hdmi_read_reg(av_base, HDMI_CORE_AV_I2S_IN_CTRL);
r = FLD_MOD(r, cfg->i2s_cfg.sck_edge_mode, 6, 6);
r = FLD_MOD(r, cfg->i2s_cfg.vbit, 4, 4);
r = FLD_MOD(r, cfg->i2s_cfg.justification, 2, 2);
r = FLD_MOD(r, cfg->i2s_cfg.direction, 1, 1);
r = FLD_MOD(r, cfg->i2s_cfg.shift, 0, 0);
hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_IN_CTRL, r);
REG_FLD_MOD(av_base, HDMI_CORE_AV_I2S_IN_LEN,
cfg->i2s_cfg.in_length_bits, 3, 0);
/* Audio channels and mode parameters */
REG_FLD_MOD(av_base, HDMI_CORE_AV_HDMI_CTRL, cfg->layout, 2, 1);
r = hdmi_read_reg(av_base, HDMI_CORE_AV_AUD_MODE);
r = FLD_MOD(r, cfg->i2s_cfg.active_sds, 7, 4);
r = FLD_MOD(r, cfg->en_dsd_audio, 3, 3);
r = FLD_MOD(r, cfg->en_parallel_aud_input, 2, 2);
r = FLD_MOD(r, cfg->en_spdif, 1, 1);
hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_MODE, r);
/* Audio channel mappings */
/* TODO: Make channel mapping dynamic. For now, map channels
* in the ALSA order: FL/FR/RL/RR/C/LFE/SL/SR. Remapping is needed as
* HDMI speaker order is different. See CEA-861 Section 6.6.2.
*/
hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_IN_MAP, 0x78);
REG_FLD_MOD(av_base, HDMI_CORE_AV_SWAP_I2S, 1, 5, 5);
}
static void hdmi_core_audio_infoframe_cfg(struct hdmi_core_data *core,
struct snd_cea_861_aud_if *info_aud)
{
u8 sum = 0, checksum = 0;
void __iomem *av_base = hdmi_av_base(core);
/*
* Set audio info frame type, version and length as
* described in HDMI 1.4a Section 8.2.2 specification.
* Checksum calculation is defined in Section 5.3.5.
*/
hdmi_write_reg(av_base, HDMI_CORE_AV_AUDIO_TYPE, 0x84);
hdmi_write_reg(av_base, HDMI_CORE_AV_AUDIO_VERS, 0x01);
hdmi_write_reg(av_base, HDMI_CORE_AV_AUDIO_LEN, 0x0a);
sum += 0x84 + 0x001 + 0x00a;
hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(0),
info_aud->db1_ct_cc);
sum += info_aud->db1_ct_cc;
hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(1),
info_aud->db2_sf_ss);
sum += info_aud->db2_sf_ss;
hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(2), info_aud->db3);
sum += info_aud->db3;
/*
* The OMAP HDMI IP requires to use the 8-channel channel code when
* transmitting more than two channels.
*/
if (info_aud->db4_ca != 0x00)
info_aud->db4_ca = 0x13;
hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(3), info_aud->db4_ca);
sum += info_aud->db4_ca;
hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(4),
info_aud->db5_dminh_lsv);
sum += info_aud->db5_dminh_lsv;
hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(5), 0x00);
hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(6), 0x00);
hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(7), 0x00);
hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(8), 0x00);
hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(9), 0x00);
checksum = 0x100 - sum;
hdmi_write_reg(av_base,
HDMI_CORE_AV_AUDIO_CHSUM, checksum);
/*
* TODO: Add MPEG and SPD enable and repeat cfg when EDID parsing
* is available.
*/
}
int hdmi4_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp,
struct omap_dss_audio *audio, u32 pclk)
{
struct hdmi_audio_format audio_format;
struct hdmi_audio_dma audio_dma;
struct hdmi_core_audio_config acore;
int err, n, cts, channel_count;
unsigned int fs_nr;
bool word_length_16b = false;
if (!audio || !audio->iec || !audio->cea || !core)
return -EINVAL;
acore.iec60958_cfg = audio->iec;
/*
* In the IEC-60958 status word, check if the audio sample word length
* is 16-bit as several optimizations can be performed in such case.
*/
if (!(audio->iec->status[4] & IEC958_AES4_CON_MAX_WORDLEN_24))
if (audio->iec->status[4] & IEC958_AES4_CON_WORDLEN_20_16)
word_length_16b = true;
/* I2S configuration. See Phillips' specification */
if (word_length_16b)
acore.i2s_cfg.justification = HDMI_AUDIO_JUSTIFY_LEFT;
else
acore.i2s_cfg.justification = HDMI_AUDIO_JUSTIFY_RIGHT;
/*
* The I2S input word length is twice the lenght given in the IEC-60958
* status word. If the word size is greater than
* 20 bits, increment by one.
*/
acore.i2s_cfg.in_length_bits = audio->iec->status[4]
& IEC958_AES4_CON_WORDLEN;
if (audio->iec->status[4] & IEC958_AES4_CON_MAX_WORDLEN_24)
acore.i2s_cfg.in_length_bits++;
acore.i2s_cfg.sck_edge_mode = HDMI_AUDIO_I2S_SCK_EDGE_RISING;
acore.i2s_cfg.vbit = HDMI_AUDIO_I2S_VBIT_FOR_PCM;
acore.i2s_cfg.direction = HDMI_AUDIO_I2S_MSB_SHIFTED_FIRST;
acore.i2s_cfg.shift = HDMI_AUDIO_I2S_FIRST_BIT_SHIFT;
/* convert sample frequency to a number */
switch (audio->iec->status[3] & IEC958_AES3_CON_FS) {
case IEC958_AES3_CON_FS_32000:
fs_nr = 32000;
break;
case IEC958_AES3_CON_FS_44100:
fs_nr = 44100;
break;
case IEC958_AES3_CON_FS_48000:
fs_nr = 48000;
break;
case IEC958_AES3_CON_FS_88200:
fs_nr = 88200;
break;
case IEC958_AES3_CON_FS_96000:
fs_nr = 96000;
break;
case IEC958_AES3_CON_FS_176400:
fs_nr = 176400;
break;
case IEC958_AES3_CON_FS_192000:
fs_nr = 192000;
break;
default:
return -EINVAL;
}
err = hdmi_compute_acr(pclk, fs_nr, &n, &cts);
/* Audio clock regeneration settings */
acore.n = n;
acore.cts = cts;
if (dss_has_feature(FEAT_HDMI_CTS_SWMODE)) {
acore.aud_par_busclk = 0;
acore.cts_mode = HDMI_AUDIO_CTS_MODE_SW;
acore.use_mclk = dss_has_feature(FEAT_HDMI_AUDIO_USE_MCLK);
} else {
acore.aud_par_busclk = (((128 * 31) - 1) << 8);
acore.cts_mode = HDMI_AUDIO_CTS_MODE_HW;
acore.use_mclk = true;
}
if (acore.use_mclk)
acore.mclk_mode = HDMI_AUDIO_MCLK_128FS;
/* Audio channels settings */
channel_count = (audio->cea->db1_ct_cc &
CEA861_AUDIO_INFOFRAME_DB1CC) + 1;
switch (channel_count) {
case 2:
audio_format.active_chnnls_msk = 0x03;
break;
case 3:
audio_format.active_chnnls_msk = 0x07;
break;
case 4:
audio_format.active_chnnls_msk = 0x0f;
break;
case 5:
audio_format.active_chnnls_msk = 0x1f;
break;
case 6:
audio_format.active_chnnls_msk = 0x3f;
break;
case 7:
audio_format.active_chnnls_msk = 0x7f;
break;
case 8:
audio_format.active_chnnls_msk = 0xff;
break;
default:
return -EINVAL;
}
/*
* the HDMI IP needs to enable four stereo channels when transmitting
* more than 2 audio channels. Similarly, the channel count in the
* Audio InfoFrame has to match the sample_present bits (some channels
* are padded with zeroes)
*/
if (channel_count == 2) {
audio_format.stereo_channels = HDMI_AUDIO_STEREO_ONECHANNEL;
acore.i2s_cfg.active_sds = HDMI_AUDIO_I2S_SD0_EN;
acore.layout = HDMI_AUDIO_LAYOUT_2CH;
} else {
audio_format.stereo_channels = HDMI_AUDIO_STEREO_FOURCHANNELS;
acore.i2s_cfg.active_sds = HDMI_AUDIO_I2S_SD0_EN |
HDMI_AUDIO_I2S_SD1_EN | HDMI_AUDIO_I2S_SD2_EN |
HDMI_AUDIO_I2S_SD3_EN;
acore.layout = HDMI_AUDIO_LAYOUT_8CH;
audio->cea->db1_ct_cc = 7;
}
acore.en_spdif = false;
/* use sample frequency from channel status word */
acore.fs_override = true;
/* enable ACR packets */
acore.en_acr_pkt = true;
/* disable direct streaming digital audio */
acore.en_dsd_audio = false;
/* use parallel audio interface */
acore.en_parallel_aud_input = true;
/* DMA settings */
if (word_length_16b)
audio_dma.transfer_size = 0x10;
else
audio_dma.transfer_size = 0x20;
audio_dma.block_size = 0xC0;
audio_dma.mode = HDMI_AUDIO_TRANSF_DMA;
audio_dma.fifo_threshold = 0x20; /* in number of samples */
/* audio FIFO format settings */
if (word_length_16b) {
audio_format.samples_per_word = HDMI_AUDIO_ONEWORD_TWOSAMPLES;
audio_format.sample_size = HDMI_AUDIO_SAMPLE_16BITS;
audio_format.justification = HDMI_AUDIO_JUSTIFY_LEFT;
} else {
audio_format.samples_per_word = HDMI_AUDIO_ONEWORD_ONESAMPLE;
audio_format.sample_size = HDMI_AUDIO_SAMPLE_24BITS;
audio_format.justification = HDMI_AUDIO_JUSTIFY_RIGHT;
}
audio_format.type = HDMI_AUDIO_TYPE_LPCM;
audio_format.sample_order = HDMI_AUDIO_SAMPLE_LEFT_FIRST;
/* disable start/stop signals of IEC 60958 blocks */
audio_format.en_sig_blk_strt_end = HDMI_AUDIO_BLOCK_SIG_STARTEND_ON;
/* configure DMA and audio FIFO format*/
hdmi_wp_audio_config_dma(wp, &audio_dma);
hdmi_wp_audio_config_format(wp, &audio_format);
/* configure the core*/
hdmi_core_audio_config(core, &acore);
/* configure CEA 861 audio infoframe*/
hdmi_core_audio_infoframe_cfg(core, audio->cea);
return 0;
}
int hdmi4_audio_start(struct hdmi_core_data *core, struct hdmi_wp_data *wp)
{
REG_FLD_MOD(hdmi_av_base(core),
HDMI_CORE_AV_AUD_MODE, true, 0, 0);
hdmi_wp_audio_core_req_enable(wp, true);
return 0;
}
void hdmi4_audio_stop(struct hdmi_core_data *core, struct hdmi_wp_data *wp)
{
REG_FLD_MOD(hdmi_av_base(core),
HDMI_CORE_AV_AUD_MODE, false, 0, 0);
hdmi_wp_audio_core_req_enable(wp, false);
}
int hdmi4_core_init(struct platform_device *pdev, struct hdmi_core_data *core)
{
struct resource *res;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core");
if (!res) {
DSSERR("can't get CORE mem resource\n");
return -EINVAL;
}
core->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(core->base)) {
DSSERR("can't ioremap CORE\n");
return PTR_ERR(core->base);
}
return 0;
}

View File

@ -0,0 +1,273 @@
/*
* HDMI header definition for OMAP4 HDMI core IP
*
* Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _HDMI4_CORE_H_
#define _HDMI4_CORE_H_
#include "hdmi.h"
/* OMAP4 HDMI IP Core System */
#define HDMI_CORE_SYS_VND_IDL 0x0
#define HDMI_CORE_SYS_DEV_IDL 0x8
#define HDMI_CORE_SYS_DEV_IDH 0xC
#define HDMI_CORE_SYS_DEV_REV 0x10
#define HDMI_CORE_SYS_SRST 0x14
#define HDMI_CORE_SYS_SYS_CTRL1 0x20
#define HDMI_CORE_SYS_SYS_STAT 0x24
#define HDMI_CORE_SYS_SYS_CTRL3 0x28
#define HDMI_CORE_SYS_DCTL 0x34
#define HDMI_CORE_SYS_DE_DLY 0xC8
#define HDMI_CORE_SYS_DE_CTRL 0xCC
#define HDMI_CORE_SYS_DE_TOP 0xD0
#define HDMI_CORE_SYS_DE_CNTL 0xD8
#define HDMI_CORE_SYS_DE_CNTH 0xDC
#define HDMI_CORE_SYS_DE_LINL 0xE0
#define HDMI_CORE_SYS_DE_LINH_1 0xE4
#define HDMI_CORE_SYS_HRES_L 0xE8
#define HDMI_CORE_SYS_HRES_H 0xEC
#define HDMI_CORE_SYS_VRES_L 0xF0
#define HDMI_CORE_SYS_VRES_H 0xF4
#define HDMI_CORE_SYS_IADJUST 0xF8
#define HDMI_CORE_SYS_POLDETECT 0xFC
#define HDMI_CORE_SYS_HWIDTH1 0x110
#define HDMI_CORE_SYS_HWIDTH2 0x114
#define HDMI_CORE_SYS_VWIDTH 0x11C
#define HDMI_CORE_SYS_VID_CTRL 0x120
#define HDMI_CORE_SYS_VID_ACEN 0x124
#define HDMI_CORE_SYS_VID_MODE 0x128
#define HDMI_CORE_SYS_VID_BLANK1 0x12C
#define HDMI_CORE_SYS_VID_BLANK2 0x130
#define HDMI_CORE_SYS_VID_BLANK3 0x134
#define HDMI_CORE_SYS_DC_HEADER 0x138
#define HDMI_CORE_SYS_VID_DITHER 0x13C
#define HDMI_CORE_SYS_RGB2XVYCC_CT 0x140
#define HDMI_CORE_SYS_R2Y_COEFF_LOW 0x144
#define HDMI_CORE_SYS_R2Y_COEFF_UP 0x148
#define HDMI_CORE_SYS_G2Y_COEFF_LOW 0x14C
#define HDMI_CORE_SYS_G2Y_COEFF_UP 0x150
#define HDMI_CORE_SYS_B2Y_COEFF_LOW 0x154
#define HDMI_CORE_SYS_B2Y_COEFF_UP 0x158
#define HDMI_CORE_SYS_R2CB_COEFF_LOW 0x15C
#define HDMI_CORE_SYS_R2CB_COEFF_UP 0x160
#define HDMI_CORE_SYS_G2CB_COEFF_LOW 0x164
#define HDMI_CORE_SYS_G2CB_COEFF_UP 0x168
#define HDMI_CORE_SYS_B2CB_COEFF_LOW 0x16C
#define HDMI_CORE_SYS_B2CB_COEFF_UP 0x170
#define HDMI_CORE_SYS_R2CR_COEFF_LOW 0x174
#define HDMI_CORE_SYS_R2CR_COEFF_UP 0x178
#define HDMI_CORE_SYS_G2CR_COEFF_LOW 0x17C
#define HDMI_CORE_SYS_G2CR_COEFF_UP 0x180
#define HDMI_CORE_SYS_B2CR_COEFF_LOW 0x184
#define HDMI_CORE_SYS_B2CR_COEFF_UP 0x188
#define HDMI_CORE_SYS_RGB_OFFSET_LOW 0x18C
#define HDMI_CORE_SYS_RGB_OFFSET_UP 0x190
#define HDMI_CORE_SYS_Y_OFFSET_LOW 0x194
#define HDMI_CORE_SYS_Y_OFFSET_UP 0x198
#define HDMI_CORE_SYS_CBCR_OFFSET_LOW 0x19C
#define HDMI_CORE_SYS_CBCR_OFFSET_UP 0x1A0
#define HDMI_CORE_SYS_INTR_STATE 0x1C0
#define HDMI_CORE_SYS_INTR1 0x1C4
#define HDMI_CORE_SYS_INTR2 0x1C8
#define HDMI_CORE_SYS_INTR3 0x1CC
#define HDMI_CORE_SYS_INTR4 0x1D0
#define HDMI_CORE_SYS_INTR_UNMASK1 0x1D4
#define HDMI_CORE_SYS_INTR_UNMASK2 0x1D8
#define HDMI_CORE_SYS_INTR_UNMASK3 0x1DC
#define HDMI_CORE_SYS_INTR_UNMASK4 0x1E0
#define HDMI_CORE_SYS_INTR_CTRL 0x1E4
#define HDMI_CORE_SYS_TMDS_CTRL 0x208
/* value definitions for HDMI_CORE_SYS_SYS_CTRL1 fields */
#define HDMI_CORE_SYS_SYS_CTRL1_VEN_FOLLOWVSYNC 0x1
#define HDMI_CORE_SYS_SYS_CTRL1_HEN_FOLLOWHSYNC 0x1
#define HDMI_CORE_SYS_SYS_CTRL1_BSEL_24BITBUS 0x1
#define HDMI_CORE_SYS_SYS_CTRL1_EDGE_RISINGEDGE 0x1
/* HDMI DDC E-DID */
#define HDMI_CORE_DDC_ADDR 0x3B4
#define HDMI_CORE_DDC_SEGM 0x3B8
#define HDMI_CORE_DDC_OFFSET 0x3BC
#define HDMI_CORE_DDC_COUNT1 0x3C0
#define HDMI_CORE_DDC_COUNT2 0x3C4
#define HDMI_CORE_DDC_STATUS 0x3C8
#define HDMI_CORE_DDC_CMD 0x3CC
#define HDMI_CORE_DDC_DATA 0x3D0
/* HDMI IP Core Audio Video */
#define HDMI_CORE_AV_ACR_CTRL 0x4
#define HDMI_CORE_AV_FREQ_SVAL 0x8
#define HDMI_CORE_AV_N_SVAL1 0xC
#define HDMI_CORE_AV_N_SVAL2 0x10
#define HDMI_CORE_AV_N_SVAL3 0x14
#define HDMI_CORE_AV_CTS_SVAL1 0x18
#define HDMI_CORE_AV_CTS_SVAL2 0x1C
#define HDMI_CORE_AV_CTS_SVAL3 0x20
#define HDMI_CORE_AV_CTS_HVAL1 0x24
#define HDMI_CORE_AV_CTS_HVAL2 0x28
#define HDMI_CORE_AV_CTS_HVAL3 0x2C
#define HDMI_CORE_AV_AUD_MODE 0x50
#define HDMI_CORE_AV_SPDIF_CTRL 0x54
#define HDMI_CORE_AV_HW_SPDIF_FS 0x60
#define HDMI_CORE_AV_SWAP_I2S 0x64
#define HDMI_CORE_AV_SPDIF_ERTH 0x6C
#define HDMI_CORE_AV_I2S_IN_MAP 0x70
#define HDMI_CORE_AV_I2S_IN_CTRL 0x74
#define HDMI_CORE_AV_I2S_CHST0 0x78
#define HDMI_CORE_AV_I2S_CHST1 0x7C
#define HDMI_CORE_AV_I2S_CHST2 0x80
#define HDMI_CORE_AV_I2S_CHST4 0x84
#define HDMI_CORE_AV_I2S_CHST5 0x88
#define HDMI_CORE_AV_ASRC 0x8C
#define HDMI_CORE_AV_I2S_IN_LEN 0x90
#define HDMI_CORE_AV_HDMI_CTRL 0xBC
#define HDMI_CORE_AV_AUDO_TXSTAT 0xC0
#define HDMI_CORE_AV_AUD_PAR_BUSCLK_1 0xCC
#define HDMI_CORE_AV_AUD_PAR_BUSCLK_2 0xD0
#define HDMI_CORE_AV_AUD_PAR_BUSCLK_3 0xD4
#define HDMI_CORE_AV_TEST_TXCTRL 0xF0
#define HDMI_CORE_AV_DPD 0xF4
#define HDMI_CORE_AV_PB_CTRL1 0xF8
#define HDMI_CORE_AV_PB_CTRL2 0xFC
#define HDMI_CORE_AV_AVI_BASE 0x100
#define HDMI_CORE_AV_AVI_TYPE 0x100
#define HDMI_CORE_AV_AVI_VERS 0x104
#define HDMI_CORE_AV_AVI_LEN 0x108
#define HDMI_CORE_AV_AVI_CHSUM 0x10C
#define HDMI_CORE_AV_AVI_DBYTE(n) (n * 4 + 0x110)
#define HDMI_CORE_AV_SPD_TYPE 0x180
#define HDMI_CORE_AV_SPD_VERS 0x184
#define HDMI_CORE_AV_SPD_LEN 0x188
#define HDMI_CORE_AV_SPD_CHSUM 0x18C
#define HDMI_CORE_AV_SPD_DBYTE(n) (n * 4 + 0x190)
#define HDMI_CORE_AV_AUDIO_TYPE 0x200
#define HDMI_CORE_AV_AUDIO_VERS 0x204
#define HDMI_CORE_AV_AUDIO_LEN 0x208
#define HDMI_CORE_AV_AUDIO_CHSUM 0x20C
#define HDMI_CORE_AV_AUD_DBYTE(n) (n * 4 + 0x210)
#define HDMI_CORE_AV_MPEG_TYPE 0x280
#define HDMI_CORE_AV_MPEG_VERS 0x284
#define HDMI_CORE_AV_MPEG_LEN 0x288
#define HDMI_CORE_AV_MPEG_CHSUM 0x28C
#define HDMI_CORE_AV_MPEG_DBYTE(n) (n * 4 + 0x290)
#define HDMI_CORE_AV_GEN_DBYTE(n) (n * 4 + 0x300)
#define HDMI_CORE_AV_CP_BYTE1 0x37C
#define HDMI_CORE_AV_GEN2_DBYTE(n) (n * 4 + 0x380)
#define HDMI_CORE_AV_CEC_ADDR_ID 0x3FC
#define HDMI_CORE_AV_SPD_DBYTE_ELSIZE 0x4
#define HDMI_CORE_AV_GEN2_DBYTE_ELSIZE 0x4
#define HDMI_CORE_AV_MPEG_DBYTE_ELSIZE 0x4
#define HDMI_CORE_AV_GEN_DBYTE_ELSIZE 0x4
#define HDMI_CORE_AV_AVI_DBYTE_NELEMS 15
#define HDMI_CORE_AV_SPD_DBYTE_NELEMS 27
#define HDMI_CORE_AV_AUD_DBYTE_NELEMS 10
#define HDMI_CORE_AV_MPEG_DBYTE_NELEMS 27
#define HDMI_CORE_AV_GEN_DBYTE_NELEMS 31
#define HDMI_CORE_AV_GEN2_DBYTE_NELEMS 31
enum hdmi_core_inputbus_width {
HDMI_INPUT_8BIT = 0,
HDMI_INPUT_10BIT = 1,
HDMI_INPUT_12BIT = 2
};
enum hdmi_core_dither_trunc {
HDMI_OUTPUTTRUNCATION_8BIT = 0,
HDMI_OUTPUTTRUNCATION_10BIT = 1,
HDMI_OUTPUTTRUNCATION_12BIT = 2,
HDMI_OUTPUTDITHER_8BIT = 3,
HDMI_OUTPUTDITHER_10BIT = 4,
HDMI_OUTPUTDITHER_12BIT = 5
};
enum hdmi_core_deepcolor_ed {
HDMI_DEEPCOLORPACKECTDISABLE = 0,
HDMI_DEEPCOLORPACKECTENABLE = 1
};
enum hdmi_core_packet_mode {
HDMI_PACKETMODERESERVEDVALUE = 0,
HDMI_PACKETMODE24BITPERPIXEL = 4,
HDMI_PACKETMODE30BITPERPIXEL = 5,
HDMI_PACKETMODE36BITPERPIXEL = 6,
HDMI_PACKETMODE48BITPERPIXEL = 7
};
enum hdmi_core_tclkselclkmult {
HDMI_FPLL05IDCK = 0,
HDMI_FPLL10IDCK = 1,
HDMI_FPLL20IDCK = 2,
HDMI_FPLL40IDCK = 3
};
enum hdmi_core_packet_ctrl {
HDMI_PACKETENABLE = 1,
HDMI_PACKETDISABLE = 0,
HDMI_PACKETREPEATON = 1,
HDMI_PACKETREPEATOFF = 0
};
enum hdmi_audio_i2s_config {
HDMI_AUDIO_I2S_MSB_SHIFTED_FIRST = 0,
HDMI_AUDIO_I2S_LSB_SHIFTED_FIRST = 1,
HDMI_AUDIO_I2S_SCK_EDGE_FALLING = 0,
HDMI_AUDIO_I2S_SCK_EDGE_RISING = 1,
HDMI_AUDIO_I2S_VBIT_FOR_PCM = 0,
HDMI_AUDIO_I2S_VBIT_FOR_COMPRESSED = 1,
HDMI_AUDIO_I2S_FIRST_BIT_SHIFT = 0,
HDMI_AUDIO_I2S_FIRST_BIT_NO_SHIFT = 1,
HDMI_AUDIO_I2S_SD0_EN = 1,
HDMI_AUDIO_I2S_SD1_EN = 1 << 1,
HDMI_AUDIO_I2S_SD2_EN = 1 << 2,
HDMI_AUDIO_I2S_SD3_EN = 1 << 3,
};
struct hdmi_core_video_config {
enum hdmi_core_inputbus_width ip_bus_width;
enum hdmi_core_dither_trunc op_dither_truc;
enum hdmi_core_deepcolor_ed deep_color_pkt;
enum hdmi_core_packet_mode pkt_mode;
enum hdmi_core_hdmi_dvi hdmi_dvi;
enum hdmi_core_tclkselclkmult tclk_sel_clkmult;
};
struct hdmi_core_packet_enable_repeat {
u32 audio_pkt;
u32 audio_pkt_repeat;
u32 avi_infoframe;
u32 avi_infoframe_repeat;
u32 gen_cntrl_pkt;
u32 gen_cntrl_pkt_repeat;
u32 generic_pkt;
u32 generic_pkt_repeat;
};
int hdmi4_read_edid(struct hdmi_core_data *core, u8 *edid, int len);
void hdmi4_configure(struct hdmi_core_data *core, struct hdmi_wp_data *wp,
struct hdmi_config *cfg);
void hdmi4_core_dump(struct hdmi_core_data *core, struct seq_file *s);
int hdmi4_core_init(struct platform_device *pdev, struct hdmi_core_data *core);
int hdmi4_audio_start(struct hdmi_core_data *core, struct hdmi_wp_data *wp);
void hdmi4_audio_stop(struct hdmi_core_data *core, struct hdmi_wp_data *wp);
int hdmi4_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp,
struct omap_dss_audio *audio, u32 pclk);
#endif

View File

@ -0,0 +1,876 @@
/*
* HDMI driver for OMAP5
*
* Copyright (C) 2014 Texas Instruments Incorporated
*
* Authors:
* Yong Zhi
* Mythri pk
* Archit Taneja <archit@ti.com>
* Tomi Valkeinen <tomi.valkeinen@ti.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define DSS_SUBSYS_NAME "HDMI"
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/clk.h>
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/component.h>
#include <video/omapdss.h>
#include <sound/omap-hdmi-audio.h>
#include "hdmi5_core.h"
#include "dss.h"
#include "dss_features.h"
static struct omap_hdmi hdmi;
static int hdmi_runtime_get(void)
{
int r;
DSSDBG("hdmi_runtime_get\n");
r = pm_runtime_get_sync(&hdmi.pdev->dev);
WARN_ON(r < 0);
if (r < 0)
return r;
return 0;
}
static void hdmi_runtime_put(void)
{
int r;
DSSDBG("hdmi_runtime_put\n");
r = pm_runtime_put_sync(&hdmi.pdev->dev);
WARN_ON(r < 0 && r != -ENOSYS);
}
static irqreturn_t hdmi_irq_handler(int irq, void *data)
{
struct hdmi_wp_data *wp = data;
u32 irqstatus;
irqstatus = hdmi_wp_get_irqstatus(wp);
hdmi_wp_set_irqstatus(wp, irqstatus);
if ((irqstatus & HDMI_IRQ_LINK_CONNECT) &&
irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
u32 v;
/*
* If we get both connect and disconnect interrupts at the same
* time, turn off the PHY, clear interrupts, and restart, which
* raises connect interrupt if a cable is connected, or nothing
* if cable is not connected.
*/
hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF);
/*
* We always get bogus CONNECT & DISCONNECT interrupts when
* setting the PHY to LDOON. To ignore those, we force the RXDET
* line to 0 until the PHY power state has been changed.
*/
v = hdmi_read_reg(hdmi.phy.base, HDMI_TXPHY_PAD_CFG_CTRL);
v = FLD_MOD(v, 1, 15, 15); /* FORCE_RXDET_HIGH */
v = FLD_MOD(v, 0, 14, 7); /* RXDET_LINE */
hdmi_write_reg(hdmi.phy.base, HDMI_TXPHY_PAD_CFG_CTRL, v);
hdmi_wp_set_irqstatus(wp, HDMI_IRQ_LINK_CONNECT |
HDMI_IRQ_LINK_DISCONNECT);
hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
REG_FLD_MOD(hdmi.phy.base, HDMI_TXPHY_PAD_CFG_CTRL, 0, 15, 15);
} else if (irqstatus & HDMI_IRQ_LINK_CONNECT) {
hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_TXON);
} else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
}
return IRQ_HANDLED;
}
static int hdmi_init_regulator(void)
{
int r;
struct regulator *reg;
if (hdmi.vdda_reg != NULL)
return 0;
reg = devm_regulator_get(&hdmi.pdev->dev, "vdda");
if (IS_ERR(reg)) {
DSSERR("can't get VDDA regulator\n");
return PTR_ERR(reg);
}
if (regulator_can_change_voltage(reg)) {
r = regulator_set_voltage(reg, 1800000, 1800000);
if (r) {
devm_regulator_put(reg);
DSSWARN("can't set the regulator voltage\n");
return r;
}
}
hdmi.vdda_reg = reg;
return 0;
}
static int hdmi_power_on_core(struct omap_dss_device *dssdev)
{
int r;
r = regulator_enable(hdmi.vdda_reg);
if (r)
return r;
r = hdmi_runtime_get();
if (r)
goto err_runtime_get;
/* Make selection of HDMI in DSS */
dss_select_hdmi_venc_clk_source(DSS_HDMI_M_PCLK);
hdmi.core_enabled = true;
return 0;
err_runtime_get:
regulator_disable(hdmi.vdda_reg);
return r;
}
static void hdmi_power_off_core(struct omap_dss_device *dssdev)
{
hdmi.core_enabled = false;
hdmi_runtime_put();
regulator_disable(hdmi.vdda_reg);
}
static int hdmi_power_on_full(struct omap_dss_device *dssdev)
{
int r;
struct omap_video_timings *p;
struct omap_overlay_manager *mgr = hdmi.output.manager;
struct dss_pll_clock_info hdmi_cinfo = { 0 };
r = hdmi_power_on_core(dssdev);
if (r)
return r;
p = &hdmi.cfg.timings;
DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res);
hdmi_pll_compute(&hdmi.pll, p->pixelclock, &hdmi_cinfo);
/* disable and clear irqs */
hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff);
hdmi_wp_set_irqstatus(&hdmi.wp,
hdmi_wp_get_irqstatus(&hdmi.wp));
r = dss_pll_enable(&hdmi.pll.pll);
if (r) {
DSSERR("Failed to enable PLL\n");
goto err_pll_enable;
}
r = dss_pll_set_config(&hdmi.pll.pll, &hdmi_cinfo);
if (r) {
DSSERR("Failed to configure PLL\n");
goto err_pll_cfg;
}
r = hdmi_phy_configure(&hdmi.phy, hdmi_cinfo.clkdco,
hdmi_cinfo.clkout[0]);
if (r) {
DSSDBG("Failed to start PHY\n");
goto err_phy_cfg;
}
r = hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_LDOON);
if (r)
goto err_phy_pwr;
hdmi5_configure(&hdmi.core, &hdmi.wp, &hdmi.cfg);
/* bypass TV gamma table */
dispc_enable_gamma_table(0);
/* tv size */
dss_mgr_set_timings(mgr, p);
r = hdmi_wp_video_start(&hdmi.wp);
if (r)
goto err_vid_enable;
r = dss_mgr_enable(mgr);
if (r)
goto err_mgr_enable;
hdmi_wp_set_irqenable(&hdmi.wp,
HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT);
return 0;
err_mgr_enable:
hdmi_wp_video_stop(&hdmi.wp);
err_vid_enable:
hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF);
err_phy_pwr:
err_phy_cfg:
err_pll_cfg:
dss_pll_disable(&hdmi.pll.pll);
err_pll_enable:
hdmi_power_off_core(dssdev);
return -EIO;
}
static void hdmi_power_off_full(struct omap_dss_device *dssdev)
{
struct omap_overlay_manager *mgr = hdmi.output.manager;
hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff);
dss_mgr_disable(mgr);
hdmi_wp_video_stop(&hdmi.wp);
hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF);
dss_pll_disable(&hdmi.pll.pll);
hdmi_power_off_core(dssdev);
}
static int hdmi_display_check_timing(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct omap_dss_device *out = &hdmi.output;
/* TODO: proper interlace support */
if (timings->interlace)
return -EINVAL;
if (!dispc_mgr_timings_ok(out->dispc_channel, timings))
return -EINVAL;
return 0;
}
static void hdmi_display_set_timing(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
mutex_lock(&hdmi.lock);
hdmi.cfg.timings = *timings;
dispc_set_tv_pclk(timings->pixelclock);
mutex_unlock(&hdmi.lock);
}
static void hdmi_display_get_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
*timings = hdmi.cfg.timings;
}
static void hdmi_dump_regs(struct seq_file *s)
{
mutex_lock(&hdmi.lock);
if (hdmi_runtime_get()) {
mutex_unlock(&hdmi.lock);
return;
}
hdmi_wp_dump(&hdmi.wp, s);
hdmi_pll_dump(&hdmi.pll, s);
hdmi_phy_dump(&hdmi.phy, s);
hdmi5_core_dump(&hdmi.core, s);
hdmi_runtime_put();
mutex_unlock(&hdmi.lock);
}
static int read_edid(u8 *buf, int len)
{
int r;
int idlemode;
mutex_lock(&hdmi.lock);
r = hdmi_runtime_get();
BUG_ON(r);
idlemode = REG_GET(hdmi.wp.base, HDMI_WP_SYSCONFIG, 3, 2);
/* No-idle mode */
REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2);
r = hdmi5_read_edid(&hdmi.core, buf, len);
REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, idlemode, 3, 2);
hdmi_runtime_put();
mutex_unlock(&hdmi.lock);
return r;
}
static void hdmi_start_audio_stream(struct omap_hdmi *hd)
{
REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2);
hdmi_wp_audio_enable(&hd->wp, true);
hdmi_wp_audio_core_req_enable(&hd->wp, true);
}
static void hdmi_stop_audio_stream(struct omap_hdmi *hd)
{
hdmi_wp_audio_core_req_enable(&hd->wp, false);
hdmi_wp_audio_enable(&hd->wp, false);
REG_FLD_MOD(hd->wp.base, HDMI_WP_SYSCONFIG, hd->wp_idlemode, 3, 2);
}
static int hdmi_display_enable(struct omap_dss_device *dssdev)
{
struct omap_dss_device *out = &hdmi.output;
unsigned long flags;
int r = 0;
DSSDBG("ENTER hdmi_display_enable\n");
mutex_lock(&hdmi.lock);
if (out->manager == NULL) {
DSSERR("failed to enable display: no output/manager\n");
r = -ENODEV;
goto err0;
}
r = hdmi_power_on_full(dssdev);
if (r) {
DSSERR("failed to power on device\n");
goto err0;
}
if (hdmi.audio_configured) {
r = hdmi5_audio_config(&hdmi.core, &hdmi.wp, &hdmi.audio_config,
hdmi.cfg.timings.pixelclock);
if (r) {
DSSERR("Error restoring audio configuration: %d", r);
hdmi.audio_abort_cb(&hdmi.pdev->dev);
hdmi.audio_configured = false;
}
}
spin_lock_irqsave(&hdmi.audio_playing_lock, flags);
if (hdmi.audio_configured && hdmi.audio_playing)
hdmi_start_audio_stream(&hdmi);
hdmi.display_enabled = true;
spin_unlock_irqrestore(&hdmi.audio_playing_lock, flags);
mutex_unlock(&hdmi.lock);
return 0;
err0:
mutex_unlock(&hdmi.lock);
return r;
}
static void hdmi_display_disable(struct omap_dss_device *dssdev)
{
unsigned long flags;
DSSDBG("Enter hdmi_display_disable\n");
mutex_lock(&hdmi.lock);
spin_lock_irqsave(&hdmi.audio_playing_lock, flags);
hdmi_stop_audio_stream(&hdmi);
hdmi.display_enabled = false;
spin_unlock_irqrestore(&hdmi.audio_playing_lock, flags);
hdmi_power_off_full(dssdev);
mutex_unlock(&hdmi.lock);
}
static int hdmi_core_enable(struct omap_dss_device *dssdev)
{
int r = 0;
DSSDBG("ENTER omapdss_hdmi_core_enable\n");
mutex_lock(&hdmi.lock);
r = hdmi_power_on_core(dssdev);
if (r) {
DSSERR("failed to power on device\n");
goto err0;
}
mutex_unlock(&hdmi.lock);
return 0;
err0:
mutex_unlock(&hdmi.lock);
return r;
}
static void hdmi_core_disable(struct omap_dss_device *dssdev)
{
DSSDBG("Enter omapdss_hdmi_core_disable\n");
mutex_lock(&hdmi.lock);
hdmi_power_off_core(dssdev);
mutex_unlock(&hdmi.lock);
}
static int hdmi_connect(struct omap_dss_device *dssdev,
struct omap_dss_device *dst)
{
struct omap_overlay_manager *mgr;
int r;
r = hdmi_init_regulator();
if (r)
return r;
mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel);
if (!mgr)
return -ENODEV;
r = dss_mgr_connect(mgr, dssdev);
if (r)
return r;
r = omapdss_output_set_device(dssdev, dst);
if (r) {
DSSERR("failed to connect output to new device: %s\n",
dst->name);
dss_mgr_disconnect(mgr, dssdev);
return r;
}
return 0;
}
static void hdmi_disconnect(struct omap_dss_device *dssdev,
struct omap_dss_device *dst)
{
WARN_ON(dst != dssdev->dst);
if (dst != dssdev->dst)
return;
omapdss_output_unset_device(dssdev);
if (dssdev->manager)
dss_mgr_disconnect(dssdev->manager, dssdev);
}
static int hdmi_read_edid(struct omap_dss_device *dssdev,
u8 *edid, int len)
{
bool need_enable;
int r;
need_enable = hdmi.core_enabled == false;
if (need_enable) {
r = hdmi_core_enable(dssdev);
if (r)
return r;
}
r = read_edid(edid, len);
if (need_enable)
hdmi_core_disable(dssdev);
return r;
}
static int hdmi_set_infoframe(struct omap_dss_device *dssdev,
const struct hdmi_avi_infoframe *avi)
{
hdmi.cfg.infoframe = *avi;
return 0;
}
static int hdmi_set_hdmi_mode(struct omap_dss_device *dssdev,
bool hdmi_mode)
{
hdmi.cfg.hdmi_dvi_mode = hdmi_mode ? HDMI_HDMI : HDMI_DVI;
return 0;
}
static const struct omapdss_hdmi_ops hdmi_ops = {
.connect = hdmi_connect,
.disconnect = hdmi_disconnect,
.enable = hdmi_display_enable,
.disable = hdmi_display_disable,
.check_timings = hdmi_display_check_timing,
.set_timings = hdmi_display_set_timing,
.get_timings = hdmi_display_get_timings,
.read_edid = hdmi_read_edid,
.set_infoframe = hdmi_set_infoframe,
.set_hdmi_mode = hdmi_set_hdmi_mode,
};
static void hdmi_init_output(struct platform_device *pdev)
{
struct omap_dss_device *out = &hdmi.output;
out->dev = &pdev->dev;
out->id = OMAP_DSS_OUTPUT_HDMI;
out->output_type = OMAP_DISPLAY_TYPE_HDMI;
out->name = "hdmi.0";
out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT;
out->ops.hdmi = &hdmi_ops;
out->owner = THIS_MODULE;
omapdss_register_output(out);
}
static void hdmi_uninit_output(struct platform_device *pdev)
{
struct omap_dss_device *out = &hdmi.output;
omapdss_unregister_output(out);
}
static int hdmi_probe_of(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct device_node *ep;
int r;
ep = omapdss_of_get_first_endpoint(node);
if (!ep)
return 0;
r = hdmi_parse_lanes_of(pdev, ep, &hdmi.phy);
if (r)
goto err;
of_node_put(ep);
return 0;
err:
of_node_put(ep);
return r;
}
/* Audio callbacks */
static int hdmi_audio_startup(struct device *dev,
void (*abort_cb)(struct device *dev))
{
struct omap_hdmi *hd = dev_get_drvdata(dev);
int ret = 0;
mutex_lock(&hd->lock);
if (!hdmi_mode_has_audio(&hd->cfg) || !hd->display_enabled) {
ret = -EPERM;
goto out;
}
hd->audio_abort_cb = abort_cb;
out:
mutex_unlock(&hd->lock);
return ret;
}
static int hdmi_audio_shutdown(struct device *dev)
{
struct omap_hdmi *hd = dev_get_drvdata(dev);
mutex_lock(&hd->lock);
hd->audio_abort_cb = NULL;
hd->audio_configured = false;
hd->audio_playing = false;
mutex_unlock(&hd->lock);
return 0;
}
static int hdmi_audio_start(struct device *dev)
{
struct omap_hdmi *hd = dev_get_drvdata(dev);
unsigned long flags;
WARN_ON(!hdmi_mode_has_audio(&hd->cfg));
spin_lock_irqsave(&hd->audio_playing_lock, flags);
if (hd->display_enabled)
hdmi_start_audio_stream(hd);
hd->audio_playing = true;
spin_unlock_irqrestore(&hd->audio_playing_lock, flags);
return 0;
}
static void hdmi_audio_stop(struct device *dev)
{
struct omap_hdmi *hd = dev_get_drvdata(dev);
unsigned long flags;
WARN_ON(!hdmi_mode_has_audio(&hd->cfg));
spin_lock_irqsave(&hd->audio_playing_lock, flags);
if (hd->display_enabled)
hdmi_stop_audio_stream(hd);
hd->audio_playing = false;
spin_unlock_irqrestore(&hd->audio_playing_lock, flags);
}
static int hdmi_audio_config(struct device *dev,
struct omap_dss_audio *dss_audio)
{
struct omap_hdmi *hd = dev_get_drvdata(dev);
int ret;
mutex_lock(&hd->lock);
if (!hdmi_mode_has_audio(&hd->cfg) || !hd->display_enabled) {
ret = -EPERM;
goto out;
}
ret = hdmi5_audio_config(&hd->core, &hd->wp, dss_audio,
hd->cfg.timings.pixelclock);
if (!ret) {
hd->audio_configured = true;
hd->audio_config = *dss_audio;
}
out:
mutex_unlock(&hd->lock);
return ret;
}
static const struct omap_hdmi_audio_ops hdmi_audio_ops = {
.audio_startup = hdmi_audio_startup,
.audio_shutdown = hdmi_audio_shutdown,
.audio_start = hdmi_audio_start,
.audio_stop = hdmi_audio_stop,
.audio_config = hdmi_audio_config,
};
static int hdmi_audio_register(struct device *dev)
{
struct omap_hdmi_audio_pdata pdata = {
.dev = dev,
.dss_version = omapdss_get_version(),
.audio_dma_addr = hdmi_wp_get_audio_dma_addr(&hdmi.wp),
.ops = &hdmi_audio_ops,
};
hdmi.audio_pdev = platform_device_register_data(
dev, "omap-hdmi-audio", PLATFORM_DEVID_AUTO,
&pdata, sizeof(pdata));
if (IS_ERR(hdmi.audio_pdev))
return PTR_ERR(hdmi.audio_pdev);
hdmi_runtime_get();
hdmi.wp_idlemode =
REG_GET(hdmi.wp.base, HDMI_WP_SYSCONFIG, 3, 2);
hdmi_runtime_put();
return 0;
}
/* HDMI HW IP initialisation */
static int hdmi5_bind(struct device *dev, struct device *master, void *data)
{
struct platform_device *pdev = to_platform_device(dev);
int r;
int irq;
hdmi.pdev = pdev;
dev_set_drvdata(&pdev->dev, &hdmi);
mutex_init(&hdmi.lock);
spin_lock_init(&hdmi.audio_playing_lock);
if (pdev->dev.of_node) {
r = hdmi_probe_of(pdev);
if (r)
return r;
}
r = hdmi_wp_init(pdev, &hdmi.wp);
if (r)
return r;
r = hdmi_pll_init(pdev, &hdmi.pll, &hdmi.wp);
if (r)
return r;
r = hdmi_phy_init(pdev, &hdmi.phy);
if (r)
goto err;
r = hdmi5_core_init(pdev, &hdmi.core);
if (r)
goto err;
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
DSSERR("platform_get_irq failed\n");
r = -ENODEV;
goto err;
}
r = devm_request_threaded_irq(&pdev->dev, irq,
NULL, hdmi_irq_handler,
IRQF_ONESHOT, "OMAP HDMI", &hdmi.wp);
if (r) {
DSSERR("HDMI IRQ request failed\n");
goto err;
}
pm_runtime_enable(&pdev->dev);
hdmi_init_output(pdev);
r = hdmi_audio_register(&pdev->dev);
if (r) {
DSSERR("Registering HDMI audio failed %d\n", r);
hdmi_uninit_output(pdev);
pm_runtime_disable(&pdev->dev);
return r;
}
dss_debugfs_create_file("hdmi", hdmi_dump_regs);
return 0;
err:
hdmi_pll_uninit(&hdmi.pll);
return r;
}
static void hdmi5_unbind(struct device *dev, struct device *master, void *data)
{
struct platform_device *pdev = to_platform_device(dev);
if (hdmi.audio_pdev)
platform_device_unregister(hdmi.audio_pdev);
hdmi_uninit_output(pdev);
hdmi_pll_uninit(&hdmi.pll);
pm_runtime_disable(&pdev->dev);
}
static const struct component_ops hdmi5_component_ops = {
.bind = hdmi5_bind,
.unbind = hdmi5_unbind,
};
static int hdmi5_probe(struct platform_device *pdev)
{
return component_add(&pdev->dev, &hdmi5_component_ops);
}
static int hdmi5_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &hdmi5_component_ops);
return 0;
}
static int hdmi_runtime_suspend(struct device *dev)
{
dispc_runtime_put();
return 0;
}
static int hdmi_runtime_resume(struct device *dev)
{
int r;
r = dispc_runtime_get();
if (r < 0)
return r;
return 0;
}
static const struct dev_pm_ops hdmi_pm_ops = {
.runtime_suspend = hdmi_runtime_suspend,
.runtime_resume = hdmi_runtime_resume,
};
static const struct of_device_id hdmi_of_match[] = {
{ .compatible = "ti,omap5-hdmi", },
{ .compatible = "ti,dra7-hdmi", },
{},
};
static struct platform_driver omapdss_hdmihw_driver = {
.probe = hdmi5_probe,
.remove = hdmi5_remove,
.driver = {
.name = "omapdss_hdmi5",
.pm = &hdmi_pm_ops,
.of_match_table = hdmi_of_match,
.suppress_bind_attrs = true,
},
};
int __init hdmi5_init_platform_driver(void)
{
return platform_driver_register(&omapdss_hdmihw_driver);
}
void hdmi5_uninit_platform_driver(void)
{
platform_driver_unregister(&omapdss_hdmihw_driver);
}

View File

@ -0,0 +1,916 @@
/*
* OMAP5 HDMI CORE IP driver library
*
* Copyright (C) 2014 Texas Instruments Incorporated
*
* Authors:
* Yong Zhi
* Mythri pk
* Archit Taneja <archit@ti.com>
* 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/kernel.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/seq_file.h>
#include <drm/drm_edid.h>
#include <sound/asound.h>
#include <sound/asoundef.h>
#include "hdmi5_core.h"
/* only 24 bit color depth used for now */
static const struct csc_table csc_table_deepcolor[] = {
/* HDMI_DEEP_COLOR_24BIT */
[0] = { 7036, 0, 0, 32, 0, 7036, 0, 32, 0, 0, 7036, 32, },
/* HDMI_DEEP_COLOR_30BIT */
[1] = { 7015, 0, 0, 128, 0, 7015, 0, 128, 0, 0, 7015, 128, },
/* HDMI_DEEP_COLOR_36BIT */
[2] = { 7010, 0, 0, 512, 0, 7010, 0, 512, 0, 0, 7010, 512, },
/* FULL RANGE */
[3] = { 8192, 0, 0, 0, 0, 8192, 0, 0, 0, 0, 8192, 0, },
};
static void hdmi_core_ddc_init(struct hdmi_core_data *core)
{
void __iomem *base = core->base;
const unsigned long long iclk = 266000000; /* DSS L3 ICLK */
const unsigned ss_scl_high = 4000; /* ns */
const unsigned ss_scl_low = 4700; /* ns */
const unsigned fs_scl_high = 600; /* ns */
const unsigned fs_scl_low = 1300; /* ns */
const unsigned sda_hold = 1000; /* ns */
const unsigned sfr_div = 10;
unsigned long long sfr;
unsigned v;
sfr = iclk / sfr_div; /* SFR_DIV */
sfr /= 1000; /* SFR clock in kHz */
/* Reset */
REG_FLD_MOD(base, HDMI_CORE_I2CM_SOFTRSTZ, 0, 0, 0);
if (hdmi_wait_for_bit_change(base, HDMI_CORE_I2CM_SOFTRSTZ,
0, 0, 1) != 1)
DSSERR("HDMI I2CM reset failed\n");
/* Standard (0) or Fast (1) Mode */
REG_FLD_MOD(base, HDMI_CORE_I2CM_DIV, 0, 3, 3);
/* Standard Mode SCL High counter */
v = DIV_ROUND_UP_ULL(ss_scl_high * sfr, 1000000);
REG_FLD_MOD(base, HDMI_CORE_I2CM_SS_SCL_HCNT_1_ADDR,
(v >> 8) & 0xff, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_I2CM_SS_SCL_HCNT_0_ADDR,
v & 0xff, 7, 0);
/* Standard Mode SCL Low counter */
v = DIV_ROUND_UP_ULL(ss_scl_low * sfr, 1000000);
REG_FLD_MOD(base, HDMI_CORE_I2CM_SS_SCL_LCNT_1_ADDR,
(v >> 8) & 0xff, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_I2CM_SS_SCL_LCNT_0_ADDR,
v & 0xff, 7, 0);
/* Fast Mode SCL High Counter */
v = DIV_ROUND_UP_ULL(fs_scl_high * sfr, 1000000);
REG_FLD_MOD(base, HDMI_CORE_I2CM_FS_SCL_HCNT_1_ADDR,
(v >> 8) & 0xff, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_I2CM_FS_SCL_HCNT_0_ADDR,
v & 0xff, 7, 0);
/* Fast Mode SCL Low Counter */
v = DIV_ROUND_UP_ULL(fs_scl_low * sfr, 1000000);
REG_FLD_MOD(base, HDMI_CORE_I2CM_FS_SCL_LCNT_1_ADDR,
(v >> 8) & 0xff, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_I2CM_FS_SCL_LCNT_0_ADDR,
v & 0xff, 7, 0);
/* SDA Hold Time */
v = DIV_ROUND_UP_ULL(sda_hold * sfr, 1000000);
REG_FLD_MOD(base, HDMI_CORE_I2CM_SDA_HOLD_ADDR, v & 0xff, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_I2CM_SLAVE, 0x50, 6, 0);
REG_FLD_MOD(base, HDMI_CORE_I2CM_SEGADDR, 0x30, 6, 0);
/* NACK_POL to high */
REG_FLD_MOD(base, HDMI_CORE_I2CM_CTLINT, 0x1, 7, 7);
/* NACK_MASK to unmasked */
REG_FLD_MOD(base, HDMI_CORE_I2CM_CTLINT, 0x0, 6, 6);
/* ARBITRATION_POL to high */
REG_FLD_MOD(base, HDMI_CORE_I2CM_CTLINT, 0x1, 3, 3);
/* ARBITRATION_MASK to unmasked */
REG_FLD_MOD(base, HDMI_CORE_I2CM_CTLINT, 0x0, 2, 2);
/* DONE_POL to high */
REG_FLD_MOD(base, HDMI_CORE_I2CM_INT, 0x1, 3, 3);
/* DONE_MASK to unmasked */
REG_FLD_MOD(base, HDMI_CORE_I2CM_INT, 0x0, 2, 2);
}
static void hdmi_core_ddc_uninit(struct hdmi_core_data *core)
{
void __iomem *base = core->base;
/* Mask I2C interrupts */
REG_FLD_MOD(base, HDMI_CORE_I2CM_CTLINT, 0x1, 6, 6);
REG_FLD_MOD(base, HDMI_CORE_I2CM_CTLINT, 0x1, 2, 2);
REG_FLD_MOD(base, HDMI_CORE_I2CM_INT, 0x1, 2, 2);
}
static int hdmi_core_ddc_edid(struct hdmi_core_data *core, u8 *pedid, u8 ext)
{
void __iomem *base = core->base;
u8 cur_addr;
char checksum = 0;
const int retries = 1000;
u8 seg_ptr = ext / 2;
u8 edidbase = ((ext % 2) * 0x80);
REG_FLD_MOD(base, HDMI_CORE_I2CM_SEGPTR, seg_ptr, 7, 0);
/*
* TODO: We use polling here, although we probably should use proper
* interrupts.
*/
for (cur_addr = 0; cur_addr < 128; ++cur_addr) {
int i;
/* clear ERROR and DONE */
REG_FLD_MOD(base, HDMI_CORE_IH_I2CM_STAT0, 0x3, 1, 0);
REG_FLD_MOD(base, HDMI_CORE_I2CM_ADDRESS,
edidbase + cur_addr, 7, 0);
if (seg_ptr)
REG_FLD_MOD(base, HDMI_CORE_I2CM_OPERATION, 1, 1, 1);
else
REG_FLD_MOD(base, HDMI_CORE_I2CM_OPERATION, 1, 0, 0);
for (i = 0; i < retries; ++i) {
u32 stat;
stat = REG_GET(base, HDMI_CORE_IH_I2CM_STAT0, 1, 0);
/* I2CM_ERROR */
if (stat & 1) {
DSSERR("HDMI I2C Master Error\n");
return -EIO;
}
/* I2CM_DONE */
if (stat & (1 << 1))
break;
usleep_range(250, 1000);
}
if (i == retries) {
DSSERR("HDMI I2C timeout reading EDID\n");
return -EIO;
}
pedid[cur_addr] = REG_GET(base, HDMI_CORE_I2CM_DATAI, 7, 0);
checksum += pedid[cur_addr];
}
return 0;
}
int hdmi5_read_edid(struct hdmi_core_data *core, u8 *edid, int len)
{
int r, n, i;
int max_ext_blocks = (len / 128) - 1;
if (len < 128)
return -EINVAL;
hdmi_core_ddc_init(core);
r = hdmi_core_ddc_edid(core, edid, 0);
if (r)
goto out;
n = edid[0x7e];
if (n > max_ext_blocks)
n = max_ext_blocks;
for (i = 1; i <= n; i++) {
r = hdmi_core_ddc_edid(core, edid + i * EDID_LENGTH, i);
if (r)
goto out;
}
out:
hdmi_core_ddc_uninit(core);
return r ? r : len;
}
void hdmi5_core_dump(struct hdmi_core_data *core, struct seq_file *s)
{
#define DUMPCORE(r) seq_printf(s, "%-35s %08x\n", #r,\
hdmi_read_reg(core->base, r))
DUMPCORE(HDMI_CORE_FC_INVIDCONF);
DUMPCORE(HDMI_CORE_FC_INHACTIV0);
DUMPCORE(HDMI_CORE_FC_INHACTIV1);
DUMPCORE(HDMI_CORE_FC_INHBLANK0);
DUMPCORE(HDMI_CORE_FC_INHBLANK1);
DUMPCORE(HDMI_CORE_FC_INVACTIV0);
DUMPCORE(HDMI_CORE_FC_INVACTIV1);
DUMPCORE(HDMI_CORE_FC_INVBLANK);
DUMPCORE(HDMI_CORE_FC_HSYNCINDELAY0);
DUMPCORE(HDMI_CORE_FC_HSYNCINDELAY1);
DUMPCORE(HDMI_CORE_FC_HSYNCINWIDTH0);
DUMPCORE(HDMI_CORE_FC_HSYNCINWIDTH1);
DUMPCORE(HDMI_CORE_FC_VSYNCINDELAY);
DUMPCORE(HDMI_CORE_FC_VSYNCINWIDTH);
DUMPCORE(HDMI_CORE_FC_CTRLDUR);
DUMPCORE(HDMI_CORE_FC_EXCTRLDUR);
DUMPCORE(HDMI_CORE_FC_EXCTRLSPAC);
DUMPCORE(HDMI_CORE_FC_CH0PREAM);
DUMPCORE(HDMI_CORE_FC_CH1PREAM);
DUMPCORE(HDMI_CORE_FC_CH2PREAM);
DUMPCORE(HDMI_CORE_FC_AVICONF0);
DUMPCORE(HDMI_CORE_FC_AVICONF1);
DUMPCORE(HDMI_CORE_FC_AVICONF2);
DUMPCORE(HDMI_CORE_FC_AVIVID);
DUMPCORE(HDMI_CORE_FC_PRCONF);
DUMPCORE(HDMI_CORE_MC_CLKDIS);
DUMPCORE(HDMI_CORE_MC_SWRSTZREQ);
DUMPCORE(HDMI_CORE_MC_FLOWCTRL);
DUMPCORE(HDMI_CORE_MC_PHYRSTZ);
DUMPCORE(HDMI_CORE_MC_LOCKONCLOCK);
DUMPCORE(HDMI_CORE_I2CM_SLAVE);
DUMPCORE(HDMI_CORE_I2CM_ADDRESS);
DUMPCORE(HDMI_CORE_I2CM_DATAO);
DUMPCORE(HDMI_CORE_I2CM_DATAI);
DUMPCORE(HDMI_CORE_I2CM_OPERATION);
DUMPCORE(HDMI_CORE_I2CM_INT);
DUMPCORE(HDMI_CORE_I2CM_CTLINT);
DUMPCORE(HDMI_CORE_I2CM_DIV);
DUMPCORE(HDMI_CORE_I2CM_SEGADDR);
DUMPCORE(HDMI_CORE_I2CM_SOFTRSTZ);
DUMPCORE(HDMI_CORE_I2CM_SEGPTR);
DUMPCORE(HDMI_CORE_I2CM_SS_SCL_HCNT_1_ADDR);
DUMPCORE(HDMI_CORE_I2CM_SS_SCL_HCNT_0_ADDR);
DUMPCORE(HDMI_CORE_I2CM_SS_SCL_LCNT_1_ADDR);
DUMPCORE(HDMI_CORE_I2CM_SS_SCL_LCNT_0_ADDR);
DUMPCORE(HDMI_CORE_I2CM_FS_SCL_HCNT_1_ADDR);
DUMPCORE(HDMI_CORE_I2CM_FS_SCL_HCNT_0_ADDR);
DUMPCORE(HDMI_CORE_I2CM_FS_SCL_LCNT_1_ADDR);
DUMPCORE(HDMI_CORE_I2CM_FS_SCL_LCNT_0_ADDR);
DUMPCORE(HDMI_CORE_I2CM_SDA_HOLD_ADDR);
}
static void hdmi_core_init(struct hdmi_core_vid_config *video_cfg,
struct hdmi_config *cfg)
{
DSSDBG("hdmi_core_init\n");
/* video core */
video_cfg->data_enable_pol = 1; /* It is always 1*/
video_cfg->v_fc_config.timings.hsync_level = cfg->timings.hsync_level;
video_cfg->v_fc_config.timings.x_res = cfg->timings.x_res;
video_cfg->v_fc_config.timings.hsw = cfg->timings.hsw - 1;
video_cfg->v_fc_config.timings.hbp = cfg->timings.hbp;
video_cfg->v_fc_config.timings.hfp = cfg->timings.hfp;
video_cfg->hblank = cfg->timings.hfp +
cfg->timings.hbp + cfg->timings.hsw - 1;
video_cfg->v_fc_config.timings.vsync_level = cfg->timings.vsync_level;
video_cfg->v_fc_config.timings.y_res = cfg->timings.y_res;
video_cfg->v_fc_config.timings.vsw = cfg->timings.vsw;
video_cfg->v_fc_config.timings.vfp = cfg->timings.vfp;
video_cfg->v_fc_config.timings.vbp = cfg->timings.vbp;
video_cfg->vblank_osc = 0; /* Always 0 - need to confirm */
video_cfg->vblank = cfg->timings.vsw +
cfg->timings.vfp + cfg->timings.vbp;
video_cfg->v_fc_config.hdmi_dvi_mode = cfg->hdmi_dvi_mode;
video_cfg->v_fc_config.timings.interlace = cfg->timings.interlace;
}
/* DSS_HDMI_CORE_VIDEO_CONFIG */
static void hdmi_core_video_config(struct hdmi_core_data *core,
struct hdmi_core_vid_config *cfg)
{
void __iomem *base = core->base;
unsigned char r = 0;
bool vsync_pol, hsync_pol;
vsync_pol =
cfg->v_fc_config.timings.vsync_level == OMAPDSS_SIG_ACTIVE_HIGH;
hsync_pol =
cfg->v_fc_config.timings.hsync_level == OMAPDSS_SIG_ACTIVE_HIGH;
/* Set hsync, vsync and data-enable polarity */
r = hdmi_read_reg(base, HDMI_CORE_FC_INVIDCONF);
r = FLD_MOD(r, vsync_pol, 6, 6);
r = FLD_MOD(r, hsync_pol, 5, 5);
r = FLD_MOD(r, cfg->data_enable_pol, 4, 4);
r = FLD_MOD(r, cfg->vblank_osc, 1, 1);
r = FLD_MOD(r, cfg->v_fc_config.timings.interlace, 0, 0);
hdmi_write_reg(base, HDMI_CORE_FC_INVIDCONF, r);
/* set x resolution */
REG_FLD_MOD(base, HDMI_CORE_FC_INHACTIV1,
cfg->v_fc_config.timings.x_res >> 8, 4, 0);
REG_FLD_MOD(base, HDMI_CORE_FC_INHACTIV0,
cfg->v_fc_config.timings.x_res & 0xFF, 7, 0);
/* set y resolution */
REG_FLD_MOD(base, HDMI_CORE_FC_INVACTIV1,
cfg->v_fc_config.timings.y_res >> 8, 4, 0);
REG_FLD_MOD(base, HDMI_CORE_FC_INVACTIV0,
cfg->v_fc_config.timings.y_res & 0xFF, 7, 0);
/* set horizontal blanking pixels */
REG_FLD_MOD(base, HDMI_CORE_FC_INHBLANK1, cfg->hblank >> 8, 4, 0);
REG_FLD_MOD(base, HDMI_CORE_FC_INHBLANK0, cfg->hblank & 0xFF, 7, 0);
/* set vertial blanking pixels */
REG_FLD_MOD(base, HDMI_CORE_FC_INVBLANK, cfg->vblank, 7, 0);
/* set horizontal sync offset */
REG_FLD_MOD(base, HDMI_CORE_FC_HSYNCINDELAY1,
cfg->v_fc_config.timings.hfp >> 8, 4, 0);
REG_FLD_MOD(base, HDMI_CORE_FC_HSYNCINDELAY0,
cfg->v_fc_config.timings.hfp & 0xFF, 7, 0);
/* set vertical sync offset */
REG_FLD_MOD(base, HDMI_CORE_FC_VSYNCINDELAY,
cfg->v_fc_config.timings.vfp, 7, 0);
/* set horizontal sync pulse width */
REG_FLD_MOD(base, HDMI_CORE_FC_HSYNCINWIDTH1,
(cfg->v_fc_config.timings.hsw >> 8), 1, 0);
REG_FLD_MOD(base, HDMI_CORE_FC_HSYNCINWIDTH0,
cfg->v_fc_config.timings.hsw & 0xFF, 7, 0);
/* set vertical sync pulse width */
REG_FLD_MOD(base, HDMI_CORE_FC_VSYNCINWIDTH,
cfg->v_fc_config.timings.vsw, 5, 0);
/* select DVI mode */
REG_FLD_MOD(base, HDMI_CORE_FC_INVIDCONF,
cfg->v_fc_config.hdmi_dvi_mode, 3, 3);
}
static void hdmi_core_config_video_packetizer(struct hdmi_core_data *core)
{
void __iomem *base = core->base;
int clr_depth = 0; /* 24 bit color depth */
/* COLOR_DEPTH */
REG_FLD_MOD(base, HDMI_CORE_VP_PR_CD, clr_depth, 7, 4);
/* BYPASS_EN */
REG_FLD_MOD(base, HDMI_CORE_VP_CONF, clr_depth ? 0 : 1, 6, 6);
/* PP_EN */
REG_FLD_MOD(base, HDMI_CORE_VP_CONF, clr_depth ? 1 : 0, 5, 5);
/* YCC422_EN */
REG_FLD_MOD(base, HDMI_CORE_VP_CONF, 0, 3, 3);
/* PP_STUFFING */
REG_FLD_MOD(base, HDMI_CORE_VP_STUFF, clr_depth ? 1 : 0, 1, 1);
/* YCC422_STUFFING */
REG_FLD_MOD(base, HDMI_CORE_VP_STUFF, 1, 2, 2);
/* OUTPUT_SELECTOR */
REG_FLD_MOD(base, HDMI_CORE_VP_CONF, clr_depth ? 0 : 2, 1, 0);
}
static void hdmi_core_config_csc(struct hdmi_core_data *core)
{
int clr_depth = 0; /* 24 bit color depth */
/* CSC_COLORDEPTH */
REG_FLD_MOD(core->base, HDMI_CORE_CSC_SCALE, clr_depth, 7, 4);
}
static void hdmi_core_config_video_sampler(struct hdmi_core_data *core)
{
int video_mapping = 1; /* for 24 bit color depth */
/* VIDEO_MAPPING */
REG_FLD_MOD(core->base, HDMI_CORE_TX_INVID0, video_mapping, 4, 0);
}
static void hdmi_core_write_avi_infoframe(struct hdmi_core_data *core,
struct hdmi_avi_infoframe *frame)
{
void __iomem *base = core->base;
u8 data[HDMI_INFOFRAME_SIZE(AVI)];
u8 *ptr;
unsigned y, a, b, s;
unsigned c, m, r;
unsigned itc, ec, q, sc;
unsigned vic;
unsigned yq, cn, pr;
hdmi_avi_infoframe_pack(frame, data, sizeof(data));
print_hex_dump_debug("AVI: ", DUMP_PREFIX_NONE, 16, 1, data,
HDMI_INFOFRAME_SIZE(AVI), false);
ptr = data + HDMI_INFOFRAME_HEADER_SIZE;
y = (ptr[0] >> 5) & 0x3;
a = (ptr[0] >> 4) & 0x1;
b = (ptr[0] >> 2) & 0x3;
s = (ptr[0] >> 0) & 0x3;
c = (ptr[1] >> 6) & 0x3;
m = (ptr[1] >> 4) & 0x3;
r = (ptr[1] >> 0) & 0x3;
itc = (ptr[2] >> 7) & 0x1;
ec = (ptr[2] >> 4) & 0x7;
q = (ptr[2] >> 2) & 0x3;
sc = (ptr[2] >> 0) & 0x3;
vic = ptr[3];
yq = (ptr[4] >> 6) & 0x3;
cn = (ptr[4] >> 4) & 0x3;
pr = (ptr[4] >> 0) & 0xf;
hdmi_write_reg(base, HDMI_CORE_FC_AVICONF0,
(a << 6) | (s << 4) | (b << 2) | (y << 0));
hdmi_write_reg(base, HDMI_CORE_FC_AVICONF1,
(c << 6) | (m << 4) | (r << 0));
hdmi_write_reg(base, HDMI_CORE_FC_AVICONF2,
(itc << 7) | (ec << 4) | (q << 2) | (sc << 0));
hdmi_write_reg(base, HDMI_CORE_FC_AVIVID, vic);
hdmi_write_reg(base, HDMI_CORE_FC_AVICONF3,
(yq << 2) | (cn << 0));
REG_FLD_MOD(base, HDMI_CORE_FC_PRCONF, pr, 3, 0);
}
static void hdmi_core_csc_config(struct hdmi_core_data *core,
struct csc_table csc_coeff)
{
void __iomem *base = core->base;
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A1_MSB, csc_coeff.a1 >> 8 , 6, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A1_LSB, csc_coeff.a1, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A2_MSB, csc_coeff.a2 >> 8, 6, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A2_LSB, csc_coeff.a2, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A3_MSB, csc_coeff.a3 >> 8, 6, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A3_LSB, csc_coeff.a3, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A4_MSB, csc_coeff.a4 >> 8, 6, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A4_LSB, csc_coeff.a4, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B1_MSB, csc_coeff.b1 >> 8, 6, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B1_LSB, csc_coeff.b1, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B2_MSB, csc_coeff.b2 >> 8, 6, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B2_LSB, csc_coeff.b2, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B3_MSB, csc_coeff.b3 >> 8, 6, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B3_LSB, csc_coeff.b3, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B4_MSB, csc_coeff.b4 >> 8, 6, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B4_LSB, csc_coeff.b4, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C1_MSB, csc_coeff.c1 >> 8, 6, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C1_LSB, csc_coeff.c1, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C2_MSB, csc_coeff.c2 >> 8, 6, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C2_LSB, csc_coeff.c2, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C3_MSB, csc_coeff.c3 >> 8, 6, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C3_LSB, csc_coeff.c3, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C4_MSB, csc_coeff.c4 >> 8, 6, 0);
REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C4_LSB, csc_coeff.c4, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_MC_FLOWCTRL, 0x1, 0, 0);
}
static void hdmi_core_configure_range(struct hdmi_core_data *core)
{
struct csc_table csc_coeff = { 0 };
/* support limited range with 24 bit color depth for now */
csc_coeff = csc_table_deepcolor[0];
hdmi_core_csc_config(core, csc_coeff);
}
static void hdmi_core_enable_video_path(struct hdmi_core_data *core)
{
void __iomem *base = core->base;
DSSDBG("hdmi_core_enable_video_path\n");
REG_FLD_MOD(base, HDMI_CORE_FC_CTRLDUR, 0x0C, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_FC_EXCTRLDUR, 0x20, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_FC_EXCTRLSPAC, 0x01, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_FC_CH0PREAM, 0x0B, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_FC_CH1PREAM, 0x16, 5, 0);
REG_FLD_MOD(base, HDMI_CORE_FC_CH2PREAM, 0x21, 5, 0);
REG_FLD_MOD(base, HDMI_CORE_MC_CLKDIS, 0x00, 0, 0);
REG_FLD_MOD(base, HDMI_CORE_MC_CLKDIS, 0x00, 1, 1);
}
static void hdmi_core_mask_interrupts(struct hdmi_core_data *core)
{
void __iomem *base = core->base;
/* Master IRQ mask */
REG_FLD_MOD(base, HDMI_CORE_IH_MUTE, 0x3, 1, 0);
/* Mask all the interrupts in HDMI core */
REG_FLD_MOD(base, HDMI_CORE_VP_MASK, 0xff, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_FC_MASK0, 0xe7, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_FC_MASK1, 0xfb, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_FC_MASK2, 0x3, 1, 0);
REG_FLD_MOD(base, HDMI_CORE_AUD_INT, 0x3, 3, 2);
REG_FLD_MOD(base, HDMI_CORE_AUD_GP_MASK, 0x3, 1, 0);
REG_FLD_MOD(base, HDMI_CORE_CEC_MASK, 0x7f, 6, 0);
REG_FLD_MOD(base, HDMI_CORE_I2CM_CTLINT, 0x1, 6, 6);
REG_FLD_MOD(base, HDMI_CORE_I2CM_CTLINT, 0x1, 2, 2);
REG_FLD_MOD(base, HDMI_CORE_I2CM_INT, 0x1, 2, 2);
REG_FLD_MOD(base, HDMI_CORE_PHY_MASK0, 0xf3, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_IH_PHY_STAT0, 0xff, 7, 0);
/* Clear all the current interrupt bits */
REG_FLD_MOD(base, HDMI_CORE_IH_VP_STAT0, 0xff, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_IH_FC_STAT0, 0xe7, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_IH_FC_STAT1, 0xfb, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_IH_FC_STAT2, 0x3, 1, 0);
REG_FLD_MOD(base, HDMI_CORE_IH_AS_STAT0, 0x7, 2, 0);
REG_FLD_MOD(base, HDMI_CORE_IH_CEC_STAT0, 0x7f, 6, 0);
REG_FLD_MOD(base, HDMI_CORE_IH_I2CM_STAT0, 0x3, 1, 0);
REG_FLD_MOD(base, HDMI_CORE_IH_PHY_STAT0, 0xff, 7, 0);
}
static void hdmi_core_enable_interrupts(struct hdmi_core_data *core)
{
/* Unmute interrupts */
REG_FLD_MOD(core->base, HDMI_CORE_IH_MUTE, 0x0, 1, 0);
}
int hdmi5_core_handle_irqs(struct hdmi_core_data *core)
{
void __iomem *base = core->base;
REG_FLD_MOD(base, HDMI_CORE_IH_FC_STAT0, 0xff, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_IH_FC_STAT1, 0xff, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_IH_FC_STAT2, 0xff, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_IH_AS_STAT0, 0xff, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_IH_PHY_STAT0, 0xff, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_IH_I2CM_STAT0, 0xff, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_IH_CEC_STAT0, 0xff, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_IH_VP_STAT0, 0xff, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_IH_I2CMPHY_STAT0, 0xff, 7, 0);
return 0;
}
void hdmi5_configure(struct hdmi_core_data *core, struct hdmi_wp_data *wp,
struct hdmi_config *cfg)
{
struct omap_video_timings video_timing;
struct hdmi_video_format video_format;
struct hdmi_core_vid_config v_core_cfg;
hdmi_core_mask_interrupts(core);
hdmi_core_init(&v_core_cfg, cfg);
hdmi_wp_init_vid_fmt_timings(&video_format, &video_timing, cfg);
hdmi_wp_video_config_timing(wp, &video_timing);
/* video config */
video_format.packing_mode = HDMI_PACK_24b_RGB_YUV444_YUV422;
hdmi_wp_video_config_format(wp, &video_format);
hdmi_wp_video_config_interface(wp, &video_timing);
/* support limited range with 24 bit color depth for now */
hdmi_core_configure_range(core);
cfg->infoframe.quantization_range = HDMI_QUANTIZATION_RANGE_LIMITED;
/*
* configure core video part, set software reset in the core
*/
v_core_cfg.packet_mode = HDMI_PACKETMODE24BITPERPIXEL;
hdmi_core_video_config(core, &v_core_cfg);
hdmi_core_config_video_packetizer(core);
hdmi_core_config_csc(core);
hdmi_core_config_video_sampler(core);
if (cfg->hdmi_dvi_mode == HDMI_HDMI)
hdmi_core_write_avi_infoframe(core, &cfg->infoframe);
hdmi_core_enable_video_path(core);
hdmi_core_enable_interrupts(core);
}
static void hdmi5_core_audio_config(struct hdmi_core_data *core,
struct hdmi_core_audio_config *cfg)
{
void __iomem *base = core->base;
u8 val;
/* Mute audio before configuring */
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCONF, 0xf, 7, 4);
/* Set the N parameter */
REG_FLD_MOD(base, HDMI_CORE_AUD_N1, cfg->n, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_AUD_N2, cfg->n >> 8, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_AUD_N3, cfg->n >> 16, 3, 0);
/*
* CTS manual mode. Automatic mode is not supported when using audio
* parallel interface.
*/
REG_FLD_MOD(base, HDMI_CORE_AUD_CTS3, 1, 4, 4);
REG_FLD_MOD(base, HDMI_CORE_AUD_CTS1, cfg->cts, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_AUD_CTS2, cfg->cts >> 8, 7, 0);
REG_FLD_MOD(base, HDMI_CORE_AUD_CTS3, cfg->cts >> 16, 3, 0);
/* Layout of Audio Sample Packets: 2-channel or multichannels */
if (cfg->layout == HDMI_AUDIO_LAYOUT_2CH)
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCONF, 0, 0, 0);
else
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCONF, 1, 0, 0);
/* Configure IEC-609580 Validity bits */
/* Channel 0 is valid */
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSV, 0, 0, 0);
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSV, 0, 4, 4);
if (cfg->layout == HDMI_AUDIO_LAYOUT_2CH)
val = 1;
else
val = 0;
/* Channels 1, 2 setting */
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSV, val, 1, 1);
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSV, val, 5, 5);
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSV, val, 2, 2);
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSV, val, 6, 6);
/* Channel 3 setting */
if (cfg->layout == HDMI_AUDIO_LAYOUT_6CH)
val = 1;
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSV, val, 3, 3);
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSV, val, 7, 7);
/* Configure IEC-60958 User bits */
/* TODO: should be set by user. */
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSU, 0, 7, 0);
/* Configure IEC-60958 Channel Status word */
/* CGMSA */
val = cfg->iec60958_cfg->status[5] & IEC958_AES5_CON_CGMSA;
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(0), val, 5, 4);
/* Copyright */
val = (cfg->iec60958_cfg->status[0] &
IEC958_AES0_CON_NOT_COPYRIGHT) >> 2;
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(0), val, 0, 0);
/* Category */
hdmi_write_reg(base, HDMI_CORE_FC_AUDSCHNLS(1),
cfg->iec60958_cfg->status[1]);
/* PCM audio mode */
val = (cfg->iec60958_cfg->status[0] & IEC958_AES0_CON_MODE) >> 6;
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(2), val, 6, 4);
/* Source number */
val = cfg->iec60958_cfg->status[2] & IEC958_AES2_CON_SOURCE;
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(2), val, 3, 0);
/* Channel number right 0 */
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(3), 2, 3, 0);
/* Channel number right 1*/
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(3), 4, 7, 4);
/* Channel number right 2 */
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(4), 6, 3, 0);
/* Channel number right 3*/
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(4), 8, 7, 4);
/* Channel number left 0 */
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(5), 1, 3, 0);
/* Channel number left 1*/
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(5), 3, 7, 4);
/* Channel number left 2 */
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(6), 5, 3, 0);
/* Channel number left 3*/
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(6), 7, 7, 4);
/* Clock accuracy and sample rate */
hdmi_write_reg(base, HDMI_CORE_FC_AUDSCHNLS(7),
cfg->iec60958_cfg->status[3]);
/* Original sample rate and word length */
hdmi_write_reg(base, HDMI_CORE_FC_AUDSCHNLS(8),
cfg->iec60958_cfg->status[4]);
/* Enable FIFO empty and full interrupts */
REG_FLD_MOD(base, HDMI_CORE_AUD_INT, 3, 3, 2);
/* Configure GPA */
/* select HBR/SPDIF interfaces */
if (cfg->layout == HDMI_AUDIO_LAYOUT_2CH) {
/* select HBR/SPDIF interfaces */
REG_FLD_MOD(base, HDMI_CORE_AUD_CONF0, 0, 5, 5);
/* enable two channels in GPA */
REG_FLD_MOD(base, HDMI_CORE_AUD_GP_CONF1, 3, 7, 0);
} else if (cfg->layout == HDMI_AUDIO_LAYOUT_6CH) {
/* select HBR/SPDIF interfaces */
REG_FLD_MOD(base, HDMI_CORE_AUD_CONF0, 0, 5, 5);
/* enable six channels in GPA */
REG_FLD_MOD(base, HDMI_CORE_AUD_GP_CONF1, 0x3F, 7, 0);
} else {
/* select HBR/SPDIF interfaces */
REG_FLD_MOD(base, HDMI_CORE_AUD_CONF0, 0, 5, 5);
/* enable eight channels in GPA */
REG_FLD_MOD(base, HDMI_CORE_AUD_GP_CONF1, 0xFF, 7, 0);
}
/* disable HBR */
REG_FLD_MOD(base, HDMI_CORE_AUD_GP_CONF2, 0, 0, 0);
/* enable PCUV */
REG_FLD_MOD(base, HDMI_CORE_AUD_GP_CONF2, 1, 1, 1);
/* enable GPA FIFO full and empty mask */
REG_FLD_MOD(base, HDMI_CORE_AUD_GP_MASK, 3, 1, 0);
/* set polarity of GPA FIFO empty interrupts */
REG_FLD_MOD(base, HDMI_CORE_AUD_GP_POL, 1, 0, 0);
/* unmute audio */
REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCONF, 0, 7, 4);
}
static void hdmi5_core_audio_infoframe_cfg(struct hdmi_core_data *core,
struct snd_cea_861_aud_if *info_aud)
{
void __iomem *base = core->base;
/* channel count and coding type fields in AUDICONF0 are swapped */
hdmi_write_reg(base, HDMI_CORE_FC_AUDICONF0,
(info_aud->db1_ct_cc & CEA861_AUDIO_INFOFRAME_DB1CC) << 4 |
(info_aud->db1_ct_cc & CEA861_AUDIO_INFOFRAME_DB1CT) >> 4);
hdmi_write_reg(base, HDMI_CORE_FC_AUDICONF1, info_aud->db2_sf_ss);
hdmi_write_reg(base, HDMI_CORE_FC_AUDICONF2, info_aud->db4_ca);
hdmi_write_reg(base, HDMI_CORE_FC_AUDICONF3,
(info_aud->db5_dminh_lsv & CEA861_AUDIO_INFOFRAME_DB5_DM_INH) >> 3 |
(info_aud->db5_dminh_lsv & CEA861_AUDIO_INFOFRAME_DB5_LSV));
}
int hdmi5_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp,
struct omap_dss_audio *audio, u32 pclk)
{
struct hdmi_audio_format audio_format;
struct hdmi_audio_dma audio_dma;
struct hdmi_core_audio_config core_cfg;
int err, n, cts, channel_count;
unsigned int fs_nr;
bool word_length_16b = false;
if (!audio || !audio->iec || !audio->cea || !core)
return -EINVAL;
core_cfg.iec60958_cfg = audio->iec;
if (!(audio->iec->status[4] & IEC958_AES4_CON_MAX_WORDLEN_24) &&
(audio->iec->status[4] & IEC958_AES4_CON_WORDLEN_20_16))
word_length_16b = true;
/* only 16-bit word length supported atm */
if (!word_length_16b)
return -EINVAL;
switch (audio->iec->status[3] & IEC958_AES3_CON_FS) {
case IEC958_AES3_CON_FS_32000:
fs_nr = 32000;
break;
case IEC958_AES3_CON_FS_44100:
fs_nr = 44100;
break;
case IEC958_AES3_CON_FS_48000:
fs_nr = 48000;
break;
case IEC958_AES3_CON_FS_88200:
fs_nr = 88200;
break;
case IEC958_AES3_CON_FS_96000:
fs_nr = 96000;
break;
case IEC958_AES3_CON_FS_176400:
fs_nr = 176400;
break;
case IEC958_AES3_CON_FS_192000:
fs_nr = 192000;
break;
default:
return -EINVAL;
}
err = hdmi_compute_acr(pclk, fs_nr, &n, &cts);
core_cfg.n = n;
core_cfg.cts = cts;
/* Audio channels settings */
channel_count = (audio->cea->db1_ct_cc & CEA861_AUDIO_INFOFRAME_DB1CC)
+ 1;
if (channel_count == 2)
core_cfg.layout = HDMI_AUDIO_LAYOUT_2CH;
else if (channel_count == 6)
core_cfg.layout = HDMI_AUDIO_LAYOUT_6CH;
else
core_cfg.layout = HDMI_AUDIO_LAYOUT_8CH;
/* DMA settings */
if (word_length_16b)
audio_dma.transfer_size = 0x10;
else
audio_dma.transfer_size = 0x20;
audio_dma.block_size = 0xC0;
audio_dma.mode = HDMI_AUDIO_TRANSF_DMA;
audio_dma.fifo_threshold = 0x20; /* in number of samples */
/* audio FIFO format settings for 16-bit samples*/
audio_format.samples_per_word = HDMI_AUDIO_ONEWORD_TWOSAMPLES;
audio_format.sample_size = HDMI_AUDIO_SAMPLE_16BITS;
audio_format.justification = HDMI_AUDIO_JUSTIFY_LEFT;
audio_format.sample_order = HDMI_AUDIO_SAMPLE_LEFT_FIRST;
/* only LPCM atm */
audio_format.type = HDMI_AUDIO_TYPE_LPCM;
/* only allowed option */
audio_format.sample_order = HDMI_AUDIO_SAMPLE_LEFT_FIRST;
/* disable start/stop signals of IEC 60958 blocks */
audio_format.en_sig_blk_strt_end = HDMI_AUDIO_BLOCK_SIG_STARTEND_ON;
/* configure DMA and audio FIFO format*/
hdmi_wp_audio_config_dma(wp, &audio_dma);
hdmi_wp_audio_config_format(wp, &audio_format);
/* configure the core */
hdmi5_core_audio_config(core, &core_cfg);
/* configure CEA 861 audio infoframe */
hdmi5_core_audio_infoframe_cfg(core, audio->cea);
return 0;
}
int hdmi5_core_init(struct platform_device *pdev, struct hdmi_core_data *core)
{
struct resource *res;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core");
if (!res) {
DSSERR("can't get CORE IORESOURCE_MEM HDMI\n");
return -EINVAL;
}
core->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(core->base)) {
DSSERR("can't ioremap HDMI core\n");
return PTR_ERR(core->base);
}
return 0;
}

View File

@ -0,0 +1,304 @@
/*
* HDMI driver definition for TI OMAP5 processors.
*
* Copyright (C) 2011-2012 Texas Instruments Incorporated - http://www.ti.com/
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _HDMI5_CORE_H_
#define _HDMI5_CORE_H_
#include "hdmi.h"
/* HDMI IP Core System */
/* HDMI Identification */
#define HDMI_CORE_DESIGN_ID 0x00000
#define HDMI_CORE_REVISION_ID 0x00004
#define HDMI_CORE_PRODUCT_ID0 0x00008
#define HDMI_CORE_PRODUCT_ID1 0x0000C
#define HDMI_CORE_CONFIG0_ID 0x00010
#define HDMI_CORE_CONFIG1_ID 0x00014
#define HDMI_CORE_CONFIG2_ID 0x00018
#define HDMI_CORE_CONFIG3_ID 0x0001C
/* HDMI Interrupt */
#define HDMI_CORE_IH_FC_STAT0 0x00400
#define HDMI_CORE_IH_FC_STAT1 0x00404
#define HDMI_CORE_IH_FC_STAT2 0x00408
#define HDMI_CORE_IH_AS_STAT0 0x0040C
#define HDMI_CORE_IH_PHY_STAT0 0x00410
#define HDMI_CORE_IH_I2CM_STAT0 0x00414
#define HDMI_CORE_IH_CEC_STAT0 0x00418
#define HDMI_CORE_IH_VP_STAT0 0x0041C
#define HDMI_CORE_IH_I2CMPHY_STAT0 0x00420
#define HDMI_CORE_IH_MUTE 0x007FC
/* HDMI Video Sampler */
#define HDMI_CORE_TX_INVID0 0x00800
#define HDMI_CORE_TX_INSTUFFING 0x00804
#define HDMI_CORE_TX_RGYDATA0 0x00808
#define HDMI_CORE_TX_RGYDATA1 0x0080C
#define HDMI_CORE_TX_RCRDATA0 0x00810
#define HDMI_CORE_TX_RCRDATA1 0x00814
#define HDMI_CORE_TX_BCBDATA0 0x00818
#define HDMI_CORE_TX_BCBDATA1 0x0081C
/* HDMI Video Packetizer */
#define HDMI_CORE_VP_STATUS 0x02000
#define HDMI_CORE_VP_PR_CD 0x02004
#define HDMI_CORE_VP_STUFF 0x02008
#define HDMI_CORE_VP_REMAP 0x0200C
#define HDMI_CORE_VP_CONF 0x02010
#define HDMI_CORE_VP_STAT 0x02014
#define HDMI_CORE_VP_INT 0x02018
#define HDMI_CORE_VP_MASK 0x0201C
#define HDMI_CORE_VP_POL 0x02020
/* Frame Composer */
#define HDMI_CORE_FC_INVIDCONF 0x04000
#define HDMI_CORE_FC_INHACTIV0 0x04004
#define HDMI_CORE_FC_INHACTIV1 0x04008
#define HDMI_CORE_FC_INHBLANK0 0x0400C
#define HDMI_CORE_FC_INHBLANK1 0x04010
#define HDMI_CORE_FC_INVACTIV0 0x04014
#define HDMI_CORE_FC_INVACTIV1 0x04018
#define HDMI_CORE_FC_INVBLANK 0x0401C
#define HDMI_CORE_FC_HSYNCINDELAY0 0x04020
#define HDMI_CORE_FC_HSYNCINDELAY1 0x04024
#define HDMI_CORE_FC_HSYNCINWIDTH0 0x04028
#define HDMI_CORE_FC_HSYNCINWIDTH1 0x0402C
#define HDMI_CORE_FC_VSYNCINDELAY 0x04030
#define HDMI_CORE_FC_VSYNCINWIDTH 0x04034
#define HDMI_CORE_FC_INFREQ0 0x04038
#define HDMI_CORE_FC_INFREQ1 0x0403C
#define HDMI_CORE_FC_INFREQ2 0x04040
#define HDMI_CORE_FC_CTRLDUR 0x04044
#define HDMI_CORE_FC_EXCTRLDUR 0x04048
#define HDMI_CORE_FC_EXCTRLSPAC 0x0404C
#define HDMI_CORE_FC_CH0PREAM 0x04050
#define HDMI_CORE_FC_CH1PREAM 0x04054
#define HDMI_CORE_FC_CH2PREAM 0x04058
#define HDMI_CORE_FC_AVICONF3 0x0405C
#define HDMI_CORE_FC_GCP 0x04060
#define HDMI_CORE_FC_AVICONF0 0x04064
#define HDMI_CORE_FC_AVICONF1 0x04068
#define HDMI_CORE_FC_AVICONF2 0x0406C
#define HDMI_CORE_FC_AVIVID 0x04070
#define HDMI_CORE_FC_AVIETB0 0x04074
#define HDMI_CORE_FC_AVIETB1 0x04078
#define HDMI_CORE_FC_AVISBB0 0x0407C
#define HDMI_CORE_FC_AVISBB1 0x04080
#define HDMI_CORE_FC_AVIELB0 0x04084
#define HDMI_CORE_FC_AVIELB1 0x04088
#define HDMI_CORE_FC_AVISRB0 0x0408C
#define HDMI_CORE_FC_AVISRB1 0x04090
#define HDMI_CORE_FC_AUDICONF0 0x04094
#define HDMI_CORE_FC_AUDICONF1 0x04098
#define HDMI_CORE_FC_AUDICONF2 0x0409C
#define HDMI_CORE_FC_AUDICONF3 0x040A0
#define HDMI_CORE_FC_VSDIEEEID0 0x040A4
#define HDMI_CORE_FC_VSDSIZE 0x040A8
#define HDMI_CORE_FC_VSDIEEEID1 0x040C0
#define HDMI_CORE_FC_VSDIEEEID2 0x040C4
#define HDMI_CORE_FC_VSDPAYLOAD(n) (n * 4 + 0x040C8)
#define HDMI_CORE_FC_SPDVENDORNAME(n) (n * 4 + 0x04128)
#define HDMI_CORE_FC_SPDPRODUCTNAME(n) (n * 4 + 0x04148)
#define HDMI_CORE_FC_SPDDEVICEINF 0x04188
#define HDMI_CORE_FC_AUDSCONF 0x0418C
#define HDMI_CORE_FC_AUDSSTAT 0x04190
#define HDMI_CORE_FC_AUDSV 0x04194
#define HDMI_CORE_FC_AUDSU 0x04198
#define HDMI_CORE_FC_AUDSCHNLS(n) (n * 4 + 0x0419C)
#define HDMI_CORE_FC_CTRLQHIGH 0x041CC
#define HDMI_CORE_FC_CTRLQLOW 0x041D0
#define HDMI_CORE_FC_ACP0 0x041D4
#define HDMI_CORE_FC_ACP(n) ((16-n) * 4 + 0x04208)
#define HDMI_CORE_FC_ISCR1_0 0x04248
#define HDMI_CORE_FC_ISCR1(n) ((16-n) * 4 + 0x0424C)
#define HDMI_CORE_FC_ISCR2(n) ((15-n) * 4 + 0x0428C)
#define HDMI_CORE_FC_DATAUTO0 0x042CC
#define HDMI_CORE_FC_DATAUTO1 0x042D0
#define HDMI_CORE_FC_DATAUTO2 0x042D4
#define HDMI_CORE_FC_DATMAN 0x042D8
#define HDMI_CORE_FC_DATAUTO3 0x042DC
#define HDMI_CORE_FC_RDRB(n) (n * 4 + 0x042E0)
#define HDMI_CORE_FC_STAT0 0x04340
#define HDMI_CORE_FC_INT0 0x04344
#define HDMI_CORE_FC_MASK0 0x04348
#define HDMI_CORE_FC_POL0 0x0434C
#define HDMI_CORE_FC_STAT1 0x04350
#define HDMI_CORE_FC_INT1 0x04354
#define HDMI_CORE_FC_MASK1 0x04358
#define HDMI_CORE_FC_POL1 0x0435C
#define HDMI_CORE_FC_STAT2 0x04360
#define HDMI_CORE_FC_INT2 0x04364
#define HDMI_CORE_FC_MASK2 0x04368
#define HDMI_CORE_FC_POL2 0x0436C
#define HDMI_CORE_FC_PRCONF 0x04380
#define HDMI_CORE_FC_GMD_STAT 0x04400
#define HDMI_CORE_FC_GMD_EN 0x04404
#define HDMI_CORE_FC_GMD_UP 0x04408
#define HDMI_CORE_FC_GMD_CONF 0x0440C
#define HDMI_CORE_FC_GMD_HB 0x04410
#define HDMI_CORE_FC_GMD_PB(n) (n * 4 + 0x04414)
#define HDMI_CORE_FC_DBGFORCE 0x04800
#define HDMI_CORE_FC_DBGAUD0CH0 0x04804
#define HDMI_CORE_FC_DBGAUD1CH0 0x04808
#define HDMI_CORE_FC_DBGAUD2CH0 0x0480C
#define HDMI_CORE_FC_DBGAUD0CH1 0x04810
#define HDMI_CORE_FC_DBGAUD1CH1 0x04814
#define HDMI_CORE_FC_DBGAUD2CH1 0x04818
#define HDMI_CORE_FC_DBGAUD0CH2 0x0481C
#define HDMI_CORE_FC_DBGAUD1CH2 0x04820
#define HDMI_CORE_FC_DBGAUD2CH2 0x04824
#define HDMI_CORE_FC_DBGAUD0CH3 0x04828
#define HDMI_CORE_FC_DBGAUD1CH3 0x0482C
#define HDMI_CORE_FC_DBGAUD2CH3 0x04830
#define HDMI_CORE_FC_DBGAUD0CH4 0x04834
#define HDMI_CORE_FC_DBGAUD1CH4 0x04838
#define HDMI_CORE_FC_DBGAUD2CH4 0x0483C
#define HDMI_CORE_FC_DBGAUD0CH5 0x04840
#define HDMI_CORE_FC_DBGAUD1CH5 0x04844
#define HDMI_CORE_FC_DBGAUD2CH5 0x04848
#define HDMI_CORE_FC_DBGAUD0CH6 0x0484C
#define HDMI_CORE_FC_DBGAUD1CH6 0x04850
#define HDMI_CORE_FC_DBGAUD2CH6 0x04854
#define HDMI_CORE_FC_DBGAUD0CH7 0x04858
#define HDMI_CORE_FC_DBGAUD1CH7 0x0485C
#define HDMI_CORE_FC_DBGAUD2CH7 0x04860
#define HDMI_CORE_FC_DBGTMDS0 0x04864
#define HDMI_CORE_FC_DBGTMDS1 0x04868
#define HDMI_CORE_FC_DBGTMDS2 0x0486C
#define HDMI_CORE_PHY_MASK0 0x0C018
#define HDMI_CORE_PHY_I2CM_INT_ADDR 0x0C09C
#define HDMI_CORE_PHY_I2CM_CTLINT_ADDR 0x0C0A0
/* HDMI Audio */
#define HDMI_CORE_AUD_CONF0 0x0C400
#define HDMI_CORE_AUD_CONF1 0x0C404
#define HDMI_CORE_AUD_INT 0x0C408
#define HDMI_CORE_AUD_N1 0x0C800
#define HDMI_CORE_AUD_N2 0x0C804
#define HDMI_CORE_AUD_N3 0x0C808
#define HDMI_CORE_AUD_CTS1 0x0C80C
#define HDMI_CORE_AUD_CTS2 0x0C810
#define HDMI_CORE_AUD_CTS3 0x0C814
#define HDMI_CORE_AUD_INCLKFS 0x0C818
#define HDMI_CORE_AUD_CC08 0x0CC08
#define HDMI_CORE_AUD_GP_CONF0 0x0D400
#define HDMI_CORE_AUD_GP_CONF1 0x0D404
#define HDMI_CORE_AUD_GP_CONF2 0x0D408
#define HDMI_CORE_AUD_D010 0x0D010
#define HDMI_CORE_AUD_GP_STAT 0x0D40C
#define HDMI_CORE_AUD_GP_INT 0x0D410
#define HDMI_CORE_AUD_GP_POL 0x0D414
#define HDMI_CORE_AUD_GP_MASK 0x0D418
/* HDMI Main Controller */
#define HDMI_CORE_MC_CLKDIS 0x10004
#define HDMI_CORE_MC_SWRSTZREQ 0x10008
#define HDMI_CORE_MC_FLOWCTRL 0x10010
#define HDMI_CORE_MC_PHYRSTZ 0x10014
#define HDMI_CORE_MC_LOCKONCLOCK 0x10018
/* HDMI COLOR SPACE CONVERTER */
#define HDMI_CORE_CSC_CFG 0x10400
#define HDMI_CORE_CSC_SCALE 0x10404
#define HDMI_CORE_CSC_COEF_A1_MSB 0x10408
#define HDMI_CORE_CSC_COEF_A1_LSB 0x1040C
#define HDMI_CORE_CSC_COEF_A2_MSB 0x10410
#define HDMI_CORE_CSC_COEF_A2_LSB 0x10414
#define HDMI_CORE_CSC_COEF_A3_MSB 0x10418
#define HDMI_CORE_CSC_COEF_A3_LSB 0x1041C
#define HDMI_CORE_CSC_COEF_A4_MSB 0x10420
#define HDMI_CORE_CSC_COEF_A4_LSB 0x10424
#define HDMI_CORE_CSC_COEF_B1_MSB 0x10428
#define HDMI_CORE_CSC_COEF_B1_LSB 0x1042C
#define HDMI_CORE_CSC_COEF_B2_MSB 0x10430
#define HDMI_CORE_CSC_COEF_B2_LSB 0x10434
#define HDMI_CORE_CSC_COEF_B3_MSB 0x10438
#define HDMI_CORE_CSC_COEF_B3_LSB 0x1043C
#define HDMI_CORE_CSC_COEF_B4_MSB 0x10440
#define HDMI_CORE_CSC_COEF_B4_LSB 0x10444
#define HDMI_CORE_CSC_COEF_C1_MSB 0x10448
#define HDMI_CORE_CSC_COEF_C1_LSB 0x1044C
#define HDMI_CORE_CSC_COEF_C2_MSB 0x10450
#define HDMI_CORE_CSC_COEF_C2_LSB 0x10454
#define HDMI_CORE_CSC_COEF_C3_MSB 0x10458
#define HDMI_CORE_CSC_COEF_C3_LSB 0x1045C
#define HDMI_CORE_CSC_COEF_C4_MSB 0x10460
#define HDMI_CORE_CSC_COEF_C4_LSB 0x10464
/* HDMI HDCP */
#define HDMI_CORE_HDCP_MASK 0x14020
/* HDMI CEC */
#define HDMI_CORE_CEC_MASK 0x17408
/* HDMI I2C Master */
#define HDMI_CORE_I2CM_SLAVE 0x157C8
#define HDMI_CORE_I2CM_ADDRESS 0x157CC
#define HDMI_CORE_I2CM_DATAO 0x157D0
#define HDMI_CORE_I2CM_DATAI 0X157D4
#define HDMI_CORE_I2CM_OPERATION 0x157D8
#define HDMI_CORE_I2CM_INT 0x157DC
#define HDMI_CORE_I2CM_CTLINT 0x157E0
#define HDMI_CORE_I2CM_DIV 0x157E4
#define HDMI_CORE_I2CM_SEGADDR 0x157E8
#define HDMI_CORE_I2CM_SOFTRSTZ 0x157EC
#define HDMI_CORE_I2CM_SEGPTR 0x157F0
#define HDMI_CORE_I2CM_SS_SCL_HCNT_1_ADDR 0x157F4
#define HDMI_CORE_I2CM_SS_SCL_HCNT_0_ADDR 0x157F8
#define HDMI_CORE_I2CM_SS_SCL_LCNT_1_ADDR 0x157FC
#define HDMI_CORE_I2CM_SS_SCL_LCNT_0_ADDR 0x15800
#define HDMI_CORE_I2CM_FS_SCL_HCNT_1_ADDR 0x15804
#define HDMI_CORE_I2CM_FS_SCL_HCNT_0_ADDR 0x15808
#define HDMI_CORE_I2CM_FS_SCL_LCNT_1_ADDR 0x1580C
#define HDMI_CORE_I2CM_FS_SCL_LCNT_0_ADDR 0x15810
#define HDMI_CORE_I2CM_SDA_HOLD_ADDR 0x15814
enum hdmi_core_packet_mode {
HDMI_PACKETMODERESERVEDVALUE = 0,
HDMI_PACKETMODE24BITPERPIXEL = 4,
HDMI_PACKETMODE30BITPERPIXEL = 5,
HDMI_PACKETMODE36BITPERPIXEL = 6,
HDMI_PACKETMODE48BITPERPIXEL = 7,
};
struct hdmi_core_vid_config {
struct hdmi_config v_fc_config;
enum hdmi_core_packet_mode packet_mode;
int data_enable_pol;
int vblank_osc;
int hblank;
int vblank;
};
struct csc_table {
u16 a1, a2, a3, a4;
u16 b1, b2, b3, b4;
u16 c1, c2, c3, c4;
};
int hdmi5_read_edid(struct hdmi_core_data *core, u8 *edid, int len);
void hdmi5_core_dump(struct hdmi_core_data *core, struct seq_file *s);
int hdmi5_core_handle_irqs(struct hdmi_core_data *core);
void hdmi5_configure(struct hdmi_core_data *core, struct hdmi_wp_data *wp,
struct hdmi_config *cfg);
int hdmi5_core_init(struct platform_device *pdev, struct hdmi_core_data *core);
int hdmi5_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp,
struct omap_dss_audio *audio, u32 pclk);
#endif

View File

@ -0,0 +1,148 @@
#define DSS_SUBSYS_NAME "HDMI"
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/of.h>
#include <video/omapdss.h>
#include "hdmi.h"
int hdmi_parse_lanes_of(struct platform_device *pdev, struct device_node *ep,
struct hdmi_phy_data *phy)
{
struct property *prop;
int r, len;
prop = of_find_property(ep, "lanes", &len);
if (prop) {
u32 lanes[8];
if (len / sizeof(u32) != ARRAY_SIZE(lanes)) {
dev_err(&pdev->dev, "bad number of lanes\n");
return -EINVAL;
}
r = of_property_read_u32_array(ep, "lanes", lanes,
ARRAY_SIZE(lanes));
if (r) {
dev_err(&pdev->dev, "failed to read lane data\n");
return r;
}
r = hdmi_phy_parse_lanes(phy, lanes);
if (r) {
dev_err(&pdev->dev, "failed to parse lane data\n");
return r;
}
} else {
static const u32 default_lanes[] = { 0, 1, 2, 3, 4, 5, 6, 7 };
r = hdmi_phy_parse_lanes(phy, default_lanes);
if (WARN_ON(r)) {
dev_err(&pdev->dev, "failed to parse lane data\n");
return r;
}
}
return 0;
}
int hdmi_compute_acr(u32 pclk, u32 sample_freq, u32 *n, u32 *cts)
{
u32 deep_color;
bool deep_color_correct = false;
if (n == NULL || cts == NULL)
return -EINVAL;
/* TODO: When implemented, query deep color mode here. */
deep_color = 100;
/*
* When using deep color, the default N value (as in the HDMI
* specification) yields to an non-integer CTS. Hence, we
* modify it while keeping the restrictions described in
* section 7.2.1 of the HDMI 1.4a specification.
*/
switch (sample_freq) {
case 32000:
case 48000:
case 96000:
case 192000:
if (deep_color == 125)
if (pclk == 27027000 || pclk == 74250000)
deep_color_correct = true;
if (deep_color == 150)
if (pclk == 27027000)
deep_color_correct = true;
break;
case 44100:
case 88200:
case 176400:
if (deep_color == 125)
if (pclk == 27027000)
deep_color_correct = true;
break;
default:
return -EINVAL;
}
if (deep_color_correct) {
switch (sample_freq) {
case 32000:
*n = 8192;
break;
case 44100:
*n = 12544;
break;
case 48000:
*n = 8192;
break;
case 88200:
*n = 25088;
break;
case 96000:
*n = 16384;
break;
case 176400:
*n = 50176;
break;
case 192000:
*n = 32768;
break;
default:
return -EINVAL;
}
} else {
switch (sample_freq) {
case 32000:
*n = 4096;
break;
case 44100:
*n = 6272;
break;
case 48000:
*n = 6144;
break;
case 88200:
*n = 12544;
break;
case 96000:
*n = 12288;
break;
case 176400:
*n = 25088;
break;
case 192000:
*n = 24576;
break;
default:
return -EINVAL;
}
}
/* Calculate CTS. See HDMI 1.3a or 1.4a specifications */
*cts = (pclk/1000) * (*n / 128) * deep_color / (sample_freq / 10);
return 0;
}

View File

@ -0,0 +1,247 @@
/*
* HDMI PHY
*
* Copyright (C) 2013 Texas Instruments Incorporated
*
* 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/kernel.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <video/omapdss.h>
#include "dss.h"
#include "hdmi.h"
struct hdmi_phy_features {
bool bist_ctrl;
bool ldo_voltage;
unsigned long max_phy;
};
static const struct hdmi_phy_features *phy_feat;
void hdmi_phy_dump(struct hdmi_phy_data *phy, struct seq_file *s)
{
#define DUMPPHY(r) seq_printf(s, "%-35s %08x\n", #r,\
hdmi_read_reg(phy->base, r))
DUMPPHY(HDMI_TXPHY_TX_CTRL);
DUMPPHY(HDMI_TXPHY_DIGITAL_CTRL);
DUMPPHY(HDMI_TXPHY_POWER_CTRL);
DUMPPHY(HDMI_TXPHY_PAD_CFG_CTRL);
if (phy_feat->bist_ctrl)
DUMPPHY(HDMI_TXPHY_BIST_CONTROL);
}
int hdmi_phy_parse_lanes(struct hdmi_phy_data *phy, const u32 *lanes)
{
int i;
for (i = 0; i < 8; i += 2) {
u8 lane, pol;
int dx, dy;
dx = lanes[i];
dy = lanes[i + 1];
if (dx < 0 || dx >= 8)
return -EINVAL;
if (dy < 0 || dy >= 8)
return -EINVAL;
if (dx & 1) {
if (dy != dx - 1)
return -EINVAL;
pol = 1;
} else {
if (dy != dx + 1)
return -EINVAL;
pol = 0;
}
lane = dx / 2;
phy->lane_function[lane] = i / 2;
phy->lane_polarity[lane] = pol;
}
return 0;
}
static void hdmi_phy_configure_lanes(struct hdmi_phy_data *phy)
{
static const u16 pad_cfg_list[] = {
0x0123,
0x0132,
0x0312,
0x0321,
0x0231,
0x0213,
0x1023,
0x1032,
0x3012,
0x3021,
0x2031,
0x2013,
0x1203,
0x1302,
0x3102,
0x3201,
0x2301,
0x2103,
0x1230,
0x1320,
0x3120,
0x3210,
0x2310,
0x2130,
};
u16 lane_cfg = 0;
int i;
unsigned lane_cfg_val;
u16 pol_val = 0;
for (i = 0; i < 4; ++i)
lane_cfg |= phy->lane_function[i] << ((3 - i) * 4);
pol_val |= phy->lane_polarity[0] << 0;
pol_val |= phy->lane_polarity[1] << 3;
pol_val |= phy->lane_polarity[2] << 2;
pol_val |= phy->lane_polarity[3] << 1;
for (i = 0; i < ARRAY_SIZE(pad_cfg_list); ++i)
if (pad_cfg_list[i] == lane_cfg)
break;
if (WARN_ON(i == ARRAY_SIZE(pad_cfg_list)))
i = 0;
lane_cfg_val = i;
REG_FLD_MOD(phy->base, HDMI_TXPHY_PAD_CFG_CTRL, lane_cfg_val, 26, 22);
REG_FLD_MOD(phy->base, HDMI_TXPHY_PAD_CFG_CTRL, pol_val, 30, 27);
}
int hdmi_phy_configure(struct hdmi_phy_data *phy, unsigned long hfbitclk,
unsigned long lfbitclk)
{
u8 freqout;
/*
* Read address 0 in order to get the SCP reset done completed
* Dummy access performed to make sure reset is done
*/
hdmi_read_reg(phy->base, HDMI_TXPHY_TX_CTRL);
/*
* In OMAP5+, the HFBITCLK must be divided by 2 before issuing the
* HDMI_PHYPWRCMD_LDOON command.
*/
if (phy_feat->bist_ctrl)
REG_FLD_MOD(phy->base, HDMI_TXPHY_BIST_CONTROL, 1, 11, 11);
/*
* If the hfbitclk != lfbitclk, it means the lfbitclk was configured
* to be used for TMDS.
*/
if (hfbitclk != lfbitclk)
freqout = 0;
else if (hfbitclk / 10 < phy_feat->max_phy)
freqout = 1;
else
freqout = 2;
/*
* Write to phy address 0 to configure the clock
* use HFBITCLK write HDMI_TXPHY_TX_CONTROL_FREQOUT field
*/
REG_FLD_MOD(phy->base, HDMI_TXPHY_TX_CTRL, freqout, 31, 30);
/* Write to phy address 1 to start HDMI line (TXVALID and TMDSCLKEN) */
hdmi_write_reg(phy->base, HDMI_TXPHY_DIGITAL_CTRL, 0xF0000000);
/* Setup max LDO voltage */
if (phy_feat->ldo_voltage)
REG_FLD_MOD(phy->base, HDMI_TXPHY_POWER_CTRL, 0xB, 3, 0);
hdmi_phy_configure_lanes(phy);
return 0;
}
static const struct hdmi_phy_features omap44xx_phy_feats = {
.bist_ctrl = false,
.ldo_voltage = true,
.max_phy = 185675000,
};
static const struct hdmi_phy_features omap54xx_phy_feats = {
.bist_ctrl = true,
.ldo_voltage = false,
.max_phy = 186000000,
};
static int hdmi_phy_init_features(struct platform_device *pdev)
{
struct hdmi_phy_features *dst;
const struct hdmi_phy_features *src;
dst = devm_kzalloc(&pdev->dev, sizeof(*dst), GFP_KERNEL);
if (!dst) {
dev_err(&pdev->dev, "Failed to allocate HDMI PHY Features\n");
return -ENOMEM;
}
switch (omapdss_get_version()) {
case OMAPDSS_VER_OMAP4430_ES1:
case OMAPDSS_VER_OMAP4430_ES2:
case OMAPDSS_VER_OMAP4:
src = &omap44xx_phy_feats;
break;
case OMAPDSS_VER_OMAP5:
case OMAPDSS_VER_DRA7xx:
src = &omap54xx_phy_feats;
break;
default:
return -ENODEV;
}
memcpy(dst, src, sizeof(*dst));
phy_feat = dst;
return 0;
}
int hdmi_phy_init(struct platform_device *pdev, struct hdmi_phy_data *phy)
{
int r;
struct resource *res;
r = hdmi_phy_init_features(pdev);
if (r)
return r;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy");
if (!res) {
DSSERR("can't get PHY mem resource\n");
return -EINVAL;
}
phy->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(phy->base)) {
DSSERR("can't ioremap TX PHY\n");
return PTR_ERR(phy->base);
}
return 0;
}

View File

@ -0,0 +1,255 @@
/*
* HDMI PLL
*
* Copyright (C) 2013 Texas Instruments Incorporated
*
* 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.
*/
#define DSS_SUBSYS_NAME "HDMIPLL"
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <video/omapdss.h>
#include "dss.h"
#include "hdmi.h"
void hdmi_pll_dump(struct hdmi_pll_data *pll, struct seq_file *s)
{
#define DUMPPLL(r) seq_printf(s, "%-35s %08x\n", #r,\
hdmi_read_reg(pll->base, r))
DUMPPLL(PLLCTRL_PLL_CONTROL);
DUMPPLL(PLLCTRL_PLL_STATUS);
DUMPPLL(PLLCTRL_PLL_GO);
DUMPPLL(PLLCTRL_CFG1);
DUMPPLL(PLLCTRL_CFG2);
DUMPPLL(PLLCTRL_CFG3);
DUMPPLL(PLLCTRL_SSC_CFG1);
DUMPPLL(PLLCTRL_SSC_CFG2);
DUMPPLL(PLLCTRL_CFG4);
}
void hdmi_pll_compute(struct hdmi_pll_data *pll,
unsigned long target_tmds, struct dss_pll_clock_info *pi)
{
unsigned long fint, clkdco, clkout;
unsigned long target_bitclk, target_clkdco;
unsigned long min_dco;
unsigned n, m, mf, m2, sd;
unsigned long clkin;
const struct dss_pll_hw *hw = pll->pll.hw;
clkin = clk_get_rate(pll->pll.clkin);
DSSDBG("clkin %lu, target tmds %lu\n", clkin, target_tmds);
target_bitclk = target_tmds * 10;
/* Fint */
n = DIV_ROUND_UP(clkin, hw->fint_max);
fint = clkin / n;
/* adjust m2 so that the clkdco will be high enough */
min_dco = roundup(hw->clkdco_min, fint);
m2 = DIV_ROUND_UP(min_dco, target_bitclk);
if (m2 == 0)
m2 = 1;
target_clkdco = target_bitclk * m2;
m = target_clkdco / fint;
clkdco = fint * m;
/* adjust clkdco with fractional mf */
if (WARN_ON(target_clkdco - clkdco > fint))
mf = 0;
else
mf = (u32)div_u64(262144ull * (target_clkdco - clkdco), fint);
if (mf > 0)
clkdco += (u32)div_u64((u64)mf * fint, 262144);
clkout = clkdco / m2;
/* sigma-delta */
sd = DIV_ROUND_UP(fint * m, 250000000);
DSSDBG("N = %u, M = %u, M.f = %u, M2 = %u, SD = %u\n",
n, m, mf, m2, sd);
DSSDBG("Fint %lu, clkdco %lu, clkout %lu\n", fint, clkdco, clkout);
pi->n = n;
pi->m = m;
pi->mf = mf;
pi->mX[0] = m2;
pi->sd = sd;
pi->fint = fint;
pi->clkdco = clkdco;
pi->clkout[0] = clkout;
}
static int hdmi_pll_enable(struct dss_pll *dsspll)
{
struct hdmi_pll_data *pll = container_of(dsspll, struct hdmi_pll_data, pll);
struct hdmi_wp_data *wp = pll->wp;
u16 r = 0;
dss_ctrl_pll_enable(DSS_PLL_HDMI, true);
r = hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_BOTHON_ALLCLKS);
if (r)
return r;
return 0;
}
static void hdmi_pll_disable(struct dss_pll *dsspll)
{
struct hdmi_pll_data *pll = container_of(dsspll, struct hdmi_pll_data, pll);
struct hdmi_wp_data *wp = pll->wp;
hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_ALLOFF);
dss_ctrl_pll_enable(DSS_PLL_HDMI, false);
}
static const struct dss_pll_ops dsi_pll_ops = {
.enable = hdmi_pll_enable,
.disable = hdmi_pll_disable,
.set_config = dss_pll_write_config_type_b,
};
static const struct dss_pll_hw dss_omap4_hdmi_pll_hw = {
.n_max = 255,
.m_min = 20,
.m_max = 4095,
.mX_max = 127,
.fint_min = 500000,
.fint_max = 2500000,
.clkdco_min = 500000000,
.clkdco_low = 1000000000,
.clkdco_max = 2000000000,
.n_msb = 8,
.n_lsb = 1,
.m_msb = 20,
.m_lsb = 9,
.mX_msb[0] = 24,
.mX_lsb[0] = 18,
.has_selfreqdco = true,
};
static const struct dss_pll_hw dss_omap5_hdmi_pll_hw = {
.n_max = 255,
.m_min = 20,
.m_max = 2045,
.mX_max = 127,
.fint_min = 620000,
.fint_max = 2500000,
.clkdco_min = 750000000,
.clkdco_low = 1500000000,
.clkdco_max = 2500000000UL,
.n_msb = 8,
.n_lsb = 1,
.m_msb = 20,
.m_lsb = 9,
.mX_msb[0] = 24,
.mX_lsb[0] = 18,
.has_selfreqdco = true,
.has_refsel = true,
};
static int dsi_init_pll_data(struct platform_device *pdev, struct hdmi_pll_data *hpll)
{
struct dss_pll *pll = &hpll->pll;
struct clk *clk;
int r;
clk = devm_clk_get(&pdev->dev, "sys_clk");
if (IS_ERR(clk)) {
DSSERR("can't get sys_clk\n");
return PTR_ERR(clk);
}
pll->name = "hdmi";
pll->id = DSS_PLL_HDMI;
pll->base = hpll->base;
pll->clkin = clk;
switch (omapdss_get_version()) {
case OMAPDSS_VER_OMAP4430_ES1:
case OMAPDSS_VER_OMAP4430_ES2:
case OMAPDSS_VER_OMAP4:
pll->hw = &dss_omap4_hdmi_pll_hw;
break;
case OMAPDSS_VER_OMAP5:
case OMAPDSS_VER_DRA7xx:
pll->hw = &dss_omap5_hdmi_pll_hw;
break;
default:
return -ENODEV;
}
pll->ops = &dsi_pll_ops;
r = dss_pll_register(pll);
if (r)
return r;
return 0;
}
int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll,
struct hdmi_wp_data *wp)
{
int r;
struct resource *res;
pll->wp = wp;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pll");
if (!res) {
DSSERR("can't get PLL mem resource\n");
return -EINVAL;
}
pll->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(pll->base)) {
DSSERR("can't ioremap PLLCTRL\n");
return PTR_ERR(pll->base);
}
r = dsi_init_pll_data(pdev, pll);
if (r) {
DSSERR("failed to init HDMI PLL\n");
return r;
}
return 0;
}
void hdmi_pll_uninit(struct hdmi_pll_data *hpll)
{
struct dss_pll *pll = &hpll->pll;
dss_pll_unregister(pll);
}

View File

@ -0,0 +1,282 @@
/*
* HDMI wrapper
*
* Copyright (C) 2013 Texas Instruments Incorporated
*
* 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.
*/
#define DSS_SUBSYS_NAME "HDMIWP"
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <video/omapdss.h>
#include "dss.h"
#include "hdmi.h"
void hdmi_wp_dump(struct hdmi_wp_data *wp, struct seq_file *s)
{
#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, hdmi_read_reg(wp->base, r))
DUMPREG(HDMI_WP_REVISION);
DUMPREG(HDMI_WP_SYSCONFIG);
DUMPREG(HDMI_WP_IRQSTATUS_RAW);
DUMPREG(HDMI_WP_IRQSTATUS);
DUMPREG(HDMI_WP_IRQENABLE_SET);
DUMPREG(HDMI_WP_IRQENABLE_CLR);
DUMPREG(HDMI_WP_IRQWAKEEN);
DUMPREG(HDMI_WP_PWR_CTRL);
DUMPREG(HDMI_WP_DEBOUNCE);
DUMPREG(HDMI_WP_VIDEO_CFG);
DUMPREG(HDMI_WP_VIDEO_SIZE);
DUMPREG(HDMI_WP_VIDEO_TIMING_H);
DUMPREG(HDMI_WP_VIDEO_TIMING_V);
DUMPREG(HDMI_WP_CLK);
DUMPREG(HDMI_WP_AUDIO_CFG);
DUMPREG(HDMI_WP_AUDIO_CFG2);
DUMPREG(HDMI_WP_AUDIO_CTRL);
DUMPREG(HDMI_WP_AUDIO_DATA);
}
u32 hdmi_wp_get_irqstatus(struct hdmi_wp_data *wp)
{
return hdmi_read_reg(wp->base, HDMI_WP_IRQSTATUS);
}
void hdmi_wp_set_irqstatus(struct hdmi_wp_data *wp, u32 irqstatus)
{
hdmi_write_reg(wp->base, HDMI_WP_IRQSTATUS, irqstatus);
/* flush posted write */
hdmi_read_reg(wp->base, HDMI_WP_IRQSTATUS);
}
void hdmi_wp_set_irqenable(struct hdmi_wp_data *wp, u32 mask)
{
hdmi_write_reg(wp->base, HDMI_WP_IRQENABLE_SET, mask);
}
void hdmi_wp_clear_irqenable(struct hdmi_wp_data *wp, u32 mask)
{
hdmi_write_reg(wp->base, HDMI_WP_IRQENABLE_CLR, mask);
}
/* PHY_PWR_CMD */
int hdmi_wp_set_phy_pwr(struct hdmi_wp_data *wp, enum hdmi_phy_pwr val)
{
/* Return if already the state */
if (REG_GET(wp->base, HDMI_WP_PWR_CTRL, 5, 4) == val)
return 0;
/* Command for power control of HDMI PHY */
REG_FLD_MOD(wp->base, HDMI_WP_PWR_CTRL, val, 7, 6);
/* Status of the power control of HDMI PHY */
if (hdmi_wait_for_bit_change(wp->base, HDMI_WP_PWR_CTRL, 5, 4, val)
!= val) {
DSSERR("Failed to set PHY power mode to %d\n", val);
return -ETIMEDOUT;
}
return 0;
}
/* PLL_PWR_CMD */
int hdmi_wp_set_pll_pwr(struct hdmi_wp_data *wp, enum hdmi_pll_pwr val)
{
/* Command for power control of HDMI PLL */
REG_FLD_MOD(wp->base, HDMI_WP_PWR_CTRL, val, 3, 2);
/* wait till PHY_PWR_STATUS is set */
if (hdmi_wait_for_bit_change(wp->base, HDMI_WP_PWR_CTRL, 1, 0, val)
!= val) {
DSSERR("Failed to set PLL_PWR_STATUS\n");
return -ETIMEDOUT;
}
return 0;
}
int hdmi_wp_video_start(struct hdmi_wp_data *wp)
{
REG_FLD_MOD(wp->base, HDMI_WP_VIDEO_CFG, true, 31, 31);
return 0;
}
void hdmi_wp_video_stop(struct hdmi_wp_data *wp)
{
int i;
hdmi_write_reg(wp->base, HDMI_WP_IRQSTATUS, HDMI_IRQ_VIDEO_FRAME_DONE);
REG_FLD_MOD(wp->base, HDMI_WP_VIDEO_CFG, false, 31, 31);
for (i = 0; i < 50; ++i) {
u32 v;
msleep(20);
v = hdmi_read_reg(wp->base, HDMI_WP_IRQSTATUS_RAW);
if (v & HDMI_IRQ_VIDEO_FRAME_DONE)
return;
}
DSSERR("no HDMI FRAMEDONE when disabling output\n");
}
void hdmi_wp_video_config_format(struct hdmi_wp_data *wp,
struct hdmi_video_format *video_fmt)
{
u32 l = 0;
REG_FLD_MOD(wp->base, HDMI_WP_VIDEO_CFG, video_fmt->packing_mode,
10, 8);
l |= FLD_VAL(video_fmt->y_res, 31, 16);
l |= FLD_VAL(video_fmt->x_res, 15, 0);
hdmi_write_reg(wp->base, HDMI_WP_VIDEO_SIZE, l);
}
void hdmi_wp_video_config_interface(struct hdmi_wp_data *wp,
struct omap_video_timings *timings)
{
u32 r;
bool vsync_pol, hsync_pol;
DSSDBG("Enter hdmi_wp_video_config_interface\n");
vsync_pol = timings->vsync_level == OMAPDSS_SIG_ACTIVE_HIGH;
hsync_pol = timings->hsync_level == OMAPDSS_SIG_ACTIVE_HIGH;
r = hdmi_read_reg(wp->base, HDMI_WP_VIDEO_CFG);
r = FLD_MOD(r, vsync_pol, 7, 7);
r = FLD_MOD(r, hsync_pol, 6, 6);
r = FLD_MOD(r, timings->interlace, 3, 3);
r = FLD_MOD(r, 1, 1, 0); /* HDMI_TIMING_MASTER_24BIT */
hdmi_write_reg(wp->base, HDMI_WP_VIDEO_CFG, r);
}
void hdmi_wp_video_config_timing(struct hdmi_wp_data *wp,
struct omap_video_timings *timings)
{
u32 timing_h = 0;
u32 timing_v = 0;
DSSDBG("Enter hdmi_wp_video_config_timing\n");
timing_h |= FLD_VAL(timings->hbp, 31, 20);
timing_h |= FLD_VAL(timings->hfp, 19, 8);
timing_h |= FLD_VAL(timings->hsw, 7, 0);
hdmi_write_reg(wp->base, HDMI_WP_VIDEO_TIMING_H, timing_h);
timing_v |= FLD_VAL(timings->vbp, 31, 20);
timing_v |= FLD_VAL(timings->vfp, 19, 8);
timing_v |= FLD_VAL(timings->vsw, 7, 0);
hdmi_write_reg(wp->base, HDMI_WP_VIDEO_TIMING_V, timing_v);
}
void hdmi_wp_init_vid_fmt_timings(struct hdmi_video_format *video_fmt,
struct omap_video_timings *timings, struct hdmi_config *param)
{
DSSDBG("Enter hdmi_wp_video_init_format\n");
video_fmt->packing_mode = HDMI_PACK_10b_RGB_YUV444;
video_fmt->y_res = param->timings.y_res;
video_fmt->x_res = param->timings.x_res;
if (param->timings.interlace)
video_fmt->y_res /= 2;
timings->hbp = param->timings.hbp;
timings->hfp = param->timings.hfp;
timings->hsw = param->timings.hsw;
timings->vbp = param->timings.vbp;
timings->vfp = param->timings.vfp;
timings->vsw = param->timings.vsw;
timings->vsync_level = param->timings.vsync_level;
timings->hsync_level = param->timings.hsync_level;
timings->interlace = param->timings.interlace;
}
void hdmi_wp_audio_config_format(struct hdmi_wp_data *wp,
struct hdmi_audio_format *aud_fmt)
{
u32 r;
DSSDBG("Enter hdmi_wp_audio_config_format\n");
r = hdmi_read_reg(wp->base, HDMI_WP_AUDIO_CFG);
if (omapdss_get_version() == OMAPDSS_VER_OMAP4430_ES1 ||
omapdss_get_version() == OMAPDSS_VER_OMAP4430_ES2 ||
omapdss_get_version() == OMAPDSS_VER_OMAP4) {
r = FLD_MOD(r, aud_fmt->stereo_channels, 26, 24);
r = FLD_MOD(r, aud_fmt->active_chnnls_msk, 23, 16);
}
r = FLD_MOD(r, aud_fmt->en_sig_blk_strt_end, 5, 5);
r = FLD_MOD(r, aud_fmt->type, 4, 4);
r = FLD_MOD(r, aud_fmt->justification, 3, 3);
r = FLD_MOD(r, aud_fmt->sample_order, 2, 2);
r = FLD_MOD(r, aud_fmt->samples_per_word, 1, 1);
r = FLD_MOD(r, aud_fmt->sample_size, 0, 0);
hdmi_write_reg(wp->base, HDMI_WP_AUDIO_CFG, r);
}
void hdmi_wp_audio_config_dma(struct hdmi_wp_data *wp,
struct hdmi_audio_dma *aud_dma)
{
u32 r;
DSSDBG("Enter hdmi_wp_audio_config_dma\n");
r = hdmi_read_reg(wp->base, HDMI_WP_AUDIO_CFG2);
r = FLD_MOD(r, aud_dma->transfer_size, 15, 8);
r = FLD_MOD(r, aud_dma->block_size, 7, 0);
hdmi_write_reg(wp->base, HDMI_WP_AUDIO_CFG2, r);
r = hdmi_read_reg(wp->base, HDMI_WP_AUDIO_CTRL);
r = FLD_MOD(r, aud_dma->mode, 9, 9);
r = FLD_MOD(r, aud_dma->fifo_threshold, 8, 0);
hdmi_write_reg(wp->base, HDMI_WP_AUDIO_CTRL, r);
}
int hdmi_wp_audio_enable(struct hdmi_wp_data *wp, bool enable)
{
REG_FLD_MOD(wp->base, HDMI_WP_AUDIO_CTRL, enable, 31, 31);
return 0;
}
int hdmi_wp_audio_core_req_enable(struct hdmi_wp_data *wp, bool enable)
{
REG_FLD_MOD(wp->base, HDMI_WP_AUDIO_CTRL, enable, 30, 30);
return 0;
}
int hdmi_wp_init(struct platform_device *pdev, struct hdmi_wp_data *wp)
{
struct resource *res;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "wp");
if (!res) {
DSSERR("can't get WP mem resource\n");
return -EINVAL;
}
wp->phys_base = res->start;
wp->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(wp->base)) {
DSSERR("can't ioremap HDMI WP\n");
return PTR_ERR(wp->base);
}
return 0;
}
phys_addr_t hdmi_wp_get_audio_dma_addr(struct hdmi_wp_data *wp)
{
return wp->phys_base + HDMI_WP_AUDIO_DATA;
}

View File

@ -0,0 +1,531 @@
/*
* Copyright (C) 2009 Nokia Corporation
* Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
*
* Some code and ideas taken from drivers/video/omap/ driver
* by Imre Deak.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define DSS_SUBSYS_NAME "MANAGER"
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/jiffies.h>
#include <video/omapdss.h>
#include "dss.h"
#include "dss_features.h"
static ssize_t manager_name_show(struct omap_overlay_manager *mgr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%s\n", mgr->name);
}
static ssize_t manager_display_show(struct omap_overlay_manager *mgr, char *buf)
{
struct omap_dss_device *dssdev = mgr->get_device(mgr);
return snprintf(buf, PAGE_SIZE, "%s\n", dssdev ?
dssdev->name : "<none>");
}
static int manager_display_match(struct omap_dss_device *dssdev, void *data)
{
const char *str = data;
return sysfs_streq(dssdev->name, str);
}
static ssize_t manager_display_store(struct omap_overlay_manager *mgr,
const char *buf, size_t size)
{
int r = 0;
size_t len = size;
struct omap_dss_device *dssdev = NULL;
struct omap_dss_device *old_dssdev;
if (buf[size-1] == '\n')
--len;
if (len > 0)
dssdev = omap_dss_find_device((void *)buf,
manager_display_match);
if (len > 0 && dssdev == NULL)
return -EINVAL;
if (dssdev) {
DSSDBG("display %s found\n", dssdev->name);
if (omapdss_device_is_connected(dssdev)) {
DSSERR("new display is already connected\n");
r = -EINVAL;
goto put_device;
}
if (omapdss_device_is_enabled(dssdev)) {
DSSERR("new display is not disabled\n");
r = -EINVAL;
goto put_device;
}
}
old_dssdev = mgr->get_device(mgr);
if (old_dssdev) {
if (omapdss_device_is_enabled(old_dssdev)) {
DSSERR("old display is not disabled\n");
r = -EINVAL;
goto put_device;
}
old_dssdev->driver->disconnect(old_dssdev);
}
if (dssdev) {
r = dssdev->driver->connect(dssdev);
if (r) {
DSSERR("failed to connect new device\n");
goto put_device;
}
old_dssdev = mgr->get_device(mgr);
if (old_dssdev != dssdev) {
DSSERR("failed to connect device to this manager\n");
dssdev->driver->disconnect(dssdev);
goto put_device;
}
r = mgr->apply(mgr);
if (r) {
DSSERR("failed to apply dispc config\n");
goto put_device;
}
}
put_device:
if (dssdev)
omap_dss_put_device(dssdev);
return r ? r : size;
}
static ssize_t manager_default_color_show(struct omap_overlay_manager *mgr,
char *buf)
{
struct omap_overlay_manager_info info;
mgr->get_manager_info(mgr, &info);
return snprintf(buf, PAGE_SIZE, "%#x\n", info.default_color);
}
static ssize_t manager_default_color_store(struct omap_overlay_manager *mgr,
const char *buf, size_t size)
{
struct omap_overlay_manager_info info;
u32 color;
int r;
r = kstrtouint(buf, 0, &color);
if (r)
return r;
mgr->get_manager_info(mgr, &info);
info.default_color = color;
r = mgr->set_manager_info(mgr, &info);
if (r)
return r;
r = mgr->apply(mgr);
if (r)
return r;
return size;
}
static const char *trans_key_type_str[] = {
"gfx-destination",
"video-source",
};
static ssize_t manager_trans_key_type_show(struct omap_overlay_manager *mgr,
char *buf)
{
enum omap_dss_trans_key_type key_type;
struct omap_overlay_manager_info info;
mgr->get_manager_info(mgr, &info);
key_type = info.trans_key_type;
BUG_ON(key_type >= ARRAY_SIZE(trans_key_type_str));
return snprintf(buf, PAGE_SIZE, "%s\n", trans_key_type_str[key_type]);
}
static ssize_t manager_trans_key_type_store(struct omap_overlay_manager *mgr,
const char *buf, size_t size)
{
enum omap_dss_trans_key_type key_type;
struct omap_overlay_manager_info info;
int r;
for (key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
key_type < ARRAY_SIZE(trans_key_type_str); key_type++) {
if (sysfs_streq(buf, trans_key_type_str[key_type]))
break;
}
if (key_type == ARRAY_SIZE(trans_key_type_str))
return -EINVAL;
mgr->get_manager_info(mgr, &info);
info.trans_key_type = key_type;
r = mgr->set_manager_info(mgr, &info);
if (r)
return r;
r = mgr->apply(mgr);
if (r)
return r;
return size;
}
static ssize_t manager_trans_key_value_show(struct omap_overlay_manager *mgr,
char *buf)
{
struct omap_overlay_manager_info info;
mgr->get_manager_info(mgr, &info);
return snprintf(buf, PAGE_SIZE, "%#x\n", info.trans_key);
}
static ssize_t manager_trans_key_value_store(struct omap_overlay_manager *mgr,
const char *buf, size_t size)
{
struct omap_overlay_manager_info info;
u32 key_value;
int r;
r = kstrtouint(buf, 0, &key_value);
if (r)
return r;
mgr->get_manager_info(mgr, &info);
info.trans_key = key_value;
r = mgr->set_manager_info(mgr, &info);
if (r)
return r;
r = mgr->apply(mgr);
if (r)
return r;
return size;
}
static ssize_t manager_trans_key_enabled_show(struct omap_overlay_manager *mgr,
char *buf)
{
struct omap_overlay_manager_info info;
mgr->get_manager_info(mgr, &info);
return snprintf(buf, PAGE_SIZE, "%d\n", info.trans_enabled);
}
static ssize_t manager_trans_key_enabled_store(struct omap_overlay_manager *mgr,
const char *buf, size_t size)
{
struct omap_overlay_manager_info info;
bool enable;
int r;
r = strtobool(buf, &enable);
if (r)
return r;
mgr->get_manager_info(mgr, &info);
info.trans_enabled = enable;
r = mgr->set_manager_info(mgr, &info);
if (r)
return r;
r = mgr->apply(mgr);
if (r)
return r;
return size;
}
static ssize_t manager_alpha_blending_enabled_show(
struct omap_overlay_manager *mgr, char *buf)
{
struct omap_overlay_manager_info info;
if(!dss_has_feature(FEAT_ALPHA_FIXED_ZORDER))
return -ENODEV;
mgr->get_manager_info(mgr, &info);
return snprintf(buf, PAGE_SIZE, "%d\n",
info.partial_alpha_enabled);
}
static ssize_t manager_alpha_blending_enabled_store(
struct omap_overlay_manager *mgr,
const char *buf, size_t size)
{
struct omap_overlay_manager_info info;
bool enable;
int r;
if(!dss_has_feature(FEAT_ALPHA_FIXED_ZORDER))
return -ENODEV;
r = strtobool(buf, &enable);
if (r)
return r;
mgr->get_manager_info(mgr, &info);
info.partial_alpha_enabled = enable;
r = mgr->set_manager_info(mgr, &info);
if (r)
return r;
r = mgr->apply(mgr);
if (r)
return r;
return size;
}
static ssize_t manager_cpr_enable_show(struct omap_overlay_manager *mgr,
char *buf)
{
struct omap_overlay_manager_info info;
mgr->get_manager_info(mgr, &info);
return snprintf(buf, PAGE_SIZE, "%d\n", info.cpr_enable);
}
static ssize_t manager_cpr_enable_store(struct omap_overlay_manager *mgr,
const char *buf, size_t size)
{
struct omap_overlay_manager_info info;
int r;
bool enable;
if (!dss_has_feature(FEAT_CPR))
return -ENODEV;
r = strtobool(buf, &enable);
if (r)
return r;
mgr->get_manager_info(mgr, &info);
if (info.cpr_enable == enable)
return size;
info.cpr_enable = enable;
r = mgr->set_manager_info(mgr, &info);
if (r)
return r;
r = mgr->apply(mgr);
if (r)
return r;
return size;
}
static ssize_t manager_cpr_coef_show(struct omap_overlay_manager *mgr,
char *buf)
{
struct omap_overlay_manager_info info;
mgr->get_manager_info(mgr, &info);
return snprintf(buf, PAGE_SIZE,
"%d %d %d %d %d %d %d %d %d\n",
info.cpr_coefs.rr,
info.cpr_coefs.rg,
info.cpr_coefs.rb,
info.cpr_coefs.gr,
info.cpr_coefs.gg,
info.cpr_coefs.gb,
info.cpr_coefs.br,
info.cpr_coefs.bg,
info.cpr_coefs.bb);
}
static ssize_t manager_cpr_coef_store(struct omap_overlay_manager *mgr,
const char *buf, size_t size)
{
struct omap_overlay_manager_info info;
struct omap_dss_cpr_coefs coefs;
int r, i;
s16 *arr;
if (!dss_has_feature(FEAT_CPR))
return -ENODEV;
if (sscanf(buf, "%hd %hd %hd %hd %hd %hd %hd %hd %hd",
&coefs.rr, &coefs.rg, &coefs.rb,
&coefs.gr, &coefs.gg, &coefs.gb,
&coefs.br, &coefs.bg, &coefs.bb) != 9)
return -EINVAL;
arr = (s16[]){ coefs.rr, coefs.rg, coefs.rb,
coefs.gr, coefs.gg, coefs.gb,
coefs.br, coefs.bg, coefs.bb };
for (i = 0; i < 9; ++i) {
if (arr[i] < -512 || arr[i] > 511)
return -EINVAL;
}
mgr->get_manager_info(mgr, &info);
info.cpr_coefs = coefs;
r = mgr->set_manager_info(mgr, &info);
if (r)
return r;
r = mgr->apply(mgr);
if (r)
return r;
return size;
}
struct manager_attribute {
struct attribute attr;
ssize_t (*show)(struct omap_overlay_manager *, char *);
ssize_t (*store)(struct omap_overlay_manager *, const char *, size_t);
};
#define MANAGER_ATTR(_name, _mode, _show, _store) \
struct manager_attribute manager_attr_##_name = \
__ATTR(_name, _mode, _show, _store)
static MANAGER_ATTR(name, S_IRUGO, manager_name_show, NULL);
static MANAGER_ATTR(display, S_IRUGO|S_IWUSR,
manager_display_show, manager_display_store);
static MANAGER_ATTR(default_color, S_IRUGO|S_IWUSR,
manager_default_color_show, manager_default_color_store);
static MANAGER_ATTR(trans_key_type, S_IRUGO|S_IWUSR,
manager_trans_key_type_show, manager_trans_key_type_store);
static MANAGER_ATTR(trans_key_value, S_IRUGO|S_IWUSR,
manager_trans_key_value_show, manager_trans_key_value_store);
static MANAGER_ATTR(trans_key_enabled, S_IRUGO|S_IWUSR,
manager_trans_key_enabled_show,
manager_trans_key_enabled_store);
static MANAGER_ATTR(alpha_blending_enabled, S_IRUGO|S_IWUSR,
manager_alpha_blending_enabled_show,
manager_alpha_blending_enabled_store);
static MANAGER_ATTR(cpr_enable, S_IRUGO|S_IWUSR,
manager_cpr_enable_show,
manager_cpr_enable_store);
static MANAGER_ATTR(cpr_coef, S_IRUGO|S_IWUSR,
manager_cpr_coef_show,
manager_cpr_coef_store);
static struct attribute *manager_sysfs_attrs[] = {
&manager_attr_name.attr,
&manager_attr_display.attr,
&manager_attr_default_color.attr,
&manager_attr_trans_key_type.attr,
&manager_attr_trans_key_value.attr,
&manager_attr_trans_key_enabled.attr,
&manager_attr_alpha_blending_enabled.attr,
&manager_attr_cpr_enable.attr,
&manager_attr_cpr_coef.attr,
NULL
};
static ssize_t manager_attr_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
struct omap_overlay_manager *manager;
struct manager_attribute *manager_attr;
manager = container_of(kobj, struct omap_overlay_manager, kobj);
manager_attr = container_of(attr, struct manager_attribute, attr);
if (!manager_attr->show)
return -ENOENT;
return manager_attr->show(manager, buf);
}
static ssize_t manager_attr_store(struct kobject *kobj, struct attribute *attr,
const char *buf, size_t size)
{
struct omap_overlay_manager *manager;
struct manager_attribute *manager_attr;
manager = container_of(kobj, struct omap_overlay_manager, kobj);
manager_attr = container_of(attr, struct manager_attribute, attr);
if (!manager_attr->store)
return -ENOENT;
return manager_attr->store(manager, buf, size);
}
static const struct sysfs_ops manager_sysfs_ops = {
.show = manager_attr_show,
.store = manager_attr_store,
};
static struct kobj_type manager_ktype = {
.sysfs_ops = &manager_sysfs_ops,
.default_attrs = manager_sysfs_attrs,
};
int dss_manager_kobj_init(struct omap_overlay_manager *mgr,
struct platform_device *pdev)
{
return kobject_init_and_add(&mgr->kobj, &manager_ktype,
&pdev->dev.kobj, "manager%d", mgr->id);
}
void dss_manager_kobj_uninit(struct omap_overlay_manager *mgr)
{
kobject_del(&mgr->kobj);
kobject_put(&mgr->kobj);
memset(&mgr->kobj, 0, sizeof(mgr->kobj));
}

View File

@ -0,0 +1,263 @@
/*
* linux/drivers/video/omap2/dss/manager.c
*
* Copyright (C) 2009 Nokia Corporation
* Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
*
* Some code and ideas taken from drivers/video/omap/ driver
* by Imre Deak.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define DSS_SUBSYS_NAME "MANAGER"
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/jiffies.h>
#include <video/omapdss.h>
#include "dss.h"
#include "dss_features.h"
static int num_managers;
static struct omap_overlay_manager *managers;
int dss_init_overlay_managers(void)
{
int i;
num_managers = dss_feat_get_num_mgrs();
managers = kzalloc(sizeof(struct omap_overlay_manager) * num_managers,
GFP_KERNEL);
BUG_ON(managers == NULL);
for (i = 0; i < num_managers; ++i) {
struct omap_overlay_manager *mgr = &managers[i];
switch (i) {
case 0:
mgr->name = "lcd";
mgr->id = OMAP_DSS_CHANNEL_LCD;
break;
case 1:
mgr->name = "tv";
mgr->id = OMAP_DSS_CHANNEL_DIGIT;
break;
case 2:
mgr->name = "lcd2";
mgr->id = OMAP_DSS_CHANNEL_LCD2;
break;
case 3:
mgr->name = "lcd3";
mgr->id = OMAP_DSS_CHANNEL_LCD3;
break;
}
mgr->caps = 0;
mgr->supported_displays =
dss_feat_get_supported_displays(mgr->id);
mgr->supported_outputs =
dss_feat_get_supported_outputs(mgr->id);
INIT_LIST_HEAD(&mgr->overlays);
}
return 0;
}
int dss_init_overlay_managers_sysfs(struct platform_device *pdev)
{
int i, r;
for (i = 0; i < num_managers; ++i) {
struct omap_overlay_manager *mgr = &managers[i];
r = dss_manager_kobj_init(mgr, pdev);
if (r)
DSSERR("failed to create sysfs file\n");
}
return 0;
}
void dss_uninit_overlay_managers(void)
{
kfree(managers);
managers = NULL;
num_managers = 0;
}
void dss_uninit_overlay_managers_sysfs(struct platform_device *pdev)
{
int i;
for (i = 0; i < num_managers; ++i) {
struct omap_overlay_manager *mgr = &managers[i];
dss_manager_kobj_uninit(mgr);
}
}
int omap_dss_get_num_overlay_managers(void)
{
return num_managers;
}
EXPORT_SYMBOL(omap_dss_get_num_overlay_managers);
struct omap_overlay_manager *omap_dss_get_overlay_manager(int num)
{
if (num >= num_managers)
return NULL;
return &managers[num];
}
EXPORT_SYMBOL(omap_dss_get_overlay_manager);
int dss_mgr_simple_check(struct omap_overlay_manager *mgr,
const struct omap_overlay_manager_info *info)
{
if (dss_has_feature(FEAT_ALPHA_FIXED_ZORDER)) {
/*
* OMAP3 supports only graphics source transparency color key
* and alpha blending simultaneously. See TRM 15.4.2.4.2.2
* Alpha Mode.
*/
if (info->partial_alpha_enabled && info->trans_enabled
&& info->trans_key_type != OMAP_DSS_COLOR_KEY_GFX_DST) {
DSSERR("check_manager: illegal transparency key\n");
return -EINVAL;
}
}
return 0;
}
static int dss_mgr_check_zorder(struct omap_overlay_manager *mgr,
struct omap_overlay_info **overlay_infos)
{
struct omap_overlay *ovl1, *ovl2;
struct omap_overlay_info *info1, *info2;
list_for_each_entry(ovl1, &mgr->overlays, list) {
info1 = overlay_infos[ovl1->id];
if (info1 == NULL)
continue;
list_for_each_entry(ovl2, &mgr->overlays, list) {
if (ovl1 == ovl2)
continue;
info2 = overlay_infos[ovl2->id];
if (info2 == NULL)
continue;
if (info1->zorder == info2->zorder) {
DSSERR("overlays %d and %d have the same "
"zorder %d\n",
ovl1->id, ovl2->id, info1->zorder);
return -EINVAL;
}
}
}
return 0;
}
int dss_mgr_check_timings(struct omap_overlay_manager *mgr,
const struct omap_video_timings *timings)
{
if (!dispc_mgr_timings_ok(mgr->id, timings)) {
DSSERR("check_manager: invalid timings\n");
return -EINVAL;
}
return 0;
}
static int dss_mgr_check_lcd_config(struct omap_overlay_manager *mgr,
const struct dss_lcd_mgr_config *config)
{
struct dispc_clock_info cinfo = config->clock_info;
int dl = config->video_port_width;
bool stallmode = config->stallmode;
bool fifohandcheck = config->fifohandcheck;
if (cinfo.lck_div < 1 || cinfo.lck_div > 255)
return -EINVAL;
if (cinfo.pck_div < 1 || cinfo.pck_div > 255)
return -EINVAL;
if (dl != 12 && dl != 16 && dl != 18 && dl != 24)
return -EINVAL;
/* fifohandcheck should be used only with stallmode */
if (!stallmode && fifohandcheck)
return -EINVAL;
/*
* io pad mode can be only checked by using dssdev connected to the
* manager. Ignore checking these for now, add checks when manager
* is capable of holding information related to the connected interface
*/
return 0;
}
int dss_mgr_check(struct omap_overlay_manager *mgr,
struct omap_overlay_manager_info *info,
const struct omap_video_timings *mgr_timings,
const struct dss_lcd_mgr_config *lcd_config,
struct omap_overlay_info **overlay_infos)
{
struct omap_overlay *ovl;
int r;
if (dss_has_feature(FEAT_ALPHA_FREE_ZORDER)) {
r = dss_mgr_check_zorder(mgr, overlay_infos);
if (r)
return r;
}
r = dss_mgr_check_timings(mgr, mgr_timings);
if (r)
return r;
r = dss_mgr_check_lcd_config(mgr, lcd_config);
if (r)
return r;
list_for_each_entry(ovl, &mgr->overlays, list) {
struct omap_overlay_info *oi;
int r;
oi = overlay_infos[ovl->id];
if (oi == NULL)
continue;
r = dss_ovl_check(ovl, oi, mgr_timings);
if (r)
return r;
}
return 0;
}

View File

@ -0,0 +1,225 @@
/*
* Copyright (C) 2014 Texas Instruments
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* As omapdss panel drivers are omapdss specific, but we want to define the
* DT-data in generic manner, we convert the compatible strings of the panel and
* encoder nodes from "panel-foo" to "omapdss,panel-foo". This way we can have
* both correct DT data and omapdss specific drivers.
*
* When we get generic panel drivers to the kernel, this file will be removed.
*/
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_graph.h>
#include <linux/slab.h>
#include <linux/list.h>
static struct list_head dss_conv_list __initdata;
static const char prefix[] __initconst = "omapdss,";
struct dss_conv_node {
struct list_head list;
struct device_node *node;
bool root;
};
static int __init omapdss_count_strings(const struct property *prop)
{
const char *p = prop->value;
int l = 0, total = 0;
int i;
for (i = 0; total < prop->length; total += l, p += l, i++)
l = strlen(p) + 1;
return i;
}
static void __init omapdss_update_prop(struct device_node *node, char *compat,
int len)
{
struct property *prop;
prop = kzalloc(sizeof(*prop), GFP_KERNEL);
if (!prop)
return;
prop->name = "compatible";
prop->value = compat;
prop->length = len;
of_update_property(node, prop);
}
static void __init omapdss_prefix_strcpy(char *dst, int dst_len,
const char *src, int src_len)
{
size_t total = 0;
while (total < src_len) {
size_t l = strlen(src) + 1;
strcpy(dst, prefix);
dst += strlen(prefix);
strcpy(dst, src);
dst += l;
src += l;
total += l;
}
}
/* prepend compatible property strings with "omapdss," */
static void __init omapdss_omapify_node(struct device_node *node)
{
struct property *prop;
char *new_compat;
int num_strs;
int new_len;
prop = of_find_property(node, "compatible", NULL);
if (!prop || !prop->value)
return;
if (strnlen(prop->value, prop->length) >= prop->length)
return;
/* is it already prefixed? */
if (strncmp(prefix, prop->value, strlen(prefix)) == 0)
return;
num_strs = omapdss_count_strings(prop);
new_len = prop->length + strlen(prefix) * num_strs;
new_compat = kmalloc(new_len, GFP_KERNEL);
omapdss_prefix_strcpy(new_compat, new_len, prop->value, prop->length);
omapdss_update_prop(node, new_compat, new_len);
}
static void __init omapdss_add_to_list(struct device_node *node, bool root)
{
struct dss_conv_node *n = kmalloc(sizeof(struct dss_conv_node),
GFP_KERNEL);
if (n) {
n->node = node;
n->root = root;
list_add(&n->list, &dss_conv_list);
}
}
static bool __init omapdss_list_contains(const struct device_node *node)
{
struct dss_conv_node *n;
list_for_each_entry(n, &dss_conv_list, list) {
if (n->node == node)
return true;
}
return false;
}
static void __init omapdss_walk_device(struct device_node *node, bool root)
{
struct device_node *n;
omapdss_add_to_list(node, root);
/*
* of_graph_get_remote_port_parent() prints an error if there is no
* port/ports node. To avoid that, check first that there's the node.
*/
n = of_get_child_by_name(node, "ports");
if (!n)
n = of_get_child_by_name(node, "port");
if (!n)
return;
of_node_put(n);
n = NULL;
while ((n = of_graph_get_next_endpoint(node, n)) != NULL) {
struct device_node *pn;
pn = of_graph_get_remote_port_parent(n);
if (!pn)
continue;
if (!of_device_is_available(pn) || omapdss_list_contains(pn)) {
of_node_put(pn);
continue;
}
omapdss_walk_device(pn, false);
}
}
static const struct of_device_id omapdss_of_match[] __initconst = {
{ .compatible = "ti,omap2-dss", },
{ .compatible = "ti,omap3-dss", },
{ .compatible = "ti,omap4-dss", },
{ .compatible = "ti,omap5-dss", },
{ .compatible = "ti,dra7-dss", },
{},
};
static int __init omapdss_boot_init(void)
{
struct device_node *dss, *child;
INIT_LIST_HEAD(&dss_conv_list);
dss = of_find_matching_node(NULL, omapdss_of_match);
if (dss == NULL || !of_device_is_available(dss))
return 0;
omapdss_walk_device(dss, true);
for_each_available_child_of_node(dss, child) {
if (!of_find_property(child, "compatible", NULL))
continue;
omapdss_walk_device(child, true);
}
while (!list_empty(&dss_conv_list)) {
struct dss_conv_node *n;
n = list_first_entry(&dss_conv_list, struct dss_conv_node,
list);
if (!n->root)
omapdss_omapify_node(n->node);
list_del(&n->list);
of_node_put(n->node);
kfree(n);
}
return 0;
}
subsys_initcall(omapdss_boot_init);

View File

@ -0,0 +1,267 @@
/*
* Copyright (C) 2012 Texas Instruments Ltd
* Author: Archit Taneja <archit@ti.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <video/omapdss.h>
#include "dss.h"
static LIST_HEAD(output_list);
static DEFINE_MUTEX(output_lock);
int omapdss_output_set_device(struct omap_dss_device *out,
struct omap_dss_device *dssdev)
{
int r;
mutex_lock(&output_lock);
if (out->dst) {
DSSERR("output already has device %s connected to it\n",
out->dst->name);
r = -EINVAL;
goto err;
}
if (out->output_type != dssdev->type) {
DSSERR("output type and display type don't match\n");
r = -EINVAL;
goto err;
}
out->dst = dssdev;
dssdev->src = out;
mutex_unlock(&output_lock);
return 0;
err:
mutex_unlock(&output_lock);
return r;
}
EXPORT_SYMBOL(omapdss_output_set_device);
int omapdss_output_unset_device(struct omap_dss_device *out)
{
int r;
mutex_lock(&output_lock);
if (!out->dst) {
DSSERR("output doesn't have a device connected to it\n");
r = -EINVAL;
goto err;
}
if (out->dst->state != OMAP_DSS_DISPLAY_DISABLED) {
DSSERR("device %s is not disabled, cannot unset device\n",
out->dst->name);
r = -EINVAL;
goto err;
}
out->dst->src = NULL;
out->dst = NULL;
mutex_unlock(&output_lock);
return 0;
err:
mutex_unlock(&output_lock);
return r;
}
EXPORT_SYMBOL(omapdss_output_unset_device);
int omapdss_register_output(struct omap_dss_device *out)
{
list_add_tail(&out->list, &output_list);
return 0;
}
EXPORT_SYMBOL(omapdss_register_output);
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)
{
struct omap_dss_device *out;
list_for_each_entry(out, &output_list, list) {
if (out->id == id)
return out;
}
return NULL;
}
EXPORT_SYMBOL(omap_dss_get_output);
struct omap_dss_device *omap_dss_find_output(const char *name)
{
struct omap_dss_device *out;
list_for_each_entry(out, &output_list, list) {
if (strcmp(out->name, name) == 0)
return omap_dss_get_device(out);
}
return NULL;
}
EXPORT_SYMBOL(omap_dss_find_output);
struct omap_dss_device *omap_dss_find_output_by_port_node(struct device_node *port)
{
struct device_node *src_node;
struct omap_dss_device *out;
u32 reg;
src_node = dss_of_port_get_parent_device(port);
if (!src_node)
return NULL;
reg = dss_of_port_get_port_number(port);
list_for_each_entry(out, &output_list, list) {
if (out->dev->of_node == src_node && out->port_num == reg) {
of_node_put(src_node);
return omap_dss_get_device(out);
}
}
of_node_put(src_node);
return NULL;
}
EXPORT_SYMBOL(omap_dss_find_output_by_port_node);
struct omap_dss_device *omapdss_find_output_from_display(struct omap_dss_device *dssdev)
{
while (dssdev->src)
dssdev = dssdev->src;
if (dssdev->id != 0)
return omap_dss_get_device(dssdev);
return NULL;
}
EXPORT_SYMBOL(omapdss_find_output_from_display);
struct omap_overlay_manager *omapdss_find_mgr_from_display(struct omap_dss_device *dssdev)
{
struct omap_dss_device *out;
struct omap_overlay_manager *mgr;
out = omapdss_find_output_from_display(dssdev);
if (out == NULL)
return NULL;
mgr = out->manager;
omap_dss_put_device(out);
return mgr;
}
EXPORT_SYMBOL(omapdss_find_mgr_from_display);
static const struct dss_mgr_ops *dss_mgr_ops;
int dss_install_mgr_ops(const struct dss_mgr_ops *mgr_ops)
{
if (dss_mgr_ops)
return -EBUSY;
dss_mgr_ops = mgr_ops;
return 0;
}
EXPORT_SYMBOL(dss_install_mgr_ops);
void dss_uninstall_mgr_ops(void)
{
dss_mgr_ops = NULL;
}
EXPORT_SYMBOL(dss_uninstall_mgr_ops);
int dss_mgr_connect(struct omap_overlay_manager *mgr,
struct omap_dss_device *dst)
{
return dss_mgr_ops->connect(mgr, dst);
}
EXPORT_SYMBOL(dss_mgr_connect);
void dss_mgr_disconnect(struct omap_overlay_manager *mgr,
struct omap_dss_device *dst)
{
dss_mgr_ops->disconnect(mgr, dst);
}
EXPORT_SYMBOL(dss_mgr_disconnect);
void dss_mgr_set_timings(struct omap_overlay_manager *mgr,
const struct omap_video_timings *timings)
{
dss_mgr_ops->set_timings(mgr, timings);
}
EXPORT_SYMBOL(dss_mgr_set_timings);
void dss_mgr_set_lcd_config(struct omap_overlay_manager *mgr,
const struct dss_lcd_mgr_config *config)
{
dss_mgr_ops->set_lcd_config(mgr, config);
}
EXPORT_SYMBOL(dss_mgr_set_lcd_config);
int dss_mgr_enable(struct omap_overlay_manager *mgr)
{
return dss_mgr_ops->enable(mgr);
}
EXPORT_SYMBOL(dss_mgr_enable);
void dss_mgr_disable(struct omap_overlay_manager *mgr)
{
dss_mgr_ops->disable(mgr);
}
EXPORT_SYMBOL(dss_mgr_disable);
void dss_mgr_start_update(struct omap_overlay_manager *mgr)
{
dss_mgr_ops->start_update(mgr);
}
EXPORT_SYMBOL(dss_mgr_start_update);
int dss_mgr_register_framedone_handler(struct omap_overlay_manager *mgr,
void (*handler)(void *), void *data)
{
return dss_mgr_ops->register_framedone_handler(mgr, handler, data);
}
EXPORT_SYMBOL(dss_mgr_register_framedone_handler);
void dss_mgr_unregister_framedone_handler(struct omap_overlay_manager *mgr,
void (*handler)(void *), void *data)
{
dss_mgr_ops->unregister_framedone_handler(mgr, handler, data);
}
EXPORT_SYMBOL(dss_mgr_unregister_framedone_handler);

View File

@ -0,0 +1,456 @@
/*
* Copyright (C) 2009 Nokia Corporation
* Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
*
* Some code and ideas taken from drivers/video/omap/ driver
* by Imre Deak.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define DSS_SUBSYS_NAME "OVERLAY"
#include <linux/module.h>
#include <linux/err.h>
#include <linux/sysfs.h>
#include <linux/kobject.h>
#include <linux/platform_device.h>
#include <video/omapdss.h>
#include "dss.h"
#include "dss_features.h"
static ssize_t overlay_name_show(struct omap_overlay *ovl, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%s\n", ovl->name);
}
static ssize_t overlay_manager_show(struct omap_overlay *ovl, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%s\n",
ovl->manager ? ovl->manager->name : "<none>");
}
static ssize_t overlay_manager_store(struct omap_overlay *ovl, const char *buf,
size_t size)
{
int i, r;
struct omap_overlay_manager *mgr = NULL;
struct omap_overlay_manager *old_mgr;
int len = size;
if (buf[size-1] == '\n')
--len;
if (len > 0) {
for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
mgr = omap_dss_get_overlay_manager(i);
if (sysfs_streq(buf, mgr->name))
break;
mgr = NULL;
}
}
if (len > 0 && mgr == NULL)
return -EINVAL;
if (mgr)
DSSDBG("manager %s found\n", mgr->name);
if (mgr == ovl->manager)
return size;
old_mgr = ovl->manager;
r = dispc_runtime_get();
if (r)
return r;
/* detach old manager */
if (old_mgr) {
r = ovl->unset_manager(ovl);
if (r) {
DSSERR("detach failed\n");
goto err;
}
r = old_mgr->apply(old_mgr);
if (r)
goto err;
}
if (mgr) {
r = ovl->set_manager(ovl, mgr);
if (r) {
DSSERR("Failed to attach overlay\n");
goto err;
}
r = mgr->apply(mgr);
if (r)
goto err;
}
dispc_runtime_put();
return size;
err:
dispc_runtime_put();
return r;
}
static ssize_t overlay_input_size_show(struct omap_overlay *ovl, char *buf)
{
struct omap_overlay_info info;
ovl->get_overlay_info(ovl, &info);
return snprintf(buf, PAGE_SIZE, "%d,%d\n",
info.width, info.height);
}
static ssize_t overlay_screen_width_show(struct omap_overlay *ovl, char *buf)
{
struct omap_overlay_info info;
ovl->get_overlay_info(ovl, &info);
return snprintf(buf, PAGE_SIZE, "%d\n", info.screen_width);
}
static ssize_t overlay_position_show(struct omap_overlay *ovl, char *buf)
{
struct omap_overlay_info info;
ovl->get_overlay_info(ovl, &info);
return snprintf(buf, PAGE_SIZE, "%d,%d\n",
info.pos_x, info.pos_y);
}
static ssize_t overlay_position_store(struct omap_overlay *ovl,
const char *buf, size_t size)
{
int r;
char *last;
struct omap_overlay_info info;
ovl->get_overlay_info(ovl, &info);
info.pos_x = simple_strtoul(buf, &last, 10);
++last;
if (last - buf >= size)
return -EINVAL;
info.pos_y = simple_strtoul(last, &last, 10);
r = ovl->set_overlay_info(ovl, &info);
if (r)
return r;
if (ovl->manager) {
r = ovl->manager->apply(ovl->manager);
if (r)
return r;
}
return size;
}
static ssize_t overlay_output_size_show(struct omap_overlay *ovl, char *buf)
{
struct omap_overlay_info info;
ovl->get_overlay_info(ovl, &info);
return snprintf(buf, PAGE_SIZE, "%d,%d\n",
info.out_width, info.out_height);
}
static ssize_t overlay_output_size_store(struct omap_overlay *ovl,
const char *buf, size_t size)
{
int r;
char *last;
struct omap_overlay_info info;
ovl->get_overlay_info(ovl, &info);
info.out_width = simple_strtoul(buf, &last, 10);
++last;
if (last - buf >= size)
return -EINVAL;
info.out_height = simple_strtoul(last, &last, 10);
r = ovl->set_overlay_info(ovl, &info);
if (r)
return r;
if (ovl->manager) {
r = ovl->manager->apply(ovl->manager);
if (r)
return r;
}
return size;
}
static ssize_t overlay_enabled_show(struct omap_overlay *ovl, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%d\n", ovl->is_enabled(ovl));
}
static ssize_t overlay_enabled_store(struct omap_overlay *ovl, const char *buf,
size_t size)
{
int r;
bool enable;
r = strtobool(buf, &enable);
if (r)
return r;
if (enable)
r = ovl->enable(ovl);
else
r = ovl->disable(ovl);
if (r)
return r;
return size;
}
static ssize_t overlay_global_alpha_show(struct omap_overlay *ovl, char *buf)
{
struct omap_overlay_info info;
ovl->get_overlay_info(ovl, &info);
return snprintf(buf, PAGE_SIZE, "%d\n",
info.global_alpha);
}
static ssize_t overlay_global_alpha_store(struct omap_overlay *ovl,
const char *buf, size_t size)
{
int r;
u8 alpha;
struct omap_overlay_info info;
if ((ovl->caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) == 0)
return -ENODEV;
r = kstrtou8(buf, 0, &alpha);
if (r)
return r;
ovl->get_overlay_info(ovl, &info);
info.global_alpha = alpha;
r = ovl->set_overlay_info(ovl, &info);
if (r)
return r;
if (ovl->manager) {
r = ovl->manager->apply(ovl->manager);
if (r)
return r;
}
return size;
}
static ssize_t overlay_pre_mult_alpha_show(struct omap_overlay *ovl,
char *buf)
{
struct omap_overlay_info info;
ovl->get_overlay_info(ovl, &info);
return snprintf(buf, PAGE_SIZE, "%d\n",
info.pre_mult_alpha);
}
static ssize_t overlay_pre_mult_alpha_store(struct omap_overlay *ovl,
const char *buf, size_t size)
{
int r;
u8 alpha;
struct omap_overlay_info info;
if ((ovl->caps & OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA) == 0)
return -ENODEV;
r = kstrtou8(buf, 0, &alpha);
if (r)
return r;
ovl->get_overlay_info(ovl, &info);
info.pre_mult_alpha = alpha;
r = ovl->set_overlay_info(ovl, &info);
if (r)
return r;
if (ovl->manager) {
r = ovl->manager->apply(ovl->manager);
if (r)
return r;
}
return size;
}
static ssize_t overlay_zorder_show(struct omap_overlay *ovl, char *buf)
{
struct omap_overlay_info info;
ovl->get_overlay_info(ovl, &info);
return snprintf(buf, PAGE_SIZE, "%d\n", info.zorder);
}
static ssize_t overlay_zorder_store(struct omap_overlay *ovl,
const char *buf, size_t size)
{
int r;
u8 zorder;
struct omap_overlay_info info;
if ((ovl->caps & OMAP_DSS_OVL_CAP_ZORDER) == 0)
return -ENODEV;
r = kstrtou8(buf, 0, &zorder);
if (r)
return r;
ovl->get_overlay_info(ovl, &info);
info.zorder = zorder;
r = ovl->set_overlay_info(ovl, &info);
if (r)
return r;
if (ovl->manager) {
r = ovl->manager->apply(ovl->manager);
if (r)
return r;
}
return size;
}
struct overlay_attribute {
struct attribute attr;
ssize_t (*show)(struct omap_overlay *, char *);
ssize_t (*store)(struct omap_overlay *, const char *, size_t);
};
#define OVERLAY_ATTR(_name, _mode, _show, _store) \
struct overlay_attribute overlay_attr_##_name = \
__ATTR(_name, _mode, _show, _store)
static OVERLAY_ATTR(name, S_IRUGO, overlay_name_show, NULL);
static OVERLAY_ATTR(manager, S_IRUGO|S_IWUSR,
overlay_manager_show, overlay_manager_store);
static OVERLAY_ATTR(input_size, S_IRUGO, overlay_input_size_show, NULL);
static OVERLAY_ATTR(screen_width, S_IRUGO, overlay_screen_width_show, NULL);
static OVERLAY_ATTR(position, S_IRUGO|S_IWUSR,
overlay_position_show, overlay_position_store);
static OVERLAY_ATTR(output_size, S_IRUGO|S_IWUSR,
overlay_output_size_show, overlay_output_size_store);
static OVERLAY_ATTR(enabled, S_IRUGO|S_IWUSR,
overlay_enabled_show, overlay_enabled_store);
static OVERLAY_ATTR(global_alpha, S_IRUGO|S_IWUSR,
overlay_global_alpha_show, overlay_global_alpha_store);
static OVERLAY_ATTR(pre_mult_alpha, S_IRUGO|S_IWUSR,
overlay_pre_mult_alpha_show,
overlay_pre_mult_alpha_store);
static OVERLAY_ATTR(zorder, S_IRUGO|S_IWUSR,
overlay_zorder_show, overlay_zorder_store);
static struct attribute *overlay_sysfs_attrs[] = {
&overlay_attr_name.attr,
&overlay_attr_manager.attr,
&overlay_attr_input_size.attr,
&overlay_attr_screen_width.attr,
&overlay_attr_position.attr,
&overlay_attr_output_size.attr,
&overlay_attr_enabled.attr,
&overlay_attr_global_alpha.attr,
&overlay_attr_pre_mult_alpha.attr,
&overlay_attr_zorder.attr,
NULL
};
static ssize_t overlay_attr_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
struct omap_overlay *overlay;
struct overlay_attribute *overlay_attr;
overlay = container_of(kobj, struct omap_overlay, kobj);
overlay_attr = container_of(attr, struct overlay_attribute, attr);
if (!overlay_attr->show)
return -ENOENT;
return overlay_attr->show(overlay, buf);
}
static ssize_t overlay_attr_store(struct kobject *kobj, struct attribute *attr,
const char *buf, size_t size)
{
struct omap_overlay *overlay;
struct overlay_attribute *overlay_attr;
overlay = container_of(kobj, struct omap_overlay, kobj);
overlay_attr = container_of(attr, struct overlay_attribute, attr);
if (!overlay_attr->store)
return -ENOENT;
return overlay_attr->store(overlay, buf, size);
}
static const struct sysfs_ops overlay_sysfs_ops = {
.show = overlay_attr_show,
.store = overlay_attr_store,
};
static struct kobj_type overlay_ktype = {
.sysfs_ops = &overlay_sysfs_ops,
.default_attrs = overlay_sysfs_attrs,
};
int dss_overlay_kobj_init(struct omap_overlay *ovl,
struct platform_device *pdev)
{
return kobject_init_and_add(&ovl->kobj, &overlay_ktype,
&pdev->dev.kobj, "overlay%d", ovl->id);
}
void dss_overlay_kobj_uninit(struct omap_overlay *ovl)
{
kobject_del(&ovl->kobj);
kobject_put(&ovl->kobj);
}

View File

@ -0,0 +1,202 @@
/*
* linux/drivers/video/omap2/dss/overlay.c
*
* Copyright (C) 2009 Nokia Corporation
* Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
*
* Some code and ideas taken from drivers/video/omap/ driver
* by Imre Deak.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define DSS_SUBSYS_NAME "OVERLAY"
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/sysfs.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <video/omapdss.h>
#include "dss.h"
#include "dss_features.h"
static int num_overlays;
static struct omap_overlay *overlays;
int omap_dss_get_num_overlays(void)
{
return num_overlays;
}
EXPORT_SYMBOL(omap_dss_get_num_overlays);
struct omap_overlay *omap_dss_get_overlay(int num)
{
if (num >= num_overlays)
return NULL;
return &overlays[num];
}
EXPORT_SYMBOL(omap_dss_get_overlay);
void dss_init_overlays(struct platform_device *pdev)
{
int i, r;
num_overlays = dss_feat_get_num_ovls();
overlays = kzalloc(sizeof(struct omap_overlay) * num_overlays,
GFP_KERNEL);
BUG_ON(overlays == NULL);
for (i = 0; i < num_overlays; ++i) {
struct omap_overlay *ovl = &overlays[i];
switch (i) {
case 0:
ovl->name = "gfx";
ovl->id = OMAP_DSS_GFX;
break;
case 1:
ovl->name = "vid1";
ovl->id = OMAP_DSS_VIDEO1;
break;
case 2:
ovl->name = "vid2";
ovl->id = OMAP_DSS_VIDEO2;
break;
case 3:
ovl->name = "vid3";
ovl->id = OMAP_DSS_VIDEO3;
break;
}
ovl->caps = dss_feat_get_overlay_caps(ovl->id);
ovl->supported_modes =
dss_feat_get_supported_color_modes(ovl->id);
r = dss_overlay_kobj_init(ovl, pdev);
if (r)
DSSERR("failed to create sysfs file\n");
}
}
void dss_uninit_overlays(struct platform_device *pdev)
{
int i;
for (i = 0; i < num_overlays; ++i) {
struct omap_overlay *ovl = &overlays[i];
dss_overlay_kobj_uninit(ovl);
}
kfree(overlays);
overlays = NULL;
num_overlays = 0;
}
int dss_ovl_simple_check(struct omap_overlay *ovl,
const struct omap_overlay_info *info)
{
if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) {
if (info->out_width != 0 && info->width != info->out_width) {
DSSERR("check_overlay: overlay %d doesn't support "
"scaling\n", ovl->id);
return -EINVAL;
}
if (info->out_height != 0 && info->height != info->out_height) {
DSSERR("check_overlay: overlay %d doesn't support "
"scaling\n", ovl->id);
return -EINVAL;
}
}
if ((ovl->supported_modes & info->color_mode) == 0) {
DSSERR("check_overlay: overlay %d doesn't support mode %d\n",
ovl->id, info->color_mode);
return -EINVAL;
}
if (info->zorder >= omap_dss_get_num_overlays()) {
DSSERR("check_overlay: zorder %d too high\n", info->zorder);
return -EINVAL;
}
if (dss_feat_rotation_type_supported(info->rotation_type) == 0) {
DSSERR("check_overlay: rotation type %d not supported\n",
info->rotation_type);
return -EINVAL;
}
return 0;
}
int dss_ovl_check(struct omap_overlay *ovl, struct omap_overlay_info *info,
const struct omap_video_timings *mgr_timings)
{
u16 outw, outh;
u16 dw, dh;
dw = mgr_timings->x_res;
dh = mgr_timings->y_res;
if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) {
outw = info->width;
outh = info->height;
} else {
if (info->out_width == 0)
outw = info->width;
else
outw = info->out_width;
if (info->out_height == 0)
outh = info->height;
else
outh = info->out_height;
}
if (dw < info->pos_x + outw) {
DSSERR("overlay %d horizontally not inside the display area "
"(%d + %d >= %d)\n",
ovl->id, info->pos_x, outw, dw);
return -EINVAL;
}
if (dh < info->pos_y + outh) {
DSSERR("overlay %d vertically not inside the display area "
"(%d + %d >= %d)\n",
ovl->id, info->pos_y, outh, dh);
return -EINVAL;
}
return 0;
}
/*
* Checks if replication logic should be used. Only use when overlay is in
* RGB12U or RGB16 mode, and video port width interface is 18bpp or 24bpp
*/
bool dss_ovl_use_replication(struct dss_lcd_mgr_config config,
enum omap_color_mode mode)
{
if (mode != OMAP_DSS_COLOR_RGB12U && mode != OMAP_DSS_COLOR_RGB16)
return false;
return config.video_port_width > 16;
}

View File

@ -0,0 +1,389 @@
/*
* Copyright (C) 2014 Texas Instruments Incorporated
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define DSS_SUBSYS_NAME "PLL"
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/regulator/consumer.h>
#include <linux/sched.h>
#include <video/omapdss.h>
#include "dss.h"
#define PLL_CONTROL 0x0000
#define PLL_STATUS 0x0004
#define PLL_GO 0x0008
#define PLL_CONFIGURATION1 0x000C
#define PLL_CONFIGURATION2 0x0010
#define PLL_CONFIGURATION3 0x0014
#define PLL_SSC_CONFIGURATION1 0x0018
#define PLL_SSC_CONFIGURATION2 0x001C
#define PLL_CONFIGURATION4 0x0020
static struct dss_pll *dss_plls[4];
int dss_pll_register(struct dss_pll *pll)
{
int i;
for (i = 0; i < ARRAY_SIZE(dss_plls); ++i) {
if (!dss_plls[i]) {
dss_plls[i] = pll;
return 0;
}
}
return -EBUSY;
}
void dss_pll_unregister(struct dss_pll *pll)
{
int i;
for (i = 0; i < ARRAY_SIZE(dss_plls); ++i) {
if (dss_plls[i] == pll) {
dss_plls[i] = NULL;
return;
}
}
}
struct dss_pll *dss_pll_find(const char *name)
{
int i;
for (i = 0; i < ARRAY_SIZE(dss_plls); ++i) {
if (dss_plls[i] && strcmp(dss_plls[i]->name, name) == 0)
return dss_plls[i];
}
return NULL;
}
int dss_pll_enable(struct dss_pll *pll)
{
int r;
r = clk_prepare_enable(pll->clkin);
if (r)
return r;
if (pll->regulator) {
r = regulator_enable(pll->regulator);
if (r)
goto err_reg;
}
r = pll->ops->enable(pll);
if (r)
goto err_enable;
return 0;
err_enable:
if (pll->regulator)
regulator_disable(pll->regulator);
err_reg:
clk_disable_unprepare(pll->clkin);
return r;
}
void dss_pll_disable(struct dss_pll *pll)
{
pll->ops->disable(pll);
if (pll->regulator)
regulator_disable(pll->regulator);
clk_disable_unprepare(pll->clkin);
memset(&pll->cinfo, 0, sizeof(pll->cinfo));
}
int dss_pll_set_config(struct dss_pll *pll, const struct dss_pll_clock_info *cinfo)
{
int r;
r = pll->ops->set_config(pll, cinfo);
if (r)
return r;
pll->cinfo = *cinfo;
return 0;
}
bool dss_pll_hsdiv_calc(const struct dss_pll *pll, unsigned long clkdco,
unsigned long out_min, unsigned long out_max,
dss_hsdiv_calc_func func, void *data)
{
const struct dss_pll_hw *hw = pll->hw;
int m, m_start, m_stop;
unsigned long out;
out_min = out_min ? out_min : 1;
out_max = out_max ? out_max : ULONG_MAX;
m_start = max(DIV_ROUND_UP(clkdco, out_max), 1ul);
m_stop = min((unsigned)(clkdco / out_min), hw->mX_max);
for (m = m_start; m <= m_stop; ++m) {
out = clkdco / m;
if (func(m, out, data))
return true;
}
return false;
}
bool dss_pll_calc(const struct dss_pll *pll, unsigned long clkin,
unsigned long pll_min, unsigned long pll_max,
dss_pll_calc_func func, void *data)
{
const struct dss_pll_hw *hw = pll->hw;
int n, n_start, n_stop;
int m, m_start, m_stop;
unsigned long fint, clkdco;
unsigned long pll_hw_max;
unsigned long fint_hw_min, fint_hw_max;
pll_hw_max = hw->clkdco_max;
fint_hw_min = hw->fint_min;
fint_hw_max = hw->fint_max;
n_start = max(DIV_ROUND_UP(clkin, fint_hw_max), 1ul);
n_stop = min((unsigned)(clkin / fint_hw_min), hw->n_max);
pll_max = pll_max ? pll_max : ULONG_MAX;
for (n = n_start; n <= n_stop; ++n) {
fint = clkin / n;
m_start = max(DIV_ROUND_UP(DIV_ROUND_UP(pll_min, fint), 2),
1ul);
m_stop = min3((unsigned)(pll_max / fint / 2),
(unsigned)(pll_hw_max / fint / 2),
hw->m_max);
for (m = m_start; m <= m_stop; ++m) {
clkdco = 2 * m * fint;
if (func(n, m, fint, clkdco, data))
return true;
}
}
return false;
}
static int wait_for_bit_change(void __iomem *reg, int bitnum, int value)
{
unsigned long timeout;
ktime_t wait;
int t;
/* first busyloop to see if the bit changes right away */
t = 100;
while (t-- > 0) {
if (FLD_GET(readl_relaxed(reg), bitnum, bitnum) == value)
return value;
}
/* then loop for 500ms, sleeping for 1ms in between */
timeout = jiffies + msecs_to_jiffies(500);
while (time_before(jiffies, timeout)) {
if (FLD_GET(readl_relaxed(reg), bitnum, bitnum) == value)
return value;
wait = ns_to_ktime(1000 * 1000);
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_hrtimeout(&wait, HRTIMER_MODE_REL);
}
return !value;
}
int dss_pll_wait_reset_done(struct dss_pll *pll)
{
void __iomem *base = pll->base;
if (wait_for_bit_change(base + PLL_STATUS, 0, 1) != 1)
return -ETIMEDOUT;
else
return 0;
}
static int dss_wait_hsdiv_ack(struct dss_pll *pll, u32 hsdiv_ack_mask)
{
int t = 100;
while (t-- > 0) {
u32 v = readl_relaxed(pll->base + PLL_STATUS);
v &= hsdiv_ack_mask;
if (v == hsdiv_ack_mask)
return 0;
}
return -ETIMEDOUT;
}
int dss_pll_write_config_type_a(struct dss_pll *pll,
const struct dss_pll_clock_info *cinfo)
{
const struct dss_pll_hw *hw = pll->hw;
void __iomem *base = pll->base;
int r = 0;
u32 l;
l = 0;
if (hw->has_stopmode)
l = FLD_MOD(l, 1, 0, 0); /* PLL_STOPMODE */
l = FLD_MOD(l, cinfo->n - 1, hw->n_msb, hw->n_lsb); /* PLL_REGN */
l = FLD_MOD(l, cinfo->m, hw->m_msb, hw->m_lsb); /* PLL_REGM */
/* M4 */
l = FLD_MOD(l, cinfo->mX[0] ? cinfo->mX[0] - 1 : 0,
hw->mX_msb[0], hw->mX_lsb[0]);
/* M5 */
l = FLD_MOD(l, cinfo->mX[1] ? cinfo->mX[1] - 1 : 0,
hw->mX_msb[1], hw->mX_lsb[1]);
writel_relaxed(l, base + PLL_CONFIGURATION1);
l = 0;
/* M6 */
l = FLD_MOD(l, cinfo->mX[2] ? cinfo->mX[2] - 1 : 0,
hw->mX_msb[2], hw->mX_lsb[2]);
/* M7 */
l = FLD_MOD(l, cinfo->mX[3] ? cinfo->mX[3] - 1 : 0,
hw->mX_msb[3], hw->mX_lsb[3]);
writel_relaxed(l, base + PLL_CONFIGURATION3);
l = readl_relaxed(base + PLL_CONFIGURATION2);
if (hw->has_freqsel) {
u32 f = cinfo->fint < 1000000 ? 0x3 :
cinfo->fint < 1250000 ? 0x4 :
cinfo->fint < 1500000 ? 0x5 :
cinfo->fint < 1750000 ? 0x6 :
0x7;
l = FLD_MOD(l, f, 4, 1); /* PLL_FREQSEL */
} else if (hw->has_selfreqdco) {
u32 f = cinfo->clkdco < hw->clkdco_low ? 0x2 : 0x4;
l = FLD_MOD(l, f, 3, 1); /* PLL_SELFREQDCO */
}
l = FLD_MOD(l, 1, 13, 13); /* PLL_REFEN */
l = FLD_MOD(l, 0, 14, 14); /* PHY_CLKINEN */
l = FLD_MOD(l, 0, 16, 16); /* M4_CLOCK_EN */
l = FLD_MOD(l, 0, 18, 18); /* M5_CLOCK_EN */
l = FLD_MOD(l, 1, 20, 20); /* HSDIVBYPASS */
if (hw->has_refsel)
l = FLD_MOD(l, 3, 22, 21); /* REFSEL = sysclk */
l = FLD_MOD(l, 0, 23, 23); /* M6_CLOCK_EN */
l = FLD_MOD(l, 0, 25, 25); /* M7_CLOCK_EN */
writel_relaxed(l, base + PLL_CONFIGURATION2);
writel_relaxed(1, base + PLL_GO); /* PLL_GO */
if (wait_for_bit_change(base + PLL_GO, 0, 0) != 0) {
DSSERR("DSS DPLL GO bit not going down.\n");
r = -EIO;
goto err;
}
if (wait_for_bit_change(base + PLL_STATUS, 1, 1) != 1) {
DSSERR("cannot lock DSS DPLL\n");
r = -EIO;
goto err;
}
l = readl_relaxed(base + PLL_CONFIGURATION2);
l = FLD_MOD(l, 1, 14, 14); /* PHY_CLKINEN */
l = FLD_MOD(l, cinfo->mX[0] ? 1 : 0, 16, 16); /* M4_CLOCK_EN */
l = FLD_MOD(l, cinfo->mX[1] ? 1 : 0, 18, 18); /* M5_CLOCK_EN */
l = FLD_MOD(l, 0, 20, 20); /* HSDIVBYPASS */
l = FLD_MOD(l, cinfo->mX[2] ? 1 : 0, 23, 23); /* M6_CLOCK_EN */
l = FLD_MOD(l, cinfo->mX[3] ? 1 : 0, 25, 25); /* M7_CLOCK_EN */
writel_relaxed(l, base + PLL_CONFIGURATION2);
r = dss_wait_hsdiv_ack(pll,
(cinfo->mX[0] ? BIT(7) : 0) |
(cinfo->mX[1] ? BIT(8) : 0) |
(cinfo->mX[2] ? BIT(10) : 0) |
(cinfo->mX[3] ? BIT(11) : 0));
if (r) {
DSSERR("failed to enable HSDIV clocks\n");
goto err;
}
err:
return r;
}
int dss_pll_write_config_type_b(struct dss_pll *pll,
const struct dss_pll_clock_info *cinfo)
{
const struct dss_pll_hw *hw = pll->hw;
void __iomem *base = pll->base;
u32 l;
l = 0;
l = FLD_MOD(l, cinfo->m, 20, 9); /* PLL_REGM */
l = FLD_MOD(l, cinfo->n - 1, 8, 1); /* PLL_REGN */
writel_relaxed(l, base + PLL_CONFIGURATION1);
l = readl_relaxed(base + PLL_CONFIGURATION2);
l = FLD_MOD(l, 0x0, 12, 12); /* PLL_HIGHFREQ divide by 2 */
l = FLD_MOD(l, 0x1, 13, 13); /* PLL_REFEN */
l = FLD_MOD(l, 0x0, 14, 14); /* PHY_CLKINEN */
if (hw->has_refsel)
l = FLD_MOD(l, 0x3, 22, 21); /* REFSEL = SYSCLK */
/* PLL_SELFREQDCO */
if (cinfo->clkdco > hw->clkdco_low)
l = FLD_MOD(l, 0x4, 3, 1);
else
l = FLD_MOD(l, 0x2, 3, 1);
writel_relaxed(l, base + PLL_CONFIGURATION2);
l = readl_relaxed(base + PLL_CONFIGURATION3);
l = FLD_MOD(l, cinfo->sd, 17, 10); /* PLL_REGSD */
writel_relaxed(l, base + PLL_CONFIGURATION3);
l = readl_relaxed(base + PLL_CONFIGURATION4);
l = FLD_MOD(l, cinfo->mX[0], 24, 18); /* PLL_REGM2 */
l = FLD_MOD(l, cinfo->mf, 17, 0); /* PLL_REGM_F */
writel_relaxed(l, base + PLL_CONFIGURATION4);
writel_relaxed(1, base + PLL_GO); /* PLL_GO */
if (wait_for_bit_change(base + PLL_GO, 0, 0) != 0) {
DSSERR("DSS DPLL GO bit not going down.\n");
return -EIO;
}
if (wait_for_bit_change(base + PLL_STATUS, 1, 1) != 1) {
DSSERR("cannot lock DSS DPLL\n");
return -ETIMEDOUT;
}
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,454 @@
/*
* linux/drivers/video/omap2/dss/sdi.c
*
* Copyright (C) 2009 Nokia Corporation
* Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define DSS_SUBSYS_NAME "SDI"
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/regulator/consumer.h>
#include <linux/export.h>
#include <linux/platform_device.h>
#include <linux/string.h>
#include <linux/of.h>
#include <linux/component.h>
#include <video/omapdss.h>
#include "dss.h"
static struct {
struct platform_device *pdev;
bool update_enabled;
struct regulator *vdds_sdi_reg;
struct dss_lcd_mgr_config mgr_config;
struct omap_video_timings timings;
int datapairs;
struct omap_dss_device output;
bool port_initialized;
} sdi;
struct sdi_clk_calc_ctx {
unsigned long pck_min, pck_max;
unsigned long fck;
struct dispc_clock_info dispc_cinfo;
};
static bool dpi_calc_dispc_cb(int lckd, int pckd, unsigned long lck,
unsigned long pck, void *data)
{
struct sdi_clk_calc_ctx *ctx = data;
ctx->dispc_cinfo.lck_div = lckd;
ctx->dispc_cinfo.pck_div = pckd;
ctx->dispc_cinfo.lck = lck;
ctx->dispc_cinfo.pck = pck;
return true;
}
static bool dpi_calc_dss_cb(unsigned long fck, void *data)
{
struct sdi_clk_calc_ctx *ctx = data;
ctx->fck = fck;
return dispc_div_calc(fck, ctx->pck_min, ctx->pck_max,
dpi_calc_dispc_cb, ctx);
}
static int sdi_calc_clock_div(unsigned long pclk,
unsigned long *fck,
struct dispc_clock_info *dispc_cinfo)
{
int i;
struct sdi_clk_calc_ctx ctx;
/*
* DSS fclk gives us very few possibilities, so finding a good pixel
* clock may not be possible. We try multiple times to find the clock,
* each time widening the pixel clock range we look for, up to
* +/- 1MHz.
*/
for (i = 0; i < 10; ++i) {
bool ok;
memset(&ctx, 0, sizeof(ctx));
if (pclk > 1000 * i * i * i)
ctx.pck_min = max(pclk - 1000 * i * i * i, 0lu);
else
ctx.pck_min = 0;
ctx.pck_max = pclk + 1000 * i * i * i;
ok = dss_div_calc(pclk, ctx.pck_min, dpi_calc_dss_cb, &ctx);
if (ok) {
*fck = ctx.fck;
*dispc_cinfo = ctx.dispc_cinfo;
return 0;
}
}
return -EINVAL;
}
static void sdi_config_lcd_manager(struct omap_dss_device *dssdev)
{
struct omap_overlay_manager *mgr = sdi.output.manager;
sdi.mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS;
sdi.mgr_config.stallmode = false;
sdi.mgr_config.fifohandcheck = false;
sdi.mgr_config.video_port_width = 24;
sdi.mgr_config.lcden_sig_polarity = 1;
dss_mgr_set_lcd_config(mgr, &sdi.mgr_config);
}
static int sdi_display_enable(struct omap_dss_device *dssdev)
{
struct omap_dss_device *out = &sdi.output;
struct omap_video_timings *t = &sdi.timings;
unsigned long fck;
struct dispc_clock_info dispc_cinfo;
unsigned long pck;
int r;
if (out->manager == NULL) {
DSSERR("failed to enable display: no output/manager\n");
return -ENODEV;
}
r = regulator_enable(sdi.vdds_sdi_reg);
if (r)
goto err_reg_enable;
r = dispc_runtime_get();
if (r)
goto err_get_dispc;
/* 15.5.9.1.2 */
t->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
t->sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
r = sdi_calc_clock_div(t->pixelclock, &fck, &dispc_cinfo);
if (r)
goto err_calc_clock_div;
sdi.mgr_config.clock_info = dispc_cinfo;
pck = fck / dispc_cinfo.lck_div / dispc_cinfo.pck_div;
if (pck != t->pixelclock) {
DSSWARN("Could not find exact pixel clock. Requested %d Hz, got %lu Hz\n",
t->pixelclock, pck);
t->pixelclock = pck;
}
dss_mgr_set_timings(out->manager, t);
r = dss_set_fck_rate(fck);
if (r)
goto err_set_dss_clock_div;
sdi_config_lcd_manager(dssdev);
/*
* LCLK and PCLK divisors are located in shadow registers, and we
* normally write them to DISPC registers when enabling the output.
* However, SDI uses pck-free as source clock for its PLL, and pck-free
* is affected by the divisors. And as we need the PLL before enabling
* the output, we need to write the divisors early.
*
* It seems just writing to the DISPC register is enough, and we don't
* need to care about the shadow register mechanism for pck-free. The
* exact reason for this is unknown.
*/
dispc_mgr_set_clock_div(out->manager->id, &sdi.mgr_config.clock_info);
dss_sdi_init(sdi.datapairs);
r = dss_sdi_enable();
if (r)
goto err_sdi_enable;
mdelay(2);
r = dss_mgr_enable(out->manager);
if (r)
goto err_mgr_enable;
return 0;
err_mgr_enable:
dss_sdi_disable();
err_sdi_enable:
err_set_dss_clock_div:
err_calc_clock_div:
dispc_runtime_put();
err_get_dispc:
regulator_disable(sdi.vdds_sdi_reg);
err_reg_enable:
return r;
}
static void sdi_display_disable(struct omap_dss_device *dssdev)
{
struct omap_overlay_manager *mgr = sdi.output.manager;
dss_mgr_disable(mgr);
dss_sdi_disable();
dispc_runtime_put();
regulator_disable(sdi.vdds_sdi_reg);
}
static void sdi_set_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
sdi.timings = *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->pixelclock == 0)
return -EINVAL;
return 0;
}
static void sdi_set_datapairs(struct omap_dss_device *dssdev, int datapairs)
{
sdi.datapairs = datapairs;
}
static int sdi_init_regulator(void)
{
struct regulator *vdds_sdi;
if (sdi.vdds_sdi_reg)
return 0;
vdds_sdi = devm_regulator_get(&sdi.pdev->dev, "vdds_sdi");
if (IS_ERR(vdds_sdi)) {
if (PTR_ERR(vdds_sdi) != -EPROBE_DEFER)
DSSERR("can't get VDDS_SDI regulator\n");
return PTR_ERR(vdds_sdi);
}
sdi.vdds_sdi_reg = vdds_sdi;
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->dst);
if (dst != dssdev->dst)
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 = sdi_display_enable,
.disable = sdi_display_disable,
.check_timings = sdi_check_timings,
.set_timings = sdi_set_timings,
.get_timings = sdi_get_timings,
.set_datapairs = sdi_set_datapairs,
};
static void sdi_init_output(struct platform_device *pdev)
{
struct omap_dss_device *out = &sdi.output;
out->dev = &pdev->dev;
out->id = OMAP_DSS_OUTPUT_SDI;
out->output_type = OMAP_DISPLAY_TYPE_SDI;
out->name = "sdi.0";
out->dispc_channel = OMAP_DSS_CHANNEL_LCD;
/* We have SDI only on OMAP3, where it's on port 1 */
out->port_num = 1;
out->ops.sdi = &sdi_ops;
out->owner = THIS_MODULE;
omapdss_register_output(out);
}
static void sdi_uninit_output(struct platform_device *pdev)
{
struct omap_dss_device *out = &sdi.output;
omapdss_unregister_output(out);
}
static int sdi_bind(struct device *dev, struct device *master, void *data)
{
struct platform_device *pdev = to_platform_device(dev);
sdi.pdev = pdev;
sdi_init_output(pdev);
return 0;
}
static void sdi_unbind(struct device *dev, struct device *master, void *data)
{
struct platform_device *pdev = to_platform_device(dev);
sdi_uninit_output(pdev);
}
static const struct component_ops sdi_component_ops = {
.bind = sdi_bind,
.unbind = sdi_unbind,
};
static int sdi_probe(struct platform_device *pdev)
{
return component_add(&pdev->dev, &sdi_component_ops);
}
static int sdi_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &sdi_component_ops);
return 0;
}
static struct platform_driver omap_sdi_driver = {
.probe = sdi_probe,
.remove = sdi_remove,
.driver = {
.name = "omapdss_sdi",
.suppress_bind_attrs = true,
},
};
int __init sdi_init_platform_driver(void)
{
return platform_driver_register(&omap_sdi_driver);
}
void sdi_uninit_platform_driver(void)
{
platform_driver_unregister(&omap_sdi_driver);
}
int sdi_init_port(struct platform_device *pdev, struct device_node *port)
{
struct device_node *ep;
u32 datapairs;
int r;
ep = omapdss_of_get_next_endpoint(port, NULL);
if (!ep)
return 0;
r = of_property_read_u32(ep, "datapairs", &datapairs);
if (r) {
DSSERR("failed to parse datapairs\n");
goto err_datapairs;
}
sdi.datapairs = datapairs;
of_node_put(ep);
sdi.pdev = pdev;
sdi_init_output(pdev);
sdi.port_initialized = true;
return 0;
err_datapairs:
of_node_put(ep);
return r;
}
void sdi_uninit_port(struct device_node *port)
{
if (!sdi.port_initialized)
return;
sdi_uninit_output(sdi.pdev);
}

View File

@ -0,0 +1,997 @@
/*
* linux/drivers/video/omap2/dss/venc.c
*
* Copyright (C) 2009 Nokia Corporation
* Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
*
* VENC settings from TI's DSS driver
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define DSS_SUBSYS_NAME "VENC"
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/mutex.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/seq_file.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
#include <linux/component.h>
#include <video/omapdss.h>
#include "dss.h"
#include "dss_features.h"
/* Venc registers */
#define VENC_REV_ID 0x00
#define VENC_STATUS 0x04
#define VENC_F_CONTROL 0x08
#define VENC_VIDOUT_CTRL 0x10
#define VENC_SYNC_CTRL 0x14
#define VENC_LLEN 0x1C
#define VENC_FLENS 0x20
#define VENC_HFLTR_CTRL 0x24
#define VENC_CC_CARR_WSS_CARR 0x28
#define VENC_C_PHASE 0x2C
#define VENC_GAIN_U 0x30
#define VENC_GAIN_V 0x34
#define VENC_GAIN_Y 0x38
#define VENC_BLACK_LEVEL 0x3C
#define VENC_BLANK_LEVEL 0x40
#define VENC_X_COLOR 0x44
#define VENC_M_CONTROL 0x48
#define VENC_BSTAMP_WSS_DATA 0x4C
#define VENC_S_CARR 0x50
#define VENC_LINE21 0x54
#define VENC_LN_SEL 0x58
#define VENC_L21__WC_CTL 0x5C
#define VENC_HTRIGGER_VTRIGGER 0x60
#define VENC_SAVID__EAVID 0x64
#define VENC_FLEN__FAL 0x68
#define VENC_LAL__PHASE_RESET 0x6C
#define VENC_HS_INT_START_STOP_X 0x70
#define VENC_HS_EXT_START_STOP_X 0x74
#define VENC_VS_INT_START_X 0x78
#define VENC_VS_INT_STOP_X__VS_INT_START_Y 0x7C
#define VENC_VS_INT_STOP_Y__VS_EXT_START_X 0x80
#define VENC_VS_EXT_STOP_X__VS_EXT_START_Y 0x84
#define VENC_VS_EXT_STOP_Y 0x88
#define VENC_AVID_START_STOP_X 0x90
#define VENC_AVID_START_STOP_Y 0x94
#define VENC_FID_INT_START_X__FID_INT_START_Y 0xA0
#define VENC_FID_INT_OFFSET_Y__FID_EXT_START_X 0xA4
#define VENC_FID_EXT_START_Y__FID_EXT_OFFSET_Y 0xA8
#define VENC_TVDETGP_INT_START_STOP_X 0xB0
#define VENC_TVDETGP_INT_START_STOP_Y 0xB4
#define VENC_GEN_CTRL 0xB8
#define VENC_OUTPUT_CONTROL 0xC4
#define VENC_OUTPUT_TEST 0xC8
#define VENC_DAC_B__DAC_C 0xC8
struct venc_config {
u32 f_control;
u32 vidout_ctrl;
u32 sync_ctrl;
u32 llen;
u32 flens;
u32 hfltr_ctrl;
u32 cc_carr_wss_carr;
u32 c_phase;
u32 gain_u;
u32 gain_v;
u32 gain_y;
u32 black_level;
u32 blank_level;
u32 x_color;
u32 m_control;
u32 bstamp_wss_data;
u32 s_carr;
u32 line21;
u32 ln_sel;
u32 l21__wc_ctl;
u32 htrigger_vtrigger;
u32 savid__eavid;
u32 flen__fal;
u32 lal__phase_reset;
u32 hs_int_start_stop_x;
u32 hs_ext_start_stop_x;
u32 vs_int_start_x;
u32 vs_int_stop_x__vs_int_start_y;
u32 vs_int_stop_y__vs_ext_start_x;
u32 vs_ext_stop_x__vs_ext_start_y;
u32 vs_ext_stop_y;
u32 avid_start_stop_x;
u32 avid_start_stop_y;
u32 fid_int_start_x__fid_int_start_y;
u32 fid_int_offset_y__fid_ext_start_x;
u32 fid_ext_start_y__fid_ext_offset_y;
u32 tvdetgp_int_start_stop_x;
u32 tvdetgp_int_start_stop_y;
u32 gen_ctrl;
};
/* from TRM */
static const struct venc_config venc_config_pal_trm = {
.f_control = 0,
.vidout_ctrl = 1,
.sync_ctrl = 0x40,
.llen = 0x35F, /* 863 */
.flens = 0x270, /* 624 */
.hfltr_ctrl = 0,
.cc_carr_wss_carr = 0x2F7225ED,
.c_phase = 0,
.gain_u = 0x111,
.gain_v = 0x181,
.gain_y = 0x140,
.black_level = 0x3B,
.blank_level = 0x3B,
.x_color = 0x7,
.m_control = 0x2,
.bstamp_wss_data = 0x3F,
.s_carr = 0x2A098ACB,
.line21 = 0,
.ln_sel = 0x01290015,
.l21__wc_ctl = 0x0000F603,
.htrigger_vtrigger = 0,
.savid__eavid = 0x06A70108,
.flen__fal = 0x00180270,
.lal__phase_reset = 0x00040135,
.hs_int_start_stop_x = 0x00880358,
.hs_ext_start_stop_x = 0x000F035F,
.vs_int_start_x = 0x01A70000,
.vs_int_stop_x__vs_int_start_y = 0x000001A7,
.vs_int_stop_y__vs_ext_start_x = 0x01AF0000,
.vs_ext_stop_x__vs_ext_start_y = 0x000101AF,
.vs_ext_stop_y = 0x00000025,
.avid_start_stop_x = 0x03530083,
.avid_start_stop_y = 0x026C002E,
.fid_int_start_x__fid_int_start_y = 0x0001008A,
.fid_int_offset_y__fid_ext_start_x = 0x002E0138,
.fid_ext_start_y__fid_ext_offset_y = 0x01380001,
.tvdetgp_int_start_stop_x = 0x00140001,
.tvdetgp_int_start_stop_y = 0x00010001,
.gen_ctrl = 0x00FF0000,
};
/* from TRM */
static const struct venc_config venc_config_ntsc_trm = {
.f_control = 0,
.vidout_ctrl = 1,
.sync_ctrl = 0x8040,
.llen = 0x359,
.flens = 0x20C,
.hfltr_ctrl = 0,
.cc_carr_wss_carr = 0x043F2631,
.c_phase = 0,
.gain_u = 0x102,
.gain_v = 0x16C,
.gain_y = 0x12F,
.black_level = 0x43,
.blank_level = 0x38,
.x_color = 0x7,
.m_control = 0x1,
.bstamp_wss_data = 0x38,
.s_carr = 0x21F07C1F,
.line21 = 0,
.ln_sel = 0x01310011,
.l21__wc_ctl = 0x0000F003,
.htrigger_vtrigger = 0,
.savid__eavid = 0x069300F4,
.flen__fal = 0x0016020C,
.lal__phase_reset = 0x00060107,
.hs_int_start_stop_x = 0x008E0350,
.hs_ext_start_stop_x = 0x000F0359,
.vs_int_start_x = 0x01A00000,
.vs_int_stop_x__vs_int_start_y = 0x020701A0,
.vs_int_stop_y__vs_ext_start_x = 0x01AC0024,
.vs_ext_stop_x__vs_ext_start_y = 0x020D01AC,
.vs_ext_stop_y = 0x00000006,
.avid_start_stop_x = 0x03480078,
.avid_start_stop_y = 0x02060024,
.fid_int_start_x__fid_int_start_y = 0x0001008A,
.fid_int_offset_y__fid_ext_start_x = 0x01AC0106,
.fid_ext_start_y__fid_ext_offset_y = 0x01060006,
.tvdetgp_int_start_stop_x = 0x00140001,
.tvdetgp_int_start_stop_y = 0x00010001,
.gen_ctrl = 0x00F90000,
};
static const struct venc_config venc_config_pal_bdghi = {
.f_control = 0,
.vidout_ctrl = 0,
.sync_ctrl = 0,
.hfltr_ctrl = 0,
.x_color = 0,
.line21 = 0,
.ln_sel = 21,
.htrigger_vtrigger = 0,
.tvdetgp_int_start_stop_x = 0x00140001,
.tvdetgp_int_start_stop_y = 0x00010001,
.gen_ctrl = 0x00FB0000,
.llen = 864-1,
.flens = 625-1,
.cc_carr_wss_carr = 0x2F7625ED,
.c_phase = 0xDF,
.gain_u = 0x111,
.gain_v = 0x181,
.gain_y = 0x140,
.black_level = 0x3e,
.blank_level = 0x3e,
.m_control = 0<<2 | 1<<1,
.bstamp_wss_data = 0x42,
.s_carr = 0x2a098acb,
.l21__wc_ctl = 0<<13 | 0x16<<8 | 0<<0,
.savid__eavid = 0x06A70108,
.flen__fal = 23<<16 | 624<<0,
.lal__phase_reset = 2<<17 | 310<<0,
.hs_int_start_stop_x = 0x00920358,
.hs_ext_start_stop_x = 0x000F035F,
.vs_int_start_x = 0x1a7<<16,
.vs_int_stop_x__vs_int_start_y = 0x000601A7,
.vs_int_stop_y__vs_ext_start_x = 0x01AF0036,
.vs_ext_stop_x__vs_ext_start_y = 0x27101af,
.vs_ext_stop_y = 0x05,
.avid_start_stop_x = 0x03530082,
.avid_start_stop_y = 0x0270002E,
.fid_int_start_x__fid_int_start_y = 0x0005008A,
.fid_int_offset_y__fid_ext_start_x = 0x002E0138,
.fid_ext_start_y__fid_ext_offset_y = 0x01380005,
};
const struct omap_video_timings omap_dss_pal_timings = {
.x_res = 720,
.y_res = 574,
.pixelclock = 13500000,
.hsw = 64,
.hfp = 12,
.hbp = 68,
.vsw = 5,
.vfp = 5,
.vbp = 41,
.interlace = true,
};
EXPORT_SYMBOL(omap_dss_pal_timings);
const struct omap_video_timings omap_dss_ntsc_timings = {
.x_res = 720,
.y_res = 482,
.pixelclock = 13500000,
.hsw = 64,
.hfp = 16,
.hbp = 58,
.vsw = 6,
.vfp = 6,
.vbp = 31,
.interlace = true,
};
EXPORT_SYMBOL(omap_dss_ntsc_timings);
static struct {
struct platform_device *pdev;
void __iomem *base;
struct mutex venc_lock;
u32 wss_data;
struct regulator *vdda_dac_reg;
struct clk *tv_dac_clk;
struct omap_video_timings timings;
enum omap_dss_venc_type type;
bool invert_polarity;
struct omap_dss_device output;
} venc;
static inline void venc_write_reg(int idx, u32 val)
{
__raw_writel(val, venc.base + idx);
}
static inline u32 venc_read_reg(int idx)
{
u32 l = __raw_readl(venc.base + idx);
return l;
}
static void venc_write_config(const struct venc_config *config)
{
DSSDBG("write venc conf\n");
venc_write_reg(VENC_LLEN, config->llen);
venc_write_reg(VENC_FLENS, config->flens);
venc_write_reg(VENC_CC_CARR_WSS_CARR, config->cc_carr_wss_carr);
venc_write_reg(VENC_C_PHASE, config->c_phase);
venc_write_reg(VENC_GAIN_U, config->gain_u);
venc_write_reg(VENC_GAIN_V, config->gain_v);
venc_write_reg(VENC_GAIN_Y, config->gain_y);
venc_write_reg(VENC_BLACK_LEVEL, config->black_level);
venc_write_reg(VENC_BLANK_LEVEL, config->blank_level);
venc_write_reg(VENC_M_CONTROL, config->m_control);
venc_write_reg(VENC_BSTAMP_WSS_DATA, config->bstamp_wss_data |
venc.wss_data);
venc_write_reg(VENC_S_CARR, config->s_carr);
venc_write_reg(VENC_L21__WC_CTL, config->l21__wc_ctl);
venc_write_reg(VENC_SAVID__EAVID, config->savid__eavid);
venc_write_reg(VENC_FLEN__FAL, config->flen__fal);
venc_write_reg(VENC_LAL__PHASE_RESET, config->lal__phase_reset);
venc_write_reg(VENC_HS_INT_START_STOP_X, config->hs_int_start_stop_x);
venc_write_reg(VENC_HS_EXT_START_STOP_X, config->hs_ext_start_stop_x);
venc_write_reg(VENC_VS_INT_START_X, config->vs_int_start_x);
venc_write_reg(VENC_VS_INT_STOP_X__VS_INT_START_Y,
config->vs_int_stop_x__vs_int_start_y);
venc_write_reg(VENC_VS_INT_STOP_Y__VS_EXT_START_X,
config->vs_int_stop_y__vs_ext_start_x);
venc_write_reg(VENC_VS_EXT_STOP_X__VS_EXT_START_Y,
config->vs_ext_stop_x__vs_ext_start_y);
venc_write_reg(VENC_VS_EXT_STOP_Y, config->vs_ext_stop_y);
venc_write_reg(VENC_AVID_START_STOP_X, config->avid_start_stop_x);
venc_write_reg(VENC_AVID_START_STOP_Y, config->avid_start_stop_y);
venc_write_reg(VENC_FID_INT_START_X__FID_INT_START_Y,
config->fid_int_start_x__fid_int_start_y);
venc_write_reg(VENC_FID_INT_OFFSET_Y__FID_EXT_START_X,
config->fid_int_offset_y__fid_ext_start_x);
venc_write_reg(VENC_FID_EXT_START_Y__FID_EXT_OFFSET_Y,
config->fid_ext_start_y__fid_ext_offset_y);
venc_write_reg(VENC_DAC_B__DAC_C, venc_read_reg(VENC_DAC_B__DAC_C));
venc_write_reg(VENC_VIDOUT_CTRL, config->vidout_ctrl);
venc_write_reg(VENC_HFLTR_CTRL, config->hfltr_ctrl);
venc_write_reg(VENC_X_COLOR, config->x_color);
venc_write_reg(VENC_LINE21, config->line21);
venc_write_reg(VENC_LN_SEL, config->ln_sel);
venc_write_reg(VENC_HTRIGGER_VTRIGGER, config->htrigger_vtrigger);
venc_write_reg(VENC_TVDETGP_INT_START_STOP_X,
config->tvdetgp_int_start_stop_x);
venc_write_reg(VENC_TVDETGP_INT_START_STOP_Y,
config->tvdetgp_int_start_stop_y);
venc_write_reg(VENC_GEN_CTRL, config->gen_ctrl);
venc_write_reg(VENC_F_CONTROL, config->f_control);
venc_write_reg(VENC_SYNC_CTRL, config->sync_ctrl);
}
static void venc_reset(void)
{
int t = 1000;
venc_write_reg(VENC_F_CONTROL, 1<<8);
while (venc_read_reg(VENC_F_CONTROL) & (1<<8)) {
if (--t == 0) {
DSSERR("Failed to reset venc\n");
return;
}
}
#ifdef CONFIG_OMAP2_DSS_SLEEP_AFTER_VENC_RESET
/* the magical sleep that makes things work */
/* XXX more info? What bug this circumvents? */
msleep(20);
#endif
}
static int venc_runtime_get(void)
{
int r;
DSSDBG("venc_runtime_get\n");
r = pm_runtime_get_sync(&venc.pdev->dev);
WARN_ON(r < 0);
return r < 0 ? r : 0;
}
static void venc_runtime_put(void)
{
int r;
DSSDBG("venc_runtime_put\n");
r = pm_runtime_put_sync(&venc.pdev->dev);
WARN_ON(r < 0 && r != -ENOSYS);
}
static const struct venc_config *venc_timings_to_config(
struct omap_video_timings *timings)
{
if (memcmp(&omap_dss_pal_timings, timings, sizeof(*timings)) == 0)
return &venc_config_pal_trm;
if (memcmp(&omap_dss_ntsc_timings, timings, sizeof(*timings)) == 0)
return &venc_config_ntsc_trm;
BUG();
return NULL;
}
static int venc_power_on(struct omap_dss_device *dssdev)
{
struct omap_overlay_manager *mgr = venc.output.manager;
u32 l;
int r;
r = venc_runtime_get();
if (r)
goto err0;
venc_reset();
venc_write_config(venc_timings_to_config(&venc.timings));
dss_set_venc_output(venc.type);
dss_set_dac_pwrdn_bgz(1);
l = 0;
if (venc.type == OMAP_DSS_VENC_TYPE_COMPOSITE)
l |= 1 << 1;
else /* S-Video */
l |= (1 << 0) | (1 << 2);
if (venc.invert_polarity == false)
l |= 1 << 3;
venc_write_reg(VENC_OUTPUT_CONTROL, l);
dss_mgr_set_timings(mgr, &venc.timings);
r = regulator_enable(venc.vdda_dac_reg);
if (r)
goto err1;
r = dss_mgr_enable(mgr);
if (r)
goto err2;
return 0;
err2:
regulator_disable(venc.vdda_dac_reg);
err1:
venc_write_reg(VENC_OUTPUT_CONTROL, 0);
dss_set_dac_pwrdn_bgz(0);
venc_runtime_put();
err0:
return r;
}
static void venc_power_off(struct omap_dss_device *dssdev)
{
struct omap_overlay_manager *mgr = venc.output.manager;
venc_write_reg(VENC_OUTPUT_CONTROL, 0);
dss_set_dac_pwrdn_bgz(0);
dss_mgr_disable(mgr);
regulator_disable(venc.vdda_dac_reg);
venc_runtime_put();
}
static int venc_display_enable(struct omap_dss_device *dssdev)
{
struct omap_dss_device *out = &venc.output;
int r;
DSSDBG("venc_display_enable\n");
mutex_lock(&venc.venc_lock);
if (out->manager == NULL) {
DSSERR("Failed to enable display: no output/manager\n");
r = -ENODEV;
goto err0;
}
r = venc_power_on(dssdev);
if (r)
goto err0;
venc.wss_data = 0;
mutex_unlock(&venc.venc_lock);
return 0;
err0:
mutex_unlock(&venc.venc_lock);
return r;
}
static void venc_display_disable(struct omap_dss_device *dssdev)
{
DSSDBG("venc_display_disable\n");
mutex_lock(&venc.venc_lock);
venc_power_off(dssdev);
mutex_unlock(&venc.venc_lock);
}
static void venc_set_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
DSSDBG("venc_set_timings\n");
mutex_lock(&venc.venc_lock);
/* Reset WSS data when the TV standard changes. */
if (memcmp(&venc.timings, timings, sizeof(*timings)))
venc.wss_data = 0;
venc.timings = *timings;
dispc_set_tv_pclk(13500000);
mutex_unlock(&venc.venc_lock);
}
static int venc_check_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
DSSDBG("venc_check_timings\n");
if (memcmp(&omap_dss_pal_timings, timings, sizeof(*timings)) == 0)
return 0;
if (memcmp(&omap_dss_ntsc_timings, timings, sizeof(*timings)) == 0)
return 0;
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);
}
static u32 venc_get_wss(struct omap_dss_device *dssdev)
{
/* Invert due to VENC_L21_WC_CTL:INV=1 */
return (venc.wss_data >> 8) ^ 0xfffff;
}
static int venc_set_wss(struct omap_dss_device *dssdev, u32 wss)
{
const struct venc_config *config;
int r;
DSSDBG("venc_set_wss\n");
mutex_lock(&venc.venc_lock);
config = venc_timings_to_config(&venc.timings);
/* Invert due to VENC_L21_WC_CTL:INV=1 */
venc.wss_data = (wss ^ 0xfffff) << 8;
r = venc_runtime_get();
if (r)
goto err;
venc_write_reg(VENC_BSTAMP_WSS_DATA, config->bstamp_wss_data |
venc.wss_data);
venc_runtime_put();
err:
mutex_unlock(&venc.venc_lock);
return r;
}
static void venc_set_type(struct omap_dss_device *dssdev,
enum omap_dss_venc_type type)
{
mutex_lock(&venc.venc_lock);
venc.type = type;
mutex_unlock(&venc.venc_lock);
}
static void venc_invert_vid_out_polarity(struct omap_dss_device *dssdev,
bool invert_polarity)
{
mutex_lock(&venc.venc_lock);
venc.invert_polarity = invert_polarity;
mutex_unlock(&venc.venc_lock);
}
static int venc_init_regulator(void)
{
struct regulator *vdda_dac;
if (venc.vdda_dac_reg != NULL)
return 0;
if (venc.pdev->dev.of_node)
vdda_dac = devm_regulator_get(&venc.pdev->dev, "vdda");
else
vdda_dac = devm_regulator_get(&venc.pdev->dev, "vdda_dac");
if (IS_ERR(vdda_dac)) {
if (PTR_ERR(vdda_dac) != -EPROBE_DEFER)
DSSERR("can't get VDDA_DAC regulator\n");
return PTR_ERR(vdda_dac);
}
venc.vdda_dac_reg = vdda_dac;
return 0;
}
static void venc_dump_regs(struct seq_file *s)
{
#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, venc_read_reg(r))
if (venc_runtime_get())
return;
DUMPREG(VENC_F_CONTROL);
DUMPREG(VENC_VIDOUT_CTRL);
DUMPREG(VENC_SYNC_CTRL);
DUMPREG(VENC_LLEN);
DUMPREG(VENC_FLENS);
DUMPREG(VENC_HFLTR_CTRL);
DUMPREG(VENC_CC_CARR_WSS_CARR);
DUMPREG(VENC_C_PHASE);
DUMPREG(VENC_GAIN_U);
DUMPREG(VENC_GAIN_V);
DUMPREG(VENC_GAIN_Y);
DUMPREG(VENC_BLACK_LEVEL);
DUMPREG(VENC_BLANK_LEVEL);
DUMPREG(VENC_X_COLOR);
DUMPREG(VENC_M_CONTROL);
DUMPREG(VENC_BSTAMP_WSS_DATA);
DUMPREG(VENC_S_CARR);
DUMPREG(VENC_LINE21);
DUMPREG(VENC_LN_SEL);
DUMPREG(VENC_L21__WC_CTL);
DUMPREG(VENC_HTRIGGER_VTRIGGER);
DUMPREG(VENC_SAVID__EAVID);
DUMPREG(VENC_FLEN__FAL);
DUMPREG(VENC_LAL__PHASE_RESET);
DUMPREG(VENC_HS_INT_START_STOP_X);
DUMPREG(VENC_HS_EXT_START_STOP_X);
DUMPREG(VENC_VS_INT_START_X);
DUMPREG(VENC_VS_INT_STOP_X__VS_INT_START_Y);
DUMPREG(VENC_VS_INT_STOP_Y__VS_EXT_START_X);
DUMPREG(VENC_VS_EXT_STOP_X__VS_EXT_START_Y);
DUMPREG(VENC_VS_EXT_STOP_Y);
DUMPREG(VENC_AVID_START_STOP_X);
DUMPREG(VENC_AVID_START_STOP_Y);
DUMPREG(VENC_FID_INT_START_X__FID_INT_START_Y);
DUMPREG(VENC_FID_INT_OFFSET_Y__FID_EXT_START_X);
DUMPREG(VENC_FID_EXT_START_Y__FID_EXT_OFFSET_Y);
DUMPREG(VENC_TVDETGP_INT_START_STOP_X);
DUMPREG(VENC_TVDETGP_INT_START_STOP_Y);
DUMPREG(VENC_GEN_CTRL);
DUMPREG(VENC_OUTPUT_CONTROL);
DUMPREG(VENC_OUTPUT_TEST);
venc_runtime_put();
#undef DUMPREG
}
static int venc_get_clocks(struct platform_device *pdev)
{
struct clk *clk;
if (dss_has_feature(FEAT_VENC_REQUIRES_TV_DAC_CLK)) {
clk = devm_clk_get(&pdev->dev, "tv_dac_clk");
if (IS_ERR(clk)) {
DSSERR("can't get tv_dac_clk\n");
return PTR_ERR(clk);
}
} else {
clk = NULL;
}
venc.tv_dac_clk = clk;
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->dst);
if (dst != dssdev->dst)
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 = venc_display_enable,
.disable = venc_display_disable,
.check_timings = venc_check_timings,
.set_timings = venc_set_timings,
.get_timings = venc_get_timings,
.set_type = venc_set_type,
.invert_vid_out_polarity = venc_invert_vid_out_polarity,
.set_wss = venc_set_wss,
.get_wss = venc_get_wss,
};
static void venc_init_output(struct platform_device *pdev)
{
struct omap_dss_device *out = &venc.output;
out->dev = &pdev->dev;
out->id = OMAP_DSS_OUTPUT_VENC;
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;
omapdss_register_output(out);
}
static void venc_uninit_output(struct platform_device *pdev)
{
struct omap_dss_device *out = &venc.output;
omapdss_unregister_output(out);
}
static int venc_probe_of(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct device_node *ep;
u32 channels;
int r;
ep = omapdss_of_get_first_endpoint(node);
if (!ep)
return 0;
venc.invert_polarity = of_property_read_bool(ep, "ti,invert-polarity");
r = of_property_read_u32(ep, "ti,channels", &channels);
if (r) {
dev_err(&pdev->dev,
"failed to read property 'ti,channels': %d\n", r);
goto err;
}
switch (channels) {
case 1:
venc.type = OMAP_DSS_VENC_TYPE_COMPOSITE;
break;
case 2:
venc.type = OMAP_DSS_VENC_TYPE_SVIDEO;
break;
default:
dev_err(&pdev->dev, "bad channel propert '%d'\n", channels);
r = -EINVAL;
goto err;
}
of_node_put(ep);
return 0;
err:
of_node_put(ep);
return 0;
}
/* VENC HW IP initialisation */
static int venc_bind(struct device *dev, struct device *master, void *data)
{
struct platform_device *pdev = to_platform_device(dev);
u8 rev_id;
struct resource *venc_mem;
int r;
venc.pdev = pdev;
mutex_init(&venc.venc_lock);
venc.wss_data = 0;
venc_mem = platform_get_resource(venc.pdev, IORESOURCE_MEM, 0);
if (!venc_mem) {
DSSERR("can't get IORESOURCE_MEM VENC\n");
return -EINVAL;
}
venc.base = devm_ioremap(&pdev->dev, venc_mem->start,
resource_size(venc_mem));
if (!venc.base) {
DSSERR("can't ioremap VENC\n");
return -ENOMEM;
}
r = venc_get_clocks(pdev);
if (r)
return r;
pm_runtime_enable(&pdev->dev);
r = venc_runtime_get();
if (r)
goto err_runtime_get;
rev_id = (u8)(venc_read_reg(VENC_REV_ID) & 0xff);
dev_dbg(&pdev->dev, "OMAP VENC rev %d\n", rev_id);
venc_runtime_put();
if (pdev->dev.of_node) {
r = venc_probe_of(pdev);
if (r) {
DSSERR("Invalid DT data\n");
goto err_probe_of;
}
}
dss_debugfs_create_file("venc", venc_dump_regs);
venc_init_output(pdev);
return 0;
err_probe_of:
err_runtime_get:
pm_runtime_disable(&pdev->dev);
return r;
}
static void venc_unbind(struct device *dev, struct device *master, void *data)
{
struct platform_device *pdev = to_platform_device(dev);
venc_uninit_output(pdev);
pm_runtime_disable(&pdev->dev);
}
static const struct component_ops venc_component_ops = {
.bind = venc_bind,
.unbind = venc_unbind,
};
static int venc_probe(struct platform_device *pdev)
{
return component_add(&pdev->dev, &venc_component_ops);
}
static int venc_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &venc_component_ops);
return 0;
}
static int venc_runtime_suspend(struct device *dev)
{
if (venc.tv_dac_clk)
clk_disable_unprepare(venc.tv_dac_clk);
dispc_runtime_put();
return 0;
}
static int venc_runtime_resume(struct device *dev)
{
int r;
r = dispc_runtime_get();
if (r < 0)
return r;
if (venc.tv_dac_clk)
clk_prepare_enable(venc.tv_dac_clk);
return 0;
}
static const struct dev_pm_ops venc_pm_ops = {
.runtime_suspend = venc_runtime_suspend,
.runtime_resume = venc_runtime_resume,
};
static const struct of_device_id venc_of_match[] = {
{ .compatible = "ti,omap2-venc", },
{ .compatible = "ti,omap3-venc", },
{ .compatible = "ti,omap4-venc", },
{},
};
static struct platform_driver omap_venchw_driver = {
.probe = venc_probe,
.remove = venc_remove,
.driver = {
.name = "omapdss_venc",
.pm = &venc_pm_ops,
.of_match_table = venc_of_match,
.suppress_bind_attrs = true,
},
};
int __init venc_init_platform_driver(void)
{
return platform_driver_register(&omap_venchw_driver);
}
void venc_uninit_platform_driver(void)
{
platform_driver_unregister(&omap_venchw_driver);
}

View File

@ -0,0 +1,211 @@
/*
* Copyright (C) 2014 Texas Instruments Ltd
*
* 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.
*
* 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/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <video/omapdss.h>
#include "dss.h"
#include "dss_features.h"
struct dss_video_pll {
struct dss_pll pll;
struct device *dev;
void __iomem *clkctrl_base;
};
#define REG_MOD(reg, val, start, end) \
writel_relaxed(FLD_MOD(readl_relaxed(reg), val, start, end), reg)
static void dss_dpll_enable_scp_clk(struct dss_video_pll *vpll)
{
REG_MOD(vpll->clkctrl_base, 1, 14, 14); /* CIO_CLK_ICG */
}
static void dss_dpll_disable_scp_clk(struct dss_video_pll *vpll)
{
REG_MOD(vpll->clkctrl_base, 0, 14, 14); /* CIO_CLK_ICG */
}
static void dss_dpll_power_enable(struct dss_video_pll *vpll)
{
REG_MOD(vpll->clkctrl_base, 2, 31, 30); /* PLL_POWER_ON_ALL */
/*
* DRA7x PLL CTRL's PLL_PWR_STATUS seems to always return 0,
* so we have to use fixed delay here.
*/
msleep(1);
}
static void dss_dpll_power_disable(struct dss_video_pll *vpll)
{
REG_MOD(vpll->clkctrl_base, 0, 31, 30); /* PLL_POWER_OFF */
}
static int dss_video_pll_enable(struct dss_pll *pll)
{
struct dss_video_pll *vpll = container_of(pll, struct dss_video_pll, pll);
int r;
r = dss_runtime_get();
if (r)
return r;
dss_ctrl_pll_enable(pll->id, true);
dss_dpll_enable_scp_clk(vpll);
r = dss_pll_wait_reset_done(pll);
if (r)
goto err_reset;
dss_dpll_power_enable(vpll);
return 0;
err_reset:
dss_dpll_disable_scp_clk(vpll);
dss_ctrl_pll_enable(pll->id, false);
dss_runtime_put();
return r;
}
static void dss_video_pll_disable(struct dss_pll *pll)
{
struct dss_video_pll *vpll = container_of(pll, struct dss_video_pll, pll);
dss_dpll_power_disable(vpll);
dss_dpll_disable_scp_clk(vpll);
dss_ctrl_pll_enable(pll->id, false);
dss_runtime_put();
}
static const struct dss_pll_ops dss_pll_ops = {
.enable = dss_video_pll_enable,
.disable = dss_video_pll_disable,
.set_config = dss_pll_write_config_type_a,
};
static const struct dss_pll_hw dss_dra7_video_pll_hw = {
.n_max = (1 << 8) - 1,
.m_max = (1 << 12) - 1,
.mX_max = (1 << 5) - 1,
.fint_min = 500000,
.fint_max = 2500000,
.clkdco_max = 1800000000,
.n_msb = 8,
.n_lsb = 1,
.m_msb = 20,
.m_lsb = 9,
.mX_msb[0] = 25,
.mX_lsb[0] = 21,
.mX_msb[1] = 30,
.mX_lsb[1] = 26,
.has_refsel = true,
};
struct dss_pll *dss_video_pll_init(struct platform_device *pdev, int id,
struct regulator *regulator)
{
const char * const reg_name[] = { "pll1", "pll2" };
const char * const clkctrl_name[] = { "pll1_clkctrl", "pll2_clkctrl" };
const char * const clkin_name[] = { "video1_clk", "video2_clk" };
struct resource *res;
struct dss_video_pll *vpll;
void __iomem *pll_base, *clkctrl_base;
struct clk *clk;
struct dss_pll *pll;
int r;
/* PLL CONTROL */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, reg_name[id]);
if (!res) {
dev_err(&pdev->dev,
"missing platform resource data for pll%d\n", id);
return ERR_PTR(-ENODEV);
}
pll_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(pll_base)) {
dev_err(&pdev->dev, "failed to ioremap pll%d reg_name\n", id);
return ERR_CAST(pll_base);
}
/* CLOCK CONTROL */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
clkctrl_name[id]);
if (!res) {
dev_err(&pdev->dev,
"missing platform resource data for pll%d\n", id);
return ERR_PTR(-ENODEV);
}
clkctrl_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(clkctrl_base)) {
dev_err(&pdev->dev, "failed to ioremap pll%d clkctrl\n", id);
return ERR_CAST(clkctrl_base);
}
/* CLKIN */
clk = devm_clk_get(&pdev->dev, clkin_name[id]);
if (IS_ERR(clk)) {
DSSERR("can't get video pll clkin\n");
return ERR_CAST(clk);
}
vpll = devm_kzalloc(&pdev->dev, sizeof(*vpll), GFP_KERNEL);
if (!vpll)
return ERR_PTR(-ENOMEM);
vpll->dev = &pdev->dev;
vpll->clkctrl_base = clkctrl_base;
pll = &vpll->pll;
pll->name = id == 0 ? "video0" : "video1";
pll->id = id == 0 ? DSS_PLL_VIDEO1 : DSS_PLL_VIDEO2;
pll->clkin = clk;
pll->regulator = regulator;
pll->base = pll_base;
pll->hw = &dss_dra7_video_pll_hw;
pll->ops = &dss_pll_ops;
r = dss_pll_register(pll);
if (r)
return ERR_PTR(r);
return pll;
}
void dss_video_pll_uninit(struct dss_pll *pll)
{
dss_pll_unregister(pll);
}