Commit a90cc3f2 authored by Dave Airlie's avatar Dave Airlie

Merge tag 'omapdrm-4.6' of git://git.kernel.org/pub/scm/linux/kernel/git/tomba/linux into drm-next

omapdrm changes for v4.6

* HDMI interlace output support
* DMAbuf import support
* Big refactoring leading to removal of legacy code
* Various non-critical fixes

* tag 'omapdrm-4.6' of git://git.kernel.org/pub/scm/linux/kernel/git/tomba/linux: (76 commits)
  drm/omap: no need to select OMAP2_DSS
  drm/omap: gem: Fix omap_gem_new() error path
  drm/omap: remove -Werror from Makefile
  drm/omap: remove dispc_ovl_check()
  drm/omap: remove dss compat code
  drm/omap: remove last uses of omap_overlay_manager
  drm/omap: DSI: remove uses of omap_overlay_manager
  drm/omap: VENC: remove uses of omap_overlay_manager
  drm/omap: SDI: remove uses of omap_overlay_manager
  drm/omap: HDMI4: remove uses of omap_overlay_manager
  drm/omap: HDMI5: remove uses of omap_overlay_manager
  drm/omap: DPI: remove uses of omap_overlay_manager
  drm/omap: remove extra manager checks on disconnect
  drm/omap: remove extra check in dpi and sdi
  drm/omap: convert dss_mgr_unregister_framedone_handler to accept omap_channel
  drm/omap: convert dss_mgr_register_framedone_handler to accept omap_channel
  drm/omap: convert dss_mgr_start_update to accept omap_channel
  drm/omap: convert dss_mgr_disable to accept omap_channel
  drm/omap: convert dss_mgr_enable to accept omap_channel
  drm/omap: convert dss_mgr_set_lcd_config to accept omap_channel
  ...
parents 912b330c 1c278e5e
......@@ -2,7 +2,6 @@ config DRM_OMAP
tristate "OMAP DRM"
depends on DRM
depends on ARCH_OMAP2PLUS || ARCH_MULTIPLATFORM
select OMAP2_DSS
select DRM_KMS_HELPER
select DRM_KMS_FB_HELPER
select FB_SYS_FILLRECT
......
......@@ -6,7 +6,7 @@
obj-y += dss/
obj-y += displays/
ccflags-y := -Iinclude/drm -Werror
ccflags-y := -Iinclude/drm
omapdrm-y := omap_drv.o \
omap_irq.o \
omap_debugfs.o \
......
......@@ -236,46 +236,6 @@ static struct omap_dss_driver dvic_driver = {
.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);
......@@ -319,17 +279,12 @@ static int dvic_probe(struct platform_device *pdev)
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 {
if (pdev->dev.of_node)
return -ENODEV;
}
r = dvic_probe_of(pdev);
if (r)
return r;
ddata->timings = dvic_default_timings;
......
......@@ -206,30 +206,6 @@ static struct omap_dss_driver hdmic_driver = {
.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);
......@@ -268,17 +244,12 @@ static int hdmic_probe(struct platform_device *pdev)
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 {
if (!pdev->dev.of_node)
return -ENODEV;
}
r = hdmic_probe_of(pdev);
if (r)
return r;
if (gpio_is_valid(ddata->hpd_gpio)) {
r = devm_gpio_request_one(&pdev->dev, ddata->hpd_gpio,
......
......@@ -166,32 +166,6 @@ static const struct omapdss_dvi_ops tfp410_dvi_ops = {
.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);
......@@ -231,17 +205,12 @@ static int tfp410_probe(struct platform_device *pdev)
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 {
if (!pdev->dev.of_node)
return -ENODEV;
}
r = tfp410_probe_of(pdev);
if (r)
return r;
if (gpio_is_valid(ddata->pd_gpio)) {
r = devm_gpio_request_one(&pdev->dev, ddata->pd_gpio,
......
......@@ -13,9 +13,8 @@
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/gpio/consumer.h>
#include <video/omapdss.h>
#include <video/omap-panel-data.h>
......@@ -24,9 +23,9 @@ struct panel_drv_data {
struct omap_dss_device dssdev;
struct omap_dss_device *in;
int ct_cp_hpd_gpio;
int ls_oe_gpio;
int hpd_gpio;
struct gpio_desc *ct_cp_hpd_gpio;
struct gpio_desc *ls_oe_gpio;
struct gpio_desc *hpd_gpio;
struct omap_video_timings timings;
};
......@@ -47,7 +46,7 @@ static int tpd_connect(struct omap_dss_device *dssdev,
dst->src = dssdev;
dssdev->dst = dst;
gpio_set_value_cansleep(ddata->ct_cp_hpd_gpio, 1);
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);
......@@ -65,7 +64,7 @@ static void tpd_disconnect(struct omap_dss_device *dssdev,
if (dst != dssdev->dst)
return;
gpio_set_value_cansleep(ddata->ct_cp_hpd_gpio, 0);
gpiod_set_value_cansleep(ddata->ct_cp_hpd_gpio, 0);
dst->src = NULL;
dssdev->dst = NULL;
......@@ -145,16 +144,14 @@ static int tpd_read_edid(struct omap_dss_device *dssdev,
struct omap_dss_device *in = ddata->in;
int r;
if (!gpio_get_value_cansleep(ddata->hpd_gpio))
if (!gpiod_get_value_cansleep(ddata->hpd_gpio))
return -ENODEV;
if (gpio_is_valid(ddata->ls_oe_gpio))
gpio_set_value_cansleep(ddata->ls_oe_gpio, 1);
gpiod_set_value_cansleep(ddata->ls_oe_gpio, 1);
r = in->ops.hdmi->read_edid(in, edid, len);
if (gpio_is_valid(ddata->ls_oe_gpio))
gpio_set_value_cansleep(ddata->ls_oe_gpio, 0);
gpiod_set_value_cansleep(ddata->ls_oe_gpio, 0);
return r;
}
......@@ -163,7 +160,7 @@ static bool tpd_detect(struct omap_dss_device *dssdev)
{
struct panel_drv_data *ddata = to_panel_data(dssdev);
return gpio_get_value_cansleep(ddata->hpd_gpio);
return gpiod_get_value_cansleep(ddata->hpd_gpio);
}
static int tpd_set_infoframe(struct omap_dss_device *dssdev,
......@@ -201,63 +198,11 @@ static const struct omapdss_hdmi_ops tpd_hdmi_ops = {
.set_hdmi_mode = tpd_set_hdmi_mode,
};
static int tpd_probe_pdata(struct platform_device *pdev)
{
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
struct encoder_tpd12s015_platform_data *pdata;
struct omap_dss_device *dssdev, *in;
pdata = dev_get_platdata(&pdev->dev);
ddata->ct_cp_hpd_gpio = pdata->ct_cp_hpd_gpio;
ddata->ls_oe_gpio = pdata->ls_oe_gpio;
ddata->hpd_gpio = pdata->hpd_gpio;
in = omap_dss_find_output(pdata->source);
if (in == NULL) {
dev_err(&pdev->dev, "Failed to find video source\n");
return -ENODEV;
}
ddata->in = in;
dssdev = &ddata->dssdev;
dssdev->name = pdata->name;
return 0;
}
static int tpd_probe_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;
/* CT CP HPD GPIO */
gpio = of_get_gpio(node, 0);
if (!gpio_is_valid(gpio)) {
dev_err(&pdev->dev, "failed to parse CT CP HPD gpio\n");
return gpio;
}
ddata->ct_cp_hpd_gpio = gpio;
/* LS OE GPIO */
gpio = of_get_gpio(node, 1);
if (gpio_is_valid(gpio) || gpio == -ENOENT) {
ddata->ls_oe_gpio = gpio;
} else {
dev_err(&pdev->dev, "failed to parse LS OE gpio\n");
return gpio;
}
/* HPD GPIO */
gpio = of_get_gpio(node, 2);
if (!gpio_is_valid(gpio)) {
dev_err(&pdev->dev, "failed to parse HPD gpio\n");
return gpio;
}
ddata->hpd_gpio = gpio;
in = omapdss_of_find_source_for_first_ep(node);
if (IS_ERR(in)) {
......@@ -275,6 +220,7 @@ 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)
......@@ -282,35 +228,35 @@ static int tpd_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, ddata);
if (dev_get_platdata(&pdev->dev)) {
r = tpd_probe_pdata(pdev);
if (r)
return r;
} else if (pdev->dev.of_node) {
r = tpd_probe_of(pdev);
if (r)
return r;
} else {
if (!pdev->dev.of_node)
return -ENODEV;
}
r = devm_gpio_request_one(&pdev->dev, ddata->ct_cp_hpd_gpio,
GPIOF_OUT_INIT_LOW, "hdmi_ct_cp_hpd");
r = tpd_probe_of(pdev);
if (r)
return r;
gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 0,
GPIOD_OUT_LOW);
if (IS_ERR(gpio))
goto err_gpio;
if (gpio_is_valid(ddata->ls_oe_gpio)) {
r = devm_gpio_request_one(&pdev->dev, ddata->ls_oe_gpio,
GPIOF_OUT_INIT_LOW, "hdmi_ls_oe");
if (r)
goto err_gpio;
}
ddata->ct_cp_hpd_gpio = gpio;
r = devm_gpio_request_one(&pdev->dev, ddata->hpd_gpio,
GPIOF_DIR_IN, "hdmi_hpd");
if (r)
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;
......
......@@ -1127,40 +1127,6 @@ static struct omap_dss_driver dsicm_ops = {
.memory_read = dsicm_memory_read,
};
static int dsicm_probe_pdata(struct platform_device *pdev)
{
const struct panel_dsicm_platform_data *pdata;
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
struct omap_dss_device *dssdev, *in;
pdata = dev_get_platdata(&pdev->dev);
in = omap_dss_find_output(pdata->source);
if (in == NULL) {
dev_err(&pdev->dev, "failed to find video source\n");
return -EPROBE_DEFER;
}
ddata->in = in;
ddata->reset_gpio = pdata->reset_gpio;
if (pdata->use_ext_te)
ddata->ext_te_gpio = pdata->ext_te_gpio;
else
ddata->ext_te_gpio = -1;
ddata->ulps_timeout = pdata->ulps_timeout;
ddata->use_dsi_backlight = pdata->use_dsi_backlight;
ddata->pin_config = pdata->pin_config;
dssdev = &ddata->dssdev;
dssdev->name = pdata->name;
return 0;
}
static int dsicm_probe_of(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
......@@ -1214,17 +1180,12 @@ static int dsicm_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, ddata);
ddata->pdev = pdev;
if (dev_get_platdata(dev)) {
r = dsicm_probe_pdata(pdev);
if (r)
return r;
} else if (pdev->dev.of_node) {
r = dsicm_probe_of(pdev);
if (r)
return r;
} else {
if (pdev->dev.of_node)
return -ENODEV;
}
r = dsicm_probe_of(pdev);
if (r)
return r;
ddata->timings.x_res = 864;
ddata->timings.y_res = 480;
......
......@@ -240,44 +240,6 @@ static struct omap_dss_driver lb035q02_ops = {
.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;
......@@ -320,17 +282,12 @@ static int lb035q02_panel_spi_probe(struct spi_device *spi)
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 {
if (!spi->dev.of_node)
return -ENODEV;
}
r = lb035q02_probe_of(spi);
if (r)
return r;
if (gpio_is_valid(ddata->backlight_gpio)) {
r = devm_gpio_request_one(&spi->dev, ddata->backlight_gpio,
......
......@@ -19,7 +19,6 @@
#include <linux/of_gpio.h>
#include <video/omapdss.h>
#include <video/omap-panel-data.h>
struct panel_drv_data {
struct omap_dss_device dssdev;
......@@ -232,34 +231,6 @@ static struct omap_dss_driver nec_8048_ops = {
.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;
......@@ -315,17 +286,12 @@ static int nec_8048_probe(struct spi_device *spi)
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 {
if (!spi->dev.of_node)
return -ENODEV;
}
r = nec_8048_probe_of(spi);
if (r)
return r;
if (gpio_is_valid(ddata->qvga_gpio)) {
r = devm_gpio_request_one(&spi->dev, ddata->qvga_gpio,
......
......@@ -18,7 +18,6 @@
#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;
......@@ -197,73 +196,6 @@ static struct omap_dss_driver sharp_ls_ops = {
.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)
{
......@@ -340,17 +272,12 @@ static int sharp_ls_probe(struct platform_device *pdev)
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 {
if (!pdev->dev.of_node)
return -ENODEV;
}
r = sharp_ls_probe_of(pdev);
if (r)
return r;
ddata->videomode = sharp_ls_timings;
......
......@@ -29,7 +29,6 @@
#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;
......@@ -365,31 +364,6 @@ static struct omap_dss_driver td028ttec1_ops = {
.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;
......@@ -432,17 +406,12 @@ static int td028ttec1_panel_probe(struct spi_device *spi)
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 {
if (!spi->dev.of_node)
return -ENODEV;
}
r = td028ttec1_probe_of(spi);
if (r)
return r;
ddata->videomode = td028ttec1_panel_timings;
......
......@@ -20,7 +20,6 @@
#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
......@@ -464,33 +463,6 @@ static struct omap_dss_driver tpo_td043_ops = {
.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;
......@@ -541,17 +513,12 @@ static int tpo_td043_probe(struct spi_device *spi)
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 {
if (!spi->dev.of_node)
return -ENODEV;
}
r = tpo_td043_probe_of(spi);
if (r)
return r;
ddata->mode = TPO_R02_MODE_800x480;
memcpy(ddata->gamma, tpo_td043_def_gamma, sizeof(ddata->gamma));
......
......@@ -3,9 +3,6 @@ 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
......
/*
* Copyright (C) 2011 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 <video/omapdss.h>
#include "dss.h"
#include "dss_features.h"
#include "dispc-compat.h"
/*
* We have 4 levels of cache for the dispc settings. First two are in SW and
* the latter two in HW.
*
* set_info()
* v
* +--------------------+
* | user_info |
* +--------------------+
* v
* apply()
* v
* +--------------------+
* | info |
* +--------------------+
* v
* write_regs()
* v
* +--------------------+
* | shadow registers |
* +--------------------+
* v
* VFP or lcd/digit_enable
* v
* +--------------------+
* | registers |
* +--------------------+
*/
struct ovl_priv_data {
bool user_info_dirty;
struct omap_overlay_info user_info;
bool info_dirty;
struct omap_overlay_info info;
bool shadow_info_dirty;
bool extra_info_dirty;
bool shadow_extra_info_dirty;
bool enabled;
u32 fifo_low, fifo_high;
/*
* True if overlay is to be enabled. Used to check and calculate configs
* for the overlay before it is enabled in the HW.
*/
bool enabling;
};
struct mgr_priv_data {
bool user_info_dirty;
struct omap_overlay_manager_info user_info;
bool info_dirty;
struct omap_overlay_manager_info info;
bool shadow_info_dirty;
/* If true, GO bit is up and shadow registers cannot be written.
* Never true for manual update displays */
bool busy;
/* If true, dispc output is enabled */
bool updating;
/* If true, a display is enabled using this manager */
bool enabled;
bool extra_info_dirty;
bool shadow_extra_info_dirty;
struct omap_video_timings timings;
struct dss_lcd_mgr_config lcd_config;
void (*framedone_handler)(void *);
void *framedone_handler_data;
};
static struct {
struct ovl_priv_data ovl_priv_data_array[MAX_DSS_OVERLAYS];
struct mgr_priv_data mgr_priv_data_array[MAX_DSS_MANAGERS];
bool irq_enabled;
} dss_data;
/* protects dss_data */
static spinlock_t data_lock;
/* lock for blocking functions */
static DEFINE_MUTEX(apply_lock);
static DECLARE_COMPLETION(extra_updated_completion);
static void dss_register_vsync_isr(void);
static struct ovl_priv_data *get_ovl_priv(struct omap_overlay *ovl)
{
return &dss_data.ovl_priv_data_array[ovl->id];
}
static struct mgr_priv_data *get_mgr_priv(struct omap_overlay_manager *mgr)
{
return &dss_data.mgr_priv_data_array[mgr->id];
}
static void apply_init_priv(void)
{
const int num_ovls = dss_feat_get_num_ovls();
struct mgr_priv_data *mp;
int i;
spin_lock_init(&data_lock);
for (i = 0; i < num_ovls; ++i) {
struct ovl_priv_data *op;
op = &dss_data.ovl_priv_data_array[i];
op->info.color_mode = OMAP_DSS_COLOR_RGB16;
op->info.rotation_type = OMAP_DSS_ROT_DMA;
op->info.global_alpha = 255;
switch (i) {
case 0:
op->info.zorder = 0;
break;
case 1:
op->info.zorder =
dss_has_feature(FEAT_ALPHA_FREE_ZORDER) ? 3 : 0;
break;
case 2:
op->info.zorder =
dss_has_feature(FEAT_ALPHA_FREE_ZORDER) ? 2 : 0;
break;
case 3:
op->info.zorder =
dss_has_feature(FEAT_ALPHA_FREE_ZORDER) ? 1 : 0;
break;
}
op->user_info = op->info;
}
/*
* Initialize some of the lcd_config fields for TV manager, this lets
* us prevent checking if the manager is LCD or TV at some places
*/
mp = &dss_data.mgr_priv_data_array[OMAP_DSS_CHANNEL_DIGIT];
mp->lcd_config.video_port_width = 24;
mp->lcd_config.clock_info.lck_div = 1;
mp->lcd_config.clock_info.pck_div = 1;
}
/*
* A LCD manager's stallmode decides whether it is in manual or auto update. TV
* manager is always auto update, stallmode field for TV manager is false by
* default
*/
static bool ovl_manual_update(struct omap_overlay *ovl)
{
struct mgr_priv_data *mp = get_mgr_priv(ovl->manager);
return mp->lcd_config.stallmode;
}
static bool mgr_manual_update(struct omap_overlay_manager *mgr)
{
struct mgr_priv_data *mp = get_mgr_priv(mgr);
return mp->lcd_config.stallmode;
}
static int dss_check_settings_low(struct omap_overlay_manager *mgr,
bool applying)
{
struct omap_overlay_info *oi;
struct omap_overlay_manager_info *mi;
struct omap_overlay *ovl;
struct omap_overlay_info *ois[MAX_DSS_OVERLAYS];
struct ovl_priv_data *op;
struct mgr_priv_data *mp;
mp = get_mgr_priv(mgr);
if (!mp->enabled)
return 0;
if (applying && mp->user_info_dirty)
mi = &mp->user_info;
else
mi = &mp->info;
/* collect the infos to be tested into the array */
list_for_each_entry(ovl, &mgr->overlays, list) {
op = get_ovl_priv(ovl);
if (!op->enabled && !op->enabling)
oi = NULL;
else if (applying && op->user_info_dirty)
oi = &op->user_info;
else
oi = &op->info;
ois[ovl->id] = oi;
}
return dss_mgr_check(mgr, mi, &mp->timings, &mp->lcd_config, ois);
}
/*
* check manager and overlay settings using overlay_info from data->info
*/
static int dss_check_settings(struct omap_overlay_manager *mgr)
{
return dss_check_settings_low(mgr, false);
}
/*
* check manager and overlay settings using overlay_info from ovl->info if
* dirty and from data->info otherwise
*/
static int dss_check_settings_apply(struct omap_overlay_manager *mgr)
{
return dss_check_settings_low(mgr, true);
}
static bool need_isr(void)
{
const int num_mgrs = dss_feat_get_num_mgrs();
int i;
for (i = 0; i < num_mgrs; ++i) {
struct omap_overlay_manager *mgr;
struct mgr_priv_data *mp;
struct omap_overlay *ovl;
mgr = omap_dss_get_overlay_manager(i);
mp = get_mgr_priv(mgr);
if (!mp->enabled)
continue;
if (mgr_manual_update(mgr)) {
/* to catch FRAMEDONE */
if (mp->updating)
return true;
} else {
/* to catch GO bit going down */
if (mp->busy)
return true;
/* to write new values to registers */
if (mp->info_dirty)
return true;
/* to set GO bit */
if (mp->shadow_info_dirty)
return true;
/*
* NOTE: we don't check extra_info flags for disabled
* managers, once the manager is enabled, the extra_info
* related manager changes will be taken in by HW.
*/
/* to write new values to registers */
if (mp->extra_info_dirty)
return true;
/* to set GO bit */
if (mp->shadow_extra_info_dirty)
return true;
list_for_each_entry(ovl, &mgr->overlays, list) {
struct ovl_priv_data *op;
op = get_ovl_priv(ovl);
/*
* NOTE: we check extra_info flags even for
* disabled overlays, as extra_infos need to be
* always written.
*/
/* to write new values to registers */
if (op->extra_info_dirty)
return true;
/* to set GO bit */
if (op->shadow_extra_info_dirty)
return true;
if (!op->enabled)
continue;
/* to write new values to registers */
if (op->info_dirty)
return true;
/* to set GO bit */
if (op->shadow_info_dirty)
return true;
}
}
}
return false;
}
static bool need_go(struct omap_overlay_manager *mgr)
{
struct omap_overlay *ovl;
struct mgr_priv_data *mp;
struct ovl_priv_data *op;
mp = get_mgr_priv(mgr);
if (mp->shadow_info_dirty || mp->shadow_extra_info_dirty)
return true;
list_for_each_entry(ovl, &mgr->overlays, list) {
op = get_ovl_priv(ovl);
if (op->shadow_info_dirty || op->shadow_extra_info_dirty)
return true;
}
return false;
}
/* returns true if an extra_info field is currently being updated */
static bool extra_info_update_ongoing(void)
{
const int num_mgrs = dss_feat_get_num_mgrs();
int i;
for (i = 0; i < num_mgrs; ++i) {
struct omap_overlay_manager *mgr;
struct omap_overlay *ovl;
struct mgr_priv_data *mp;
mgr = omap_dss_get_overlay_manager(i);
mp = get_mgr_priv(mgr);
if (!mp->enabled)
continue;
if (!mp->updating)
continue;
if (mp->extra_info_dirty || mp->shadow_extra_info_dirty)
return true;
list_for_each_entry(ovl, &mgr->overlays, list) {
struct ovl_priv_data *op = get_ovl_priv(ovl);
if (op->extra_info_dirty || op->shadow_extra_info_dirty)
return true;
}
}
return false;
}
/* wait until no extra_info updates are pending */
static void wait_pending_extra_info_updates(void)
{
bool updating;
unsigned long flags;
unsigned long t;
int r;
spin_lock_irqsave(&data_lock, flags);
updating = extra_info_update_ongoing();
if (!updating) {
spin_unlock_irqrestore(&data_lock, flags);
return;
}
init_completion(&extra_updated_completion);
spin_unlock_irqrestore(&data_lock, flags);
t = msecs_to_jiffies(500);
r = wait_for_completion_timeout(&extra_updated_completion, t);
if (r == 0)
DSSWARN("timeout in wait_pending_extra_info_updates\n");
}
static struct omap_dss_device *dss_mgr_get_device(struct omap_overlay_manager *mgr)
{
struct omap_dss_device *dssdev;
dssdev = mgr->output;
if (dssdev == NULL)
return NULL;
while (dssdev->dst)
dssdev = dssdev->dst;
if (dssdev->driver)
return dssdev;
else
return NULL;
}
static struct omap_dss_device *dss_ovl_get_device(struct omap_overlay *ovl)
{
return ovl->manager ? dss_mgr_get_device(ovl->manager) : NULL;
}
static int dss_mgr_wait_for_vsync(struct omap_overlay_manager *mgr)
{
unsigned long timeout = msecs_to_jiffies(500);
u32 irq;
int r;
if (mgr->output == NULL)
return -ENODEV;
r = dispc_runtime_get();
if (r)
return r;
switch (mgr->output->id) {
case OMAP_DSS_OUTPUT_VENC:
irq = DISPC_IRQ_EVSYNC_ODD;
break;
case OMAP_DSS_OUTPUT_HDMI:
irq = DISPC_IRQ_EVSYNC_EVEN;
break;
default:
irq = dispc_mgr_get_vsync_irq(mgr->id);
break;
}
r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
dispc_runtime_put();
return r;
}
static int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr)
{
unsigned long timeout = msecs_to_jiffies(500);
struct mgr_priv_data *mp = get_mgr_priv(mgr);
u32 irq;
unsigned long flags;
int r;
int i;
spin_lock_irqsave(&data_lock, flags);
if (mgr_manual_update(mgr)) {
spin_unlock_irqrestore(&data_lock, flags);
return 0;
}
if (!mp->enabled) {
spin_unlock_irqrestore(&data_lock, flags);
return 0;
}
spin_unlock_irqrestore(&data_lock, flags);
r = dispc_runtime_get();
if (r)
return r;
irq = dispc_mgr_get_vsync_irq(mgr->id);
i = 0;
while (1) {
bool shadow_dirty, dirty;
spin_lock_irqsave(&data_lock, flags);
dirty = mp->info_dirty;
shadow_dirty = mp->shadow_info_dirty;
spin_unlock_irqrestore(&data_lock, flags);
if (!dirty && !shadow_dirty) {
r = 0;
break;
}
/* 4 iterations is the worst case:
* 1 - initial iteration, dirty = true (between VFP and VSYNC)
* 2 - first VSYNC, dirty = true
* 3 - dirty = false, shadow_dirty = true
* 4 - shadow_dirty = false */
if (i++ == 3) {
DSSERR("mgr(%d)->wait_for_go() not finishing\n",
mgr->id);
r = 0;
break;
}
r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
if (r == -ERESTARTSYS)
break;
if (r) {
DSSERR("mgr(%d)->wait_for_go() timeout\n", mgr->id);
break;
}
}
dispc_runtime_put();
return r;
}
static int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl)
{
unsigned long timeout = msecs_to_jiffies(500);
struct ovl_priv_data *op;
struct mgr_priv_data *mp;
u32 irq;
unsigned long flags;
int r;
int i;
if (!ovl->manager)
return 0;
mp = get_mgr_priv(ovl->manager);
spin_lock_irqsave(&data_lock, flags);
if (ovl_manual_update(ovl)) {
spin_unlock_irqrestore(&data_lock, flags);
return 0;
}
if (!mp->enabled) {
spin_unlock_irqrestore(&data_lock, flags);
return 0;
}
spin_unlock_irqrestore(&data_lock, flags);
r = dispc_runtime_get();
if (r)
return r;
irq = dispc_mgr_get_vsync_irq(ovl->manager->id);
op = get_ovl_priv(ovl);
i = 0;
while (1) {
bool shadow_dirty, dirty;
spin_lock_irqsave(&data_lock, flags);
dirty = op->info_dirty;
shadow_dirty = op->shadow_info_dirty;
spin_unlock_irqrestore(&data_lock, flags);
if (!dirty && !shadow_dirty) {
r = 0;
break;
}
/* 4 iterations is the worst case:
* 1 - initial iteration, dirty = true (between VFP and VSYNC)
* 2 - first VSYNC, dirty = true
* 3 - dirty = false, shadow_dirty = true
* 4 - shadow_dirty = false */
if (i++ == 3) {
DSSERR("ovl(%d)->wait_for_go() not finishing\n",
ovl->id);
r = 0;
break;
}
r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
if (r == -ERESTARTSYS)
break;
if (r) {
DSSERR("ovl(%d)->wait_for_go() timeout\n", ovl->id);
break;
}
}
dispc_runtime_put();
return r;
}
static void dss_ovl_write_regs(struct omap_overlay *ovl)
{
struct ovl_priv_data *op = get_ovl_priv(ovl);
struct omap_overlay_info *oi;
bool replication;
struct mgr_priv_data *mp;
int r;
DSSDBG("writing ovl %d regs\n", ovl->id);
if (!op->enabled || !op->info_dirty)
return;
oi = &op->info;
mp = get_mgr_priv(ovl->manager);
replication = dss_ovl_use_replication(mp->lcd_config, oi->color_mode);
r = dispc_ovl_setup(ovl->id, oi, replication, &mp->timings, false);
if (r) {
/*
* We can't do much here, as this function can be called from
* vsync interrupt.
*/
DSSERR("dispc_ovl_setup failed for ovl %d\n", ovl->id);
/* This will leave fifo configurations in a nonoptimal state */
op->enabled = false;
dispc_ovl_enable(ovl->id, false);
return;
}
op->info_dirty = false;
if (mp->updating)
op->shadow_info_dirty = true;
}
static void dss_ovl_write_regs_extra(struct omap_overlay *ovl)
{
struct ovl_priv_data *op = get_ovl_priv(ovl);
struct mgr_priv_data *mp;
DSSDBG("writing ovl %d regs extra\n", ovl->id);
if (!op->extra_info_dirty)
return;
/* note: write also when op->enabled == false, so that the ovl gets
* disabled */
dispc_ovl_enable(ovl->id, op->enabled);
dispc_ovl_set_fifo_threshold(ovl->id, op->fifo_low, op->fifo_high);
mp = get_mgr_priv(ovl->manager);
op->extra_info_dirty = false;
if (mp->updating)
op->shadow_extra_info_dirty = true;
}
static void dss_mgr_write_regs(struct omap_overlay_manager *mgr)
{
struct mgr_priv_data *mp = get_mgr_priv(mgr);
struct omap_overlay *ovl;
DSSDBG("writing mgr %d regs\n", mgr->id);
if (!mp->enabled)
return;
WARN_ON(mp->busy);
/* Commit overlay settings */
list_for_each_entry(ovl, &mgr->overlays, list) {
dss_ovl_write_regs(ovl);
dss_ovl_write_regs_extra(ovl);
}
if (mp->info_dirty) {
dispc_mgr_setup(mgr->id, &mp->info);
mp->info_dirty = false;
if (mp->updating)
mp->shadow_info_dirty = true;
}
}
static void dss_mgr_write_regs_extra(struct omap_overlay_manager *mgr)
{
struct mgr_priv_data *mp = get_mgr_priv(mgr);
DSSDBG("writing mgr %d regs extra\n", mgr->id);
if (!mp->extra_info_dirty)
return;
dispc_mgr_set_timings(mgr->id, &mp->timings);
/* lcd_config parameters */
if (dss_mgr_is_lcd(mgr->id))
dispc_mgr_set_lcd_config(mgr->id, &mp->lcd_config);
mp->extra_info_dirty = false;
if (mp->updating)
mp->shadow_extra_info_dirty = true;
}
static void dss_write_regs(void)
{
const int num_mgrs = omap_dss_get_num_overlay_managers();
int i;
for (i = 0; i < num_mgrs; ++i) {
struct omap_overlay_manager *mgr;
struct mgr_priv_data *mp;
int r;
mgr = omap_dss_get_overlay_manager(i);
mp = get_mgr_priv(mgr);
if (!mp->enabled || mgr_manual_update(mgr) || mp->busy)
continue;
r = dss_check_settings(mgr);
if (r) {
DSSERR("cannot write registers for manager %s: "
"illegal configuration\n", mgr->name);
continue;
}
dss_mgr_write_regs(mgr);
dss_mgr_write_regs_extra(mgr);
}
}
static void dss_set_go_bits(void)
{
const int num_mgrs = omap_dss_get_num_overlay_managers();
int i;
for (i = 0; i < num_mgrs; ++i) {
struct omap_overlay_manager *mgr;
struct mgr_priv_data *mp;
mgr = omap_dss_get_overlay_manager(i);
mp = get_mgr_priv(mgr);
if (!mp->enabled || mgr_manual_update(mgr) || mp->busy)
continue;
if (!need_go(mgr))
continue;
mp->busy = true;
if (!dss_data.irq_enabled && need_isr())
dss_register_vsync_isr();
dispc_mgr_go(mgr->id);
}
}
static void mgr_clear_shadow_dirty(struct omap_overlay_manager *mgr)
{
struct omap_overlay *ovl;
struct mgr_priv_data *mp;
struct ovl_priv_data *op;
mp = get_mgr_priv(mgr);
mp->shadow_info_dirty = false;
mp->shadow_extra_info_dirty = false;
list_for_each_entry(ovl, &mgr->overlays, list) {
op = get_ovl_priv(ovl);
op->shadow_info_dirty = false;
op->shadow_extra_info_dirty = false;
}
}
static int dss_mgr_connect_compat(struct omap_overlay_manager *mgr,
struct omap_dss_device *dst)
{
return mgr->set_output(mgr, dst);
}
static void dss_mgr_disconnect_compat(struct omap_overlay_manager *mgr,
struct omap_dss_device *dst)
{
mgr->unset_output(mgr);
}
static void dss_mgr_start_update_compat(struct omap_overlay_manager *mgr)
{
struct mgr_priv_data *mp = get_mgr_priv(mgr);
unsigned long flags;
int r;
spin_lock_irqsave(&data_lock, flags);
WARN_ON(mp->updating);
r = dss_check_settings(mgr);
if (r) {
DSSERR("cannot start manual update: illegal configuration\n");
spin_unlock_irqrestore(&data_lock, flags);
return;
}
dss_mgr_write_regs(mgr);
dss_mgr_write_regs_extra(mgr);
mp->updating = true;
if (!dss_data.irq_enabled && need_isr())
dss_register_vsync_isr();
dispc_mgr_enable_sync(mgr->id);
spin_unlock_irqrestore(&data_lock, flags);
}
static void dss_apply_irq_handler(void *data, u32 mask);
static void dss_register_vsync_isr(void)
{
const int num_mgrs = dss_feat_get_num_mgrs();
u32 mask;
int r, i;
mask = 0;
for (i = 0; i < num_mgrs; ++i)
mask |= dispc_mgr_get_vsync_irq(i);
for (i = 0; i < num_mgrs; ++i)
mask |= dispc_mgr_get_framedone_irq(i);
r = omap_dispc_register_isr(dss_apply_irq_handler, NULL, mask);
WARN_ON(r);
dss_data.irq_enabled = true;
}
static void dss_unregister_vsync_isr(void)
{
const int num_mgrs = dss_feat_get_num_mgrs();
u32 mask;
int r, i;
mask = 0;
for (i = 0; i < num_mgrs; ++i)
mask |= dispc_mgr_get_vsync_irq(i);
for (i = 0; i < num_mgrs; ++i)
mask |= dispc_mgr_get_framedone_irq(i);
r = omap_dispc_unregister_isr(dss_apply_irq_handler, NULL, mask);
WARN_ON(r);
dss_data.irq_enabled = false;
}
static void dss_apply_irq_handler(void *data, u32 mask)
{
const int num_mgrs = dss_feat_get_num_mgrs();
int i;
bool extra_updating;
spin_lock(&data_lock);
/* clear busy, updating flags, shadow_dirty flags */
for (i = 0; i < num_mgrs; i++) {
struct omap_overlay_manager *mgr;
struct mgr_priv_data *mp;
mgr = omap_dss_get_overlay_manager(i);
mp = get_mgr_priv(mgr);
if (!mp->enabled)
continue;
mp->updating = dispc_mgr_is_enabled(i);
if (!mgr_manual_update(mgr)) {
bool was_busy = mp->busy;
mp->busy = dispc_mgr_go_busy(i);
if (was_busy && !mp->busy)
mgr_clear_shadow_dirty(mgr);
}
}
dss_write_regs();
dss_set_go_bits();
extra_updating = extra_info_update_ongoing();
if (!extra_updating)
complete_all(&extra_updated_completion);
/* call framedone handlers for manual update displays */
for (i = 0; i < num_mgrs; i++) {
struct omap_overlay_manager *mgr;
struct mgr_priv_data *mp;
mgr = omap_dss_get_overlay_manager(i);
mp = get_mgr_priv(mgr);
if (!mgr_manual_update(mgr) || !mp->framedone_handler)
continue;
if (mask & dispc_mgr_get_framedone_irq(i))
mp->framedone_handler(mp->framedone_handler_data);
}
if (!need_isr())
dss_unregister_vsync_isr();
spin_unlock(&data_lock);
}
static void omap_dss_mgr_apply_ovl(struct omap_overlay *ovl)
{
struct ovl_priv_data *op;
op = get_ovl_priv(ovl);
if (!op->user_info_dirty)
return;
op->user_info_dirty = false;
op->info_dirty = true;
op->info = op->user_info;
}
static void omap_dss_mgr_apply_mgr(struct omap_overlay_manager *mgr)
{
struct mgr_priv_data *mp;
mp = get_mgr_priv(mgr);
if (!mp->user_info_dirty)
return;
mp->user_info_dirty = false;
mp->info_dirty = true;
mp->info = mp->user_info;
}
static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
{
unsigned long flags;
struct omap_overlay *ovl;
int r;
DSSDBG("omap_dss_mgr_apply(%s)\n", mgr->name);
spin_lock_irqsave(&data_lock, flags);
r = dss_check_settings_apply(mgr);
if (r) {
spin_unlock_irqrestore(&data_lock, flags);
DSSERR("failed to apply settings: illegal configuration.\n");
return r;
}
/* Configure overlays */
list_for_each_entry(ovl, &mgr->overlays, list)
omap_dss_mgr_apply_ovl(ovl);
/* Configure manager */
omap_dss_mgr_apply_mgr(mgr);
dss_write_regs();
dss_set_go_bits();
spin_unlock_irqrestore(&data_lock, flags);
return 0;
}
static void dss_apply_ovl_enable(struct omap_overlay *ovl, bool enable)
{
struct ovl_priv_data *op;
op = get_ovl_priv(ovl);
if (op->enabled == enable)
return;
op->enabled = enable;
op->extra_info_dirty = true;
}
static void dss_apply_ovl_fifo_thresholds(struct omap_overlay *ovl,
u32 fifo_low, u32 fifo_high)
{
struct ovl_priv_data *op = get_ovl_priv(ovl);
if (op->fifo_low == fifo_low && op->fifo_high == fifo_high)
return;
op->fifo_low = fifo_low;
op->fifo_high = fifo_high;
op->extra_info_dirty = true;
}
static void dss_ovl_setup_fifo(struct omap_overlay *ovl)
{
struct ovl_priv_data *op = get_ovl_priv(ovl);
u32 fifo_low, fifo_high;
bool use_fifo_merge = false;
if (!op->enabled && !op->enabling)
return;
dispc_ovl_compute_fifo_thresholds(ovl->id, &fifo_low, &fifo_high,
use_fifo_merge, ovl_manual_update(ovl));
dss_apply_ovl_fifo_thresholds(ovl, fifo_low, fifo_high);
}
static void dss_mgr_setup_fifos(struct omap_overlay_manager *mgr)
{
struct omap_overlay *ovl;
struct mgr_priv_data *mp;
mp = get_mgr_priv(mgr);
if (!mp->enabled)
return;
list_for_each_entry(ovl, &mgr->overlays, list)
dss_ovl_setup_fifo(ovl);
}
static void dss_setup_fifos(void)
{
const int num_mgrs = omap_dss_get_num_overlay_managers();
struct omap_overlay_manager *mgr;
int i;
for (i = 0; i < num_mgrs; ++i) {
mgr = omap_dss_get_overlay_manager(i);
dss_mgr_setup_fifos(mgr);
}
}
static int dss_mgr_enable_compat(struct omap_overlay_manager *mgr)
{
struct mgr_priv_data *mp = get_mgr_priv(mgr);
unsigned long flags;
int r;
mutex_lock(&apply_lock);
if (mp->enabled)
goto out;
spin_lock_irqsave(&data_lock, flags);
mp->enabled = true;
r = dss_check_settings(mgr);
if (r) {
DSSERR("failed to enable manager %d: check_settings failed\n",
mgr->id);
goto err;
}
dss_setup_fifos();
dss_write_regs();
dss_set_go_bits();
if (!mgr_manual_update(mgr))
mp->updating = true;
if (!dss_data.irq_enabled && need_isr())
dss_register_vsync_isr();
spin_unlock_irqrestore(&data_lock, flags);
if (!mgr_manual_update(mgr))
dispc_mgr_enable_sync(mgr->id);
out:
mutex_unlock(&apply_lock);
return 0;
err:
mp->enabled = false;
spin_unlock_irqrestore(&data_lock, flags);
mutex_unlock(&apply_lock);
return r;
}
static void dss_mgr_disable_compat(struct omap_overlay_manager *mgr)
{
struct mgr_priv_data *mp = get_mgr_priv(mgr);
unsigned long flags;
mutex_lock(&apply_lock);
if (!mp->enabled)
goto out;
wait_pending_extra_info_updates();
if (!mgr_manual_update(mgr))
dispc_mgr_disable_sync(mgr->id);
spin_lock_irqsave(&data_lock, flags);
mp->updating = false;
mp->enabled = false;
spin_unlock_irqrestore(&data_lock, flags);
out:
mutex_unlock(&apply_lock);
}
static int dss_mgr_set_info(struct omap_overlay_manager *mgr,
struct omap_overlay_manager_info *info)
{
struct mgr_priv_data *mp = get_mgr_priv(mgr);
unsigned long flags;
int r;
r = dss_mgr_simple_check(mgr, info);
if (r)
return r;
spin_lock_irqsave(&data_lock, flags);
mp->user_info = *info;
mp->user_info_dirty = true;
spin_unlock_irqrestore(&data_lock, flags);
return 0;
}
static void dss_mgr_get_info(struct omap_overlay_manager *mgr,
struct omap_overlay_manager_info *info)
{
struct mgr_priv_data *mp = get_mgr_priv(mgr);
unsigned long flags;
spin_lock_irqsave(&data_lock, flags);
*info = mp->user_info;
spin_unlock_irqrestore(&data_lock, flags);
}
static int dss_mgr_set_output(struct omap_overlay_manager *mgr,
struct omap_dss_device *output)
{
int r;
mutex_lock(&apply_lock);
if (mgr->output) {
DSSERR("manager %s is already connected to an output\n",
mgr->name);
r = -EINVAL;
goto err;
}
if ((mgr->supported_outputs & output->id) == 0) {
DSSERR("output does not support manager %s\n",
mgr->name);
r = -EINVAL;
goto err;
}
output->manager = mgr;
mgr->output = output;
mutex_unlock(&apply_lock);
return 0;
err:
mutex_unlock(&apply_lock);
return r;
}
static int dss_mgr_unset_output(struct omap_overlay_manager *mgr)
{
int r;
struct mgr_priv_data *mp = get_mgr_priv(mgr);
unsigned long flags;
mutex_lock(&apply_lock);
if (!mgr->output) {
DSSERR("failed to unset output, output not set\n");
r = -EINVAL;
goto err;
}
spin_lock_irqsave(&data_lock, flags);
if (mp->enabled) {
DSSERR("output can't be unset when manager is enabled\n");
r = -EINVAL;
goto err1;
}
spin_unlock_irqrestore(&data_lock, flags);
mgr->output->manager = NULL;
mgr->output = NULL;
mutex_unlock(&apply_lock);
return 0;
err1:
spin_unlock_irqrestore(&data_lock, flags);
err:
mutex_unlock(&apply_lock);
return r;
}
static void dss_apply_mgr_timings(struct omap_overlay_manager *mgr,
const struct omap_video_timings *timings)
{
struct mgr_priv_data *mp = get_mgr_priv(mgr);
mp->timings = *timings;
mp->extra_info_dirty = true;
}
static void dss_mgr_set_timings_compat(struct omap_overlay_manager *mgr,
const struct omap_video_timings *timings)
{
unsigned long flags;
struct mgr_priv_data *mp = get_mgr_priv(mgr);
spin_lock_irqsave(&data_lock, flags);
if (mp->updating) {
DSSERR("cannot set timings for %s: manager needs to be disabled\n",
mgr->name);
goto out;
}
dss_apply_mgr_timings(mgr, timings);
out:
spin_unlock_irqrestore(&data_lock, flags);
}
static void dss_apply_mgr_lcd_config(struct omap_overlay_manager *mgr,
const struct dss_lcd_mgr_config *config)
{
struct mgr_priv_data *mp = get_mgr_priv(mgr);
mp->lcd_config = *config;
mp->extra_info_dirty = true;
}
static void dss_mgr_set_lcd_config_compat(struct omap_overlay_manager *mgr,
const struct dss_lcd_mgr_config *config)
{
unsigned long flags;
struct mgr_priv_data *mp = get_mgr_priv(mgr);
spin_lock_irqsave(&data_lock, flags);
if (mp->enabled) {
DSSERR("cannot apply lcd config for %s: manager needs to be disabled\n",
mgr->name);
goto out;
}
dss_apply_mgr_lcd_config(mgr, config);
out:
spin_unlock_irqrestore(&data_lock, flags);
}
static int dss_ovl_set_info(struct omap_overlay *ovl,
struct omap_overlay_info *info)
{
struct ovl_priv_data *op = get_ovl_priv(ovl);
unsigned long flags;
int r;
r = dss_ovl_simple_check(ovl, info);
if (r)
return r;
spin_lock_irqsave(&data_lock, flags);
op->user_info = *info;
op->user_info_dirty = true;
spin_unlock_irqrestore(&data_lock, flags);
return 0;
}
static void dss_ovl_get_info(struct omap_overlay *ovl,
struct omap_overlay_info *info)
{
struct ovl_priv_data *op = get_ovl_priv(ovl);
unsigned long flags;
spin_lock_irqsave(&data_lock, flags);
*info = op->user_info;
spin_unlock_irqrestore(&data_lock, flags);
}
static int dss_ovl_set_manager(struct omap_overlay *ovl,
struct omap_overlay_manager *mgr)
{
struct ovl_priv_data *op = get_ovl_priv(ovl);
unsigned long flags;
int r;
if (!mgr)
return -EINVAL;
mutex_lock(&apply_lock);
if (ovl->manager) {
DSSERR("overlay '%s' already has a manager '%s'\n",
ovl->name, ovl->manager->name);
r = -EINVAL;
goto err;
}
r = dispc_runtime_get();
if (r)
goto err;
spin_lock_irqsave(&data_lock, flags);
if (op->enabled) {
spin_unlock_irqrestore(&data_lock, flags);
DSSERR("overlay has to be disabled to change the manager\n");
r = -EINVAL;
goto err1;
}
dispc_ovl_set_channel_out(ovl->id, mgr->id);
ovl->manager = mgr;
list_add_tail(&ovl->list, &mgr->overlays);
spin_unlock_irqrestore(&data_lock, flags);
dispc_runtime_put();
mutex_unlock(&apply_lock);
return 0;
err1:
dispc_runtime_put();
err:
mutex_unlock(&apply_lock);
return r;
}
static int dss_ovl_unset_manager(struct omap_overlay *ovl)
{
struct ovl_priv_data *op = get_ovl_priv(ovl);
unsigned long flags;
int r;
mutex_lock(&apply_lock);
if (!ovl->manager) {
DSSERR("failed to detach overlay: manager not set\n");
r = -EINVAL;
goto err;
}
spin_lock_irqsave(&data_lock, flags);
if (op->enabled) {
spin_unlock_irqrestore(&data_lock, flags);
DSSERR("overlay has to be disabled to unset the manager\n");
r = -EINVAL;
goto err;
}
spin_unlock_irqrestore(&data_lock, flags);
/* wait for pending extra_info updates to ensure the ovl is disabled */
wait_pending_extra_info_updates();
/*
* For a manual update display, there is no guarantee that the overlay
* is really disabled in HW, we may need an extra update from this
* manager before the configurations can go in. Return an error if the
* overlay needed an update from the manager.
*
* TODO: Instead of returning an error, try to do a dummy manager update
* here to disable the overlay in hardware. Use the *GATED fields in
* the DISPC_CONFIG registers to do a dummy update.
*/
spin_lock_irqsave(&data_lock, flags);
if (ovl_manual_update(ovl) && op->extra_info_dirty) {
spin_unlock_irqrestore(&data_lock, flags);
DSSERR("need an update to change the manager\n");
r = -EINVAL;
goto err;
}
ovl->manager = NULL;
list_del(&ovl->list);
spin_unlock_irqrestore(&data_lock, flags);
mutex_unlock(&apply_lock);
return 0;
err:
mutex_unlock(&apply_lock);
return r;
}
static bool dss_ovl_is_enabled(struct omap_overlay *ovl)
{
struct ovl_priv_data *op = get_ovl_priv(ovl);
unsigned long flags;
bool e;
spin_lock_irqsave(&data_lock, flags);
e = op->enabled;
spin_unlock_irqrestore(&data_lock, flags);
return e;
}
static int dss_ovl_enable(struct omap_overlay *ovl)
{
struct ovl_priv_data *op = get_ovl_priv(ovl);
unsigned long flags;
int r;
mutex_lock(&apply_lock);
if (op->enabled) {
r = 0;
goto err1;
}
if (ovl->manager == NULL || ovl->manager->output == NULL) {
r = -EINVAL;
goto err1;
}
spin_lock_irqsave(&data_lock, flags);
op->enabling = true;
r = dss_check_settings(ovl->manager);
if (r) {
DSSERR("failed to enable overlay %d: check_settings failed\n",
ovl->id);
goto err2;
}
dss_setup_fifos();
op->enabling = false;
dss_apply_ovl_enable(ovl, true);
dss_write_regs();
dss_set_go_bits();
spin_unlock_irqrestore(&data_lock, flags);
mutex_unlock(&apply_lock);
return 0;
err2:
op->enabling = false;
spin_unlock_irqrestore(&data_lock, flags);
err1:
mutex_unlock(&apply_lock);
return r;
}
static int dss_ovl_disable(struct omap_overlay *ovl)
{
struct ovl_priv_data *op = get_ovl_priv(ovl);
unsigned long flags;
int r;
mutex_lock(&apply_lock);
if (!op->enabled) {
r = 0;
goto err;
}
if (ovl->manager == NULL || ovl->manager->output == NULL) {
r = -EINVAL;
goto err;
}
spin_lock_irqsave(&data_lock, flags);
dss_apply_ovl_enable(ovl, false);
dss_write_regs();
dss_set_go_bits();
spin_unlock_irqrestore(&data_lock, flags);
mutex_unlock(&apply_lock);
return 0;
err:
mutex_unlock(&apply_lock);
return r;
}
static int dss_mgr_register_framedone_handler_compat(struct omap_overlay_manager *mgr,
void (*handler)(void *), void *data)
{
struct mgr_priv_data *mp = get_mgr_priv(mgr);
if (mp->framedone_handler)
return -EBUSY;
mp->framedone_handler = handler;
mp->framedone_handler_data = data;
return 0;
}
static void dss_mgr_unregister_framedone_handler_compat(struct omap_overlay_manager *mgr,
void (*handler)(void *), void *data)
{
struct mgr_priv_data *mp = get_mgr_priv(mgr);
WARN_ON(mp->framedone_handler != handler ||
mp->framedone_handler_data != data);
mp->framedone_handler = NULL;
mp->framedone_handler_data = NULL;
}
static const struct dss_mgr_ops apply_mgr_ops = {
.connect = dss_mgr_connect_compat,
.disconnect = dss_mgr_disconnect_compat,
.start_update = dss_mgr_start_update_compat,
.enable = dss_mgr_enable_compat,
.disable = dss_mgr_disable_compat,
.set_timings = dss_mgr_set_timings_compat,
.set_lcd_config = dss_mgr_set_lcd_config_compat,
.register_framedone_handler = dss_mgr_register_framedone_handler_compat,
.unregister_framedone_handler = dss_mgr_unregister_framedone_handler_compat,
};
static int compat_refcnt;
static DEFINE_MUTEX(compat_init_lock);
int omapdss_compat_init(void)
{
struct platform_device *pdev = dss_get_core_pdev();
int i, r;
mutex_lock(&compat_init_lock);
if (compat_refcnt++ > 0)
goto out;
apply_init_priv();
dss_init_overlay_managers_sysfs(pdev);
dss_init_overlays(pdev);
for (i = 0; i < omap_dss_get_num_overlay_managers(); i++) {
struct omap_overlay_manager *mgr;
mgr = omap_dss_get_overlay_manager(i);
mgr->set_output = &dss_mgr_set_output;
mgr->unset_output = &dss_mgr_unset_output;
mgr->apply = &omap_dss_mgr_apply;
mgr->set_manager_info = &dss_mgr_set_info;
mgr->get_manager_info = &dss_mgr_get_info;
mgr->wait_for_go = &dss_mgr_wait_for_go;
mgr->wait_for_vsync = &dss_mgr_wait_for_vsync;
mgr->get_device = &dss_mgr_get_device;
}
for (i = 0; i < omap_dss_get_num_overlays(); i++) {
struct omap_overlay *ovl = omap_dss_get_overlay(i);
ovl->is_enabled = &dss_ovl_is_enabled;
ovl->enable = &dss_ovl_enable;
ovl->disable = &dss_ovl_disable;
ovl->set_manager = &dss_ovl_set_manager;
ovl->unset_manager = &dss_ovl_unset_manager;
ovl->set_overlay_info = &dss_ovl_set_info;
ovl->get_overlay_info = &dss_ovl_get_info;
ovl->wait_for_go = &dss_mgr_wait_for_go_ovl;
ovl->get_device = &dss_ovl_get_device;
}
r = dss_install_mgr_ops(&apply_mgr_ops);
if (r)
goto err_mgr_ops;
r = display_init_sysfs(pdev);
if (r)
goto err_disp_sysfs;
dispc_runtime_get();
r = dss_dispc_initialize_irq();
if (r)
goto err_init_irq;
dispc_runtime_put();
out:
mutex_unlock(&compat_init_lock);
return 0;
err_init_irq:
dispc_runtime_put();
display_uninit_sysfs(pdev);
err_disp_sysfs:
dss_uninstall_mgr_ops();
err_mgr_ops:
dss_uninit_overlay_managers_sysfs(pdev);
dss_uninit_overlays(pdev);
compat_refcnt--;
mutex_unlock(&compat_init_lock);
return r;
}
EXPORT_SYMBOL(omapdss_compat_init);
void omapdss_compat_uninit(void)
{
struct platform_device *pdev = dss_get_core_pdev();
mutex_lock(&compat_init_lock);
if (--compat_refcnt > 0)
goto out;
dss_dispc_uninitialize_irq();
display_uninit_sysfs(pdev);
dss_uninstall_mgr_ops();
dss_uninit_overlay_managers_sysfs(pdev);
dss_uninit_overlays(pdev);
out:
mutex_unlock(&compat_init_lock);
}
EXPORT_SYMBOL(omapdss_compat_uninit);
......@@ -165,32 +165,20 @@ int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *))
#endif /* CONFIG_OMAP2_DSS_DEBUGFS */
/* PLATFORM DEVICE */
static int omap_dss_pm_notif(struct notifier_block *b, unsigned long v, void *d)
static void dss_disable_all_devices(void)
{
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;
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 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;
......@@ -211,8 +199,6 @@ static int __init omap_dss_probe(struct platform_device *pdev)
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:
......@@ -222,8 +208,6 @@ static int __init omap_dss_probe(struct platform_device *pdev)
static int omap_dss_remove(struct platform_device *pdev)
{
unregister_pm_notifier(&omap_dss_pm_notif_block);
dss_uninitialize_debugfs();
return 0;
......
/*
* 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;
}
/*
* 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
......@@ -104,6 +104,15 @@ struct dispc_features {
bool supports_sync_align:1;
bool has_writeback:1;
bool supports_double_pixel:1;
/*
* Field order for VENC is different than HDMI. We should handle this in
* some intelligent manner, but as the SoCs have either HDMI or VENC,
* never both, we can just use this flag for now.
*/
bool reverse_ilace_field_order:1;
};
#define DISPC_MAX_NR_FIFOS 5
......@@ -2552,47 +2561,6 @@ static int dispc_ovl_calc_scaling(unsigned long pclk, unsigned long lclk,
return 0;
}
int dispc_ovl_check(enum omap_plane plane, enum omap_channel channel,
const struct omap_overlay_info *oi,
const struct omap_video_timings *timings,
int *x_predecim, int *y_predecim)
{
enum omap_overlay_caps caps = dss_feat_get_overlay_caps(plane);
bool five_taps = true;
bool fieldmode = false;
u16 in_height = oi->height;
u16 in_width = oi->width;
bool ilace = timings->interlace;
u16 out_width, out_height;
int pos_x = oi->pos_x;
unsigned long pclk = dispc_mgr_pclk_rate(channel);
unsigned long lclk = dispc_mgr_lclk_rate(channel);
out_width = oi->out_width == 0 ? oi->width : oi->out_width;
out_height = oi->out_height == 0 ? oi->height : oi->out_height;
if (ilace && oi->height == out_height)
fieldmode = true;
if (ilace) {
if (fieldmode)
in_height /= 2;
out_height /= 2;
DSSDBG("adjusting for ilace: height %d, out_height %d\n",
in_height, out_height);
}
if (!dss_feat_color_mode_supported(plane, oi->color_mode))
return -EINVAL;
return dispc_ovl_calc_scaling(pclk, lclk, caps, timings, in_width,
in_height, out_width, out_height, oi->color_mode,
&five_taps, x_predecim, y_predecim, pos_x,
oi->rotation_type, false);
}
EXPORT_SYMBOL(dispc_ovl_check);
static int dispc_ovl_setup_common(enum omap_plane plane,
enum omap_overlay_caps caps, u32 paddr, u32 p_uv_addr,
u16 screen_width, int pos_x, int pos_y, u16 width, u16 height,
......@@ -2747,6 +2715,9 @@ static int dispc_ovl_setup_common(enum omap_plane plane,
dispc_ovl_configure_burst_type(plane, rotation_type);
if (dispc.feat->reverse_ilace_field_order)
swap(offset0, offset1);
dispc_ovl_set_ba0(plane, paddr + offset0);
dispc_ovl_set_ba1(plane, paddr + offset1);
......@@ -2898,6 +2869,12 @@ bool dispc_ovl_enabled(enum omap_plane plane)
}
EXPORT_SYMBOL(dispc_ovl_enabled);
enum omap_dss_output_id dispc_mgr_get_supported_outputs(enum omap_channel channel)
{
return dss_feat_get_supported_outputs(channel);
}
EXPORT_SYMBOL(dispc_mgr_get_supported_outputs);
void dispc_mgr_enable(enum omap_channel channel, bool enable)
{
mgr_fld_write(channel, DISPC_MGR_FLD_ENABLE, enable);
......@@ -3287,6 +3264,10 @@ void dispc_mgr_set_timings(enum omap_channel channel,
} else {
if (t.interlace)
t.y_res /= 2;
if (dispc.feat->supports_double_pixel)
REG_FLD_MOD(DISPC_CONTROL, t.double_pixel ? 1 : 0,
19, 17);
}
dispc_mgr_set_size(channel, t.x_res, t.y_res);
......@@ -3951,6 +3932,8 @@ static const struct dispc_features omap44xx_dispc_feats = {
.set_max_preload = true,
.supports_sync_align = true,
.has_writeback = true,
.supports_double_pixel = true,
.reverse_ilace_field_order = true,
};
static const struct dispc_features omap54xx_dispc_feats = {
......@@ -3974,6 +3957,8 @@ static const struct dispc_features omap54xx_dispc_feats = {
.set_max_preload = true,
.supports_sync_align = true,
.has_writeback = true,
.supports_double_pixel = true,
.reverse_ilace_field_order = true,
};
static int dispc_init_features(struct platform_device *pdev)
......@@ -4129,8 +4114,6 @@ static int dispc_bind(struct device *dev, struct device *master, void *data)
dispc_runtime_put();
dss_init_overlay_managers();
dss_debugfs_create_file("dispc", dispc_dump_regs);
return 0;
......@@ -4144,8 +4127,6 @@ static void dispc_unbind(struct device *dev, struct device *master,
void *data)
{
pm_runtime_disable(dev);
dss_uninit_overlay_managers();
}
static const struct component_ops dispc_component_ops = {
......
/*
* 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));
}
}
......@@ -78,55 +78,6 @@ void omapdss_default_get_timings(struct omap_dss_device *dssdev,
}
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;
......
......@@ -334,7 +334,7 @@ static int dpi_set_dispc_clk(struct dpi_data *dpi, unsigned long pck_req,
static int dpi_set_mode(struct dpi_data *dpi)
{
struct omap_dss_device *out = &dpi->output;
struct omap_overlay_manager *mgr = out->manager;
enum omap_channel channel = out->dispc_channel;
struct omap_video_timings *t = &dpi->timings;
int lck_div = 0, pck_div = 0;
unsigned long fck = 0;
......@@ -342,7 +342,7 @@ static int dpi_set_mode(struct dpi_data *dpi)
int r = 0;
if (dpi->pll)
r = dpi_set_dsi_clk(dpi, mgr->id, t->pixelclock, &fck,
r = dpi_set_dsi_clk(dpi, channel, t->pixelclock, &fck,
&lck_div, &pck_div);
else
r = dpi_set_dispc_clk(dpi, t->pixelclock, &fck,
......@@ -359,7 +359,7 @@ static int dpi_set_mode(struct dpi_data *dpi)
t->pixelclock = pck;
}
dss_mgr_set_timings(mgr, t);
dss_mgr_set_timings(channel, t);
return 0;
}
......@@ -367,7 +367,7 @@ static int dpi_set_mode(struct dpi_data *dpi)
static void dpi_config_lcd_manager(struct dpi_data *dpi)
{
struct omap_dss_device *out = &dpi->output;
struct omap_overlay_manager *mgr = out->manager;
enum omap_channel channel = out->dispc_channel;
dpi->mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS;
......@@ -378,13 +378,14 @@ static void dpi_config_lcd_manager(struct dpi_data *dpi)
dpi->mgr_config.lcden_sig_polarity = 0;
dss_mgr_set_lcd_config(mgr, &dpi->mgr_config);
dss_mgr_set_lcd_config(channel, &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;
enum omap_channel channel = out->dispc_channel;
int r;
mutex_lock(&dpi->lock);
......@@ -395,7 +396,7 @@ static int dpi_display_enable(struct omap_dss_device *dssdev)
goto err_no_reg;
}
if (out->manager == NULL) {
if (!out->dispc_channel_connected) {
DSSERR("failed to enable display: no output/manager\n");
r = -ENODEV;
goto err_no_out_mgr;
......@@ -411,7 +412,7 @@ static int dpi_display_enable(struct omap_dss_device *dssdev)
if (r)
goto err_get_dispc;
r = dss_dpi_select_source(out->port_num, out->manager->id);
r = dss_dpi_select_source(out->port_num, channel);
if (r)
goto err_src_sel;
......@@ -429,7 +430,7 @@ static int dpi_display_enable(struct omap_dss_device *dssdev)
mdelay(2);
r = dss_mgr_enable(out->manager);
r = dss_mgr_enable(channel);
if (r)
goto err_mgr_enable;
......@@ -457,14 +458,14 @@ static int dpi_display_enable(struct omap_dss_device *dssdev)
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;
enum omap_channel channel = dpi->output.dispc_channel;
mutex_lock(&dpi->lock);
dss_mgr_disable(mgr);
dss_mgr_disable(channel);
if (dpi->pll) {
dss_select_lcd_clk_source(mgr->id, OMAP_DSS_CLK_SRC_FCK);
dss_select_lcd_clk_source(channel, OMAP_DSS_CLK_SRC_FCK);
dss_pll_disable(dpi->pll);
}
......@@ -506,14 +507,17 @@ 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;
enum omap_channel channel = dpi->output.dispc_channel;
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))
if (timings->x_res % 8 != 0)
return -EINVAL;
if (!dispc_mgr_timings_ok(channel, timings))
return -EINVAL;
if (timings->pixelclock == 0)
......@@ -660,7 +664,7 @@ 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;
enum omap_channel channel = dpi->output.dispc_channel;
int r;
r = dpi_init_regulator(dpi);
......@@ -669,11 +673,7 @@ static int dpi_connect(struct omap_dss_device *dssdev,
dpi_init_pll(dpi);
mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel);
if (!mgr)
return -ENODEV;
r = dss_mgr_connect(mgr, dssdev);
r = dss_mgr_connect(channel, dssdev);
if (r)
return r;
......@@ -681,7 +681,7 @@ static int dpi_connect(struct omap_dss_device *dssdev,
if (r) {
DSSERR("failed to connect output to new device: %s\n",
dst->name);
dss_mgr_disconnect(mgr, dssdev);
dss_mgr_disconnect(channel, dssdev);
return r;
}
......@@ -691,6 +691,9 @@ static int dpi_connect(struct omap_dss_device *dssdev,
static void dpi_disconnect(struct omap_dss_device *dssdev,
struct omap_dss_device *dst)
{
struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
enum omap_channel channel = dpi->output.dispc_channel;
WARN_ON(dst != dssdev->dst);
if (dst != dssdev->dst)
......@@ -698,8 +701,7 @@ static void dpi_disconnect(struct omap_dss_device *dssdev,
omapdss_output_unset_device(dssdev);
if (dssdev->manager)
dss_mgr_disconnect(dssdev->manager, dssdev);
dss_mgr_disconnect(channel, dssdev);
}
static const struct omapdss_dpi_ops dpi_ops = {
......
......@@ -214,9 +214,9 @@ struct dsi_reg { u16 module; u16 idx; };
typedef void (*omap_dsi_isr_t) (void *arg, u32 mask);
static int dsi_display_init_dispc(struct platform_device *dsidev,
struct omap_overlay_manager *mgr);
enum omap_channel channel);
static void dsi_display_uninit_dispc(struct platform_device *dsidev,
struct omap_overlay_manager *mgr);
enum omap_channel channel);
static int dsi_vc_send_null(struct omap_dss_device *dssdev, int channel);
......@@ -3826,19 +3826,19 @@ static int dsi_enable_video_output(struct omap_dss_device *dssdev, int channel)
{
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
struct omap_overlay_manager *mgr = dsi->output.manager;
enum omap_channel dispc_channel = dssdev->dispc_channel;
int bpp = dsi_get_pixel_size(dsi->pix_fmt);
struct omap_dss_device *out = &dsi->output;
u8 data_type;
u16 word_count;
int r;
if (out->manager == NULL) {
if (!out->dispc_channel_connected) {
DSSERR("failed to enable display: no output/manager\n");
return -ENODEV;
}
r = dsi_display_init_dispc(dsidev, mgr);
r = dsi_display_init_dispc(dsidev, dispc_channel);
if (r)
goto err_init_dispc;
......@@ -3876,7 +3876,7 @@ static int dsi_enable_video_output(struct omap_dss_device *dssdev, int channel)
dsi_if_enable(dsidev, true);
}
r = dss_mgr_enable(mgr);
r = dss_mgr_enable(dispc_channel);
if (r)
goto err_mgr_enable;
......@@ -3888,7 +3888,7 @@ static int dsi_enable_video_output(struct omap_dss_device *dssdev, int channel)
dsi_vc_enable(dsidev, channel, false);
}
err_pix_fmt:
dsi_display_uninit_dispc(dsidev, mgr);
dsi_display_uninit_dispc(dsidev, dispc_channel);
err_init_dispc:
return r;
}
......@@ -3897,7 +3897,7 @@ static void dsi_disable_video_output(struct omap_dss_device *dssdev, int channel
{
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
struct omap_overlay_manager *mgr = dsi->output.manager;
enum omap_channel dispc_channel = dssdev->dispc_channel;
if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) {
dsi_if_enable(dsidev, false);
......@@ -3910,15 +3910,15 @@ static void dsi_disable_video_output(struct omap_dss_device *dssdev, int channel
dsi_if_enable(dsidev, true);
}
dss_mgr_disable(mgr);
dss_mgr_disable(dispc_channel);
dsi_display_uninit_dispc(dsidev, mgr);
dsi_display_uninit_dispc(dsidev, dispc_channel);
}
static void dsi_update_screen_dispc(struct platform_device *dsidev)
{
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
struct omap_overlay_manager *mgr = dsi->output.manager;
enum omap_channel dispc_channel = dsi->output.dispc_channel;
unsigned bytespp;
unsigned bytespl;
unsigned bytespf;
......@@ -3980,9 +3980,9 @@ static void dsi_update_screen_dispc(struct platform_device *dsidev)
msecs_to_jiffies(250));
BUG_ON(r == 0);
dss_mgr_set_timings(mgr, &dsi->timings);
dss_mgr_set_timings(dispc_channel, &dsi->timings);
dss_mgr_start_update(mgr);
dss_mgr_start_update(dispc_channel);
if (dsi->te_enabled) {
/* disable LP_RX_TO, so that we can receive TE. Time to wait
......@@ -4105,17 +4105,17 @@ static int dsi_configure_dispc_clocks(struct platform_device *dsidev)
}
static int dsi_display_init_dispc(struct platform_device *dsidev,
struct omap_overlay_manager *mgr)
enum omap_channel channel)
{
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
int r;
dss_select_lcd_clk_source(mgr->id, dsi->module_id == 0 ?
dss_select_lcd_clk_source(channel, dsi->module_id == 0 ?
OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC :
OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC);
if (dsi->mode == OMAP_DSS_DSI_CMD_MODE) {
r = dss_mgr_register_framedone_handler(mgr,
r = dss_mgr_register_framedone_handler(channel,
dsi_framedone_irq_callback, dsidev);
if (r) {
DSSERR("can't register FRAMEDONE handler\n");
......@@ -4140,7 +4140,7 @@ static int dsi_display_init_dispc(struct platform_device *dsidev,
dsi->timings.de_level = OMAPDSS_SIG_ACTIVE_HIGH;
dsi->timings.sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE;
dss_mgr_set_timings(mgr, &dsi->timings);
dss_mgr_set_timings(channel, &dsi->timings);
r = dsi_configure_dispc_clocks(dsidev);
if (r)
......@@ -4151,28 +4151,28 @@ static int dsi_display_init_dispc(struct platform_device *dsidev,
dsi_get_pixel_size(dsi->pix_fmt);
dsi->mgr_config.lcden_sig_polarity = 0;
dss_mgr_set_lcd_config(mgr, &dsi->mgr_config);
dss_mgr_set_lcd_config(channel, &dsi->mgr_config);
return 0;
err1:
if (dsi->mode == OMAP_DSS_DSI_CMD_MODE)
dss_mgr_unregister_framedone_handler(mgr,
dss_mgr_unregister_framedone_handler(channel,
dsi_framedone_irq_callback, dsidev);
err:
dss_select_lcd_clk_source(mgr->id, OMAP_DSS_CLK_SRC_FCK);
dss_select_lcd_clk_source(channel, OMAP_DSS_CLK_SRC_FCK);
return r;
}
static void dsi_display_uninit_dispc(struct platform_device *dsidev,
struct omap_overlay_manager *mgr)
enum omap_channel channel)
{
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
if (dsi->mode == OMAP_DSS_DSI_CMD_MODE)
dss_mgr_unregister_framedone_handler(mgr,
dss_mgr_unregister_framedone_handler(channel,
dsi_framedone_irq_callback, dsidev);
dss_select_lcd_clk_source(mgr->id, OMAP_DSS_CLK_SRC_FCK);
dss_select_lcd_clk_source(channel, OMAP_DSS_CLK_SRC_FCK);
}
static int dsi_configure_dsi_clocks(struct platform_device *dsidev)
......@@ -4983,18 +4983,14 @@ static int dsi_connect(struct omap_dss_device *dssdev,
struct omap_dss_device *dst)
{
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
struct omap_overlay_manager *mgr;
enum omap_channel dispc_channel = dssdev->dispc_channel;
int r;
r = dsi_regulator_init(dsidev);
if (r)
return r;
mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel);
if (!mgr)
return -ENODEV;
r = dss_mgr_connect(mgr, dssdev);
r = dss_mgr_connect(dispc_channel, dssdev);
if (r)
return r;
......@@ -5002,7 +4998,7 @@ static int dsi_connect(struct omap_dss_device *dssdev,
if (r) {
DSSERR("failed to connect output to new device: %s\n",
dssdev->name);
dss_mgr_disconnect(mgr, dssdev);
dss_mgr_disconnect(dispc_channel, dssdev);
return r;
}
......@@ -5012,6 +5008,8 @@ static int dsi_connect(struct omap_dss_device *dssdev,
static void dsi_disconnect(struct omap_dss_device *dssdev,
struct omap_dss_device *dst)
{
enum omap_channel dispc_channel = dssdev->dispc_channel;
WARN_ON(dst != dssdev->dst);
if (dst != dssdev->dst)
......@@ -5019,8 +5017,7 @@ static void dsi_disconnect(struct omap_dss_device *dssdev,
omapdss_output_unset_device(dssdev);
if (dssdev->manager)
dss_mgr_disconnect(dssdev->manager, dssdev);
dss_mgr_disconnect(dispc_channel, dssdev);
}
static const struct omapdss_dsi_ops dsi_ops = {
......
......@@ -25,6 +25,8 @@
#include <linux/interrupt.h>
#include "omapdss.h"
#ifdef pr_fmt
#undef pr_fmt
#endif
......@@ -205,29 +207,6 @@ 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 ||
......@@ -237,24 +216,6 @@ static inline bool dss_mgr_is_lcd(enum omap_channel id)
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);
......
......@@ -165,9 +165,10 @@ 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;
enum omap_channel channel = dssdev->dispc_channel;
struct hdmi_wp_data *wp = &hdmi.wp;
struct dss_pll_clock_info hdmi_cinfo = { 0 };
unsigned pc;
r = hdmi_power_on_core(dssdev);
if (r)
......@@ -181,7 +182,11 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
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);
pc = p->pixelclock;
if (p->double_pixel)
pc *= 2;
hdmi_pll_compute(&hdmi.pll, pc, &hdmi_cinfo);
r = dss_pll_enable(&hdmi.pll.pll);
if (r) {
......@@ -212,24 +217,24 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
dispc_enable_gamma_table(0);
/* tv size */
dss_mgr_set_timings(mgr, p);
dss_mgr_set_timings(channel, p);
r = hdmi_wp_video_start(&hdmi.wp);
r = dss_mgr_enable(channel);
if (r)
goto err_vid_enable;
goto err_mgr_enable;
r = dss_mgr_enable(mgr);
r = hdmi_wp_video_start(&hdmi.wp);
if (r)
goto err_mgr_enable;
goto err_vid_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:
dss_mgr_disable(channel);
err_mgr_enable:
hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF);
err_phy_pwr:
err_phy_cfg:
......@@ -242,14 +247,14 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
static void hdmi_power_off_full(struct omap_dss_device *dssdev)
{
struct omap_overlay_manager *mgr = hdmi.output.manager;
enum omap_channel channel = dssdev->dispc_channel;
hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff);
dss_mgr_disable(mgr);
hdmi_wp_video_stop(&hdmi.wp);
dss_mgr_disable(channel);
hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF);
dss_pll_disable(&hdmi.pll.pll);
......@@ -260,9 +265,7 @@ static void hdmi_power_off_full(struct omap_dss_device *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))
if (!dispc_mgr_timings_ok(dssdev->dispc_channel, timings))
return -EINVAL;
return 0;
......@@ -343,7 +346,7 @@ static int hdmi_display_enable(struct omap_dss_device *dssdev)
mutex_lock(&hdmi.lock);
if (out->manager == NULL) {
if (!out->dispc_channel_connected) {
DSSERR("failed to enable display: no output/manager\n");
r = -ENODEV;
goto err0;
......@@ -433,18 +436,14 @@ static void hdmi_core_disable(struct omap_dss_device *dssdev)
static int hdmi_connect(struct omap_dss_device *dssdev,
struct omap_dss_device *dst)
{
struct omap_overlay_manager *mgr;
enum omap_channel channel = dssdev->dispc_channel;
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);
r = dss_mgr_connect(channel, dssdev);
if (r)
return r;
......@@ -452,7 +451,7 @@ static int hdmi_connect(struct omap_dss_device *dssdev,
if (r) {
DSSERR("failed to connect output to new device: %s\n",
dst->name);
dss_mgr_disconnect(mgr, dssdev);
dss_mgr_disconnect(channel, dssdev);
return r;
}
......@@ -462,6 +461,8 @@ static int hdmi_connect(struct omap_dss_device *dssdev,
static void hdmi_disconnect(struct omap_dss_device *dssdev,
struct omap_dss_device *dst)
{
enum omap_channel channel = dssdev->dispc_channel;
WARN_ON(dst != dssdev->dst);
if (dst != dssdev->dst)
......@@ -469,8 +470,7 @@ static void hdmi_disconnect(struct omap_dss_device *dssdev,
omapdss_output_unset_device(dssdev);
if (dssdev->manager)
dss_mgr_disconnect(dssdev->manager, dssdev);
dss_mgr_disconnect(channel, dssdev);
}
static int hdmi_read_edid(struct omap_dss_device *dssdev,
......
......@@ -182,8 +182,9 @@ 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;
enum omap_channel channel = dssdev->dispc_channel;
struct dss_pll_clock_info hdmi_cinfo = { 0 };
unsigned pc;
r = hdmi_power_on_core(dssdev);
if (r)
......@@ -193,7 +194,11 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
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);
pc = p->pixelclock;
if (p->double_pixel)
pc *= 2;
hdmi_pll_compute(&hdmi.pll, pc, &hdmi_cinfo);
/* disable and clear irqs */
hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff);
......@@ -229,24 +234,24 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
dispc_enable_gamma_table(0);
/* tv size */
dss_mgr_set_timings(mgr, p);
dss_mgr_set_timings(channel, p);
r = hdmi_wp_video_start(&hdmi.wp);
r = dss_mgr_enable(channel);
if (r)
goto err_vid_enable;
goto err_mgr_enable;
r = dss_mgr_enable(mgr);
r = hdmi_wp_video_start(&hdmi.wp);
if (r)
goto err_mgr_enable;
goto err_vid_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:
dss_mgr_disable(channel);
err_mgr_enable:
hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF);
err_phy_pwr:
err_phy_cfg:
......@@ -259,14 +264,14 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
static void hdmi_power_off_full(struct omap_dss_device *dssdev)
{
struct omap_overlay_manager *mgr = hdmi.output.manager;
enum omap_channel channel = dssdev->dispc_channel;
hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff);
dss_mgr_disable(mgr);
hdmi_wp_video_stop(&hdmi.wp);
dss_mgr_disable(channel);
hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF);
dss_pll_disable(&hdmi.pll.pll);
......@@ -277,13 +282,7 @@ static void hdmi_power_off_full(struct omap_dss_device *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))
if (!dispc_mgr_timings_ok(dssdev->dispc_channel, timings))
return -EINVAL;
return 0;
......@@ -373,7 +372,7 @@ static int hdmi_display_enable(struct omap_dss_device *dssdev)
mutex_lock(&hdmi.lock);
if (out->manager == NULL) {
if (!out->dispc_channel_connected) {
DSSERR("failed to enable display: no output/manager\n");
r = -ENODEV;
goto err0;
......@@ -463,18 +462,14 @@ static void hdmi_core_disable(struct omap_dss_device *dssdev)
static int hdmi_connect(struct omap_dss_device *dssdev,
struct omap_dss_device *dst)
{
struct omap_overlay_manager *mgr;
enum omap_channel channel = dssdev->dispc_channel;
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);
r = dss_mgr_connect(channel, dssdev);
if (r)
return r;
......@@ -482,7 +477,7 @@ static int hdmi_connect(struct omap_dss_device *dssdev,
if (r) {
DSSERR("failed to connect output to new device: %s\n",
dst->name);
dss_mgr_disconnect(mgr, dssdev);
dss_mgr_disconnect(channel, dssdev);
return r;
}
......@@ -492,6 +487,8 @@ static int hdmi_connect(struct omap_dss_device *dssdev,
static void hdmi_disconnect(struct omap_dss_device *dssdev,
struct omap_dss_device *dst)
{
enum omap_channel channel = dssdev->dispc_channel;
WARN_ON(dst != dssdev->dst);
if (dst != dssdev->dst)
......@@ -499,8 +496,7 @@ static void hdmi_disconnect(struct omap_dss_device *dssdev,
omapdss_output_unset_device(dssdev);
if (dssdev->manager)
dss_mgr_disconnect(dssdev->manager, dssdev);
dss_mgr_disconnect(channel, dssdev);
}
static int hdmi_read_edid(struct omap_dss_device *dssdev,
......
......@@ -292,25 +292,36 @@ static void hdmi_core_init(struct hdmi_core_vid_config *video_cfg,
{
DSSDBG("hdmi_core_init\n");
video_cfg->v_fc_config.timings = cfg->timings;
/* 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 */
cfg->timings.hbp + cfg->timings.hsw;
video_cfg->vblank_osc = 0;
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;
if (cfg->timings.interlace) {
/* set vblank_osc if vblank is fractional */
if (video_cfg->vblank % 2 != 0)
video_cfg->vblank_osc = 1;
video_cfg->v_fc_config.timings.y_res /= 2;
video_cfg->vblank /= 2;
video_cfg->v_fc_config.timings.vfp /= 2;
video_cfg->v_fc_config.timings.vsw /= 2;
video_cfg->v_fc_config.timings.vbp /= 2;
}
if (cfg->timings.double_pixel) {
video_cfg->v_fc_config.timings.x_res *= 2;
video_cfg->hblank *= 2;
video_cfg->v_fc_config.timings.hfp *= 2;
video_cfg->v_fc_config.timings.hsw *= 2;
video_cfg->v_fc_config.timings.hbp *= 2;
}
}
/* DSS_HDMI_CORE_VIDEO_CONFIG */
......@@ -377,6 +388,11 @@ static void hdmi_core_video_config(struct hdmi_core_data *core,
/* select DVI mode */
REG_FLD_MOD(base, HDMI_CORE_FC_INVIDCONF,
cfg->v_fc_config.hdmi_dvi_mode, 3, 3);
if (cfg->v_fc_config.timings.double_pixel)
REG_FLD_MOD(base, HDMI_CORE_FC_PRCONF, 2, 7, 4);
else
REG_FLD_MOD(base, HDMI_CORE_FC_PRCONF, 1, 7, 4);
}
static void hdmi_core_config_video_packetizer(struct hdmi_core_data *core)
......
......@@ -165,12 +165,24 @@ void hdmi_wp_video_config_timing(struct hdmi_wp_data *wp,
{
u32 timing_h = 0;
u32 timing_v = 0;
unsigned hsw_offset = 1;
DSSDBG("Enter hdmi_wp_video_config_timing\n");
/*
* On OMAP4 and OMAP5 ES1 the HSW field is programmed as is. On OMAP5
* ES2+ (including DRA7/AM5 SoCs) HSW field is programmed to hsw-1.
* However, we don't support OMAP5 ES1 at all, so we can just check for
* OMAP4 here.
*/
if (omapdss_get_version() == OMAPDSS_VER_OMAP4430_ES1 ||
omapdss_get_version() == OMAPDSS_VER_OMAP4430_ES2 ||
omapdss_get_version() == OMAPDSS_VER_OMAP4)
hsw_offset = 0;
timing_h |= FLD_VAL(timings->hbp, 31, 20);
timing_h |= FLD_VAL(timings->hfp, 19, 8);
timing_h |= FLD_VAL(timings->hsw, 7, 0);
timing_h |= FLD_VAL(timings->hsw - hsw_offset, 7, 0);
hdmi_write_reg(wp->base, HDMI_WP_VIDEO_TIMING_H, timing_h);
timing_v |= FLD_VAL(timings->vbp, 31, 20);
......@@ -187,8 +199,6 @@ void hdmi_wp_init_vid_fmt_timings(struct hdmi_video_format *video_fmt,
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;
......@@ -196,9 +206,25 @@ void hdmi_wp_init_vid_fmt_timings(struct hdmi_video_format *video_fmt,
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;
timings->double_pixel = param->timings.double_pixel;
if (param->timings.interlace) {
video_fmt->y_res /= 2;
timings->vbp /= 2;
timings->vfp /= 2;
timings->vsw /= 2;
}
if (param->timings.double_pixel) {
video_fmt->x_res *= 2;
timings->hfp *= 2;
timings->hsw *= 2;
timings->hbp *= 2;
}
}
void hdmi_wp_audio_config_format(struct hdmi_wp_data *wp,
......
/*
* 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));
}
/*
* 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;
}
/*
* Copyright (C) 2016 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 __OMAP_DRM_DSS_H
#define __OMAP_DRM_DSS_H
#include <video/omapdss.h>
u32 dispc_read_irqstatus(void);
void dispc_clear_irqstatus(u32 mask);
u32 dispc_read_irqenable(void);
void dispc_write_irqenable(u32 mask);
int dispc_request_irq(irq_handler_t handler, void *dev_id);
void dispc_free_irq(void *dev_id);
int dispc_runtime_get(void);
void dispc_runtime_put(void);
void dispc_mgr_enable(enum omap_channel channel, bool enable);
bool dispc_mgr_is_enabled(enum omap_channel channel);
u32 dispc_mgr_get_vsync_irq(enum omap_channel channel);
u32 dispc_mgr_get_framedone_irq(enum omap_channel channel);
u32 dispc_mgr_get_sync_lost_irq(enum omap_channel channel);
bool dispc_mgr_go_busy(enum omap_channel channel);
void dispc_mgr_go(enum omap_channel channel);
void dispc_mgr_set_lcd_config(enum omap_channel channel,
const struct dss_lcd_mgr_config *config);
void dispc_mgr_set_timings(enum omap_channel channel,
const struct omap_video_timings *timings);
void dispc_mgr_setup(enum omap_channel channel,
const struct omap_overlay_manager_info *info);
int dispc_ovl_enable(enum omap_plane plane, bool enable);
bool dispc_ovl_enabled(enum omap_plane plane);
void dispc_ovl_set_channel_out(enum omap_plane plane,
enum omap_channel channel);
int dispc_ovl_setup(enum omap_plane plane, const struct omap_overlay_info *oi,
bool replication, const struct omap_video_timings *mgr_timings,
bool mem_to_mem);
enum omap_dss_output_id dispc_mgr_get_supported_outputs(enum omap_channel channel);
struct dss_mgr_ops {
int (*connect)(enum omap_channel channel,
struct omap_dss_device *dst);
void (*disconnect)(enum omap_channel channel,
struct omap_dss_device *dst);
void (*start_update)(enum omap_channel channel);
int (*enable)(enum omap_channel channel);
void (*disable)(enum omap_channel channel);
void (*set_timings)(enum omap_channel channel,
const struct omap_video_timings *timings);
void (*set_lcd_config)(enum omap_channel channel,
const struct dss_lcd_mgr_config *config);
int (*register_framedone_handler)(enum omap_channel channel,
void (*handler)(void *), void *data);
void (*unregister_framedone_handler)(enum omap_channel channel,
void (*handler)(void *), void *data);
};
int dss_install_mgr_ops(const struct dss_mgr_ops *mgr_ops);
void dss_uninstall_mgr_ops(void);
int dss_mgr_connect(enum omap_channel channel,
struct omap_dss_device *dst);
void dss_mgr_disconnect(enum omap_channel channel,
struct omap_dss_device *dst);
void dss_mgr_set_timings(enum omap_channel channel,
const struct omap_video_timings *timings);
void dss_mgr_set_lcd_config(enum omap_channel channel,
const struct dss_lcd_mgr_config *config);
int dss_mgr_enable(enum omap_channel channel);
void dss_mgr_disable(enum omap_channel channel);
void dss_mgr_start_update(enum omap_channel channel);
int dss_mgr_register_framedone_handler(enum omap_channel channel,
void (*handler)(void *), void *data);
void dss_mgr_unregister_framedone_handler(enum omap_channel channel,
void (*handler)(void *), void *data);
#endif /* __OMAP_DRM_DSS_H */
......@@ -169,24 +169,6 @@ struct omap_dss_device *omapdss_find_output_from_display(struct omap_dss_device
}
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)
......@@ -206,62 +188,62 @@ void dss_uninstall_mgr_ops(void)
}
EXPORT_SYMBOL(dss_uninstall_mgr_ops);
int dss_mgr_connect(struct omap_overlay_manager *mgr,
int dss_mgr_connect(enum omap_channel channel,
struct omap_dss_device *dst)
{
return dss_mgr_ops->connect(mgr, dst);
return dss_mgr_ops->connect(channel, dst);
}
EXPORT_SYMBOL(dss_mgr_connect);
void dss_mgr_disconnect(struct omap_overlay_manager *mgr,
void dss_mgr_disconnect(enum omap_channel channel,
struct omap_dss_device *dst)
{
dss_mgr_ops->disconnect(mgr, dst);
dss_mgr_ops->disconnect(channel, dst);
}
EXPORT_SYMBOL(dss_mgr_disconnect);
void dss_mgr_set_timings(struct omap_overlay_manager *mgr,
void dss_mgr_set_timings(enum omap_channel channel,
const struct omap_video_timings *timings)
{
dss_mgr_ops->set_timings(mgr, timings);
dss_mgr_ops->set_timings(channel, timings);
}
EXPORT_SYMBOL(dss_mgr_set_timings);
void dss_mgr_set_lcd_config(struct omap_overlay_manager *mgr,
void dss_mgr_set_lcd_config(enum omap_channel channel,
const struct dss_lcd_mgr_config *config)
{
dss_mgr_ops->set_lcd_config(mgr, config);
dss_mgr_ops->set_lcd_config(channel, config);
}
EXPORT_SYMBOL(dss_mgr_set_lcd_config);
int dss_mgr_enable(struct omap_overlay_manager *mgr)
int dss_mgr_enable(enum omap_channel channel)
{
return dss_mgr_ops->enable(mgr);
return dss_mgr_ops->enable(channel);
}
EXPORT_SYMBOL(dss_mgr_enable);
void dss_mgr_disable(struct omap_overlay_manager *mgr)
void dss_mgr_disable(enum omap_channel channel)
{
dss_mgr_ops->disable(mgr);
dss_mgr_ops->disable(channel);
}
EXPORT_SYMBOL(dss_mgr_disable);
void dss_mgr_start_update(struct omap_overlay_manager *mgr)
void dss_mgr_start_update(enum omap_channel channel)
{
dss_mgr_ops->start_update(mgr);
dss_mgr_ops->start_update(channel);
}
EXPORT_SYMBOL(dss_mgr_start_update);
int dss_mgr_register_framedone_handler(struct omap_overlay_manager *mgr,
int dss_mgr_register_framedone_handler(enum omap_channel channel,
void (*handler)(void *), void *data)
{
return dss_mgr_ops->register_framedone_handler(mgr, handler, data);
return dss_mgr_ops->register_framedone_handler(channel, handler, data);
}
EXPORT_SYMBOL(dss_mgr_register_framedone_handler);
void dss_mgr_unregister_framedone_handler(struct omap_overlay_manager *mgr,
void dss_mgr_unregister_framedone_handler(enum omap_channel channel,
void (*handler)(void *), void *data)
{
dss_mgr_ops->unregister_framedone_handler(mgr, handler, data);
dss_mgr_ops->unregister_framedone_handler(channel, handler, data);
}
EXPORT_SYMBOL(dss_mgr_unregister_framedone_handler);
/*
* 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);
}
/*
* 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;
}
......@@ -880,7 +880,7 @@ static int rfbi_display_enable(struct omap_dss_device *dssdev)
struct omap_dss_device *out = &rfbi.output;
int r;
if (out->manager == NULL) {
if (!out->dispc_channel_connected) {
DSSERR("failed to enable display: no output/manager\n");
return -ENODEV;
}
......
......@@ -114,7 +114,7 @@ static int sdi_calc_clock_div(unsigned long pclk,
static void sdi_config_lcd_manager(struct omap_dss_device *dssdev)
{
struct omap_overlay_manager *mgr = sdi.output.manager;
enum omap_channel channel = dssdev->dispc_channel;
sdi.mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS;
......@@ -124,19 +124,20 @@ static void sdi_config_lcd_manager(struct omap_dss_device *dssdev)
sdi.mgr_config.video_port_width = 24;
sdi.mgr_config.lcden_sig_polarity = 1;
dss_mgr_set_lcd_config(mgr, &sdi.mgr_config);
dss_mgr_set_lcd_config(channel, &sdi.mgr_config);
}
static int sdi_display_enable(struct omap_dss_device *dssdev)
{
struct omap_dss_device *out = &sdi.output;
enum omap_channel channel = dssdev->dispc_channel;
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) {
if (!out->dispc_channel_connected) {
DSSERR("failed to enable display: no output/manager\n");
return -ENODEV;
}
......@@ -169,7 +170,7 @@ static int sdi_display_enable(struct omap_dss_device *dssdev)
}
dss_mgr_set_timings(out->manager, t);
dss_mgr_set_timings(channel, t);
r = dss_set_fck_rate(fck);
if (r)
......@@ -188,7 +189,7 @@ static int sdi_display_enable(struct omap_dss_device *dssdev)
* 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);
dispc_mgr_set_clock_div(channel, &sdi.mgr_config.clock_info);
dss_sdi_init(sdi.datapairs);
r = dss_sdi_enable();
......@@ -196,7 +197,7 @@ static int sdi_display_enable(struct omap_dss_device *dssdev)
goto err_sdi_enable;
mdelay(2);
r = dss_mgr_enable(out->manager);
r = dss_mgr_enable(channel);
if (r)
goto err_mgr_enable;
......@@ -216,9 +217,9 @@ static int sdi_display_enable(struct omap_dss_device *dssdev)
static void sdi_display_disable(struct omap_dss_device *dssdev)
{
struct omap_overlay_manager *mgr = sdi.output.manager;
enum omap_channel channel = dssdev->dispc_channel;
dss_mgr_disable(mgr);
dss_mgr_disable(channel);
dss_sdi_disable();
......@@ -242,9 +243,9 @@ static void sdi_get_timings(struct omap_dss_device *dssdev,
static int sdi_check_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
struct omap_overlay_manager *mgr = sdi.output.manager;
enum omap_channel channel = dssdev->dispc_channel;
if (mgr && !dispc_mgr_timings_ok(mgr->id, timings))
if (!dispc_mgr_timings_ok(channel, timings))
return -EINVAL;
if (timings->pixelclock == 0)
......@@ -280,18 +281,14 @@ static int sdi_init_regulator(void)
static int sdi_connect(struct omap_dss_device *dssdev,
struct omap_dss_device *dst)
{
struct omap_overlay_manager *mgr;
enum omap_channel channel = dssdev->dispc_channel;
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);
r = dss_mgr_connect(channel, dssdev);
if (r)
return r;
......@@ -299,7 +296,7 @@ static int sdi_connect(struct omap_dss_device *dssdev,
if (r) {
DSSERR("failed to connect output to new device: %s\n",
dst->name);
dss_mgr_disconnect(mgr, dssdev);
dss_mgr_disconnect(channel, dssdev);
return r;
}
......@@ -309,6 +306,8 @@ static int sdi_connect(struct omap_dss_device *dssdev,
static void sdi_disconnect(struct omap_dss_device *dssdev,
struct omap_dss_device *dst)
{
enum omap_channel channel = dssdev->dispc_channel;
WARN_ON(dst != dssdev->dst);
if (dst != dssdev->dst)
......@@ -316,8 +315,7 @@ static void sdi_disconnect(struct omap_dss_device *dssdev,
omapdss_output_unset_device(dssdev);
if (dssdev->manager)
dss_mgr_disconnect(dssdev->manager, dssdev);
dss_mgr_disconnect(channel, dssdev);
}
static const struct omapdss_sdi_ops sdi_ops = {
......
......@@ -443,7 +443,7 @@ static const struct venc_config *venc_timings_to_config(
static int venc_power_on(struct omap_dss_device *dssdev)
{
struct omap_overlay_manager *mgr = venc.output.manager;
enum omap_channel channel = dssdev->dispc_channel;
u32 l;
int r;
......@@ -469,13 +469,13 @@ static int venc_power_on(struct omap_dss_device *dssdev)
venc_write_reg(VENC_OUTPUT_CONTROL, l);
dss_mgr_set_timings(mgr, &venc.timings);
dss_mgr_set_timings(channel, &venc.timings);
r = regulator_enable(venc.vdda_dac_reg);
if (r)
goto err1;
r = dss_mgr_enable(mgr);
r = dss_mgr_enable(channel);
if (r)
goto err2;
......@@ -494,12 +494,12 @@ static int venc_power_on(struct omap_dss_device *dssdev)
static void venc_power_off(struct omap_dss_device *dssdev)
{
struct omap_overlay_manager *mgr = venc.output.manager;
enum omap_channel channel = dssdev->dispc_channel;
venc_write_reg(VENC_OUTPUT_CONTROL, 0);
dss_set_dac_pwrdn_bgz(0);
dss_mgr_disable(mgr);
dss_mgr_disable(channel);
regulator_disable(venc.vdda_dac_reg);
......@@ -515,7 +515,7 @@ static int venc_display_enable(struct omap_dss_device *dssdev)
mutex_lock(&venc.venc_lock);
if (out->manager == NULL) {
if (!out->dispc_channel_connected) {
DSSERR("Failed to enable display: no output/manager\n");
r = -ENODEV;
goto err0;
......@@ -742,18 +742,14 @@ static int venc_get_clocks(struct platform_device *pdev)
static int venc_connect(struct omap_dss_device *dssdev,
struct omap_dss_device *dst)
{
struct omap_overlay_manager *mgr;
enum omap_channel channel = dssdev->dispc_channel;
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);
r = dss_mgr_connect(channel, dssdev);
if (r)
return r;
......@@ -761,7 +757,7 @@ static int venc_connect(struct omap_dss_device *dssdev,
if (r) {
DSSERR("failed to connect output to new device: %s\n",
dst->name);
dss_mgr_disconnect(mgr, dssdev);
dss_mgr_disconnect(channel, dssdev);
return r;
}
......@@ -771,6 +767,8 @@ static int venc_connect(struct omap_dss_device *dssdev,
static void venc_disconnect(struct omap_dss_device *dssdev,
struct omap_dss_device *dst)
{
enum omap_channel channel = dssdev->dispc_channel;
WARN_ON(dst != dssdev->dst);
if (dst != dssdev->dst)
......@@ -778,8 +776,7 @@ static void venc_disconnect(struct omap_dss_device *dssdev,
omapdss_output_unset_device(dssdev);
if (dssdev->manager)
dss_mgr_disconnect(dssdev->manager, dssdev);
dss_mgr_disconnect(channel, dssdev);
}
static const struct omapdss_atv_ops venc_ops = {
......
......@@ -63,6 +63,9 @@ void copy_timings_omap_to_drm(struct drm_display_mode *mode,
if (timings->interlace)
mode->flags |= DRM_MODE_FLAG_INTERLACE;
if (timings->double_pixel)
mode->flags |= DRM_MODE_FLAG_DBLCLK;
if (timings->hsync_level == OMAPDSS_SIG_ACTIVE_HIGH)
mode->flags |= DRM_MODE_FLAG_PHSYNC;
else
......@@ -90,6 +93,7 @@ void copy_timings_drm_to_omap(struct omap_video_timings *timings,
timings->vbp = mode->vtotal - mode->vsync_end;
timings->interlace = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
timings->double_pixel = !!(mode->flags & DRM_MODE_FLAG_DBLCLK);
if (mode->flags & DRM_MODE_FLAG_PHSYNC)
timings->hsync_level = OMAPDSS_SIG_ACTIVE_HIGH;
......
......@@ -34,14 +34,6 @@ struct omap_crtc {
const char *name;
enum omap_channel channel;
/*
* Temporary: eventually this will go away, but it is needed
* for now to keep the output's happy. (They only need
* mgr->id.) Eventually this will be replaced w/ something
* more common-panel-framework-y
*/
struct omap_overlay_manager *mgr;
struct omap_video_timings timings;
struct omap_drm_irq vblank_irq;
......@@ -80,9 +72,13 @@ int omap_crtc_wait_pending(struct drm_crtc *crtc)
{
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
/*
* Timeout is set to a "sufficiently" high value, which should cover
* a single frame refresh even on slower displays.
*/
return wait_event_timeout(omap_crtc->pending_wait,
!omap_crtc->pending,
msecs_to_jiffies(50));
msecs_to_jiffies(250));
}
/* -----------------------------------------------------------------------------
......@@ -100,31 +96,32 @@ int omap_crtc_wait_pending(struct drm_crtc *crtc)
/* ovl-mgr-id -> crtc */
static struct omap_crtc *omap_crtcs[8];
static struct omap_dss_device *omap_crtc_output[8];
/* we can probably ignore these until we support command-mode panels: */
static int omap_crtc_dss_connect(struct omap_overlay_manager *mgr,
static int omap_crtc_dss_connect(enum omap_channel channel,
struct omap_dss_device *dst)
{
if (mgr->output)
if (omap_crtc_output[channel])
return -EINVAL;
if ((mgr->supported_outputs & dst->id) == 0)
if ((dispc_mgr_get_supported_outputs(channel) & dst->id) == 0)
return -EINVAL;
dst->manager = mgr;
mgr->output = dst;
omap_crtc_output[channel] = dst;
dst->dispc_channel_connected = true;
return 0;
}
static void omap_crtc_dss_disconnect(struct omap_overlay_manager *mgr,
static void omap_crtc_dss_disconnect(enum omap_channel channel,
struct omap_dss_device *dst)
{
mgr->output->manager = NULL;
mgr->output = NULL;
omap_crtc_output[channel] = NULL;
dst->dispc_channel_connected = false;
}
static void omap_crtc_dss_start_update(struct omap_overlay_manager *mgr)
static void omap_crtc_dss_start_update(enum omap_channel channel)
{
}
......@@ -138,6 +135,11 @@ static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable)
u32 framedone_irq, vsync_irq;
int ret;
if (omap_crtc_output[channel]->output_type == OMAP_DISPLAY_TYPE_HDMI) {
dispc_mgr_enable(channel, enable);
return;
}
if (dispc_mgr_is_enabled(channel) == enable)
return;
......@@ -186,9 +188,9 @@ static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable)
}
static int omap_crtc_dss_enable(struct omap_overlay_manager *mgr)
static int omap_crtc_dss_enable(enum omap_channel channel)
{
struct omap_crtc *omap_crtc = omap_crtcs[mgr->id];
struct omap_crtc *omap_crtc = omap_crtcs[channel];
struct omap_overlay_manager_info info;
memset(&info, 0, sizeof(info));
......@@ -205,38 +207,38 @@ static int omap_crtc_dss_enable(struct omap_overlay_manager *mgr)
return 0;
}
static void omap_crtc_dss_disable(struct omap_overlay_manager *mgr)
static void omap_crtc_dss_disable(enum omap_channel channel)
{
struct omap_crtc *omap_crtc = omap_crtcs[mgr->id];
struct omap_crtc *omap_crtc = omap_crtcs[channel];
omap_crtc_set_enabled(&omap_crtc->base, false);
}
static void omap_crtc_dss_set_timings(struct omap_overlay_manager *mgr,
static void omap_crtc_dss_set_timings(enum omap_channel channel,
const struct omap_video_timings *timings)
{
struct omap_crtc *omap_crtc = omap_crtcs[mgr->id];
struct omap_crtc *omap_crtc = omap_crtcs[channel];
DBG("%s", omap_crtc->name);
omap_crtc->timings = *timings;
}
static void omap_crtc_dss_set_lcd_config(struct omap_overlay_manager *mgr,
static void omap_crtc_dss_set_lcd_config(enum omap_channel channel,
const struct dss_lcd_mgr_config *config)
{
struct omap_crtc *omap_crtc = omap_crtcs[mgr->id];
struct omap_crtc *omap_crtc = omap_crtcs[channel];
DBG("%s", omap_crtc->name);
dispc_mgr_set_lcd_config(omap_crtc->channel, config);
}
static int omap_crtc_dss_register_framedone(
struct omap_overlay_manager *mgr,
enum omap_channel channel,
void (*handler)(void *), void *data)
{
return 0;
}
static void omap_crtc_dss_unregister_framedone(
struct omap_overlay_manager *mgr,
enum omap_channel channel,
void (*handler)(void *), void *data)
{
}
......@@ -403,24 +405,40 @@ static void omap_crtc_atomic_flush(struct drm_crtc *crtc,
}
}
static bool omap_crtc_is_plane_prop(struct drm_device *dev,
struct drm_property *property)
{
struct omap_drm_private *priv = dev->dev_private;
return property == priv->zorder_prop ||
property == dev->mode_config.rotation_property;
}
static int omap_crtc_atomic_set_property(struct drm_crtc *crtc,
struct drm_crtc_state *state,
struct drm_property *property,
uint64_t val)
{
struct drm_plane_state *plane_state;
struct drm_plane *plane = crtc->primary;
struct drm_device *dev = crtc->dev;
/*
* Delegate property set to the primary plane. Get the plane state and
* set the property directly.
*/
if (omap_crtc_is_plane_prop(dev, property)) {
struct drm_plane_state *plane_state;
struct drm_plane *plane = crtc->primary;
plane_state = drm_atomic_get_plane_state(state->state, plane);
if (!plane_state)
return -EINVAL;
/*
* Delegate property set to the primary plane. Get the plane
* state and set the property directly.
*/
plane_state = drm_atomic_get_plane_state(state->state, plane);
if (IS_ERR(plane_state))
return PTR_ERR(plane_state);
return drm_atomic_plane_set_property(plane, plane_state, property, val);
return drm_atomic_plane_set_property(plane, plane_state,
property, val);
}
return -EINVAL;
}
static int omap_crtc_atomic_get_property(struct drm_crtc *crtc,
......@@ -428,14 +446,20 @@ static int omap_crtc_atomic_get_property(struct drm_crtc *crtc,
struct drm_property *property,
uint64_t *val)
{
/*
* Delegate property get to the primary plane. The
* drm_atomic_plane_get_property() function isn't exported, but can be
* called through drm_object_property_get_value() as that will call
* drm_atomic_get_property() for atomic drivers.
*/
return drm_object_property_get_value(&crtc->primary->base, property,
val);
struct drm_device *dev = crtc->dev;
if (omap_crtc_is_plane_prop(dev, property)) {
/*
* Delegate property get to the primary plane. The
* drm_atomic_plane_get_property() function isn't exported, but
* can be called through drm_object_property_get_value() as that
* will call drm_atomic_get_property() for atomic drivers.
*/
return drm_object_property_get_value(&crtc->primary->base,
property, val);
}
return -EINVAL;
}
static const struct drm_crtc_funcs omap_crtc_funcs = {
......@@ -509,9 +533,6 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev,
omap_crtc->error_irq.irq = omap_crtc_error_irq;
omap_irq_register(dev, &omap_crtc->error_irq);
/* temporary: */
omap_crtc->mgr = omap_dss_get_overlay_manager(channel);
ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL,
&omap_crtc_funcs, NULL);
if (ret < 0) {
......
......@@ -79,6 +79,16 @@ static const uint32_t reg[][4] = {
DMM_PAT_DESCR__2, DMM_PAT_DESCR__3},
};
static u32 dmm_read(struct dmm *dmm, u32 reg)
{
return readl(dmm->base + reg);
}
static void dmm_write(struct dmm *dmm, u32 val, u32 reg)
{
writel(val, dmm->base + reg);
}
/* simple allocator to grab next 16 byte aligned memory from txn */
static void *alloc_dma(struct dmm_txn *txn, size_t sz, dma_addr_t *pa)
{
......@@ -108,7 +118,7 @@ static int wait_status(struct refill_engine *engine, uint32_t wait_mask)
i = DMM_FIXED_RETRY_COUNT;
while (true) {
r = readl(dmm->base + reg[PAT_STATUS][engine->id]);
r = dmm_read(dmm, reg[PAT_STATUS][engine->id]);
err = r & DMM_PATSTATUS_ERR;
if (err)
return -EFAULT;
......@@ -140,11 +150,11 @@ static void release_engine(struct refill_engine *engine)
static irqreturn_t omap_dmm_irq_handler(int irq, void *arg)
{
struct dmm *dmm = arg;
uint32_t status = readl(dmm->base + DMM_PAT_IRQSTATUS);
uint32_t status = dmm_read(dmm, DMM_PAT_IRQSTATUS);
int i;
/* ack IRQ */
writel(status, dmm->base + DMM_PAT_IRQSTATUS);
dmm_write(dmm, status, DMM_PAT_IRQSTATUS);
for (i = 0; i < dmm->num_engines; i++) {
if (status & DMM_IRQSTAT_LST) {
......@@ -264,7 +274,7 @@ static int dmm_txn_commit(struct dmm_txn *txn, bool wait)
txn->last_pat->next_pa = 0;
/* write to PAT_DESCR to clear out any pending transaction */
writel(0x0, dmm->base + reg[PAT_DESCR][engine->id]);
dmm_write(dmm, 0x0, reg[PAT_DESCR][engine->id]);
/* wait for engine ready: */
ret = wait_status(engine, DMM_PATSTATUS_READY);
......@@ -280,8 +290,7 @@ static int dmm_txn_commit(struct dmm_txn *txn, bool wait)
smp_mb();
/* kick reload */
writel(engine->refill_pa,
dmm->base + reg[PAT_DESCR][engine->id]);
dmm_write(dmm, engine->refill_pa, reg[PAT_DESCR][engine->id]);
if (wait) {
if (!wait_for_completion_timeout(&engine->compl,
......@@ -309,6 +318,21 @@ static int fill(struct tcm_area *area, struct page **pages,
struct tcm_area slice, area_s;
struct dmm_txn *txn;
/*
* FIXME
*
* Asynchronous fill does not work reliably, as the driver does not
* handle errors in the async code paths. The fill operation may
* silently fail, leading to leaking DMM engines, which may eventually
* lead to deadlock if we run out of DMM engines.
*
* For now, always set 'wait' so that we only use sync fills. Async
* fills should be fixed, or alternatively we could decide to only
* support sync fills and so the whole async code path could be removed.
*/
wait = true;
txn = dmm_txn_init(omap_dmm, area->tcm);
if (IS_ERR_OR_NULL(txn))
return -ENOMEM;
......@@ -642,7 +666,7 @@ static int omap_dmm_probe(struct platform_device *dev)
omap_dmm->dev = &dev->dev;
hwinfo = readl(omap_dmm->base + DMM_PAT_HWINFO);
hwinfo = dmm_read(omap_dmm, DMM_PAT_HWINFO);
omap_dmm->num_engines = (hwinfo >> 24) & 0x1F;
omap_dmm->num_lut = (hwinfo >> 16) & 0x1F;
omap_dmm->container_width = 256;
......@@ -651,7 +675,7 @@ static int omap_dmm_probe(struct platform_device *dev)
atomic_set(&omap_dmm->engine_counter, omap_dmm->num_engines);
/* read out actual LUT width and height */
pat_geom = readl(omap_dmm->base + DMM_PAT_GEOMETRY);
pat_geom = dmm_read(omap_dmm, DMM_PAT_GEOMETRY);
omap_dmm->lut_width = ((pat_geom >> 16) & 0xF) << 5;
omap_dmm->lut_height = ((pat_geom >> 24) & 0xF) << 5;
......@@ -661,12 +685,12 @@ static int omap_dmm_probe(struct platform_device *dev)
omap_dmm->num_lut++;
/* initialize DMM registers */
writel(0x88888888, omap_dmm->base + DMM_PAT_VIEW__0);
writel(0x88888888, omap_dmm->base + DMM_PAT_VIEW__1);
writel(0x80808080, omap_dmm->base + DMM_PAT_VIEW_MAP__0);
writel(0x80000000, omap_dmm->base + DMM_PAT_VIEW_MAP_BASE);
writel(0x88888888, omap_dmm->base + DMM_TILER_OR__0);
writel(0x88888888, omap_dmm->base + DMM_TILER_OR__1);
dmm_write(omap_dmm, 0x88888888, DMM_PAT_VIEW__0);
dmm_write(omap_dmm, 0x88888888, DMM_PAT_VIEW__1);
dmm_write(omap_dmm, 0x80808080, DMM_PAT_VIEW_MAP__0);
dmm_write(omap_dmm, 0x80000000, DMM_PAT_VIEW_MAP_BASE);
dmm_write(omap_dmm, 0x88888888, DMM_TILER_OR__0);
dmm_write(omap_dmm, 0x88888888, DMM_TILER_OR__1);
ret = request_irq(omap_dmm->irq, omap_dmm_irq_handler, IRQF_SHARED,
"omap_dmm_irq_handler", omap_dmm);
......@@ -684,7 +708,7 @@ static int omap_dmm_probe(struct platform_device *dev)
* buffers for accelerated pan/scroll) and FILL_DSC<n> which
* we just generally don't care about.
*/
writel(0x7e7e7e7e, omap_dmm->base + DMM_PAT_IRQENABLE_SET);
dmm_write(omap_dmm, 0x7e7e7e7e, DMM_PAT_IRQENABLE_SET);
omap_dmm->dummy_page = alloc_page(GFP_KERNEL | __GFP_DMA32);
if (!omap_dmm->dummy_page) {
......
......@@ -340,7 +340,7 @@ static int omap_modeset_init(struct drm_device *dev)
struct drm_connector *connector;
struct drm_encoder *encoder;
enum omap_channel channel;
struct omap_overlay_manager *mgr;
struct omap_dss_device *out;
if (!omapdss_device_is_connected(dssdev))
continue;
......@@ -387,8 +387,10 @@ static int omap_modeset_init(struct drm_device *dev)
* not considered.
*/
mgr = omapdss_find_mgr_from_display(dssdev);
channel = mgr->id;
out = omapdss_find_output_from_display(dssdev);
channel = out->dispc_channel;
omap_dss_put_device(out);
/*
* if this channel hasn't already been taken by a previously
* allocated crtc, we create a new crtc for it
......@@ -858,12 +860,52 @@ static int pdev_remove(struct platform_device *device)
}
#ifdef CONFIG_PM_SLEEP
static int omap_drm_suspend_all_displays(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;
}
static int omap_drm_resume_all_displays(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;
}
static int omap_drm_suspend(struct device *dev)
{
struct drm_device *drm_dev = dev_get_drvdata(dev);
drm_kms_helper_poll_disable(drm_dev);
drm_modeset_lock_all(drm_dev);
omap_drm_suspend_all_displays();
drm_modeset_unlock_all(drm_dev);
return 0;
}
......@@ -871,6 +913,10 @@ static int omap_drm_resume(struct device *dev)
{
struct drm_device *drm_dev = dev_get_drvdata(dev);
drm_modeset_lock_all(drm_dev);
omap_drm_resume_all_displays();
drm_modeset_unlock_all(drm_dev);
drm_kms_helper_poll_enable(drm_dev);
return omap_gem_resume(dev);
......
......@@ -31,6 +31,8 @@
#include <drm/drm_gem.h>
#include <drm/omap_drm.h>
#include "dss/omapdss.h"
#define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
#define VERB(fmt, ...) if (0) DRM_DEBUG(fmt, ##__VA_ARGS__) /* verbose debug */
......@@ -188,12 +190,15 @@ void omap_framebuffer_update_scanout(struct drm_framebuffer *fb,
struct omap_drm_window *win, struct omap_overlay_info *info);
struct drm_connector *omap_framebuffer_get_next_connector(
struct drm_framebuffer *fb, struct drm_connector *from);
bool omap_framebuffer_supports_rotation(struct drm_framebuffer *fb);
void omap_gem_init(struct drm_device *dev);
void omap_gem_deinit(struct drm_device *dev);
struct drm_gem_object *omap_gem_new(struct drm_device *dev,
union omap_gem_size gsize, uint32_t flags);
struct drm_gem_object *omap_gem_new_dmabuf(struct drm_device *dev, size_t size,
struct sg_table *sgt);
int omap_gem_new_handle(struct drm_device *dev, struct drm_file *file,
union omap_gem_size gsize, uint32_t flags, uint32_t *handle);
void omap_gem_free_object(struct drm_gem_object *obj);
......
......@@ -139,11 +139,16 @@ static void omap_encoder_enable(struct drm_encoder *encoder)
struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
struct omap_dss_device *dssdev = omap_encoder->dssdev;
struct omap_dss_driver *dssdrv = dssdev->driver;
int r;
omap_encoder_update(encoder, omap_crtc_channel(encoder->crtc),
omap_crtc_timings(encoder->crtc));
dssdrv->enable(dssdev);
r = dssdrv->enable(dssdev);
if (r)
dev_err(encoder->dev->dev,
"Failed to enable display '%s': %d\n",
dssdev->name, r);
}
static int omap_encoder_atomic_check(struct drm_encoder *encoder,
......
......@@ -145,6 +145,14 @@ static uint32_t get_linear_addr(struct plane *plane,
return plane->paddr + offset;
}
bool omap_framebuffer_supports_rotation(struct drm_framebuffer *fb)
{
struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
struct plane *plane = &omap_fb->planes[0];
return omap_gem_flags(plane->bo) & OMAP_BO_TILED;
}
/* update ovl info for scanout, handles cases of multi-planar fb's, etc.
*/
void omap_framebuffer_update_scanout(struct drm_framebuffer *fb,
......@@ -449,6 +457,14 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev,
goto fail;
}
if (i > 0 && pitch != mode_cmd->pitches[i - 1]) {
dev_err(dev->dev,
"pitches are not the same between framebuffer planes %d != %d\n",
pitch, mode_cmd->pitches[i - 1]);
ret = -EINVAL;
goto fail;
}
plane->bo = bos[i];
plane->offset = mode_cmd->offsets[i];
plane->pitch = pitch;
......
......@@ -31,9 +31,9 @@
*/
/* note: we use upper 8 bits of flags for driver-internal flags: */
#define OMAP_BO_DMA 0x01000000 /* actually is physically contiguous */
#define OMAP_BO_EXT_SYNC 0x02000000 /* externally allocated sync object */
#define OMAP_BO_EXT_MEM 0x04000000 /* externally allocated memory */
#define OMAP_BO_MEM_DMA_API 0x01000000 /* memory allocated with the dma_alloc_* API */
#define OMAP_BO_MEM_SHMEM 0x02000000 /* memory allocated through shmem backing */
#define OMAP_BO_MEM_DMABUF 0x08000000 /* memory imported from a dmabuf */
struct omap_gem_object {
struct drm_gem_object base;
......@@ -49,17 +49,25 @@ struct omap_gem_object {
uint32_t roll;
/**
* If buffer is allocated physically contiguous, the OMAP_BO_DMA flag
* is set and the paddr is valid. Also if the buffer is remapped in
* TILER and paddr_cnt > 0, then paddr is valid. But if you are using
* the physical address and OMAP_BO_DMA is not set, then you should
* be going thru omap_gem_{get,put}_paddr() to ensure the mapping is
* not removed from under your feet.
* paddr contains the buffer DMA address. It is valid for
*
* Note that OMAP_BO_SCANOUT is a hint from userspace that DMA capable
* buffer is requested, but doesn't mean that it is. Use the
* OMAP_BO_DMA flag to determine if the buffer has a DMA capable
* physical address.
* - buffers allocated through the DMA mapping API (with the
* OMAP_BO_MEM_DMA_API flag set)
*
* - buffers imported from dmabuf (with the OMAP_BO_MEM_DMABUF flag set)
* if they are physically contiguous (when sgt->orig_nents == 1)
*
* - buffers mapped through the TILER when paddr_cnt is not zero, in
* which case the DMA address points to the TILER aperture
*
* Physically contiguous buffers have their DMA address equal to the
* physical address as we don't remap those buffers through the TILER.
*
* Buffers mapped to the TILER have their DMA address pointing to the
* TILER aperture. As TILER mappings are refcounted (through paddr_cnt)
* the DMA address must be accessed through omap_get_get_paddr() to
* ensure that the mapping won't disappear unexpectedly. References must
* be released with omap_gem_put_paddr().
*/
dma_addr_t paddr;
......@@ -68,6 +76,12 @@ struct omap_gem_object {
*/
uint32_t paddr_cnt;
/**
* If the buffer has been imported from a dmabuf the OMAP_DB_DMABUF flag
* is set and the sgt field is valid.
*/
struct sg_table *sgt;
/**
* tiler block used when buffer is remapped in DMM/TILER.
*/
......@@ -91,17 +105,7 @@ struct omap_gem_object {
* sync-object allocated on demand (if needed)
*
* Per-buffer sync-object for tracking pending and completed hw/dma
* read and write operations. The layout in memory is dictated by
* the SGX firmware, which uses this information to stall the command
* stream if a surface is not ready yet.
*
* Note that when buffer is used by SGX, the sync-object needs to be
* allocated from a special heap of sync-objects. This way many sync
* objects can be packed in a page, and not waste GPU virtual address
* space. Because of this we have to have a omap_gem_set_sync_object()
* API to allow replacement of the syncobj after it has (potentially)
* already been allocated. A bit ugly but I haven't thought of a
* better alternative.
* read and write operations.
*/
struct {
uint32_t write_pending;
......@@ -166,16 +170,15 @@ static uint64_t mmap_offset(struct drm_gem_object *obj)
return drm_vma_node_offset_addr(&obj->vma_node);
}
/* GEM objects can either be allocated from contiguous memory (in which
* case obj->filp==NULL), or w/ shmem backing (obj->filp!=NULL). But non
* contiguous buffers can be remapped in TILER/DMM if they need to be
* contiguous... but we don't do this all the time to reduce pressure
* on TILER/DMM space when we know at allocation time that the buffer
* will need to be scanned out.
*/
static inline bool is_shmem(struct drm_gem_object *obj)
static bool is_contiguous(struct omap_gem_object *omap_obj)
{
return obj->filp != NULL;
if (omap_obj->flags & OMAP_BO_MEM_DMA_API)
return true;
if ((omap_obj->flags & OMAP_BO_MEM_DMABUF) && omap_obj->sgt->nents == 1)
return true;
return false;
}
/* -----------------------------------------------------------------------------
......@@ -264,6 +267,19 @@ static int omap_gem_attach_pages(struct drm_gem_object *obj)
for (i = 0; i < npages; i++) {
addrs[i] = dma_map_page(dev->dev, pages[i],
0, PAGE_SIZE, DMA_BIDIRECTIONAL);
if (dma_mapping_error(dev->dev, addrs[i])) {
dev_warn(dev->dev,
"%s: failed to map page\n", __func__);
for (i = i - 1; i >= 0; --i) {
dma_unmap_page(dev->dev, addrs[i],
PAGE_SIZE, DMA_BIDIRECTIONAL);
}
ret = -ENOMEM;
goto free_addrs;
}
}
} else {
addrs = kzalloc(npages * sizeof(*addrs), GFP_KERNEL);
......@@ -278,6 +294,8 @@ static int omap_gem_attach_pages(struct drm_gem_object *obj)
return 0;
free_addrs:
kfree(addrs);
free_pages:
drm_gem_put_pages(obj, pages, true, false);
......@@ -292,7 +310,7 @@ static int get_pages(struct drm_gem_object *obj, struct page ***pages)
struct omap_gem_object *omap_obj = to_omap_bo(obj);
int ret = 0;
if (is_shmem(obj) && !omap_obj->pages) {
if ((omap_obj->flags & OMAP_BO_MEM_SHMEM) && !omap_obj->pages) {
ret = omap_gem_attach_pages(obj);
if (ret) {
dev_err(obj->dev->dev, "could not attach pages\n");
......@@ -396,7 +414,7 @@ static int fault_1d(struct drm_gem_object *obj,
omap_gem_cpu_sync(obj, pgoff);
pfn = page_to_pfn(omap_obj->pages[pgoff]);
} else {
BUG_ON(!(omap_obj->flags & OMAP_BO_DMA));
BUG_ON(!is_contiguous(omap_obj));
pfn = (omap_obj->paddr >> PAGE_SHIFT) + pgoff;
}
......@@ -560,6 +578,11 @@ int omap_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
case 0:
case -ERESTARTSYS:
case -EINTR:
case -EBUSY:
/*
* EBUSY is ok: this just means that another thread
* already did the job.
*/
return VM_FAULT_NOPAGE;
case -ENOMEM:
return VM_FAULT_OOM;
......@@ -728,7 +751,8 @@ int omap_gem_roll(struct drm_gem_object *obj, uint32_t roll)
static inline bool is_cached_coherent(struct drm_gem_object *obj)
{
struct omap_gem_object *omap_obj = to_omap_bo(obj);
return is_shmem(obj) &&
return (omap_obj->flags & OMAP_BO_MEM_SHMEM) &&
((omap_obj->flags & OMAP_BO_CACHE_MASK) == OMAP_BO_CACHED);
}
......@@ -761,9 +785,20 @@ void omap_gem_dma_sync(struct drm_gem_object *obj,
for (i = 0; i < npages; i++) {
if (!omap_obj->addrs[i]) {
omap_obj->addrs[i] = dma_map_page(dev->dev, pages[i], 0,
dma_addr_t addr;
addr = dma_map_page(dev->dev, pages[i], 0,
PAGE_SIZE, DMA_BIDIRECTIONAL);
if (dma_mapping_error(dev->dev, addr)) {
dev_warn(dev->dev,
"%s: failed to map page\n",
__func__);
break;
}
dirty = true;
omap_obj->addrs[i] = addr;
}
}
......@@ -787,7 +822,7 @@ int omap_gem_get_paddr(struct drm_gem_object *obj,
mutex_lock(&obj->dev->struct_mutex);
if (remap && is_shmem(obj) && priv->has_dmm) {
if (!is_contiguous(omap_obj) && remap && priv->has_dmm) {
if (omap_obj->paddr_cnt == 0) {
struct page **pages;
uint32_t npages = obj->size >> PAGE_SHIFT;
......@@ -834,7 +869,7 @@ int omap_gem_get_paddr(struct drm_gem_object *obj,
omap_obj->paddr_cnt++;
*paddr = omap_obj->paddr;
} else if (omap_obj->flags & OMAP_BO_DMA) {
} else if (is_contiguous(omap_obj)) {
*paddr = omap_obj->paddr;
} else {
ret = -EINVAL;
......@@ -1138,20 +1173,6 @@ static inline int sync_op(struct drm_gem_object *obj,
return ret;
}
/* it is a bit lame to handle updates in this sort of polling way, but
* in case of PVR, the GPU can directly update read/write complete
* values, and not really tell us which ones it updated.. this also
* means that sync_lock is not quite sufficient. So we'll need to
* do something a bit better when it comes time to add support for
* separate 2d hw..
*/
void omap_gem_op_update(void)
{
spin_lock(&sync_lock);
sync_op_update();
spin_unlock(&sync_lock);
}
/* mark the start of read and/or write operation */
int omap_gem_op_start(struct drm_gem_object *obj, enum omap_gem_op op)
{
......@@ -1219,7 +1240,7 @@ int omap_gem_op_sync(struct drm_gem_object *obj, enum omap_gem_op op)
* is currently blocked.. fxn() can be called from any context
*
* (TODO for now fxn is called back from whichever context calls
* omap_gem_op_update().. but this could be better defined later
* omap_gem_op_finish().. but this could be better defined later
* if needed)
*
* TODO more code in common w/ _sync()..
......@@ -1261,50 +1282,10 @@ int omap_gem_op_async(struct drm_gem_object *obj, enum omap_gem_op op,
return 0;
}
/* special API so PVR can update the buffer to use a sync-object allocated
* from it's sync-obj heap. Only used for a newly allocated (from PVR's
* perspective) sync-object, so we overwrite the new syncobj w/ values
* from the already allocated syncobj (if there is one)
*/
int omap_gem_set_sync_object(struct drm_gem_object *obj, void *syncobj)
{
struct omap_gem_object *omap_obj = to_omap_bo(obj);
int ret = 0;
spin_lock(&sync_lock);
if ((omap_obj->flags & OMAP_BO_EXT_SYNC) && !syncobj) {
/* clearing a previously set syncobj */
syncobj = kmemdup(omap_obj->sync, sizeof(*omap_obj->sync),
GFP_ATOMIC);
if (!syncobj) {
ret = -ENOMEM;
goto unlock;
}
omap_obj->flags &= ~OMAP_BO_EXT_SYNC;
omap_obj->sync = syncobj;
} else if (syncobj && !(omap_obj->flags & OMAP_BO_EXT_SYNC)) {
/* replacing an existing syncobj */
if (omap_obj->sync) {
memcpy(syncobj, omap_obj->sync, sizeof(*omap_obj->sync));
kfree(omap_obj->sync);
}
omap_obj->flags |= OMAP_BO_EXT_SYNC;
omap_obj->sync = syncobj;
}
unlock:
spin_unlock(&sync_lock);
return ret;
}
/* -----------------------------------------------------------------------------
* Constructor & Destructor
*/
/* don't call directly.. called from GEM core when it is time to actually
* free the object..
*/
void omap_gem_free_object(struct drm_gem_object *obj)
{
struct drm_device *dev = obj->dev;
......@@ -1324,22 +1305,23 @@ void omap_gem_free_object(struct drm_gem_object *obj)
*/
WARN_ON(omap_obj->paddr_cnt > 0);
/* don't free externally allocated backing memory */
if (!(omap_obj->flags & OMAP_BO_EXT_MEM)) {
if (omap_obj->pages)
if (omap_obj->pages) {
if (omap_obj->flags & OMAP_BO_MEM_DMABUF)
kfree(omap_obj->pages);
else
omap_gem_detach_pages(obj);
}
if (!is_shmem(obj)) {
dma_free_writecombine(dev->dev, obj->size,
omap_obj->vaddr, omap_obj->paddr);
} else if (omap_obj->vaddr) {
vunmap(omap_obj->vaddr);
}
if (omap_obj->flags & OMAP_BO_MEM_DMA_API) {
dma_free_writecombine(dev->dev, obj->size,
omap_obj->vaddr, omap_obj->paddr);
} else if (omap_obj->vaddr) {
vunmap(omap_obj->vaddr);
} else if (obj->import_attach) {
drm_prime_gem_destroy(obj, omap_obj->sgt);
}
/* don't free externally allocated syncobj */
if (!(omap_obj->flags & OMAP_BO_EXT_SYNC))
kfree(omap_obj->sync);
kfree(omap_obj->sync);
drm_gem_object_release(obj);
......@@ -1357,84 +1339,160 @@ struct drm_gem_object *omap_gem_new(struct drm_device *dev,
size_t size;
int ret;
/* Validate the flags and compute the memory and cache flags. */
if (flags & OMAP_BO_TILED) {
if (!priv->usergart) {
dev_err(dev->dev, "Tiled buffers require DMM\n");
return NULL;
}
/* tiled buffers are always shmem paged backed.. when they are
* scanned out, they are remapped into DMM/TILER
/*
* Tiled buffers are always shmem paged backed. When they are
* scanned out, they are remapped into DMM/TILER.
*/
flags &= ~OMAP_BO_SCANOUT;
flags |= OMAP_BO_MEM_SHMEM;
/* currently don't allow cached buffers.. there is some caching
* stuff that needs to be handled better
/*
* Currently don't allow cached buffers. There is some caching
* stuff that needs to be handled better.
*/
flags &= ~(OMAP_BO_CACHED|OMAP_BO_WC|OMAP_BO_UNCACHED);
flags |= tiler_get_cpu_cache_flags();
/* align dimensions to slot boundaries... */
tiler_align(gem2fmt(flags),
&gsize.tiled.width, &gsize.tiled.height);
/* ...and calculate size based on aligned dimensions */
size = tiler_size(gem2fmt(flags),
gsize.tiled.width, gsize.tiled.height);
} else {
size = PAGE_ALIGN(gsize.bytes);
} else if ((flags & OMAP_BO_SCANOUT) && !priv->has_dmm) {
/*
* OMAP_BO_SCANOUT hints that the buffer doesn't need to be
* tiled. However, to lower the pressure on memory allocation,
* use contiguous memory only if no TILER is available.
*/
flags |= OMAP_BO_MEM_DMA_API;
} else if (!(flags & OMAP_BO_MEM_DMABUF)) {
/*
* All other buffers not backed by dma_buf are shmem-backed.
*/
flags |= OMAP_BO_MEM_SHMEM;
}
/* Allocate the initialize the OMAP GEM object. */
omap_obj = kzalloc(sizeof(*omap_obj), GFP_KERNEL);
if (!omap_obj)
return NULL;
obj = &omap_obj->base;
omap_obj->flags = flags;
if ((flags & OMAP_BO_SCANOUT) && !priv->has_dmm) {
/* attempt to allocate contiguous memory if we don't
* have DMM for remappign discontiguous buffers
if (flags & OMAP_BO_TILED) {
/*
* For tiled buffers align dimensions to slot boundaries and
* calculate size based on aligned dimensions.
*/
omap_obj->vaddr = dma_alloc_writecombine(dev->dev, size,
&omap_obj->paddr, GFP_KERNEL);
if (!omap_obj->vaddr) {
kfree(omap_obj);
tiler_align(gem2fmt(flags), &gsize.tiled.width,
&gsize.tiled.height);
return NULL;
}
flags |= OMAP_BO_DMA;
}
size = tiler_size(gem2fmt(flags), gsize.tiled.width,
gsize.tiled.height);
spin_lock(&priv->list_lock);
list_add(&omap_obj->mm_list, &priv->obj_list);
spin_unlock(&priv->list_lock);
omap_obj->flags = flags;
if (flags & OMAP_BO_TILED) {
omap_obj->width = gsize.tiled.width;
omap_obj->height = gsize.tiled.height;
} else {
size = PAGE_ALIGN(gsize.bytes);
}
if (flags & (OMAP_BO_DMA|OMAP_BO_EXT_MEM)) {
/* Initialize the GEM object. */
if (!(flags & OMAP_BO_MEM_SHMEM)) {
drm_gem_private_object_init(dev, obj, size);
} else {
ret = drm_gem_object_init(dev, obj, size);
if (ret)
goto fail;
goto err_free;
mapping = file_inode(obj->filp)->i_mapping;
mapping_set_gfp_mask(mapping, GFP_USER | __GFP_DMA32);
}
/* Allocate memory if needed. */
if (flags & OMAP_BO_MEM_DMA_API) {
omap_obj->vaddr = dma_alloc_writecombine(dev->dev, size,
&omap_obj->paddr,
GFP_KERNEL);
if (!omap_obj->vaddr)
goto err_release;
}
spin_lock(&priv->list_lock);
list_add(&omap_obj->mm_list, &priv->obj_list);
spin_unlock(&priv->list_lock);
return obj;
fail:
omap_gem_free_object(obj);
err_release:
drm_gem_object_release(obj);
err_free:
kfree(omap_obj);
return NULL;
}
struct drm_gem_object *omap_gem_new_dmabuf(struct drm_device *dev, size_t size,
struct sg_table *sgt)
{
struct omap_drm_private *priv = dev->dev_private;
struct omap_gem_object *omap_obj;
struct drm_gem_object *obj;
union omap_gem_size gsize;
/* Without a DMM only physically contiguous buffers can be supported. */
if (sgt->orig_nents != 1 && !priv->has_dmm)
return ERR_PTR(-EINVAL);
mutex_lock(&dev->struct_mutex);
gsize.bytes = PAGE_ALIGN(size);
obj = omap_gem_new(dev, gsize, OMAP_BO_MEM_DMABUF | OMAP_BO_WC);
if (!obj) {
obj = ERR_PTR(-ENOMEM);
goto done;
}
omap_obj = to_omap_bo(obj);
omap_obj->sgt = sgt;
if (sgt->orig_nents == 1) {
omap_obj->paddr = sg_dma_address(sgt->sgl);
} else {
/* Create pages list from sgt */
struct sg_page_iter iter;
struct page **pages;
unsigned int npages;
unsigned int i = 0;
npages = DIV_ROUND_UP(size, PAGE_SIZE);
pages = kcalloc(npages, sizeof(*pages), GFP_KERNEL);
if (!pages) {
omap_gem_free_object(obj);
obj = ERR_PTR(-ENOMEM);
goto done;
}
omap_obj->pages = pages;
for_each_sg_page(sgt->sgl, &iter, sgt->orig_nents, 0) {
pages[i++] = sg_page_iter_page(&iter);
if (i > npages)
break;
}
if (WARN_ON(i != npages)) {
omap_gem_free_object(obj);
obj = ERR_PTR(-ENOMEM);
goto done;
}
}
done:
mutex_unlock(&dev->struct_mutex);
return obj;
}
/* convenience method to construct a GEM buffer object, and userspace handle */
int omap_gem_new_handle(struct drm_device *dev, struct drm_file *file,
union omap_gem_size gsize, uint32_t flags, uint32_t *handle)
......
......@@ -21,6 +21,10 @@
#include "omap_drv.h"
/* -----------------------------------------------------------------------------
* DMABUF Export
*/
static struct sg_table *omap_gem_map_dma_buf(
struct dma_buf_attachment *attachment,
enum dma_data_direction dir)
......@@ -178,15 +182,20 @@ struct dma_buf *omap_gem_prime_export(struct drm_device *dev,
return dma_buf_export(&exp_info);
}
/* -----------------------------------------------------------------------------
* DMABUF Import
*/
struct drm_gem_object *omap_gem_prime_import(struct drm_device *dev,
struct dma_buf *buffer)
struct dma_buf *dma_buf)
{
struct dma_buf_attachment *attach;
struct drm_gem_object *obj;
struct sg_table *sgt;
int ret;
/* is this one of own objects? */
if (buffer->ops == &omap_dmabuf_ops) {
obj = buffer->priv;
/* is it from our device? */
if (dma_buf->ops == &omap_dmabuf_ops) {
obj = dma_buf->priv;
if (obj->dev == dev) {
/*
* Importing dmabuf exported from out own gem increases
......@@ -197,9 +206,33 @@ struct drm_gem_object *omap_gem_prime_import(struct drm_device *dev,
}
}
/*
* TODO add support for importing buffers from other devices..
* for now we don't need this but would be nice to add eventually
*/
return ERR_PTR(-EINVAL);
attach = dma_buf_attach(dma_buf, dev->dev);
if (IS_ERR(attach))
return ERR_CAST(attach);
get_dma_buf(dma_buf);
sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
if (IS_ERR(sgt)) {
ret = PTR_ERR(sgt);
goto fail_detach;
}
obj = omap_gem_new_dmabuf(dev, dma_buf->size, sgt);
if (IS_ERR(obj)) {
ret = PTR_ERR(obj);
goto fail_unmap;
}
obj->import_attach = attach;
return obj;
fail_unmap:
dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL);
fail_detach:
dma_buf_detach(dma_buf, attach);
dma_buf_put(dma_buf);
return ERR_PTR(ret);
}
......@@ -177,6 +177,12 @@ static int omap_plane_atomic_check(struct drm_plane *plane,
if (state->crtc_y + state->crtc_h > crtc_state->adjusted_mode.vdisplay)
return -EINVAL;
if (state->fb) {
if (state->rotation != BIT(DRM_ROTATE_0) &&
!omap_framebuffer_supports_rotation(state->fb))
return -EINVAL;
}
return 0;
}
......
......@@ -915,4 +915,5 @@ static inline u16 DISPC_MFLAG_THRESHOLD_OFFSET(enum omap_plane plane)
return 0;
}
}
#endif
......@@ -412,6 +412,44 @@ 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);
u32 dispc_read_irqstatus(void);
void dispc_clear_irqstatus(u32 mask);
u32 dispc_read_irqenable(void);
void dispc_write_irqenable(u32 mask);
int dispc_request_irq(irq_handler_t handler, void *dev_id);
void dispc_free_irq(void *dev_id);
int dispc_runtime_get(void);
void dispc_runtime_put(void);
void dispc_mgr_enable(enum omap_channel channel, bool enable);
bool dispc_mgr_is_enabled(enum omap_channel channel);
u32 dispc_mgr_get_vsync_irq(enum omap_channel channel);
u32 dispc_mgr_get_framedone_irq(enum omap_channel channel);
u32 dispc_mgr_get_sync_lost_irq(enum omap_channel channel);
bool dispc_mgr_go_busy(enum omap_channel channel);
void dispc_mgr_go(enum omap_channel channel);
void dispc_mgr_set_lcd_config(enum omap_channel channel,
const struct dss_lcd_mgr_config *config);
void dispc_mgr_set_timings(enum omap_channel channel,
const struct omap_video_timings *timings);
void dispc_mgr_setup(enum omap_channel channel,
const struct omap_overlay_manager_info *info);
int dispc_ovl_check(enum omap_plane plane, enum omap_channel channel,
const struct omap_overlay_info *oi,
const struct omap_video_timings *timings,
int *x_predecim, int *y_predecim);
int dispc_ovl_enable(enum omap_plane plane, bool enable);
bool dispc_ovl_enabled(enum omap_plane plane);
void dispc_ovl_set_channel_out(enum omap_plane plane,
enum omap_channel channel);
int dispc_ovl_setup(enum omap_plane plane, const struct omap_overlay_info *oi,
bool replication, const struct omap_video_timings *mgr_timings,
bool mem_to_mem);
/* VENC */
int venc_init_platform_driver(void) __init;
void venc_uninit_platform_driver(void);
......@@ -465,4 +503,44 @@ 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);
/* compat */
struct dss_mgr_ops {
int (*connect)(struct omap_overlay_manager *mgr,
struct omap_dss_device *dst);
void (*disconnect)(struct omap_overlay_manager *mgr,
struct omap_dss_device *dst);
void (*start_update)(struct omap_overlay_manager *mgr);
int (*enable)(struct omap_overlay_manager *mgr);
void (*disable)(struct omap_overlay_manager *mgr);
void (*set_timings)(struct omap_overlay_manager *mgr,
const struct omap_video_timings *timings);
void (*set_lcd_config)(struct omap_overlay_manager *mgr,
const struct dss_lcd_mgr_config *config);
int (*register_framedone_handler)(struct omap_overlay_manager *mgr,
void (*handler)(void *), void *data);
void (*unregister_framedone_handler)(struct omap_overlay_manager *mgr,
void (*handler)(void *), void *data);
};
int dss_install_mgr_ops(const struct dss_mgr_ops *mgr_ops);
void dss_uninstall_mgr_ops(void);
int dss_mgr_connect(struct omap_overlay_manager *mgr,
struct omap_dss_device *dst);
void dss_mgr_disconnect(struct omap_overlay_manager *mgr,
struct omap_dss_device *dst);
void dss_mgr_set_timings(struct omap_overlay_manager *mgr,
const struct omap_video_timings *timings);
void dss_mgr_set_lcd_config(struct omap_overlay_manager *mgr,
const struct dss_lcd_mgr_config *config);
int dss_mgr_enable(struct omap_overlay_manager *mgr);
void dss_mgr_disable(struct omap_overlay_manager *mgr);
void dss_mgr_start_update(struct omap_overlay_manager *mgr);
int dss_mgr_register_framedone_handler(struct omap_overlay_manager *mgr,
void (*handler)(void *), void *data);
void dss_mgr_unregister_framedone_handler(struct omap_overlay_manager *mgr,
void (*handler)(void *), void *data);
#endif
......@@ -45,21 +45,6 @@ struct encoder_tfp410_platform_data {
int data_lines;
};
/**
* encoder_tpd12s015 platform data
* @name: name for this display entity
* @ct_cp_hpd_gpio: CT_CP_HPD gpio number
* @ls_oe_gpio: LS_OE gpio number
* @hpd_gpio: HPD gpio number
*/
struct encoder_tpd12s015_platform_data {
const char *name;
const char *source;
int ct_cp_hpd_gpio;
int ls_oe_gpio;
int hpd_gpio;
};
/**
* connector_dvi platform data
......
......@@ -51,6 +51,7 @@
#define DISPC_IRQ_FRAMEDONEWB (1 << 23)
#define DISPC_IRQ_FRAMEDONETV (1 << 24)
#define DISPC_IRQ_WBBUFFEROVERFLOW (1 << 25)
#define DISPC_IRQ_WBUNCOMPLETEERROR (1 << 26)
#define DISPC_IRQ_SYNC_LOST3 (1 << 27)
#define DISPC_IRQ_VSYNC3 (1 << 28)
#define DISPC_IRQ_ACBIAS_COUNT_STAT3 (1 << 29)
......@@ -331,8 +332,6 @@ struct omap_dss_board_info {
/* Init with the board info */
extern int omap_display_init(struct omap_dss_board_info *board_data);
/* HDMI mux init*/
extern int omap_hdmi_init(enum omap_hdmi_flags flags);
struct omap_video_timings {
/* Unit: pixels */
......@@ -366,6 +365,8 @@ struct omap_video_timings {
enum omap_dss_signal_level de_level;
/* Pixel clock edges to drive HSYNC and VSYNC signals */
enum omap_dss_signal_edge sync_pclk_edge;
bool double_pixel;
};
/* Hardcoded timings for tv modes. Venc only uses these to
......@@ -769,6 +770,7 @@ struct omap_dss_device {
/* DISPC channel for this output */
enum omap_channel dispc_channel;
bool dispc_channel_connected;
/* output instance */
enum omap_dss_output_id id;
......@@ -782,13 +784,6 @@ struct omap_dss_device {
struct omap_dss_device *dst;
};
struct omap_dss_hdmi_data
{
int ct_cp_hpd_gpio;
int ls_oe_gpio;
int hpd_gpio;
};
struct omap_dss_driver {
int (*probe)(struct omap_dss_device *);
void (*remove)(struct omap_dss_device *);
......@@ -897,85 +892,9 @@ typedef void (*omap_dispc_isr_t) (void *arg, u32 mask);
int omap_dispc_register_isr(omap_dispc_isr_t isr, void *arg, u32 mask);
int omap_dispc_unregister_isr(omap_dispc_isr_t isr, void *arg, u32 mask);
u32 dispc_read_irqstatus(void);
void dispc_clear_irqstatus(u32 mask);
u32 dispc_read_irqenable(void);
void dispc_write_irqenable(u32 mask);
int dispc_request_irq(irq_handler_t handler, void *dev_id);
void dispc_free_irq(void *dev_id);
int dispc_runtime_get(void);
void dispc_runtime_put(void);
void dispc_mgr_enable(enum omap_channel channel, bool enable);
bool dispc_mgr_is_enabled(enum omap_channel channel);
u32 dispc_mgr_get_vsync_irq(enum omap_channel channel);
u32 dispc_mgr_get_framedone_irq(enum omap_channel channel);
u32 dispc_mgr_get_sync_lost_irq(enum omap_channel channel);
bool dispc_mgr_go_busy(enum omap_channel channel);
void dispc_mgr_go(enum omap_channel channel);
void dispc_mgr_set_lcd_config(enum omap_channel channel,
const struct dss_lcd_mgr_config *config);
void dispc_mgr_set_timings(enum omap_channel channel,
const struct omap_video_timings *timings);
void dispc_mgr_setup(enum omap_channel channel,
const struct omap_overlay_manager_info *info);
int dispc_ovl_check(enum omap_plane plane, enum omap_channel channel,
const struct omap_overlay_info *oi,
const struct omap_video_timings *timings,
int *x_predecim, int *y_predecim);
int dispc_ovl_enable(enum omap_plane plane, bool enable);
bool dispc_ovl_enabled(enum omap_plane plane);
void dispc_ovl_set_channel_out(enum omap_plane plane,
enum omap_channel channel);
int dispc_ovl_setup(enum omap_plane plane, const struct omap_overlay_info *oi,
bool replication, const struct omap_video_timings *mgr_timings,
bool mem_to_mem);
int omapdss_compat_init(void);
void omapdss_compat_uninit(void);
struct dss_mgr_ops {
int (*connect)(struct omap_overlay_manager *mgr,
struct omap_dss_device *dst);
void (*disconnect)(struct omap_overlay_manager *mgr,
struct omap_dss_device *dst);
void (*start_update)(struct omap_overlay_manager *mgr);
int (*enable)(struct omap_overlay_manager *mgr);
void (*disable)(struct omap_overlay_manager *mgr);
void (*set_timings)(struct omap_overlay_manager *mgr,
const struct omap_video_timings *timings);
void (*set_lcd_config)(struct omap_overlay_manager *mgr,
const struct dss_lcd_mgr_config *config);
int (*register_framedone_handler)(struct omap_overlay_manager *mgr,
void (*handler)(void *), void *data);
void (*unregister_framedone_handler)(struct omap_overlay_manager *mgr,
void (*handler)(void *), void *data);
};
int dss_install_mgr_ops(const struct dss_mgr_ops *mgr_ops);
void dss_uninstall_mgr_ops(void);
int dss_mgr_connect(struct omap_overlay_manager *mgr,
struct omap_dss_device *dst);
void dss_mgr_disconnect(struct omap_overlay_manager *mgr,
struct omap_dss_device *dst);
void dss_mgr_set_timings(struct omap_overlay_manager *mgr,
const struct omap_video_timings *timings);
void dss_mgr_set_lcd_config(struct omap_overlay_manager *mgr,
const struct dss_lcd_mgr_config *config);
int dss_mgr_enable(struct omap_overlay_manager *mgr);
void dss_mgr_disable(struct omap_overlay_manager *mgr);
void dss_mgr_start_update(struct omap_overlay_manager *mgr);
int dss_mgr_register_framedone_handler(struct omap_overlay_manager *mgr,
void (*handler)(void *), void *data);
void dss_mgr_unregister_framedone_handler(struct omap_overlay_manager *mgr,
void (*handler)(void *), void *data);
static inline bool omapdss_device_is_connected(struct omap_dss_device *dssdev)
{
return dssdev->src;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment