Commit 193984f4 authored by Florian Tobias Schandinat's avatar Florian Tobias Schandinat

Merge branch 'for-florian' of git://gitorious.org/linux-omap-dss2/linux into fbdev-next

parents c5627f65 9a901683
......@@ -595,20 +595,6 @@ static void __init omap_sfh7741prox_init(void)
__func__, OMAP4_SFH7741_ENABLE_GPIO, error);
}
static void sdp4430_hdmi_mux_init(void)
{
/* PAD0_HDMI_HPD_PAD1_HDMI_CEC */
omap_mux_init_signal("hdmi_hpd",
OMAP_PIN_INPUT_PULLUP);
omap_mux_init_signal("hdmi_cec",
OMAP_PIN_INPUT_PULLUP);
/* PAD0_HDMI_DDC_SCL_PAD1_HDMI_DDC_SDA */
omap_mux_init_signal("hdmi_ddc_scl",
OMAP_PIN_INPUT_PULLUP);
omap_mux_init_signal("hdmi_ddc_sda",
OMAP_PIN_INPUT_PULLUP);
}
static struct gpio sdp4430_hdmi_gpios[] = {
{ HDMI_GPIO_HPD, GPIOF_OUT_INIT_HIGH, "hdmi_gpio_hpd" },
{ HDMI_GPIO_LS_OE, GPIOF_OUT_INIT_HIGH, "hdmi_gpio_ls_oe" },
......@@ -826,9 +812,16 @@ static void omap_4430sdp_display_init(void)
pr_err("%s: Could not get display_sel GPIO\n", __func__);
sdp4430_lcd_init();
sdp4430_hdmi_mux_init();
sdp4430_picodlp_init();
omap_display_init(&sdp4430_dss_data);
/*
* OMAP4460SDP/Blaze and OMAP4430 ES2.3 SDP/Blaze boards and
* later have external pull up on the HDMI I2C lines
*/
if (cpu_is_omap446x() || omap_rev() > OMAP4430_REV_ES2_2)
omap_hdmi_init(OMAP_HDMI_SDA_SCL_EXTERNAL_PULLUP);
else
omap_hdmi_init(0);
}
#ifdef CONFIG_OMAP_MUX
......
......@@ -478,21 +478,6 @@ int __init omap4_panda_dvi_init(void)
return r;
}
static void omap4_panda_hdmi_mux_init(void)
{
/* PAD0_HDMI_HPD_PAD1_HDMI_CEC */
omap_mux_init_signal("hdmi_hpd",
OMAP_PIN_INPUT_PULLUP);
omap_mux_init_signal("hdmi_cec",
OMAP_PIN_INPUT_PULLUP);
/* PAD0_HDMI_DDC_SCL_PAD1_HDMI_DDC_SDA */
omap_mux_init_signal("hdmi_ddc_scl",
OMAP_PIN_INPUT_PULLUP);
omap_mux_init_signal("hdmi_ddc_sda",
OMAP_PIN_INPUT_PULLUP);
}
static struct gpio panda_hdmi_gpios[] = {
{ HDMI_GPIO_HPD, GPIOF_OUT_INIT_HIGH, "hdmi_gpio_hpd" },
{ HDMI_GPIO_LS_OE, GPIOF_OUT_INIT_HIGH, "hdmi_gpio_ls_oe" },
......@@ -544,8 +529,16 @@ void omap4_panda_display_init(void)
if (r)
pr_err("error initializing panda DVI\n");
omap4_panda_hdmi_mux_init();
omap_display_init(&omap4_panda_dss_data);
/*
* OMAP4460SDP/Blaze and OMAP4430 ES2.3 SDP/Blaze boards and
* later have external pull up on the HDMI I2C lines
*/
if (cpu_is_omap446x() || omap_rev() > OMAP4430_REV_ES2_2)
omap_hdmi_init(OMAP_HDMI_SDA_SCL_EXTERNAL_PULLUP);
else
omap_hdmi_init(0);
}
static void __init omap4_panda_init(void)
......
......@@ -29,6 +29,7 @@
#include <plat/omap-pm.h>
#include <plat/common.h>
#include "mux.h"
#include "control.h"
#include "display.h"
......@@ -96,6 +97,36 @@ static const struct omap_dss_hwmod_data omap4_dss_hwmod_data[] __initdata = {
{ "dss_hdmi", "omapdss_hdmi", -1 },
};
static void omap4_hdmi_mux_pads(enum omap_hdmi_flags flags)
{
u32 reg;
u16 control_i2c_1;
/* PAD0_HDMI_HPD_PAD1_HDMI_CEC */
omap_mux_init_signal("hdmi_hpd",
OMAP_PIN_INPUT_PULLUP);
omap_mux_init_signal("hdmi_cec",
OMAP_PIN_INPUT_PULLUP);
/* PAD0_HDMI_DDC_SCL_PAD1_HDMI_DDC_SDA */
omap_mux_init_signal("hdmi_ddc_scl",
OMAP_PIN_INPUT_PULLUP);
omap_mux_init_signal("hdmi_ddc_sda",
OMAP_PIN_INPUT_PULLUP);
/*
* CONTROL_I2C_1: HDMI_DDC_SDA_PULLUPRESX (bit 28) and
* HDMI_DDC_SCL_PULLUPRESX (bit 24) are set to disable
* internal pull up resistor.
*/
if (flags & OMAP_HDMI_SDA_SCL_EXTERNAL_PULLUP) {
control_i2c_1 = OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_I2C_1;
reg = omap4_ctrl_pad_readl(control_i2c_1);
reg |= (OMAP4_HDMI_DDC_SDA_PULLUPRESX_MASK |
OMAP4_HDMI_DDC_SCL_PULLUPRESX_MASK);
omap4_ctrl_pad_writel(reg, control_i2c_1);
}
}
static int omap4_dsi_mux_pads(int dsi_id, unsigned lanes)
{
u32 enable_mask, enable_shift;
......@@ -129,6 +160,14 @@ static int omap4_dsi_mux_pads(int dsi_id, unsigned lanes)
return 0;
}
int omap_hdmi_init(enum omap_hdmi_flags flags)
{
if (cpu_is_omap44xx())
omap4_hdmi_mux_pads(flags);
return 0;
}
static int omap_dsi_enable_pads(int dsi_id, unsigned lane_mask)
{
if (cpu_is_omap44xx())
......
......@@ -423,7 +423,7 @@ static int omapvid_setup_overlay(struct omap_vout_device *vout,
"%s enable=%d addr=%x width=%d\n height=%d color_mode=%d\n"
"rotation=%d mirror=%d posx=%d posy=%d out_width = %d \n"
"out_height=%d rotation_type=%d screen_width=%d\n",
__func__, info.enabled, info.paddr, info.width, info.height,
__func__, ovl->is_enabled(ovl), info.paddr, info.width, info.height,
info.color_mode, info.rotation, info.mirror, info.pos_x,
info.pos_y, info.out_width, info.out_height, info.rotation_type,
info.screen_width);
......@@ -942,12 +942,8 @@ static int omap_vout_release(struct file *file)
/* Disable all the overlay managers connected with this interface */
for (i = 0; i < ovid->num_overlays; i++) {
struct omap_overlay *ovl = ovid->overlays[i];
if (ovl->manager && ovl->manager->device) {
struct omap_overlay_info info;
ovl->get_overlay_info(ovl, &info);
info.enabled = 0;
ovl->set_overlay_info(ovl, &info);
}
if (ovl->manager && ovl->manager->device)
ovl->disable(ovl);
}
/* Turn off the pipeline */
ret = omapvid_apply_changes(vout);
......@@ -1667,7 +1663,6 @@ static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
if (ovl->manager && ovl->manager->device) {
struct omap_overlay_info info;
ovl->get_overlay_info(ovl, &info);
info.enabled = 1;
info.paddr = addr;
if (ovl->set_overlay_info(ovl, &info)) {
ret = -EINVAL;
......@@ -1686,6 +1681,16 @@ static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
if (ret)
v4l2_err(&vout->vid_dev->v4l2_dev, "failed to change mode\n");
for (j = 0; j < ovid->num_overlays; j++) {
struct omap_overlay *ovl = ovid->overlays[j];
if (ovl->manager && ovl->manager->device) {
ret = ovl->enable(ovl);
if (ret)
goto streamon_err1;
}
}
ret = 0;
streamon_err1:
......@@ -1715,16 +1720,8 @@ static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
for (j = 0; j < ovid->num_overlays; j++) {
struct omap_overlay *ovl = ovid->overlays[j];
if (ovl->manager && ovl->manager->device) {
struct omap_overlay_info info;
ovl->get_overlay_info(ovl, &info);
info.enabled = 0;
ret = ovl->set_overlay_info(ovl, &info);
if (ret)
v4l2_err(&vout->vid_dev->v4l2_dev,
"failed to update overlay info in streamoff\n");
}
if (ovl->manager && ovl->manager->device)
ovl->disable(ovl);
}
/* Turn of the pipeline */
......
......@@ -198,7 +198,7 @@ static int ams_delta_panel_resume(struct platform_device *pdev)
return 0;
}
struct platform_driver ams_delta_panel_driver = {
static struct platform_driver ams_delta_panel_driver = {
.probe = ams_delta_panel_probe,
.remove = ams_delta_panel_remove,
.suspend = ams_delta_panel_suspend,
......@@ -209,15 +209,4 @@ struct platform_driver ams_delta_panel_driver = {
},
};
static int __init ams_delta_panel_drv_init(void)
{
return platform_driver_register(&ams_delta_panel_driver);
}
static void __exit ams_delta_panel_drv_cleanup(void)
{
platform_driver_unregister(&ams_delta_panel_driver);
}
module_init(ams_delta_panel_drv_init);
module_exit(ams_delta_panel_drv_cleanup);
module_platform_driver(ams_delta_panel_driver);
......@@ -113,7 +113,7 @@ static int h3_panel_resume(struct platform_device *pdev)
return 0;
}
struct platform_driver h3_panel_driver = {
static struct platform_driver h3_panel_driver = {
.probe = h3_panel_probe,
.remove = h3_panel_remove,
.suspend = h3_panel_suspend,
......@@ -124,16 +124,4 @@ struct platform_driver h3_panel_driver = {
},
};
static int __init h3_panel_drv_init(void)
{
return platform_driver_register(&h3_panel_driver);
}
static void __exit h3_panel_drv_cleanup(void)
{
platform_driver_unregister(&h3_panel_driver);
}
module_init(h3_panel_drv_init);
module_exit(h3_panel_drv_cleanup);
module_platform_driver(h3_panel_driver);
......@@ -104,7 +104,7 @@ static int htcherald_panel_resume(struct platform_device *pdev)
return 0;
}
struct platform_driver htcherald_panel_driver = {
static struct platform_driver htcherald_panel_driver = {
.probe = htcherald_panel_probe,
.remove = htcherald_panel_remove,
.suspend = htcherald_panel_suspend,
......@@ -115,16 +115,4 @@ struct platform_driver htcherald_panel_driver = {
},
};
static int __init htcherald_panel_drv_init(void)
{
return platform_driver_register(&htcherald_panel_driver);
}
static void __exit htcherald_panel_drv_cleanup(void)
{
platform_driver_unregister(&htcherald_panel_driver);
}
module_init(htcherald_panel_drv_init);
module_exit(htcherald_panel_drv_cleanup);
module_platform_driver(htcherald_panel_driver);
......@@ -98,7 +98,7 @@ static int innovator1510_panel_resume(struct platform_device *pdev)
return 0;
}
struct platform_driver innovator1510_panel_driver = {
static struct platform_driver innovator1510_panel_driver = {
.probe = innovator1510_panel_probe,
.remove = innovator1510_panel_remove,
.suspend = innovator1510_panel_suspend,
......@@ -109,16 +109,4 @@ struct platform_driver innovator1510_panel_driver = {
},
};
static int __init innovator1510_panel_drv_init(void)
{
return platform_driver_register(&innovator1510_panel_driver);
}
static void __exit innovator1510_panel_drv_cleanup(void)
{
platform_driver_unregister(&innovator1510_panel_driver);
}
module_init(innovator1510_panel_drv_init);
module_exit(innovator1510_panel_drv_cleanup);
module_platform_driver(innovator1510_panel_driver);
......@@ -122,7 +122,7 @@ static int innovator1610_panel_resume(struct platform_device *pdev)
return 0;
}
struct platform_driver innovator1610_panel_driver = {
static struct platform_driver innovator1610_panel_driver = {
.probe = innovator1610_panel_probe,
.remove = innovator1610_panel_remove,
.suspend = innovator1610_panel_suspend,
......@@ -133,16 +133,4 @@ struct platform_driver innovator1610_panel_driver = {
},
};
static int __init innovator1610_panel_drv_init(void)
{
return platform_driver_register(&innovator1610_panel_driver);
}
static void __exit innovator1610_panel_drv_cleanup(void)
{
platform_driver_unregister(&innovator1610_panel_driver);
}
module_init(innovator1610_panel_drv_init);
module_exit(innovator1610_panel_drv_cleanup);
module_platform_driver(innovator1610_panel_driver);
......@@ -116,7 +116,7 @@ static int osk_panel_resume(struct platform_device *pdev)
return 0;
}
struct platform_driver osk_panel_driver = {
static struct platform_driver osk_panel_driver = {
.probe = osk_panel_probe,
.remove = osk_panel_remove,
.suspend = osk_panel_suspend,
......@@ -127,16 +127,4 @@ struct platform_driver osk_panel_driver = {
},
};
static int __init osk_panel_drv_init(void)
{
return platform_driver_register(&osk_panel_driver);
}
static void __exit osk_panel_drv_cleanup(void)
{
platform_driver_unregister(&osk_panel_driver);
}
module_init(osk_panel_drv_init);
module_exit(osk_panel_drv_cleanup);
module_platform_driver(osk_panel_driver);
......@@ -97,7 +97,7 @@ static int palmte_panel_resume(struct platform_device *pdev)
return 0;
}
struct platform_driver palmte_panel_driver = {
static struct platform_driver palmte_panel_driver = {
.probe = palmte_panel_probe,
.remove = palmte_panel_remove,
.suspend = palmte_panel_suspend,
......@@ -108,16 +108,4 @@ struct platform_driver palmte_panel_driver = {
},
};
static int __init palmte_panel_drv_init(void)
{
return platform_driver_register(&palmte_panel_driver);
}
static void __exit palmte_panel_drv_cleanup(void)
{
platform_driver_unregister(&palmte_panel_driver);
}
module_init(palmte_panel_drv_init);
module_exit(palmte_panel_drv_cleanup);
module_platform_driver(palmte_panel_driver);
......@@ -102,7 +102,7 @@ static int palmtt_panel_resume(struct platform_device *pdev)
return 0;
}
struct platform_driver palmtt_panel_driver = {
static struct platform_driver palmtt_panel_driver = {
.probe = palmtt_panel_probe,
.remove = palmtt_panel_remove,
.suspend = palmtt_panel_suspend,
......@@ -113,15 +113,4 @@ struct platform_driver palmtt_panel_driver = {
},
};
static int __init palmtt_panel_drv_init(void)
{
return platform_driver_register(&palmtt_panel_driver);
}
static void __exit palmtt_panel_drv_cleanup(void)
{
platform_driver_unregister(&palmtt_panel_driver);
}
module_init(palmtt_panel_drv_init);
module_exit(palmtt_panel_drv_cleanup);
module_platform_driver(palmtt_panel_driver);
......@@ -98,7 +98,7 @@ static int palmz71_panel_resume(struct platform_device *pdev)
return 0;
}
struct platform_driver palmz71_panel_driver = {
static struct platform_driver palmz71_panel_driver = {
.probe = palmz71_panel_probe,
.remove = palmz71_panel_remove,
.suspend = palmz71_panel_suspend,
......@@ -109,15 +109,4 @@ struct platform_driver palmz71_panel_driver = {
},
};
static int __init palmz71_panel_drv_init(void)
{
return platform_driver_register(&palmz71_panel_driver);
}
static void __exit palmz71_panel_drv_cleanup(void)
{
platform_driver_unregister(&palmz71_panel_driver);
}
module_init(palmz71_panel_drv_init);
module_exit(palmz71_panel_drv_cleanup);
module_platform_driver(palmz71_panel_driver);
......@@ -41,7 +41,7 @@ config PANEL_NEC_NL8048HL11_01B
config PANEL_PICODLP
tristate "TI PICO DLP mini-projector"
depends on OMAP2_DSS && I2C
depends on OMAP2_DSS_DPI && I2C
help
A mini-projector used in TI's SDP4430 and EVM boards
For more info please visit http://www.dlp.com/projector/
......
......@@ -297,6 +297,72 @@ static struct panel_config generic_dpi_panels[] = {
.name = "apollon",
},
/* FocalTech ETM070003DH6 */
{
{
.x_res = 800,
.y_res = 480,
.pixel_clock = 28000,
.hsw = 48,
.hfp = 40,
.hbp = 40,
.vsw = 3,
.vfp = 13,
.vbp = 29,
},
.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
OMAP_DSS_LCD_IHS,
.name = "focaltech_etm070003dh6",
},
/* Microtips Technologies - UMSH-8173MD */
{
{
.x_res = 800,
.y_res = 480,
.pixel_clock = 34560,
.hsw = 13,
.hfp = 101,
.hbp = 101,
.vsw = 23,
.vfp = 1,
.vbp = 1,
},
.acbi = 0x0,
.acb = 0x0,
.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC,
.power_on_delay = 0,
.power_off_delay = 0,
.name = "microtips_umsh_8173md",
},
/* OrtusTech COM43H4M10XTC */
{
{
.x_res = 480,
.y_res = 272,
.pixel_clock = 8000,
.hsw = 41,
.hfp = 8,
.hbp = 4,
.vsw = 10,
.vfp = 4,
.vbp = 2,
},
.config = OMAP_DSS_LCD_TFT,
.name = "ortustech_com43h4m10xtc",
},
};
struct panel_drv_data {
......
......@@ -163,50 +163,93 @@ static void nec_8048_panel_remove(struct omap_dss_device *dssdev)
kfree(necd);
}
static int nec_8048_panel_enable(struct omap_dss_device *dssdev)
static int nec_8048_panel_power_on(struct omap_dss_device *dssdev)
{
int r = 0;
int r;
struct nec_8048_data *necd = dev_get_drvdata(&dssdev->dev);
struct backlight_device *bl = necd->bl;
if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
return 0;
r = omapdss_dpi_display_enable(dssdev);
if (r)
goto err0;
if (dssdev->platform_enable) {
r = dssdev->platform_enable(dssdev);
if (r)
return r;
goto err1;
}
r = nec_8048_bl_update_status(bl);
if (r < 0)
dev_err(&dssdev->dev, "failed to set lcd brightness\n");
r = omapdss_dpi_display_enable(dssdev);
return 0;
err1:
omapdss_dpi_display_disable(dssdev);
err0:
return r;
}
static void nec_8048_panel_disable(struct omap_dss_device *dssdev)
static void nec_8048_panel_power_off(struct omap_dss_device *dssdev)
{
struct nec_8048_data *necd = dev_get_drvdata(&dssdev->dev);
struct backlight_device *bl = necd->bl;
omapdss_dpi_display_disable(dssdev);
if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
return;
bl->props.brightness = 0;
nec_8048_bl_update_status(bl);
if (dssdev->platform_disable)
dssdev->platform_disable(dssdev);
omapdss_dpi_display_disable(dssdev);
}
static int nec_8048_panel_enable(struct omap_dss_device *dssdev)
{
int r;
r = nec_8048_panel_power_on(dssdev);
if (r)
return r;
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return 0;
}
static void nec_8048_panel_disable(struct omap_dss_device *dssdev)
{
nec_8048_panel_power_off(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
}
static int nec_8048_panel_suspend(struct omap_dss_device *dssdev)
{
nec_8048_panel_disable(dssdev);
nec_8048_panel_power_off(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
return 0;
}
static int nec_8048_panel_resume(struct omap_dss_device *dssdev)
{
return nec_8048_panel_enable(dssdev);
int r;
r = nec_8048_panel_power_on(dssdev);
if (r)
return r;
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
return 0;
}
static int nec_8048_recommended_bpp(struct omap_dss_device *dssdev)
......
......@@ -198,12 +198,6 @@ struct taal_data {
bool te_enabled;
atomic_t do_update;
struct {
u16 x;
u16 y;
u16 w;
u16 h;
} update_region;
int channel;
struct delayed_work te_timeout_work;
......@@ -1188,6 +1182,10 @@ static int taal_power_on(struct omap_dss_device *dssdev)
if (r)
goto err;
r = dsi_enable_video_output(dssdev, td->channel);
if (r)
goto err;
td->enabled = 1;
if (!td->intro_printed) {
......@@ -1217,6 +1215,8 @@ static void taal_power_off(struct omap_dss_device *dssdev)
struct taal_data *td = dev_get_drvdata(&dssdev->dev);
int r;
dsi_disable_video_output(dssdev, td->channel);
r = taal_dcs_write_0(td, MIPI_DCS_SET_DISPLAY_OFF);
if (!r)
r = taal_sleep_in(td);
......@@ -1394,12 +1394,8 @@ static irqreturn_t taal_te_isr(int irq, void *data)
if (old) {
cancel_delayed_work(&td->te_timeout_work);
r = omap_dsi_update(dssdev, td->channel,
td->update_region.x,
td->update_region.y,
td->update_region.w,
td->update_region.h,
taal_framedone_cb, dssdev);
r = omap_dsi_update(dssdev, td->channel, taal_framedone_cb,
dssdev);
if (r)
goto err;
}
......@@ -1444,26 +1440,20 @@ static int taal_update(struct omap_dss_device *dssdev,
goto err;
}
r = omap_dsi_prepare_update(dssdev, &x, &y, &w, &h, true);
if (r)
goto err;
r = taal_set_update_window(td, x, y, w, h);
/* XXX no need to send this every frame, but dsi break if not done */
r = taal_set_update_window(td, 0, 0,
td->panel_config->timings.x_res,
td->panel_config->timings.y_res);
if (r)
goto err;
if (td->te_enabled && panel_data->use_ext_te) {
td->update_region.x = x;
td->update_region.y = y;
td->update_region.w = w;
td->update_region.h = h;
barrier();
schedule_delayed_work(&td->te_timeout_work,
msecs_to_jiffies(250));
atomic_set(&td->do_update, 1);
} else {
r = omap_dsi_update(dssdev, td->channel, x, y, w, h,
taal_framedone_cb, dssdev);
r = omap_dsi_update(dssdev, td->channel, taal_framedone_cb,
dssdev);
if (r)
goto err;
}
......
obj-$(CONFIG_OMAP2_DSS) += omapdss.o
omapdss-y := core.o dss.o dss_features.o dispc.o display.o manager.o overlay.o
omapdss-y := core.o dss.o dss_features.o dispc.o dispc_coefs.o display.o \
manager.o overlay.o apply.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/slab.h>
#include <linux/spinlock.h>
#include <linux/jiffies.h>
#include <video/omapdss.h>
#include "dss.h"
#include "dss_features.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;
enum omap_channel channel;
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;
};
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];
}
void dss_apply_init(void)
{
const int num_ovls = dss_feat_get_num_ovls();
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.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;
}
}
static bool ovl_manual_update(struct omap_overlay *ovl)
{
return ovl->manager->device->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
}
static bool mgr_manual_update(struct omap_overlay_manager *mgr)
{
return mgr->device->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
}
static int dss_check_settings_low(struct omap_overlay_manager *mgr,
struct omap_dss_device *dssdev, 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 (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, dssdev, mi, ois);
}
/*
* check manager and overlay settings using overlay_info from data->info
*/
static int dss_check_settings(struct omap_overlay_manager *mgr,
struct omap_dss_device *dssdev)
{
return dss_check_settings_low(mgr, dssdev, 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,
struct omap_dss_device *dssdev)
{
return dss_check_settings_low(mgr, dssdev, 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;
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)
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_ovls = omap_dss_get_num_overlays();
struct ovl_priv_data *op;
struct omap_overlay *ovl;
struct mgr_priv_data *mp;
int i;
for (i = 0; i < num_ovls; ++i) {
ovl = omap_dss_get_overlay(i);
op = get_ovl_priv(ovl);
if (!ovl->manager)
continue;
mp = get_mgr_priv(ovl->manager);
if (!mp->enabled)
continue;
if (!mp->updating)
continue;
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;
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);
wait_for_completion_timeout(&extra_updated_completion, t);
updating = extra_info_update_ongoing();
WARN_ON(updating);
}
int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr)
{
unsigned long timeout = msecs_to_jiffies(500);
struct mgr_priv_data *mp;
u32 irq;
int r;
int i;
struct omap_dss_device *dssdev = mgr->device;
if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
return 0;
if (mgr_manual_update(mgr))
return 0;
irq = dispc_mgr_get_vsync_irq(mgr->id);
mp = get_mgr_priv(mgr);
i = 0;
while (1) {
unsigned long flags;
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;
}
}
return r;
}
int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl)
{
unsigned long timeout = msecs_to_jiffies(500);
struct ovl_priv_data *op;
struct omap_dss_device *dssdev;
u32 irq;
int r;
int i;
if (!ovl->manager)
return 0;
dssdev = ovl->manager->device;
if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
return 0;
if (ovl_manual_update(ovl))
return 0;
irq = dispc_mgr_get_vsync_irq(ovl->manager->id);
op = get_ovl_priv(ovl);
i = 0;
while (1) {
unsigned long flags;
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;
}
}
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 ilace, replication;
struct mgr_priv_data *mp;
int r;
DSSDBGF("%d", ovl->id);
if (!op->enabled || !op->info_dirty)
return;
oi = &op->info;
replication = dss_use_replication(ovl->manager->device, oi->color_mode);
ilace = ovl->manager->device->type == OMAP_DISPLAY_TYPE_VENC;
r = dispc_ovl_setup(ovl->id, oi, ilace, replication);
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;
}
mp = get_mgr_priv(ovl->manager);
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;
DSSDBGF("%d", 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_channel_out(ovl->id, op->channel);
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;
DSSDBGF("%d", 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_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, mgr->device);
if (r) {
DSSERR("cannot write registers for manager %s: "
"illegal configuration\n", mgr->name);
continue;
}
dss_mgr_write_regs(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);
}
}
void dss_mgr_start_update(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, mgr->device);
if (r) {
DSSERR("cannot start manual update: illegal configuration\n");
spin_unlock_irqrestore(&data_lock, flags);
return;
}
dss_mgr_write_regs(mgr);
mp->updating = true;
if (!dss_data.irq_enabled && need_isr())
dss_register_vsync_isr();
dispc_mgr_enable(mgr->id, true);
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 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;
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 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;
bool was_updating;
mgr = omap_dss_get_overlay_manager(i);
mp = get_mgr_priv(mgr);
if (!mp->enabled)
continue;
was_updating = mp->updating;
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);
} else {
if (was_updating && !mp->updating)
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);
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;
}
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, mgr->device);
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);
struct omap_dss_device *dssdev;
u32 size, burst_size;
u32 fifo_low, fifo_high;
if (!op->enabled && !op->enabling)
return;
dssdev = ovl->manager->device;
size = dispc_ovl_get_fifo_size(ovl->id);
burst_size = dispc_ovl_get_burst_size(ovl->id);
switch (dssdev->type) {
case OMAP_DISPLAY_TYPE_DPI:
case OMAP_DISPLAY_TYPE_DBI:
case OMAP_DISPLAY_TYPE_SDI:
case OMAP_DISPLAY_TYPE_VENC:
case OMAP_DISPLAY_TYPE_HDMI:
default_get_overlay_fifo_thresholds(ovl->id, size,
burst_size, &fifo_low, &fifo_high);
break;
#ifdef CONFIG_OMAP2_DSS_DSI
case OMAP_DISPLAY_TYPE_DSI:
dsi_get_overlay_fifo_thresholds(ovl->id, size,
burst_size, &fifo_low, &fifo_high);
break;
#endif
default:
BUG();
}
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);
}
}
int dss_mgr_enable(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, mgr->device);
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;
spin_unlock_irqrestore(&data_lock, flags);
if (!mgr_manual_update(mgr))
dispc_mgr_enable(mgr->id, true);
out:
mutex_unlock(&apply_lock);
return 0;
err:
mp->enabled = false;
spin_unlock_irqrestore(&data_lock, flags);
mutex_unlock(&apply_lock);
return r;
}
void dss_mgr_disable(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;
if (!mgr_manual_update(mgr))
dispc_mgr_enable(mgr->id, false);
spin_lock_irqsave(&data_lock, flags);
mp->updating = false;
mp->enabled = false;
spin_unlock_irqrestore(&data_lock, flags);
out:
mutex_unlock(&apply_lock);
}
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;
}
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);
}
int dss_mgr_set_device(struct omap_overlay_manager *mgr,
struct omap_dss_device *dssdev)
{
int r;
mutex_lock(&apply_lock);
if (dssdev->manager) {
DSSERR("display '%s' already has a manager '%s'\n",
dssdev->name, dssdev->manager->name);
r = -EINVAL;
goto err;
}
if ((mgr->supported_displays & dssdev->type) == 0) {
DSSERR("display '%s' does not support manager '%s'\n",
dssdev->name, mgr->name);
r = -EINVAL;
goto err;
}
dssdev->manager = mgr;
mgr->device = dssdev;
mutex_unlock(&apply_lock);
return 0;
err:
mutex_unlock(&apply_lock);
return r;
}
int dss_mgr_unset_device(struct omap_overlay_manager *mgr)
{
int r;
mutex_lock(&apply_lock);
if (!mgr->device) {
DSSERR("failed to unset display, display not set.\n");
r = -EINVAL;
goto err;
}
/*
* Don't allow currently enabled displays to have the overlay manager
* pulled out from underneath them
*/
if (mgr->device->state != OMAP_DSS_DISPLAY_DISABLED) {
r = -EINVAL;
goto err;
}
mgr->device->manager = NULL;
mgr->device = NULL;
mutex_unlock(&apply_lock);
return 0;
err:
mutex_unlock(&apply_lock);
return r;
}
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;
}
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);
}
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;
}
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 err;
}
op->channel = mgr->id;
op->extra_info_dirty = true;
ovl->manager = mgr;
list_add_tail(&ovl->list, &mgr->overlays);
spin_unlock_irqrestore(&data_lock, flags);
/* XXX: When there is an overlay on a DSI manual update display, and
* the overlay is first disabled, then moved to tv, and enabled, we
* seem to get SYNC_LOST_DIGIT error.
*
* Waiting doesn't seem to help, but updating the manual update display
* after disabling the overlay seems to fix this. This hints that the
* overlay is perhaps somehow tied to the LCD output until the output
* is updated.
*
* Userspace workaround for this is to update the LCD after disabling
* the overlay, but before moving the overlay to TV.
*/
mutex_unlock(&apply_lock);
return 0;
err:
mutex_unlock(&apply_lock);
return r;
}
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;
}
op->channel = -1;
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;
}
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;
}
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->device == NULL) {
r = -EINVAL;
goto err1;
}
spin_lock_irqsave(&data_lock, flags);
op->enabling = true;
r = dss_check_settings(ovl->manager, ovl->manager->device);
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;
}
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->device == 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;
}
......@@ -178,6 +178,8 @@ static int omap_dss_probe(struct platform_device *pdev)
dss_features_init();
dss_apply_init();
dss_init_overlay_managers(pdev);
dss_init_overlays(pdev);
......
......@@ -64,22 +64,6 @@ struct omap_dispc_isr_data {
u32 mask;
};
struct dispc_h_coef {
s8 hc4;
s8 hc3;
u8 hc2;
s8 hc1;
s8 hc0;
};
struct dispc_v_coef {
s8 vc22;
s8 vc2;
u8 vc1;
s8 vc0;
s8 vc00;
};
enum omap_burst_size {
BURST_SIZE_X2 = 0,
BURST_SIZE_X4 = 1,
......@@ -438,6 +422,34 @@ static struct omap_dss_device *dispc_mgr_get_device(enum omap_channel channel)
return mgr ? mgr->device : NULL;
}
u32 dispc_mgr_get_vsync_irq(enum omap_channel channel)
{
switch (channel) {
case OMAP_DSS_CHANNEL_LCD:
return DISPC_IRQ_VSYNC;
case OMAP_DSS_CHANNEL_LCD2:
return DISPC_IRQ_VSYNC2;
case OMAP_DSS_CHANNEL_DIGIT:
return DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN;
default:
BUG();
}
}
u32 dispc_mgr_get_framedone_irq(enum omap_channel channel)
{
switch (channel) {
case OMAP_DSS_CHANNEL_LCD:
return DISPC_IRQ_FRAMEDONE;
case OMAP_DSS_CHANNEL_LCD2:
return DISPC_IRQ_FRAMEDONE2;
case OMAP_DSS_CHANNEL_DIGIT:
return 0;
default:
BUG();
}
}
bool dispc_mgr_go_busy(enum omap_channel channel)
{
int bit;
......@@ -533,105 +545,27 @@ static void dispc_ovl_write_firv2_reg(enum omap_plane plane, int reg, u32 value)
dispc_write_reg(DISPC_OVL_FIR_COEF_V2(plane, reg), value);
}
static void dispc_ovl_set_scale_coef(enum omap_plane plane, int hscaleup,
int vscaleup, int five_taps,
enum omap_color_component color_comp)
{
/* Coefficients for horizontal up-sampling */
static const struct dispc_h_coef coef_hup[8] = {
{ 0, 0, 128, 0, 0 },
{ -1, 13, 124, -8, 0 },
{ -2, 30, 112, -11, -1 },
{ -5, 51, 95, -11, -2 },
{ 0, -9, 73, 73, -9 },
{ -2, -11, 95, 51, -5 },
{ -1, -11, 112, 30, -2 },
{ 0, -8, 124, 13, -1 },
};
/* Coefficients for vertical up-sampling */
static const struct dispc_v_coef coef_vup_3tap[8] = {
{ 0, 0, 128, 0, 0 },
{ 0, 3, 123, 2, 0 },
{ 0, 12, 111, 5, 0 },
{ 0, 32, 89, 7, 0 },
{ 0, 0, 64, 64, 0 },
{ 0, 7, 89, 32, 0 },
{ 0, 5, 111, 12, 0 },
{ 0, 2, 123, 3, 0 },
};
static const struct dispc_v_coef coef_vup_5tap[8] = {
{ 0, 0, 128, 0, 0 },
{ -1, 13, 124, -8, 0 },
{ -2, 30, 112, -11, -1 },
{ -5, 51, 95, -11, -2 },
{ 0, -9, 73, 73, -9 },
{ -2, -11, 95, 51, -5 },
{ -1, -11, 112, 30, -2 },
{ 0, -8, 124, 13, -1 },
};
/* Coefficients for horizontal down-sampling */
static const struct dispc_h_coef coef_hdown[8] = {
{ 0, 36, 56, 36, 0 },
{ 4, 40, 55, 31, -2 },
{ 8, 44, 54, 27, -5 },
{ 12, 48, 53, 22, -7 },
{ -9, 17, 52, 51, 17 },
{ -7, 22, 53, 48, 12 },
{ -5, 27, 54, 44, 8 },
{ -2, 31, 55, 40, 4 },
};
/* Coefficients for vertical down-sampling */
static const struct dispc_v_coef coef_vdown_3tap[8] = {
{ 0, 36, 56, 36, 0 },
{ 0, 40, 57, 31, 0 },
{ 0, 45, 56, 27, 0 },
{ 0, 50, 55, 23, 0 },
{ 0, 18, 55, 55, 0 },
{ 0, 23, 55, 50, 0 },
{ 0, 27, 56, 45, 0 },
{ 0, 31, 57, 40, 0 },
};
static const struct dispc_v_coef coef_vdown_5tap[8] = {
{ 0, 36, 56, 36, 0 },
{ 4, 40, 55, 31, -2 },
{ 8, 44, 54, 27, -5 },
{ 12, 48, 53, 22, -7 },
{ -9, 17, 52, 51, 17 },
{ -7, 22, 53, 48, 12 },
{ -5, 27, 54, 44, 8 },
{ -2, 31, 55, 40, 4 },
};
const struct dispc_h_coef *h_coef;
const struct dispc_v_coef *v_coef;
static void dispc_ovl_set_scale_coef(enum omap_plane plane, int fir_hinc,
int fir_vinc, int five_taps,
enum omap_color_component color_comp)
{
const struct dispc_coef *h_coef, *v_coef;
int i;
if (hscaleup)
h_coef = coef_hup;
else
h_coef = coef_hdown;
if (vscaleup)
v_coef = five_taps ? coef_vup_5tap : coef_vup_3tap;
else
v_coef = five_taps ? coef_vdown_5tap : coef_vdown_3tap;
h_coef = dispc_ovl_get_scale_coef(fir_hinc, true);
v_coef = dispc_ovl_get_scale_coef(fir_vinc, five_taps);
for (i = 0; i < 8; i++) {
u32 h, hv;
h = FLD_VAL(h_coef[i].hc0, 7, 0)
| FLD_VAL(h_coef[i].hc1, 15, 8)
| FLD_VAL(h_coef[i].hc2, 23, 16)
| FLD_VAL(h_coef[i].hc3, 31, 24);
hv = FLD_VAL(h_coef[i].hc4, 7, 0)
| FLD_VAL(v_coef[i].vc0, 15, 8)
| FLD_VAL(v_coef[i].vc1, 23, 16)
| FLD_VAL(v_coef[i].vc2, 31, 24);
h = FLD_VAL(h_coef[i].hc0_vc00, 7, 0)
| FLD_VAL(h_coef[i].hc1_vc0, 15, 8)
| FLD_VAL(h_coef[i].hc2_vc1, 23, 16)
| FLD_VAL(h_coef[i].hc3_vc2, 31, 24);
hv = FLD_VAL(h_coef[i].hc4_vc22, 7, 0)
| FLD_VAL(v_coef[i].hc1_vc0, 15, 8)
| FLD_VAL(v_coef[i].hc2_vc1, 23, 16)
| FLD_VAL(v_coef[i].hc3_vc2, 31, 24);
if (color_comp == DISPC_COLOR_COMPONENT_RGB_Y) {
dispc_ovl_write_firh_reg(plane, i, h);
......@@ -646,8 +580,8 @@ static void dispc_ovl_set_scale_coef(enum omap_plane plane, int hscaleup,
if (five_taps) {
for (i = 0; i < 8; i++) {
u32 v;
v = FLD_VAL(v_coef[i].vc00, 7, 0)
| FLD_VAL(v_coef[i].vc22, 15, 8);
v = FLD_VAL(v_coef[i].hc0_vc00, 7, 0)
| FLD_VAL(v_coef[i].hc4_vc22, 15, 8);
if (color_comp == DISPC_COLOR_COMPONENT_RGB_Y)
dispc_ovl_write_firv_reg(plane, i, v);
else
......@@ -875,8 +809,7 @@ static void dispc_ovl_set_color_mode(enum omap_plane plane,
REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), m, 4, 1);
}
static void dispc_ovl_set_channel_out(enum omap_plane plane,
enum omap_channel channel)
void dispc_ovl_set_channel_out(enum omap_plane plane, enum omap_channel channel)
{
int shift;
u32 val;
......@@ -923,6 +856,39 @@ static void dispc_ovl_set_channel_out(enum omap_plane plane,
dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), val);
}
static enum omap_channel dispc_ovl_get_channel_out(enum omap_plane plane)
{
int shift;
u32 val;
enum omap_channel channel;
switch (plane) {
case OMAP_DSS_GFX:
shift = 8;
break;
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
case OMAP_DSS_VIDEO3:
shift = 16;
break;
default:
BUG();
}
val = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane));
if (dss_has_feature(FEAT_MGR_LCD2)) {
if (FLD_GET(val, 31, 30) == 0)
channel = FLD_GET(val, shift, shift);
else
channel = OMAP_DSS_CHANNEL_LCD2;
} else {
channel = FLD_GET(val, shift, shift);
}
return channel;
}
static void dispc_ovl_set_burst_size(enum omap_plane plane,
enum omap_burst_size burst_size)
{
......@@ -964,7 +930,7 @@ void dispc_enable_gamma_table(bool enable)
REG_FLD_MOD(DISPC_CONFIG, enable, 9, 9);
}
void dispc_mgr_enable_cpr(enum omap_channel channel, bool enable)
static void dispc_mgr_enable_cpr(enum omap_channel channel, bool enable)
{
u16 reg;
......@@ -978,7 +944,7 @@ void dispc_mgr_enable_cpr(enum omap_channel channel, bool enable)
REG_FLD_MOD(reg, enable, 15, 15);
}
void dispc_mgr_set_cpr_coef(enum omap_channel channel,
static void dispc_mgr_set_cpr_coef(enum omap_channel channel,
struct omap_dss_cpr_coefs *coefs)
{
u32 coef_r, coef_g, coef_b;
......@@ -1057,8 +1023,7 @@ u32 dispc_ovl_get_fifo_size(enum omap_plane plane)
return dispc.fifo_size[plane];
}
static void dispc_ovl_set_fifo_threshold(enum omap_plane plane, u32 low,
u32 high)
void dispc_ovl_set_fifo_threshold(enum omap_plane plane, u32 low, u32 high)
{
u8 hi_start, hi_end, lo_start, lo_end;
u32 unit;
......@@ -1169,17 +1134,12 @@ static void dispc_ovl_set_scale_param(enum omap_plane plane,
enum omap_color_component color_comp)
{
int fir_hinc, fir_vinc;
int hscaleup, vscaleup;
hscaleup = orig_width <= out_width;
vscaleup = orig_height <= out_height;
dispc_ovl_set_scale_coef(plane, hscaleup, vscaleup, five_taps,
color_comp);
fir_hinc = 1024 * orig_width / out_width;
fir_vinc = 1024 * orig_height / out_height;
dispc_ovl_set_scale_coef(plane, fir_hinc, fir_vinc, five_taps,
color_comp);
dispc_ovl_set_fir(plane, fir_hinc, fir_vinc, color_comp);
}
......@@ -1654,6 +1614,9 @@ static unsigned long calc_fclk_five_taps(enum omap_channel channel, u16 width,
u32 fclk = 0;
u64 tmp, pclk = dispc_mgr_pclk_rate(channel);
if (height <= out_height && width <= out_width)
return (unsigned long) pclk;
if (height > out_height) {
struct omap_dss_device *dssdev = dispc_mgr_get_device(channel);
unsigned int ppl = dssdev->panel.timings.x_res;
......@@ -1708,7 +1671,16 @@ static unsigned long calc_fclk(enum omap_channel channel, u16 width,
else
vf = 1;
return dispc_mgr_pclk_rate(channel) * vf * hf;
if (cpu_is_omap24xx()) {
if (vf > 1 && hf > 1)
return dispc_mgr_pclk_rate(channel) * 4;
else
return dispc_mgr_pclk_rate(channel) * 2;
} else if (cpu_is_omap34xx()) {
return dispc_mgr_pclk_rate(channel) * vf * hf;
} else {
return dispc_mgr_pclk_rate(channel) * hf;
}
}
static int dispc_ovl_calc_scaling(enum omap_plane plane,
......@@ -1718,6 +1690,8 @@ static int dispc_ovl_calc_scaling(enum omap_plane plane,
{
struct omap_overlay *ovl = omap_dss_get_overlay(plane);
const int maxdownscale = dss_feat_get_param_max(FEAT_PARAM_DOWNSCALE);
const int maxsinglelinewidth =
dss_feat_get_param_max(FEAT_PARAM_LINEWIDTH);
unsigned long fclk = 0;
if (width == out_width && height == out_height)
......@@ -1734,28 +1708,40 @@ static int dispc_ovl_calc_scaling(enum omap_plane plane,
out_height > height * 8)
return -EINVAL;
/* Must use 5-tap filter? */
*five_taps = height > out_height * 2;
if (!*five_taps) {
if (cpu_is_omap24xx()) {
if (width > maxsinglelinewidth)
DSSERR("Cannot scale max input width exceeded");
*five_taps = false;
fclk = calc_fclk(channel, width, height, out_width,
out_height);
} else if (cpu_is_omap34xx()) {
if (width > (maxsinglelinewidth * 2)) {
DSSERR("Cannot setup scaling");
DSSERR("width exceeds maximum width possible");
return -EINVAL;
}
fclk = calc_fclk_five_taps(channel, width, height, out_width,
out_height, color_mode);
if (width > maxsinglelinewidth) {
if (height > out_height && height < out_height * 2)
*five_taps = false;
else {
DSSERR("cannot setup scaling with five taps");
return -EINVAL;
}
}
if (!*five_taps)
fclk = calc_fclk(channel, width, height, out_width,
out_height);
} else {
if (width > maxsinglelinewidth) {
DSSERR("Cannot scale width exceeds max line width");
return -EINVAL;
}
fclk = calc_fclk(channel, width, height, out_width,
out_height);
/* Try 5-tap filter if 3-tap fclk is too high */
if (cpu_is_omap34xx() && height > out_height &&
fclk > dispc_fclk_rate())
*five_taps = true;
}
if (width > (2048 >> *five_taps)) {
DSSERR("failed to set up scaling, fclk too low\n");
return -EINVAL;
}
if (*five_taps)
fclk = calc_fclk_five_taps(channel, width, height,
out_width, out_height, color_mode);
DSSDBG("required fclk rate = %lu Hz\n", fclk);
DSSDBG("current fclk rate = %lu Hz\n", dispc_fclk_rate());
......@@ -1771,11 +1757,10 @@ static int dispc_ovl_calc_scaling(enum omap_plane plane,
}
int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi,
bool ilace, enum omap_channel channel, bool replication,
u32 fifo_low, u32 fifo_high)
bool ilace, bool replication)
{
struct omap_overlay *ovl = omap_dss_get_overlay(plane);
bool five_taps = false;
bool five_taps = true;
bool fieldmode = 0;
int r, cconv = 0;
unsigned offset0, offset1;
......@@ -1783,36 +1768,43 @@ int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi,
s32 pix_inc;
u16 frame_height = oi->height;
unsigned int field_offset = 0;
u16 outw, outh;
enum omap_channel channel;
channel = dispc_ovl_get_channel_out(plane);
DSSDBG("dispc_ovl_setup %d, pa %x, pa_uv %x, sw %d, %d,%d, %dx%d -> "
"%dx%d, cmode %x, rot %d, mir %d, ilace %d chan %d repl %d "
"fifo_low %d fifo high %d\n", plane, oi->paddr, oi->p_uv_addr,
"%dx%d, cmode %x, rot %d, mir %d, ilace %d chan %d repl %d\n",
plane, oi->paddr, oi->p_uv_addr,
oi->screen_width, oi->pos_x, oi->pos_y, oi->width, oi->height,
oi->out_width, oi->out_height, oi->color_mode, oi->rotation,
oi->mirror, ilace, channel, replication, fifo_low, fifo_high);
oi->mirror, ilace, channel, replication);
if (oi->paddr == 0)
return -EINVAL;
if (ilace && oi->height == oi->out_height)
outw = oi->out_width == 0 ? oi->width : oi->out_width;
outh = oi->out_height == 0 ? oi->height : oi->out_height;
if (ilace && oi->height == outh)
fieldmode = 1;
if (ilace) {
if (fieldmode)
oi->height /= 2;
oi->pos_y /= 2;
oi->out_height /= 2;
outh /= 2;
DSSDBG("adjusting for ilace: height %d, pos_y %d, "
"out_height %d\n",
oi->height, oi->pos_y, oi->out_height);
oi->height, oi->pos_y, outh);
}
if (!dss_feat_color_mode_supported(plane, oi->color_mode))
return -EINVAL;
r = dispc_ovl_calc_scaling(plane, channel, oi->width, oi->height,
oi->out_width, oi->out_height, oi->color_mode,
outw, outh, oi->color_mode,
&five_taps);
if (r)
return r;
......@@ -1830,10 +1822,10 @@ int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi,
* so the integer part must be added to the base address of the
* bottom field.
*/
if (!oi->height || oi->height == oi->out_height)
if (!oi->height || oi->height == outh)
field_offset = 0;
else
field_offset = oi->height / oi->out_height / 2;
field_offset = oi->height / outh / 2;
}
/* Fields are independent but interleaved in memory. */
......@@ -1869,7 +1861,7 @@ int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi,
dispc_ovl_set_pix_inc(plane, pix_inc);
DSSDBG("%d,%d %dx%d -> %dx%d\n", oi->pos_x, oi->pos_y, oi->width,
oi->height, oi->out_width, oi->out_height);
oi->height, outw, outh);
dispc_ovl_set_pos(plane, oi->pos_x, oi->pos_y);
......@@ -1877,10 +1869,10 @@ int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi,
if (ovl->caps & OMAP_DSS_OVL_CAP_SCALE) {
dispc_ovl_set_scaling(plane, oi->width, oi->height,
oi->out_width, oi->out_height,
outw, outh,
ilace, five_taps, fieldmode,
oi->color_mode, oi->rotation);
dispc_ovl_set_vid_size(plane, oi->out_width, oi->out_height);
dispc_ovl_set_vid_size(plane, outw, outh);
dispc_ovl_set_vid_color_conv(plane, cconv);
}
......@@ -1891,10 +1883,7 @@ int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi,
dispc_ovl_set_pre_mult_alpha(plane, oi->pre_mult_alpha);
dispc_ovl_setup_global_alpha(plane, oi->global_alpha);
dispc_ovl_set_channel_out(plane, channel);
dispc_ovl_enable_replication(plane, replication);
dispc_ovl_set_fifo_threshold(plane, fifo_low, fifo_high);
return 0;
}
......@@ -1916,10 +1905,14 @@ static void dispc_disable_isr(void *data, u32 mask)
static void _enable_lcd_out(enum omap_channel channel, bool enable)
{
if (channel == OMAP_DSS_CHANNEL_LCD2)
if (channel == OMAP_DSS_CHANNEL_LCD2) {
REG_FLD_MOD(DISPC_CONTROL2, enable ? 1 : 0, 0, 0);
else
/* flush posted write */
dispc_read_reg(DISPC_CONTROL2);
} else {
REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 0, 0);
dispc_read_reg(DISPC_CONTROL);
}
}
static void dispc_mgr_enable_lcd_out(enum omap_channel channel, bool enable)
......@@ -1967,6 +1960,8 @@ static void dispc_mgr_enable_lcd_out(enum omap_channel channel, bool enable)
static void _enable_digit_out(bool enable)
{
REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 1, 1);
/* flush posted write */
dispc_read_reg(DISPC_CONTROL);
}
static void dispc_mgr_enable_digit_out(bool enable)
......@@ -2124,25 +2119,12 @@ void dispc_set_loadmode(enum omap_dss_load_mode mode)
}
void dispc_mgr_set_default_color(enum omap_channel channel, u32 color)
static void dispc_mgr_set_default_color(enum omap_channel channel, u32 color)
{
dispc_write_reg(DISPC_DEFAULT_COLOR(channel), color);
}
u32 dispc_mgr_get_default_color(enum omap_channel channel)
{
u32 l;
BUG_ON(channel != OMAP_DSS_CHANNEL_DIGIT &&
channel != OMAP_DSS_CHANNEL_LCD &&
channel != OMAP_DSS_CHANNEL_LCD2);
l = dispc_read_reg(DISPC_DEFAULT_COLOR(channel));
return l;
}
void dispc_mgr_set_trans_key(enum omap_channel ch,
static void dispc_mgr_set_trans_key(enum omap_channel ch,
enum omap_dss_trans_key_type type,
u32 trans_key)
{
......@@ -2156,26 +2138,7 @@ void dispc_mgr_set_trans_key(enum omap_channel ch,
dispc_write_reg(DISPC_TRANS_COLOR(ch), trans_key);
}
void dispc_mgr_get_trans_key(enum omap_channel ch,
enum omap_dss_trans_key_type *type,
u32 *trans_key)
{
if (type) {
if (ch == OMAP_DSS_CHANNEL_LCD)
*type = REG_GET(DISPC_CONFIG, 11, 11);
else if (ch == OMAP_DSS_CHANNEL_DIGIT)
*type = REG_GET(DISPC_CONFIG, 13, 13);
else if (ch == OMAP_DSS_CHANNEL_LCD2)
*type = REG_GET(DISPC_CONFIG2, 11, 11);
else
BUG();
}
if (trans_key)
*trans_key = dispc_read_reg(DISPC_TRANS_COLOR(ch));
}
void dispc_mgr_enable_trans_key(enum omap_channel ch, bool enable)
static void dispc_mgr_enable_trans_key(enum omap_channel ch, bool enable)
{
if (ch == OMAP_DSS_CHANNEL_LCD)
REG_FLD_MOD(DISPC_CONFIG, enable, 10, 10);
......@@ -2185,7 +2148,8 @@ void dispc_mgr_enable_trans_key(enum omap_channel ch, bool enable)
REG_FLD_MOD(DISPC_CONFIG2, enable, 10, 10);
}
void dispc_mgr_enable_alpha_fixed_zorder(enum omap_channel ch, bool enable)
static void dispc_mgr_enable_alpha_fixed_zorder(enum omap_channel ch,
bool enable)
{
if (!dss_has_feature(FEAT_ALPHA_FIXED_ZORDER))
return;
......@@ -2196,40 +2160,20 @@ void dispc_mgr_enable_alpha_fixed_zorder(enum omap_channel ch, bool enable)
REG_FLD_MOD(DISPC_CONFIG, enable, 19, 19);
}
bool dispc_mgr_alpha_fixed_zorder_enabled(enum omap_channel ch)
{
bool enabled;
if (!dss_has_feature(FEAT_ALPHA_FIXED_ZORDER))
return false;
if (ch == OMAP_DSS_CHANNEL_LCD)
enabled = REG_GET(DISPC_CONFIG, 18, 18);
else if (ch == OMAP_DSS_CHANNEL_DIGIT)
enabled = REG_GET(DISPC_CONFIG, 19, 19);
else
BUG();
return enabled;
}
bool dispc_mgr_trans_key_enabled(enum omap_channel ch)
void dispc_mgr_setup(enum omap_channel channel,
struct omap_overlay_manager_info *info)
{
bool enabled;
if (ch == OMAP_DSS_CHANNEL_LCD)
enabled = REG_GET(DISPC_CONFIG, 10, 10);
else if (ch == OMAP_DSS_CHANNEL_DIGIT)
enabled = REG_GET(DISPC_CONFIG, 12, 12);
else if (ch == OMAP_DSS_CHANNEL_LCD2)
enabled = REG_GET(DISPC_CONFIG2, 10, 10);
else
BUG();
return enabled;
dispc_mgr_set_default_color(channel, info->default_color);
dispc_mgr_set_trans_key(channel, info->trans_key_type, info->trans_key);
dispc_mgr_enable_trans_key(channel, info->trans_enabled);
dispc_mgr_enable_alpha_fixed_zorder(channel,
info->partial_alpha_enabled);
if (dss_has_feature(FEAT_CPR)) {
dispc_mgr_enable_cpr(channel, info->cpr_enable);
dispc_mgr_set_cpr_coef(channel, &info->cpr_coefs);
}
}
void dispc_mgr_set_tft_data_lines(enum omap_channel channel, u8 data_lines)
{
int code;
......@@ -3184,7 +3128,8 @@ static void dispc_error_worker(struct work_struct *work)
for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
struct omap_overlay_manager *mgr;
mgr = omap_dss_get_overlay_manager(i);
mgr->device->driver->disable(mgr->device);
if (mgr->device && mgr->device->driver)
mgr->device->driver->disable(mgr->device);
}
}
......
......@@ -97,6 +97,17 @@
#define DISPC_OVL_PRELOAD(n) (DISPC_OVL_BASE(n) + \
DISPC_PRELOAD_OFFSET(n))
/* DISPC up/downsampling FIR filter coefficient structure */
struct dispc_coef {
s8 hc4_vc22;
s8 hc3_vc2;
u8 hc2_vc1;
s8 hc1_vc0;
s8 hc0_vc00;
};
const struct dispc_coef *dispc_ovl_get_scale_coef(int inc, int five_taps);
/* DISPC manager/channel specific registers */
static inline u16 DISPC_DEFAULT_COLOR(enum omap_channel channel)
{
......
/*
* linux/drivers/video/omap2/dss/dispc_coefs.c
*
* Copyright (C) 2011 Texas Instruments
* Author: Chandrabhanu Mahapatra <cmahapatra@ti.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/kernel.h>
#include <video/omapdss.h>
#include "dispc.h"
#define ARRAY_LEN(array) (sizeof(array) / sizeof(array[0]))
static const struct dispc_coef coef3_M8[8] = {
{ 0, 0, 128, 0, 0 },
{ 0, -4, 123, 9, 0 },
{ 0, -4, 108, 87, 0 },
{ 0, -2, 87, 43, 0 },
{ 0, 64, 64, 0, 0 },
{ 0, 43, 87, -2, 0 },
{ 0, 24, 108, -4, 0 },
{ 0, 9, 123, -4, 0 },
};
static const struct dispc_coef coef3_M9[8] = {
{ 0, 6, 116, 6, 0 },
{ 0, 0, 112, 16, 0 },
{ 0, -2, 100, 30, 0 },
{ 0, -2, 83, 47, 0 },
{ 0, 64, 64, 0, 0 },
{ 0, 47, 83, -2, 0 },
{ 0, 30, 100, -2, 0 },
{ 0, 16, 112, 0, 0 },
};
static const struct dispc_coef coef3_M10[8] = {
{ 0, 10, 108, 10, 0 },
{ 0, 3, 104, 21, 0 },
{ 0, 0, 94, 34, 0 },
{ 0, -1, 80, 49, 0 },
{ 0, 64, 64, 0, 0 },
{ 0, 49, 80, -1, 0 },
{ 0, 34, 94, 0, 0 },
{ 0, 21, 104, 3, 0 },
};
static const struct dispc_coef coef3_M11[8] = {
{ 0, 14, 100, 14, 0 },
{ 0, 6, 98, 24, 0 },
{ 0, 2, 90, 36, 0 },
{ 0, 0, 78, 50, 0 },
{ 0, 64, 64, 0, 0 },
{ 0, 50, 78, 0, 0 },
{ 0, 36, 90, 2, 0 },
{ 0, 24, 98, 6, 0 },
};
static const struct dispc_coef coef3_M12[8] = {
{ 0, 16, 96, 16, 0 },
{ 0, 9, 93, 26, 0 },
{ 0, 4, 86, 38, 0 },
{ 0, 1, 76, 51, 0 },
{ 0, 64, 64, 0, 0 },
{ 0, 51, 76, 1, 0 },
{ 0, 38, 86, 4, 0 },
{ 0, 26, 93, 9, 0 },
};
static const struct dispc_coef coef3_M13[8] = {
{ 0, 18, 92, 18, 0 },
{ 0, 10, 90, 28, 0 },
{ 0, 5, 83, 40, 0 },
{ 0, 1, 75, 52, 0 },
{ 0, 64, 64, 0, 0 },
{ 0, 52, 75, 1, 0 },
{ 0, 40, 83, 5, 0 },
{ 0, 28, 90, 10, 0 },
};
static const struct dispc_coef coef3_M14[8] = {
{ 0, 20, 88, 20, 0 },
{ 0, 12, 86, 30, 0 },
{ 0, 6, 81, 41, 0 },
{ 0, 2, 74, 52, 0 },
{ 0, 64, 64, 0, 0 },
{ 0, 52, 74, 2, 0 },
{ 0, 41, 81, 6, 0 },
{ 0, 30, 86, 12, 0 },
};
static const struct dispc_coef coef3_M16[8] = {
{ 0, 22, 84, 22, 0 },
{ 0, 14, 82, 32, 0 },
{ 0, 8, 78, 42, 0 },
{ 0, 3, 72, 53, 0 },
{ 0, 64, 64, 0, 0 },
{ 0, 53, 72, 3, 0 },
{ 0, 42, 78, 8, 0 },
{ 0, 32, 82, 14, 0 },
};
static const struct dispc_coef coef3_M19[8] = {
{ 0, 24, 80, 24, 0 },
{ 0, 16, 79, 33, 0 },
{ 0, 9, 76, 43, 0 },
{ 0, 4, 70, 54, 0 },
{ 0, 64, 64, 0, 0 },
{ 0, 54, 70, 4, 0 },
{ 0, 43, 76, 9, 0 },
{ 0, 33, 79, 16, 0 },
};
static const struct dispc_coef coef3_M22[8] = {
{ 0, 25, 78, 25, 0 },
{ 0, 17, 77, 34, 0 },
{ 0, 10, 74, 44, 0 },
{ 0, 5, 69, 54, 0 },
{ 0, 64, 64, 0, 0 },
{ 0, 54, 69, 5, 0 },
{ 0, 44, 74, 10, 0 },
{ 0, 34, 77, 17, 0 },
};
static const struct dispc_coef coef3_M26[8] = {
{ 0, 26, 76, 26, 0 },
{ 0, 19, 74, 35, 0 },
{ 0, 11, 72, 45, 0 },
{ 0, 5, 69, 54, 0 },
{ 0, 64, 64, 0, 0 },
{ 0, 54, 69, 5, 0 },
{ 0, 45, 72, 11, 0 },
{ 0, 35, 74, 19, 0 },
};
static const struct dispc_coef coef3_M32[8] = {
{ 0, 27, 74, 27, 0 },
{ 0, 19, 73, 36, 0 },
{ 0, 12, 71, 45, 0 },
{ 0, 6, 68, 54, 0 },
{ 0, 64, 64, 0, 0 },
{ 0, 54, 68, 6, 0 },
{ 0, 45, 71, 12, 0 },
{ 0, 36, 73, 19, 0 },
};
static const struct dispc_coef coef5_M8[8] = {
{ 0, 0, 128, 0, 0 },
{ -2, 14, 125, -10, 1 },
{ -6, 33, 114, -15, 2 },
{ -10, 55, 98, -16, 1 },
{ 0, -14, 78, 78, -14 },
{ 1, -16, 98, 55, -10 },
{ 2, -15, 114, 33, -6 },
{ 1, -10, 125, 14, -2 },
};
static const struct dispc_coef coef5_M9[8] = {
{ -3, 10, 114, 10, -3 },
{ -6, 24, 110, 0, -1 },
{ -8, 40, 103, -7, 0 },
{ -11, 58, 91, -11, 1 },
{ 0, -12, 76, 76, -12 },
{ 1, -11, 91, 58, -11 },
{ 0, -7, 103, 40, -8 },
{ -1, 0, 111, 24, -6 },
};
static const struct dispc_coef coef5_M10[8] = {
{ -4, 18, 100, 18, -4 },
{ -6, 30, 99, 8, -3 },
{ -8, 44, 93, 0, -1 },
{ -9, 58, 84, -5, 0 },
{ 0, -8, 72, 72, -8 },
{ 0, -5, 84, 58, -9 },
{ -1, 0, 93, 44, -8 },
{ -3, 8, 99, 30, -6 },
};
static const struct dispc_coef coef5_M11[8] = {
{ -5, 23, 92, 23, -5 },
{ -6, 34, 90, 13, -3 },
{ -6, 45, 85, 6, -2 },
{ -6, 57, 78, 0, -1 },
{ 0, -4, 68, 68, -4 },
{ -1, 0, 78, 57, -6 },
{ -2, 6, 85, 45, -6 },
{ -3, 13, 90, 34, -6 },
};
static const struct dispc_coef coef5_M12[8] = {
{ -4, 26, 84, 26, -4 },
{ -5, 36, 82, 18, -3 },
{ -4, 46, 78, 10, -2 },
{ -3, 55, 72, 5, -1 },
{ 0, 0, 64, 64, 0 },
{ -1, 5, 72, 55, -3 },
{ -2, 10, 78, 46, -4 },
{ -3, 18, 82, 36, -5 },
};
static const struct dispc_coef coef5_M13[8] = {
{ -3, 28, 78, 28, -3 },
{ -3, 37, 76, 21, -3 },
{ -2, 45, 73, 14, -2 },
{ 0, 53, 68, 8, -1 },
{ 0, 3, 61, 61, 3 },
{ -1, 8, 68, 53, 0 },
{ -2, 14, 73, 45, -2 },
{ -3, 21, 76, 37, -3 },
};
static const struct dispc_coef coef5_M14[8] = {
{ -2, 30, 72, 30, -2 },
{ -1, 37, 71, 23, -2 },
{ 0, 45, 69, 16, -2 },
{ 3, 52, 64, 10, -1 },
{ 0, 6, 58, 58, 6 },
{ -1, 10, 64, 52, 3 },
{ -2, 16, 69, 45, 0 },
{ -2, 23, 71, 37, -1 },
};
static const struct dispc_coef coef5_M16[8] = {
{ 0, 31, 66, 31, 0 },
{ 1, 38, 65, 25, -1 },
{ 3, 44, 62, 20, -1 },
{ 6, 49, 59, 14, 0 },
{ 0, 10, 54, 54, 10 },
{ 0, 14, 59, 49, 6 },
{ -1, 20, 62, 44, 3 },
{ -1, 25, 65, 38, 1 },
};
static const struct dispc_coef coef5_M19[8] = {
{ 3, 32, 58, 32, 3 },
{ 4, 38, 58, 27, 1 },
{ 7, 42, 55, 23, 1 },
{ 10, 46, 54, 18, 0 },
{ 0, 14, 50, 50, 14 },
{ 0, 18, 54, 46, 10 },
{ 1, 23, 55, 42, 7 },
{ 1, 27, 58, 38, 4 },
};
static const struct dispc_coef coef5_M22[8] = {
{ 4, 33, 54, 33, 4 },
{ 6, 37, 54, 28, 3 },
{ 9, 41, 53, 24, 1 },
{ 12, 45, 51, 20, 0 },
{ 0, 16, 48, 48, 16 },
{ 0, 20, 51, 45, 12 },
{ 1, 24, 53, 41, 9 },
{ 3, 28, 54, 37, 6 },
};
static const struct dispc_coef coef5_M26[8] = {
{ 6, 33, 50, 33, 6 },
{ 8, 36, 51, 29, 4 },
{ 11, 40, 50, 25, 2 },
{ 14, 43, 48, 22, 1 },
{ 0, 18, 46, 46, 18 },
{ 1, 22, 48, 43, 14 },
{ 2, 25, 50, 40, 11 },
{ 4, 29, 51, 36, 8 },
};
static const struct dispc_coef coef5_M32[8] = {
{ 7, 33, 48, 33, 7 },
{ 10, 36, 48, 29, 5 },
{ 13, 39, 47, 26, 3 },
{ 16, 42, 46, 23, 1 },
{ 0, 19, 45, 45, 19 },
{ 1, 23, 46, 42, 16 },
{ 3, 26, 47, 39, 13 },
{ 5, 29, 48, 36, 10 },
};
const struct dispc_coef *dispc_ovl_get_scale_coef(int inc, int five_taps)
{
int i;
static const struct {
int Mmin;
int Mmax;
const struct dispc_coef *coef_3;
const struct dispc_coef *coef_5;
} coefs[] = {
{ 27, 32, coef3_M32, coef5_M32 },
{ 23, 26, coef3_M26, coef5_M26 },
{ 20, 22, coef3_M22, coef5_M22 },
{ 17, 19, coef3_M19, coef5_M19 },
{ 15, 16, coef3_M16, coef5_M16 },
{ 14, 14, coef3_M14, coef5_M14 },
{ 13, 13, coef3_M13, coef5_M13 },
{ 12, 12, coef3_M12, coef5_M12 },
{ 11, 11, coef3_M11, coef5_M11 },
{ 10, 10, coef3_M10, coef5_M10 },
{ 9, 9, coef3_M9, coef5_M9 },
{ 4, 8, coef3_M8, coef5_M8 },
/*
* When upscaling more than two times, blockiness and outlines
* around the image are observed when M8 tables are used. M11,
* M16 and M19 tables are used to prevent this.
*/
{ 3, 3, coef3_M11, coef5_M11 },
{ 2, 2, coef3_M16, coef5_M16 },
{ 0, 1, coef3_M19, coef5_M19 },
};
inc /= 128;
for (i = 0; i < ARRAY_LEN(coefs); ++i)
if (inc >= coefs[i].Mmin && inc <= coefs[i].Mmax)
return five_taps ? coefs[i].coef_5 : coefs[i].coef_3;
return NULL;
}
......@@ -223,10 +223,13 @@ int omapdss_dpi_display_enable(struct omap_dss_device *dssdev)
mdelay(2);
dssdev->manager->enable(dssdev->manager);
r = dss_mgr_enable(dssdev->manager);
if (r)
goto err_mgr_enable;
return 0;
err_mgr_enable:
err_set_mode:
if (dpi_use_dsi_pll(dssdev))
dsi_pll_uninit(dpi.dsidev, true);
......@@ -249,7 +252,7 @@ EXPORT_SYMBOL(omapdss_dpi_display_enable);
void omapdss_dpi_display_disable(struct omap_dss_device *dssdev)
{
dssdev->manager->disable(dssdev->manager);
dss_mgr_disable(dssdev->manager);
if (dpi_use_dsi_pll(dssdev)) {
dss_select_dispc_clk_source(OMAP_DSS_CLK_SRC_FCK);
......
......@@ -203,6 +203,21 @@ struct dsi_reg { u16 idx; };
typedef void (*omap_dsi_isr_t) (void *arg, u32 mask);
#define DSI_MAX_NR_ISRS 2
#define DSI_MAX_NR_LANES 5
enum dsi_lane_function {
DSI_LANE_UNUSED = 0,
DSI_LANE_CLK,
DSI_LANE_DATA1,
DSI_LANE_DATA2,
DSI_LANE_DATA3,
DSI_LANE_DATA4,
};
struct dsi_lane_config {
enum dsi_lane_function function;
u8 polarity;
};
struct dsi_isr_data {
omap_dsi_isr_t isr;
......@@ -223,24 +238,6 @@ enum dsi_vc_source {
DSI_VC_SOURCE_VP,
};
enum dsi_lane {
DSI_CLK_P = 1 << 0,
DSI_CLK_N = 1 << 1,
DSI_DATA1_P = 1 << 2,
DSI_DATA1_N = 1 << 3,
DSI_DATA2_P = 1 << 4,
DSI_DATA2_N = 1 << 5,
DSI_DATA3_P = 1 << 6,
DSI_DATA3_N = 1 << 7,
DSI_DATA4_P = 1 << 8,
DSI_DATA4_N = 1 << 9,
};
struct dsi_update_region {
u16 x, y, w, h;
struct omap_dss_device *device;
};
struct dsi_irq_stats {
unsigned long last_reset;
unsigned irq_count;
......@@ -290,7 +287,9 @@ struct dsi_data {
struct dsi_isr_tables isr_tables_copy;
int update_channel;
struct dsi_update_region update_region;
#ifdef DEBUG
unsigned update_bytes;
#endif
bool te_enabled;
bool ulps_enabled;
......@@ -327,7 +326,10 @@ struct dsi_data {
unsigned long fint_min, fint_max;
unsigned long lpdiv_max;
int num_data_lanes;
unsigned num_lanes_supported;
struct dsi_lane_config lanes[DSI_MAX_NR_LANES];
unsigned num_lanes_used;
unsigned scp_clk_refcount;
};
......@@ -413,14 +415,29 @@ static void dsi_completion_handler(void *data, u32 mask)
static inline int wait_for_bit_change(struct platform_device *dsidev,
const struct dsi_reg idx, int bitnum, int value)
{
int t = 100000;
unsigned long timeout;
ktime_t wait;
int t;
while (REG_GET(dsidev, idx, bitnum, bitnum) != value) {
if (--t == 0)
return !value;
/* first busyloop to see if the bit changes right away */
t = 100;
while (t-- > 0) {
if (REG_GET(dsidev, idx, bitnum, bitnum) == value)
return value;
}
return value;
/* then loop for 500ms, sleeping for 1ms in between */
timeout = jiffies + msecs_to_jiffies(500);
while (time_before(jiffies, timeout)) {
if (REG_GET(dsidev, idx, bitnum, bitnum) == value)
return value;
wait = ns_to_ktime(1000 * 1000);
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_hrtimeout(&wait, HRTIMER_MODE_REL);
}
return !value;
}
u8 dsi_get_pixel_size(enum omap_dss_dsi_pixel_format fmt)
......@@ -454,7 +471,6 @@ static void dsi_perf_mark_start(struct platform_device *dsidev)
static void dsi_perf_show(struct platform_device *dsidev, const char *name)
{
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
struct omap_dss_device *dssdev = dsi->update_region.device;
ktime_t t, setup_time, trans_time;
u32 total_bytes;
u32 setup_us, trans_us, total_us;
......@@ -476,9 +492,7 @@ static void dsi_perf_show(struct platform_device *dsidev, const char *name)
total_us = setup_us + trans_us;
total_bytes = dsi->update_region.w *
dsi->update_region.h *
dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt) / 8;
total_bytes = dsi->update_bytes;
printk(KERN_INFO "DSI(%s): %u us + %u us = %u us (%uHz), "
"%u bytes, %u kbytes/sec\n",
......@@ -1720,17 +1734,19 @@ static void dsi_dump_dsidev_clocks(struct platform_device *dsidev,
seq_printf(s, "CLKIN4DDR\t%-16luregm %u\n",
cinfo->clkin4ddr, cinfo->regm);
seq_printf(s, "%s (%s)\t%-16luregm_dispc %u\t(%s)\n",
dss_get_generic_clk_source_name(dispc_clk_src),
dss_feat_get_clk_source_name(dispc_clk_src),
seq_printf(s, "DSI_PLL_HSDIV_DISPC (%s)\t%-16luregm_dispc %u\t(%s)\n",
dss_feat_get_clk_source_name(dsi_module == 0 ?
OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC :
OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC),
cinfo->dsi_pll_hsdiv_dispc_clk,
cinfo->regm_dispc,
dispc_clk_src == OMAP_DSS_CLK_SRC_FCK ?
"off" : "on");
seq_printf(s, "%s (%s)\t%-16luregm_dsi %u\t(%s)\n",
dss_get_generic_clk_source_name(dsi_clk_src),
dss_feat_get_clk_source_name(dsi_clk_src),
seq_printf(s, "DSI_PLL_HSDIV_DSI (%s)\t%-16luregm_dsi %u\t(%s)\n",
dss_feat_get_clk_source_name(dsi_module == 0 ?
OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI :
OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DSI),
cinfo->dsi_pll_hsdiv_dsi_clk,
cinfo->regm_dsi,
dsi_clk_src == OMAP_DSS_CLK_SRC_FCK ?
......@@ -2029,34 +2045,6 @@ static int dsi_cio_power(struct platform_device *dsidev,
return 0;
}
/* Number of data lanes present on DSI interface */
static inline int dsi_get_num_data_lanes(struct platform_device *dsidev)
{
/* DSI on OMAP3 doesn't have register DSI_GNQ, set number
* of data lanes as 2 by default */
if (dss_has_feature(FEAT_DSI_GNQ))
return REG_GET(dsidev, DSI_GNQ, 11, 9); /* NB_DATA_LANES */
else
return 2;
}
/* Number of data lanes used by the dss device */
static inline int dsi_get_num_data_lanes_dssdev(struct omap_dss_device *dssdev)
{
int num_data_lanes = 0;
if (dssdev->phy.dsi.data1_lane != 0)
num_data_lanes++;
if (dssdev->phy.dsi.data2_lane != 0)
num_data_lanes++;
if (dssdev->phy.dsi.data3_lane != 0)
num_data_lanes++;
if (dssdev->phy.dsi.data4_lane != 0)
num_data_lanes++;
return num_data_lanes;
}
static unsigned dsi_get_line_buf_size(struct platform_device *dsidev)
{
int val;
......@@ -2088,59 +2076,112 @@ static unsigned dsi_get_line_buf_size(struct platform_device *dsidev)
}
}
static void dsi_set_lane_config(struct omap_dss_device *dssdev)
static int dsi_parse_lane_config(struct omap_dss_device *dssdev)
{
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
u32 r;
int num_data_lanes_dssdev = dsi_get_num_data_lanes_dssdev(dssdev);
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
u8 lanes[DSI_MAX_NR_LANES];
u8 polarities[DSI_MAX_NR_LANES];
int num_lanes, i;
static const enum dsi_lane_function functions[] = {
DSI_LANE_CLK,
DSI_LANE_DATA1,
DSI_LANE_DATA2,
DSI_LANE_DATA3,
DSI_LANE_DATA4,
};
lanes[0] = dssdev->phy.dsi.clk_lane;
lanes[1] = dssdev->phy.dsi.data1_lane;
lanes[2] = dssdev->phy.dsi.data2_lane;
lanes[3] = dssdev->phy.dsi.data3_lane;
lanes[4] = dssdev->phy.dsi.data4_lane;
polarities[0] = dssdev->phy.dsi.clk_pol;
polarities[1] = dssdev->phy.dsi.data1_pol;
polarities[2] = dssdev->phy.dsi.data2_pol;
polarities[3] = dssdev->phy.dsi.data3_pol;
polarities[4] = dssdev->phy.dsi.data4_pol;
int clk_lane = dssdev->phy.dsi.clk_lane;
int data1_lane = dssdev->phy.dsi.data1_lane;
int data2_lane = dssdev->phy.dsi.data2_lane;
int clk_pol = dssdev->phy.dsi.clk_pol;
int data1_pol = dssdev->phy.dsi.data1_pol;
int data2_pol = dssdev->phy.dsi.data2_pol;
num_lanes = 0;
for (i = 0; i < dsi->num_lanes_supported; ++i)
dsi->lanes[i].function = DSI_LANE_UNUSED;
for (i = 0; i < dsi->num_lanes_supported; ++i) {
int num;
if (lanes[i] == DSI_LANE_UNUSED)
break;
num = lanes[i] - 1;
if (num >= dsi->num_lanes_supported)
return -EINVAL;
if (dsi->lanes[num].function != DSI_LANE_UNUSED)
return -EINVAL;
dsi->lanes[num].function = functions[i];
dsi->lanes[num].polarity = polarities[i];
num_lanes++;
}
if (num_lanes < 2 || num_lanes > dsi->num_lanes_supported)
return -EINVAL;
dsi->num_lanes_used = num_lanes;
return 0;
}
static int dsi_set_lane_config(struct omap_dss_device *dssdev)
{
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
static const u8 offsets[] = { 0, 4, 8, 12, 16 };
static const enum dsi_lane_function functions[] = {
DSI_LANE_CLK,
DSI_LANE_DATA1,
DSI_LANE_DATA2,
DSI_LANE_DATA3,
DSI_LANE_DATA4,
};
u32 r;
int i;
r = dsi_read_reg(dsidev, DSI_COMPLEXIO_CFG1);
r = FLD_MOD(r, clk_lane, 2, 0);
r = FLD_MOD(r, clk_pol, 3, 3);
r = FLD_MOD(r, data1_lane, 6, 4);
r = FLD_MOD(r, data1_pol, 7, 7);
r = FLD_MOD(r, data2_lane, 10, 8);
r = FLD_MOD(r, data2_pol, 11, 11);
if (num_data_lanes_dssdev > 2) {
int data3_lane = dssdev->phy.dsi.data3_lane;
int data3_pol = dssdev->phy.dsi.data3_pol;
r = FLD_MOD(r, data3_lane, 14, 12);
r = FLD_MOD(r, data3_pol, 15, 15);
for (i = 0; i < dsi->num_lanes_used; ++i) {
unsigned offset = offsets[i];
unsigned polarity, lane_number;
unsigned t;
for (t = 0; t < dsi->num_lanes_supported; ++t)
if (dsi->lanes[t].function == functions[i])
break;
if (t == dsi->num_lanes_supported)
return -EINVAL;
lane_number = t;
polarity = dsi->lanes[t].polarity;
r = FLD_MOD(r, lane_number + 1, offset + 2, offset);
r = FLD_MOD(r, polarity, offset + 3, offset + 3);
}
if (num_data_lanes_dssdev > 3) {
int data4_lane = dssdev->phy.dsi.data4_lane;
int data4_pol = dssdev->phy.dsi.data4_pol;
r = FLD_MOD(r, data4_lane, 18, 16);
r = FLD_MOD(r, data4_pol, 19, 19);
/* clear the unused lanes */
for (; i < dsi->num_lanes_supported; ++i) {
unsigned offset = offsets[i];
r = FLD_MOD(r, 0, offset + 2, offset);
r = FLD_MOD(r, 0, offset + 3, offset + 3);
}
dsi_write_reg(dsidev, DSI_COMPLEXIO_CFG1, r);
/* The configuration of the DSI complex I/O (number of data lanes,
position, differential order) should not be changed while
DSS.DSI_CLK_CRTRL[20] LP_CLK_ENABLE bit is set to 1. In order for
the hardware to take into account a new configuration of the complex
I/O (done in DSS.DSI_COMPLEXIO_CFG1 register), it is recommended to
follow this sequence: First set the DSS.DSI_CTRL[0] IF_EN bit to 1,
then reset the DSS.DSI_CTRL[0] IF_EN to 0, then set
DSS.DSI_CLK_CTRL[20] LP_CLK_ENABLE to 1 and finally set again the
DSS.DSI_CTRL[0] IF_EN bit to 1. If the sequence is not followed, the
DSI complex I/O configuration is unknown. */
dsi_write_reg(dsidev, DSI_COMPLEXIO_CFG1, r);
/*
REG_FLD_MOD(dsidev, DSI_CTRL, 1, 0, 0);
REG_FLD_MOD(dsidev, DSI_CTRL, 0, 0, 0);
REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 1, 20, 20);
REG_FLD_MOD(dsidev, DSI_CTRL, 1, 0, 0);
*/
return 0;
}
static inline unsigned ns2ddr(struct platform_device *dsidev, unsigned ns)
......@@ -2230,49 +2271,28 @@ static void dsi_cio_timings(struct platform_device *dsidev)
dsi_write_reg(dsidev, DSI_DSIPHY_CFG2, r);
}
/* lane masks have lane 0 at lsb. mask_p for positive lines, n for negative */
static void dsi_cio_enable_lane_override(struct omap_dss_device *dssdev,
enum dsi_lane lanes)
unsigned mask_p, unsigned mask_n)
{
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
int clk_lane = dssdev->phy.dsi.clk_lane;
int data1_lane = dssdev->phy.dsi.data1_lane;
int data2_lane = dssdev->phy.dsi.data2_lane;
int data3_lane = dssdev->phy.dsi.data3_lane;
int data4_lane = dssdev->phy.dsi.data4_lane;
int clk_pol = dssdev->phy.dsi.clk_pol;
int data1_pol = dssdev->phy.dsi.data1_pol;
int data2_pol = dssdev->phy.dsi.data2_pol;
int data3_pol = dssdev->phy.dsi.data3_pol;
int data4_pol = dssdev->phy.dsi.data4_pol;
u32 l = 0;
u8 lptxscp_start = dsi->num_data_lanes == 2 ? 22 : 26;
if (lanes & DSI_CLK_P)
l |= 1 << ((clk_lane - 1) * 2 + (clk_pol ? 0 : 1));
if (lanes & DSI_CLK_N)
l |= 1 << ((clk_lane - 1) * 2 + (clk_pol ? 1 : 0));
if (lanes & DSI_DATA1_P)
l |= 1 << ((data1_lane - 1) * 2 + (data1_pol ? 0 : 1));
if (lanes & DSI_DATA1_N)
l |= 1 << ((data1_lane - 1) * 2 + (data1_pol ? 1 : 0));
if (lanes & DSI_DATA2_P)
l |= 1 << ((data2_lane - 1) * 2 + (data2_pol ? 0 : 1));
if (lanes & DSI_DATA2_N)
l |= 1 << ((data2_lane - 1) * 2 + (data2_pol ? 1 : 0));
if (lanes & DSI_DATA3_P)
l |= 1 << ((data3_lane - 1) * 2 + (data3_pol ? 0 : 1));
if (lanes & DSI_DATA3_N)
l |= 1 << ((data3_lane - 1) * 2 + (data3_pol ? 1 : 0));
if (lanes & DSI_DATA4_P)
l |= 1 << ((data4_lane - 1) * 2 + (data4_pol ? 0 : 1));
if (lanes & DSI_DATA4_N)
l |= 1 << ((data4_lane - 1) * 2 + (data4_pol ? 1 : 0));
int i;
u32 l;
u8 lptxscp_start = dsi->num_lanes_supported == 3 ? 22 : 26;
l = 0;
for (i = 0; i < dsi->num_lanes_supported; ++i) {
unsigned p = dsi->lanes[i].polarity;
if (mask_p & (1 << i))
l |= 1 << (i * 2 + (p ? 0 : 1));
if (mask_n & (1 << i))
l |= 1 << (i * 2 + (p ? 1 : 0));
}
/*
* Bits in REGLPTXSCPDAT4TO0DXDY:
* 17: DY0 18: DX0
......@@ -2305,51 +2325,40 @@ static void dsi_cio_disable_lane_override(struct platform_device *dsidev)
static int dsi_cio_wait_tx_clk_esc_reset(struct omap_dss_device *dssdev)
{
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
int t;
int bits[3];
bool in_use[3];
if (dss_has_feature(FEAT_DSI_REVERSE_TXCLKESC)) {
bits[0] = 28;
bits[1] = 27;
bits[2] = 26;
} else {
bits[0] = 24;
bits[1] = 25;
bits[2] = 26;
}
in_use[0] = false;
in_use[1] = false;
in_use[2] = false;
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
int t, i;
bool in_use[DSI_MAX_NR_LANES];
static const u8 offsets_old[] = { 28, 27, 26 };
static const u8 offsets_new[] = { 24, 25, 26, 27, 28 };
const u8 *offsets;
if (dss_has_feature(FEAT_DSI_REVERSE_TXCLKESC))
offsets = offsets_old;
else
offsets = offsets_new;
if (dssdev->phy.dsi.clk_lane != 0)
in_use[dssdev->phy.dsi.clk_lane - 1] = true;
if (dssdev->phy.dsi.data1_lane != 0)
in_use[dssdev->phy.dsi.data1_lane - 1] = true;
if (dssdev->phy.dsi.data2_lane != 0)
in_use[dssdev->phy.dsi.data2_lane - 1] = true;
for (i = 0; i < dsi->num_lanes_supported; ++i)
in_use[i] = dsi->lanes[i].function != DSI_LANE_UNUSED;
t = 100000;
while (true) {
u32 l;
int i;
int ok;
l = dsi_read_reg(dsidev, DSI_DSIPHY_CFG5);
ok = 0;
for (i = 0; i < 3; ++i) {
if (!in_use[i] || (l & (1 << bits[i])))
for (i = 0; i < dsi->num_lanes_supported; ++i) {
if (!in_use[i] || (l & (1 << offsets[i])))
ok++;
}
if (ok == 3)
if (ok == dsi->num_lanes_supported)
break;
if (--t == 0) {
for (i = 0; i < 3; ++i) {
if (!in_use[i] || (l & (1 << bits[i])))
for (i = 0; i < dsi->num_lanes_supported; ++i) {
if (!in_use[i] || (l & (1 << offsets[i])))
continue;
DSSERR("CIO TXCLKESC%d domain not coming " \
......@@ -2362,22 +2371,20 @@ static int dsi_cio_wait_tx_clk_esc_reset(struct omap_dss_device *dssdev)
return 0;
}
/* return bitmask of enabled lanes, lane0 being the lsb */
static unsigned dsi_get_lane_mask(struct omap_dss_device *dssdev)
{
unsigned lanes = 0;
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
unsigned mask = 0;
int i;
if (dssdev->phy.dsi.clk_lane != 0)
lanes |= 1 << (dssdev->phy.dsi.clk_lane - 1);
if (dssdev->phy.dsi.data1_lane != 0)
lanes |= 1 << (dssdev->phy.dsi.data1_lane - 1);
if (dssdev->phy.dsi.data2_lane != 0)
lanes |= 1 << (dssdev->phy.dsi.data2_lane - 1);
if (dssdev->phy.dsi.data3_lane != 0)
lanes |= 1 << (dssdev->phy.dsi.data3_lane - 1);
if (dssdev->phy.dsi.data4_lane != 0)
lanes |= 1 << (dssdev->phy.dsi.data4_lane - 1);
for (i = 0; i < dsi->num_lanes_supported; ++i) {
if (dsi->lanes[i].function != DSI_LANE_UNUSED)
mask |= 1 << i;
}
return lanes;
return mask;
}
static int dsi_cio_init(struct omap_dss_device *dssdev)
......@@ -2385,7 +2392,6 @@ static int dsi_cio_init(struct omap_dss_device *dssdev)
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
int r;
int num_data_lanes_dssdev = dsi_get_num_data_lanes_dssdev(dssdev);
u32 l;
DSSDBGF();
......@@ -2407,7 +2413,9 @@ static int dsi_cio_init(struct omap_dss_device *dssdev)
goto err_scp_clk_dom;
}
dsi_set_lane_config(dssdev);
r = dsi_set_lane_config(dssdev);
if (r)
goto err_scp_clk_dom;
/* set TX STOP MODE timer to maximum for this operation */
l = dsi_read_reg(dsidev, DSI_TIMING1);
......@@ -2418,7 +2426,8 @@ static int dsi_cio_init(struct omap_dss_device *dssdev)
dsi_write_reg(dsidev, DSI_TIMING1, l);
if (dsi->ulps_enabled) {
u32 lane_mask = DSI_CLK_P | DSI_DATA1_P | DSI_DATA2_P;
unsigned mask_p;
int i;
DSSDBG("manual ulps exit\n");
......@@ -2427,16 +2436,19 @@ static int dsi_cio_init(struct omap_dss_device *dssdev)
* ULPS exit sequence, as after reset the DSS HW thinks
* that we are not in ULPS mode, and refuses to send the
* sequence. So we need to send the ULPS exit sequence
* manually.
* manually by setting positive lines high and negative lines
* low for 1ms.
*/
if (num_data_lanes_dssdev > 2)
lane_mask |= DSI_DATA3_P;
mask_p = 0;
if (num_data_lanes_dssdev > 3)
lane_mask |= DSI_DATA4_P;
for (i = 0; i < dsi->num_lanes_supported; ++i) {
if (dsi->lanes[i].function == DSI_LANE_UNUSED)
continue;
mask_p |= 1 << i;
}
dsi_cio_enable_lane_override(dssdev, lane_mask);
dsi_cio_enable_lane_override(dssdev, mask_p, 0);
}
r = dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_ON);
......@@ -2913,6 +2925,9 @@ static int dsi_vc_send_bta(struct platform_device *dsidev, int channel)
REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 1, 6, 6); /* BTA_EN */
/* flush posted write */
dsi_read_reg(dsidev, DSI_VC_CTRL(channel));
return 0;
}
......@@ -3513,7 +3528,8 @@ static int dsi_enter_ulps(struct platform_device *dsidev)
{
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
DECLARE_COMPLETION_ONSTACK(completion);
int r;
int r, i;
unsigned mask;
DSSDBGF();
......@@ -3524,9 +3540,11 @@ static int dsi_enter_ulps(struct platform_device *dsidev)
if (dsi->ulps_enabled)
return 0;
/* DDR_CLK_ALWAYS_ON */
if (REG_GET(dsidev, DSI_CLK_CTRL, 13, 13)) {
DSSERR("DDR_CLK_ALWAYS_ON enabled when entering ULPS\n");
return -EIO;
dsi_if_enable(dsidev, 0);
REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 0, 13, 13);
dsi_if_enable(dsidev, 1);
}
dsi_sync_vc(dsidev, 0);
......@@ -3556,10 +3574,19 @@ static int dsi_enter_ulps(struct platform_device *dsidev)
if (r)
return r;
mask = 0;
for (i = 0; i < dsi->num_lanes_supported; ++i) {
if (dsi->lanes[i].function == DSI_LANE_UNUSED)
continue;
mask |= 1 << i;
}
/* Assert TxRequestEsc for data lanes and TxUlpsClk for clk lane */
/* LANEx_ULPS_SIG2 */
REG_FLD_MOD(dsidev, DSI_COMPLEXIO_CFG2, (1 << 0) | (1 << 1) | (1 << 2),
7, 5);
REG_FLD_MOD(dsidev, DSI_COMPLEXIO_CFG2, mask, 9, 5);
/* flush posted write and wait for SCP interface to finish the write */
dsi_read_reg(dsidev, DSI_COMPLEXIO_CFG2);
if (wait_for_completion_timeout(&completion,
msecs_to_jiffies(1000)) == 0) {
......@@ -3572,8 +3599,10 @@ static int dsi_enter_ulps(struct platform_device *dsidev)
DSI_CIO_IRQ_ULPSACTIVENOT_ALL0);
/* Reset LANEx_ULPS_SIG2 */
REG_FLD_MOD(dsidev, DSI_COMPLEXIO_CFG2, (0 << 0) | (0 << 1) | (0 << 2),
7, 5);
REG_FLD_MOD(dsidev, DSI_COMPLEXIO_CFG2, 0, 9, 5);
/* flush posted write and wait for SCP interface to finish the write */
dsi_read_reg(dsidev, DSI_COMPLEXIO_CFG2);
dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_ULPS);
......@@ -3836,6 +3865,7 @@ static int dsi_proto_config(struct omap_dss_device *dssdev)
static void dsi_proto_timings(struct omap_dss_device *dssdev)
{
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
unsigned tlpx, tclk_zero, tclk_prepare, tclk_trail;
unsigned tclk_pre, tclk_post;
unsigned ths_prepare, ths_prepare_ths_zero, ths_zero;
......@@ -3843,7 +3873,7 @@ static void dsi_proto_timings(struct omap_dss_device *dssdev)
unsigned ddr_clk_pre, ddr_clk_post;
unsigned enter_hs_mode_lat, exit_hs_mode_lat;
unsigned ths_eot;
int ndl = dsi_get_num_data_lanes_dssdev(dssdev);
int ndl = dsi->num_lanes_used - 1;
u32 r;
r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG0);
......@@ -3945,68 +3975,82 @@ static void dsi_proto_timings(struct omap_dss_device *dssdev)
}
}
int dsi_video_mode_enable(struct omap_dss_device *dssdev, int channel)
int dsi_enable_video_output(struct omap_dss_device *dssdev, int channel)
{
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
int bpp = dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt);
u8 data_type;
u16 word_count;
int r;
switch (dssdev->panel.dsi_pix_fmt) {
case OMAP_DSS_DSI_FMT_RGB888:
data_type = MIPI_DSI_PACKED_PIXEL_STREAM_24;
break;
case OMAP_DSS_DSI_FMT_RGB666:
data_type = MIPI_DSI_PIXEL_STREAM_3BYTE_18;
break;
case OMAP_DSS_DSI_FMT_RGB666_PACKED:
data_type = MIPI_DSI_PACKED_PIXEL_STREAM_18;
break;
case OMAP_DSS_DSI_FMT_RGB565:
data_type = MIPI_DSI_PACKED_PIXEL_STREAM_16;
break;
default:
BUG();
};
if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_VIDEO_MODE) {
switch (dssdev->panel.dsi_pix_fmt) {
case OMAP_DSS_DSI_FMT_RGB888:
data_type = MIPI_DSI_PACKED_PIXEL_STREAM_24;
break;
case OMAP_DSS_DSI_FMT_RGB666:
data_type = MIPI_DSI_PIXEL_STREAM_3BYTE_18;
break;
case OMAP_DSS_DSI_FMT_RGB666_PACKED:
data_type = MIPI_DSI_PACKED_PIXEL_STREAM_18;
break;
case OMAP_DSS_DSI_FMT_RGB565:
data_type = MIPI_DSI_PACKED_PIXEL_STREAM_16;
break;
default:
BUG();
};
dsi_if_enable(dsidev, false);
dsi_vc_enable(dsidev, channel, false);
dsi_if_enable(dsidev, false);
dsi_vc_enable(dsidev, channel, false);
/* MODE, 1 = video mode */
REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 1, 4, 4);
/* MODE, 1 = video mode */
REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 1, 4, 4);
word_count = DIV_ROUND_UP(dssdev->panel.timings.x_res * bpp, 8);
word_count = DIV_ROUND_UP(dssdev->panel.timings.x_res * bpp, 8);
dsi_vc_write_long_header(dsidev, channel, data_type, word_count, 0);
dsi_vc_write_long_header(dsidev, channel, data_type,
word_count, 0);
dsi_vc_enable(dsidev, channel, true);
dsi_if_enable(dsidev, true);
dsi_vc_enable(dsidev, channel, true);
dsi_if_enable(dsidev, true);
}
dssdev->manager->enable(dssdev->manager);
r = dss_mgr_enable(dssdev->manager);
if (r) {
if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_VIDEO_MODE) {
dsi_if_enable(dsidev, false);
dsi_vc_enable(dsidev, channel, false);
}
return r;
}
return 0;
}
EXPORT_SYMBOL(dsi_video_mode_enable);
EXPORT_SYMBOL(dsi_enable_video_output);
void dsi_video_mode_disable(struct omap_dss_device *dssdev, int channel)
void dsi_disable_video_output(struct omap_dss_device *dssdev, int channel)
{
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
dsi_if_enable(dsidev, false);
dsi_vc_enable(dsidev, channel, false);
if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_VIDEO_MODE) {
dsi_if_enable(dsidev, false);
dsi_vc_enable(dsidev, channel, false);
/* MODE, 0 = command mode */
REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 0, 4, 4);
/* MODE, 0 = command mode */
REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 0, 4, 4);
dsi_vc_enable(dsidev, channel, true);
dsi_if_enable(dsidev, true);
dsi_vc_enable(dsidev, channel, true);
dsi_if_enable(dsidev, true);
}
dssdev->manager->disable(dssdev->manager);
dss_mgr_disable(dssdev->manager);
}
EXPORT_SYMBOL(dsi_video_mode_disable);
EXPORT_SYMBOL(dsi_disable_video_output);
static void dsi_update_screen_dispc(struct omap_dss_device *dssdev,
u16 x, u16 y, u16 w, u16 h)
u16 w, u16 h)
{
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
......@@ -4021,8 +4065,7 @@ static void dsi_update_screen_dispc(struct omap_dss_device *dssdev,
const unsigned channel = dsi->update_channel;
const unsigned line_buf_size = dsi_get_line_buf_size(dsidev);
DSSDBG("dsi_update_screen_dispc(%d,%d %dx%d)\n",
x, y, w, h);
DSSDBG("dsi_update_screen_dispc(%dx%d)\n", w, h);
dsi_vc_config_source(dsidev, channel, DSI_VC_SOURCE_VP);
......@@ -4070,7 +4113,7 @@ static void dsi_update_screen_dispc(struct omap_dss_device *dssdev,
msecs_to_jiffies(250));
BUG_ON(r == 0);
dss_start_update(dssdev);
dss_mgr_start_update(dssdev->manager);
if (dsi->te_enabled) {
/* disable LP_RX_TO, so that we can receive TE. Time to wait
......@@ -4146,66 +4189,27 @@ static void dsi_framedone_irq_callback(void *data, u32 mask)
#endif
}
int omap_dsi_prepare_update(struct omap_dss_device *dssdev,
u16 *x, u16 *y, u16 *w, u16 *h,
bool enlarge_update_area)
int omap_dsi_update(struct omap_dss_device *dssdev, int channel,
void (*callback)(int, void *), void *data)
{
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
u16 dw, dh;
dssdev->driver->get_resolution(dssdev, &dw, &dh);
if (*x > dw || *y > dh)
return -EINVAL;
if (*x + *w > dw)
return -EINVAL;
if (*y + *h > dh)
return -EINVAL;
if (*w == 1)
return -EINVAL;
if (*w == 0 || *h == 0)
return -EINVAL;
dsi_perf_mark_setup(dsidev);
dss_setup_partial_planes(dssdev, x, y, w, h,
enlarge_update_area);
dispc_mgr_set_lcd_size(dssdev->manager->id, *w, *h);
return 0;
}
EXPORT_SYMBOL(omap_dsi_prepare_update);
int omap_dsi_update(struct omap_dss_device *dssdev,
int channel,
u16 x, u16 y, u16 w, u16 h,
void (*callback)(int, void *), void *data)
{
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
dsi->update_channel = channel;
/* OMAP DSS cannot send updates of odd widths.
* omap_dsi_prepare_update() makes the widths even, but add a BUG_ON
* here to make sure we catch erroneous updates. Otherwise we'll only
* see rather obscure HW error happening, as DSS halts. */
BUG_ON(x % 2 == 1);
dsi->framedone_callback = callback;
dsi->framedone_data = data;
dsi->update_region.x = x;
dsi->update_region.y = y;
dsi->update_region.w = w;
dsi->update_region.h = h;
dsi->update_region.device = dssdev;
dssdev->driver->get_resolution(dssdev, &dw, &dh);
dsi_update_screen_dispc(dssdev, x, y, w, h);
#ifdef DEBUG
dsi->update_bytes = dw * dh *
dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt) / 8;
#endif
dsi_update_screen_dispc(dssdev, dw, dh);
return 0;
}
......@@ -4218,6 +4222,7 @@ static int dsi_display_init_dispc(struct omap_dss_device *dssdev)
int r;
if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_CMD_MODE) {
u16 dw, dh;
u32 irq;
struct omap_video_timings timings = {
.hsw = 1,
......@@ -4228,6 +4233,10 @@ static int dsi_display_init_dispc(struct omap_dss_device *dssdev)
.vbp = 0,
};
dssdev->driver->get_resolution(dssdev, &dw, &dh);
timings.x_res = dw;
timings.y_res = dh;
irq = dssdev->manager->id == OMAP_DSS_CHANNEL_LCD ?
DISPC_IRQ_FRAMEDONE : DISPC_IRQ_FRAMEDONE2;
......@@ -4330,6 +4339,12 @@ static int dsi_display_init_dsi(struct omap_dss_device *dssdev)
int dsi_module = dsi_get_dsidev_id(dsidev);
int r;
r = dsi_parse_lane_config(dssdev);
if (r) {
DSSERR("illegal lane config");
goto err0;
}
r = dsi_pll_init(dsidev, true, true);
if (r)
goto err0;
......@@ -4521,7 +4536,6 @@ int dsi_init_display(struct omap_dss_device *dssdev)
{
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
int dsi_module = dsi_get_dsidev_id(dsidev);
DSSDBG("DSI init\n");
......@@ -4543,12 +4557,6 @@ int dsi_init_display(struct omap_dss_device *dssdev)
dsi->vdds_dsi_reg = vdds_dsi;
}
if (dsi_get_num_data_lanes_dssdev(dssdev) > dsi->num_data_lanes) {
DSSERR("DSI%d can't support more than %d data lanes\n",
dsi_module + 1, dsi->num_data_lanes);
return -EINVAL;
}
return 0;
}
......@@ -4771,7 +4779,13 @@ static int omap_dsihw_probe(struct platform_device *dsidev)
dev_dbg(&dsidev->dev, "OMAP DSI rev %d.%d\n",
FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
dsi->num_data_lanes = dsi_get_num_data_lanes(dsidev);
/* DSI on OMAP3 doesn't have register DSI_GNQ, set number
* of data to 3 by default */
if (dss_has_feature(FEAT_DSI_GNQ))
/* NB_DATA_LANES */
dsi->num_lanes_supported = 1 + REG_GET(dsidev, DSI_GNQ, 11, 9);
else
dsi->num_lanes_supported = 3;
dsi_runtime_put(dsidev);
......
......@@ -163,6 +163,34 @@ struct bus_type *dss_get_bus(void);
struct regulator *dss_get_vdds_dsi(void);
struct regulator *dss_get_vdds_sdi(void);
/* apply */
void dss_apply_init(void);
int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr);
int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl);
void dss_mgr_start_update(struct omap_overlay_manager *mgr);
int omap_dss_mgr_apply(struct omap_overlay_manager *mgr);
int dss_mgr_enable(struct omap_overlay_manager *mgr);
void dss_mgr_disable(struct omap_overlay_manager *mgr);
int dss_mgr_set_info(struct omap_overlay_manager *mgr,
struct omap_overlay_manager_info *info);
void dss_mgr_get_info(struct omap_overlay_manager *mgr,
struct omap_overlay_manager_info *info);
int dss_mgr_set_device(struct omap_overlay_manager *mgr,
struct omap_dss_device *dssdev);
int dss_mgr_unset_device(struct omap_overlay_manager *mgr);
bool dss_ovl_is_enabled(struct omap_overlay *ovl);
int dss_ovl_enable(struct omap_overlay *ovl);
int dss_ovl_disable(struct omap_overlay *ovl);
int dss_ovl_set_info(struct omap_overlay *ovl,
struct omap_overlay_info *info);
void dss_ovl_get_info(struct omap_overlay *ovl,
struct omap_overlay_info *info);
int dss_ovl_set_manager(struct omap_overlay *ovl,
struct omap_overlay_manager *mgr);
int dss_ovl_unset_manager(struct omap_overlay *ovl);
/* display */
int dss_suspend_all_devices(void);
int dss_resume_all_devices(void);
......@@ -181,21 +209,22 @@ void default_get_overlay_fifo_thresholds(enum omap_plane plane,
/* manager */
int dss_init_overlay_managers(struct platform_device *pdev);
void dss_uninit_overlay_managers(struct platform_device *pdev);
int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl);
void dss_setup_partial_planes(struct omap_dss_device *dssdev,
u16 *x, u16 *y, u16 *w, u16 *h,
bool enlarge_update_area);
void dss_start_update(struct omap_dss_device *dssdev);
int dss_mgr_simple_check(struct omap_overlay_manager *mgr,
const struct omap_overlay_manager_info *info);
int dss_mgr_check(struct omap_overlay_manager *mgr,
struct omap_dss_device *dssdev,
struct omap_overlay_manager_info *info,
struct omap_overlay_info **overlay_infos);
/* overlay */
void dss_init_overlays(struct platform_device *pdev);
void dss_uninit_overlays(struct platform_device *pdev);
int dss_check_overlay(struct omap_overlay *ovl, struct omap_dss_device *dssdev);
void dss_overlay_setup_dispc_manager(struct omap_overlay_manager *mgr);
#ifdef L4_EXAMPLE
void dss_overlay_setup_l4_manager(struct omap_overlay_manager *mgr);
#endif
void dss_recheck_connections(struct omap_dss_device *dssdev, bool force);
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, struct omap_dss_device *dssdev);
/* DSS */
int dss_init_platform_driver(void);
......@@ -399,21 +428,22 @@ int dispc_calc_clock_rates(unsigned long dispc_fclk_rate,
struct dispc_clock_info *cinfo);
void dispc_ovl_set_fifo_threshold(enum omap_plane plane, u32 low, u32 high);
u32 dispc_ovl_get_fifo_size(enum omap_plane plane);
u32 dispc_ovl_get_burst_size(enum omap_plane plane);
int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi,
bool ilace, enum omap_channel channel, bool replication,
u32 fifo_low, u32 fifo_high);
bool ilace, bool replication);
int dispc_ovl_enable(enum omap_plane plane, bool enable);
void dispc_ovl_set_channel_out(enum omap_plane plane,
enum omap_channel channel);
void dispc_mgr_enable_fifohandcheck(enum omap_channel channel, bool enable);
void dispc_mgr_set_lcd_size(enum omap_channel channel, u16 width, u16 height);
void dispc_mgr_enable_cpr(enum omap_channel channel, bool enable);
void dispc_mgr_set_cpr_coef(enum omap_channel channel,
struct omap_dss_cpr_coefs *coefs);
u32 dispc_mgr_get_vsync_irq(enum omap_channel channel);
u32 dispc_mgr_get_framedone_irq(enum omap_channel channel);
bool dispc_mgr_go_busy(enum omap_channel channel);
void dispc_mgr_go(enum omap_channel channel);
bool dispc_mgr_is_enabled(enum omap_channel channel);
void dispc_mgr_enable(enum omap_channel channel, bool enable);
bool dispc_mgr_is_channel_enabled(enum omap_channel channel);
void dispc_mgr_set_io_pad_mode(enum dss_io_pad_mode mode);
......@@ -421,18 +451,6 @@ void dispc_mgr_enable_stallmode(enum omap_channel channel, bool enable);
void dispc_mgr_set_tft_data_lines(enum omap_channel channel, u8 data_lines);
void dispc_mgr_set_lcd_display_type(enum omap_channel channel,
enum omap_lcd_display_type type);
void dispc_mgr_set_default_color(enum omap_channel channel, u32 color);
u32 dispc_mgr_get_default_color(enum omap_channel channel);
void dispc_mgr_set_trans_key(enum omap_channel ch,
enum omap_dss_trans_key_type type,
u32 trans_key);
void dispc_mgr_get_trans_key(enum omap_channel ch,
enum omap_dss_trans_key_type *type,
u32 *trans_key);
void dispc_mgr_enable_trans_key(enum omap_channel ch, bool enable);
void dispc_mgr_enable_alpha_fixed_zorder(enum omap_channel ch, bool enable);
bool dispc_mgr_trans_key_enabled(enum omap_channel ch);
bool dispc_mgr_alpha_fixed_zorder_enabled(enum omap_channel ch);
void dispc_mgr_set_lcd_timings(enum omap_channel channel,
struct omap_video_timings *timings);
void dispc_mgr_set_pol_freq(enum omap_channel channel,
......@@ -443,6 +461,8 @@ int dispc_mgr_set_clock_div(enum omap_channel channel,
struct dispc_clock_info *cinfo);
int dispc_mgr_get_clock_div(enum omap_channel channel,
struct dispc_clock_info *cinfo);
void dispc_mgr_setup(enum omap_channel channel,
struct omap_overlay_manager_info *info);
/* VENC */
#ifdef CONFIG_OMAP2_DSS_VENC
......
......@@ -304,6 +304,11 @@ static const struct dss_param_range omap2_dss_param_range[] = {
[FEAT_PARAM_DSIPLL_FINT] = { 0, 0 },
[FEAT_PARAM_DSIPLL_LPDIV] = { 0, 0 },
[FEAT_PARAM_DOWNSCALE] = { 1, 2 },
/*
* Assuming the line width buffer to be 768 pixels as OMAP2 DISPC
* scaler cannot scale a image with width more than 768.
*/
[FEAT_PARAM_LINEWIDTH] = { 1, 768 },
};
static const struct dss_param_range omap3_dss_param_range[] = {
......@@ -316,6 +321,7 @@ static const struct dss_param_range omap3_dss_param_range[] = {
[FEAT_PARAM_DSIPLL_FINT] = { 750000, 2100000 },
[FEAT_PARAM_DSIPLL_LPDIV] = { 1, (1 << 13) - 1},
[FEAT_PARAM_DOWNSCALE] = { 1, 4 },
[FEAT_PARAM_LINEWIDTH] = { 1, 1024 },
};
static const struct dss_param_range omap4_dss_param_range[] = {
......@@ -328,6 +334,7 @@ static const struct dss_param_range omap4_dss_param_range[] = {
[FEAT_PARAM_DSIPLL_FINT] = { 500000, 2500000 },
[FEAT_PARAM_DSIPLL_LPDIV] = { 0, (1 << 13) - 1 },
[FEAT_PARAM_DOWNSCALE] = { 1, 4 },
[FEAT_PARAM_LINEWIDTH] = { 1, 2048 },
};
/* OMAP2 DSS Features */
......@@ -465,6 +472,10 @@ static const struct ti_hdmi_ip_ops omap4_hdmi_functions = {
.dump_core = ti_hdmi_4xxx_core_dump,
.dump_pll = ti_hdmi_4xxx_pll_dump,
.dump_phy = ti_hdmi_4xxx_phy_dump,
#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \
defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE)
.audio_enable = ti_hdmi_4xxx_wp_audio_enable,
#endif
};
......
......@@ -86,6 +86,7 @@ enum dss_range_param {
FEAT_PARAM_DSIPLL_FINT,
FEAT_PARAM_DSIPLL_LPDIV,
FEAT_PARAM_DOWNSCALE,
FEAT_PARAM_LINEWIDTH,
};
/* DSS Feature Functions */
......
......@@ -333,7 +333,7 @@ static int hdmi_power_on(struct omap_dss_device *dssdev)
if (r)
return r;
dispc_mgr_enable(OMAP_DSS_CHANNEL_DIGIT, 0);
dss_mgr_disable(dssdev->manager);
p = &dssdev->panel.timings;
......@@ -387,9 +387,16 @@ static int hdmi_power_on(struct omap_dss_device *dssdev)
hdmi.ip_data.ops->video_enable(&hdmi.ip_data, 1);
dispc_mgr_enable(OMAP_DSS_CHANNEL_DIGIT, 1);
r = dss_mgr_enable(dssdev->manager);
if (r)
goto err_mgr_enable;
return 0;
err_mgr_enable:
hdmi.ip_data.ops->video_enable(&hdmi.ip_data, 0);
hdmi.ip_data.ops->phy_disable(&hdmi.ip_data);
hdmi.ip_data.ops->pll_disable(&hdmi.ip_data);
err:
hdmi_runtime_put();
return -EIO;
......@@ -397,7 +404,7 @@ static int hdmi_power_on(struct omap_dss_device *dssdev)
static void hdmi_power_off(struct omap_dss_device *dssdev)
{
dispc_mgr_enable(OMAP_DSS_CHANNEL_DIGIT, 0);
dss_mgr_disable(dssdev->manager);
hdmi.ip_data.ops->video_enable(&hdmi.ip_data, 0);
hdmi.ip_data.ops->phy_disable(&hdmi.ip_data);
......@@ -554,11 +561,44 @@ void omapdss_hdmi_display_disable(struct omap_dss_device *dssdev)
#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \
defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE)
static int hdmi_audio_hw_params(struct hdmi_ip_data *ip_data,
struct snd_pcm_substream *substream,
static int hdmi_audio_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec *codec = rtd->codec;
struct platform_device *pdev = to_platform_device(codec->dev);
struct hdmi_ip_data *ip_data = snd_soc_codec_get_drvdata(codec);
int err = 0;
if (!(ip_data->ops) && !(ip_data->ops->audio_enable)) {
dev_err(&pdev->dev, "Cannot enable/disable audio\n");
return -ENODEV;
}
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
ip_data->ops->audio_enable(ip_data, true);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
ip_data->ops->audio_enable(ip_data, false);
break;
default:
err = -EINVAL;
}
return err;
}
static int hdmi_audio_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec *codec = rtd->codec;
struct hdmi_ip_data *ip_data = snd_soc_codec_get_drvdata(codec);
struct hdmi_audio_format audio_format;
struct hdmi_audio_dma audio_dma;
struct hdmi_core_audio_config core_cfg;
......@@ -698,7 +738,16 @@ static int hdmi_audio_startup(struct snd_pcm_substream *substream,
return 0;
}
static int hdmi_audio_codec_probe(struct snd_soc_codec *codec)
{
struct hdmi_ip_data *priv = &hdmi.ip_data;
snd_soc_codec_set_drvdata(codec, priv);
return 0;
}
static struct snd_soc_codec_driver hdmi_audio_codec_drv = {
.probe = hdmi_audio_codec_probe,
};
static struct snd_soc_dai_ops hdmi_audio_codec_ops = {
......
......@@ -26,17 +26,15 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/jiffies.h>
#include <video/omapdss.h>
#include <plat/cpu.h>
#include "dss.h"
#include "dss_features.h"
static int num_managers;
static struct list_head manager_list;
static struct omap_overlay_manager *managers;
static ssize_t manager_name_show(struct omap_overlay_manager *mgr, char *buf)
{
......@@ -106,7 +104,11 @@ static ssize_t manager_display_store(struct omap_overlay_manager *mgr,
static ssize_t manager_default_color_show(struct omap_overlay_manager *mgr,
char *buf)
{
return snprintf(buf, PAGE_SIZE, "%#x\n", mgr->info.default_color);
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,
......@@ -144,8 +146,11 @@ 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 = mgr->info.trans_key_type;
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]);
......@@ -185,7 +190,11 @@ static ssize_t manager_trans_key_type_store(struct omap_overlay_manager *mgr,
static ssize_t manager_trans_key_value_show(struct omap_overlay_manager *mgr,
char *buf)
{
return snprintf(buf, PAGE_SIZE, "%#x\n", mgr->info.trans_key);
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,
......@@ -217,7 +226,11 @@ static ssize_t manager_trans_key_value_store(struct omap_overlay_manager *mgr,
static ssize_t manager_trans_key_enabled_show(struct omap_overlay_manager *mgr,
char *buf)
{
return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.trans_enabled);
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,
......@@ -249,10 +262,14 @@ static ssize_t manager_trans_key_enabled_store(struct omap_overlay_manager *mgr,
static ssize_t manager_alpha_blending_enabled_show(
struct omap_overlay_manager *mgr, char *buf)
{
struct omap_overlay_manager_info info;
mgr->get_manager_info(mgr, &info);
WARN_ON(!dss_has_feature(FEAT_ALPHA_FIXED_ZORDER));
return snprintf(buf, PAGE_SIZE, "%d\n",
mgr->info.partial_alpha_enabled);
info.partial_alpha_enabled);
}
static ssize_t manager_alpha_blending_enabled_store(
......@@ -287,7 +304,11 @@ static ssize_t manager_alpha_blending_enabled_store(
static ssize_t manager_cpr_enable_show(struct omap_overlay_manager *mgr,
char *buf)
{
return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.cpr_enable);
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,
......@@ -469,143 +490,6 @@ static struct kobj_type manager_ktype = {
.default_attrs = manager_sysfs_attrs,
};
/*
* We have 4 levels of cache for the dispc settings. First two are in SW and
* the latter two in HW.
*
* +--------------------+
* |overlay/manager_info|
* +--------------------+
* v
* apply()
* v
* +--------------------+
* | dss_cache |
* +--------------------+
* v
* configure()
* v
* +--------------------+
* | shadow registers |
* +--------------------+
* v
* VFP or lcd/digit_enable
* v
* +--------------------+
* | registers |
* +--------------------+
*/
struct overlay_cache_data {
/* If true, cache changed, but not written to shadow registers. Set
* in apply(), cleared when registers written. */
bool dirty;
/* If true, shadow registers contain changed values not yet in real
* registers. Set when writing to shadow registers, cleared at
* VSYNC/EVSYNC */
bool shadow_dirty;
bool enabled;
struct omap_overlay_info info;
enum omap_channel channel;
bool replication;
bool ilace;
u32 fifo_low;
u32 fifo_high;
};
struct manager_cache_data {
/* If true, cache changed, but not written to shadow registers. Set
* in apply(), cleared when registers written. */
bool dirty;
/* If true, shadow registers contain changed values not yet in real
* registers. Set when writing to shadow registers, cleared at
* VSYNC/EVSYNC */
bool shadow_dirty;
struct omap_overlay_manager_info info;
bool manual_update;
bool do_manual_update;
/* manual update region */
u16 x, y, w, h;
/* enlarge the update area if the update area contains scaled
* overlays */
bool enlarge_update_area;
};
static struct {
spinlock_t lock;
struct overlay_cache_data overlay_cache[MAX_DSS_OVERLAYS];
struct manager_cache_data manager_cache[MAX_DSS_MANAGERS];
bool irq_enabled;
} dss_cache;
static int omap_dss_set_device(struct omap_overlay_manager *mgr,
struct omap_dss_device *dssdev)
{
int i;
int r;
if (dssdev->manager) {
DSSERR("display '%s' already has a manager '%s'\n",
dssdev->name, dssdev->manager->name);
return -EINVAL;
}
if ((mgr->supported_displays & dssdev->type) == 0) {
DSSERR("display '%s' does not support manager '%s'\n",
dssdev->name, mgr->name);
return -EINVAL;
}
for (i = 0; i < mgr->num_overlays; i++) {
struct omap_overlay *ovl = mgr->overlays[i];
if (ovl->manager != mgr || !ovl->info.enabled)
continue;
r = dss_check_overlay(ovl, dssdev);
if (r)
return r;
}
dssdev->manager = mgr;
mgr->device = dssdev;
mgr->device_changed = true;
return 0;
}
static int omap_dss_unset_device(struct omap_overlay_manager *mgr)
{
if (!mgr->device) {
DSSERR("failed to unset display, display not set.\n");
return -EINVAL;
}
/*
* Don't allow currently enabled displays to have the overlay manager
* pulled out from underneath them
*/
if (mgr->device->state != OMAP_DSS_DISPLAY_DISABLED)
return -EINVAL;
mgr->device->manager = NULL;
mgr->device = NULL;
mgr->device_changed = true;
return 0;
}
static int dss_mgr_wait_for_vsync(struct omap_overlay_manager *mgr)
{
unsigned long timeout = msecs_to_jiffies(500);
......@@ -624,1022 +508,169 @@ static int dss_mgr_wait_for_vsync(struct omap_overlay_manager *mgr)
return omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
}
static int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr)
{
unsigned long timeout = msecs_to_jiffies(500);
struct manager_cache_data *mc;
u32 irq;
int r;
int i;
struct omap_dss_device *dssdev = mgr->device;
if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
return 0;
if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE)
return 0;
if (dssdev->type == OMAP_DISPLAY_TYPE_VENC
|| dssdev->type == OMAP_DISPLAY_TYPE_HDMI) {
irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN;
} else {
irq = (dssdev->manager->id == OMAP_DSS_CHANNEL_LCD) ?
DISPC_IRQ_VSYNC : DISPC_IRQ_VSYNC2;
}
mc = &dss_cache.manager_cache[mgr->id];
i = 0;
while (1) {
unsigned long flags;
bool shadow_dirty, dirty;
spin_lock_irqsave(&dss_cache.lock, flags);
dirty = mc->dirty;
shadow_dirty = mc->shadow_dirty;
spin_unlock_irqrestore(&dss_cache.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;
}
}
return r;
}
int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl)
{
unsigned long timeout = msecs_to_jiffies(500);
struct overlay_cache_data *oc;
struct omap_dss_device *dssdev;
u32 irq;
int r;
int i;
if (!ovl->manager)
return 0;
dssdev = ovl->manager->device;
if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
return 0;
if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE)
return 0;
if (dssdev->type == OMAP_DISPLAY_TYPE_VENC
|| dssdev->type == OMAP_DISPLAY_TYPE_HDMI) {
irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN;
} else {
irq = (dssdev->manager->id == OMAP_DSS_CHANNEL_LCD) ?
DISPC_IRQ_VSYNC : DISPC_IRQ_VSYNC2;
}
oc = &dss_cache.overlay_cache[ovl->id];
i = 0;
while (1) {
unsigned long flags;
bool shadow_dirty, dirty;
spin_lock_irqsave(&dss_cache.lock, flags);
dirty = oc->dirty;
shadow_dirty = oc->shadow_dirty;
spin_unlock_irqrestore(&dss_cache.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;
}
}
return r;
}
static int overlay_enabled(struct omap_overlay *ovl)
{
return ovl->info.enabled && ovl->manager && ovl->manager->device;
}
/* Is rect1 a subset of rect2? */
static bool rectangle_subset(int x1, int y1, int w1, int h1,
int x2, int y2, int w2, int h2)
{
if (x1 < x2 || y1 < y2)
return false;
if (x1 + w1 > x2 + w2)
return false;
if (y1 + h1 > y2 + h2)
return false;
return true;
}
/* Do rect1 and rect2 overlap? */
static bool rectangle_intersects(int x1, int y1, int w1, int h1,
int x2, int y2, int w2, int h2)
{
if (x1 >= x2 + w2)
return false;
if (x2 >= x1 + w1)
return false;
if (y1 >= y2 + h2)
return false;
if (y2 >= y1 + h1)
return false;
return true;
}
static bool dispc_is_overlay_scaled(struct overlay_cache_data *oc)
{
struct omap_overlay_info *oi = &oc->info;
if (oi->out_width != 0 && oi->width != oi->out_width)
return true;
if (oi->out_height != 0 && oi->height != oi->out_height)
return true;
return false;
}
static int configure_overlay(enum omap_plane plane)
int dss_init_overlay_managers(struct platform_device *pdev)
{
struct overlay_cache_data *c;
struct manager_cache_data *mc;
struct omap_overlay_info *oi, new_oi;
struct omap_overlay_manager_info *mi;
u16 outw, outh;
u16 x, y, w, h;
u32 paddr;
int r;
u16 orig_w, orig_h, orig_outw, orig_outh;
int i, r;
DSSDBGF("%d", plane);
num_managers = dss_feat_get_num_mgrs();
c = &dss_cache.overlay_cache[plane];
oi = &c->info;
managers = kzalloc(sizeof(struct omap_overlay_manager) * num_managers,
GFP_KERNEL);
if (!c->enabled) {
dispc_ovl_enable(plane, 0);
return 0;
}
BUG_ON(managers == NULL);
mc = &dss_cache.manager_cache[c->channel];
mi = &mc->info;
x = oi->pos_x;
y = oi->pos_y;
w = oi->width;
h = oi->height;
outw = oi->out_width == 0 ? oi->width : oi->out_width;
outh = oi->out_height == 0 ? oi->height : oi->out_height;
paddr = oi->paddr;
orig_w = w;
orig_h = h;
orig_outw = outw;
orig_outh = outh;
if (mc->manual_update && mc->do_manual_update) {
unsigned bpp;
unsigned scale_x_m = w, scale_x_d = outw;
unsigned scale_y_m = h, scale_y_d = outh;
/* If the overlay is outside the update region, disable it */
if (!rectangle_intersects(mc->x, mc->y, mc->w, mc->h,
x, y, outw, outh)) {
dispc_ovl_enable(plane, 0);
return 0;
}
for (i = 0; i < num_managers; ++i) {
struct omap_overlay_manager *mgr = &managers[i];
switch (oi->color_mode) {
case OMAP_DSS_COLOR_NV12:
bpp = 8;
break;
case OMAP_DSS_COLOR_RGB16:
case OMAP_DSS_COLOR_ARGB16:
case OMAP_DSS_COLOR_YUV2:
case OMAP_DSS_COLOR_UYVY:
case OMAP_DSS_COLOR_RGBA16:
case OMAP_DSS_COLOR_RGBX16:
case OMAP_DSS_COLOR_ARGB16_1555:
case OMAP_DSS_COLOR_XRGB16_1555:
bpp = 16;
switch (i) {
case 0:
mgr->name = "lcd";
mgr->id = OMAP_DSS_CHANNEL_LCD;
break;
case OMAP_DSS_COLOR_RGB24P:
bpp = 24;
case 1:
mgr->name = "tv";
mgr->id = OMAP_DSS_CHANNEL_DIGIT;
break;
case OMAP_DSS_COLOR_RGB24U:
case OMAP_DSS_COLOR_ARGB32:
case OMAP_DSS_COLOR_RGBA32:
case OMAP_DSS_COLOR_RGBX32:
bpp = 32;
case 2:
mgr->name = "lcd2";
mgr->id = OMAP_DSS_CHANNEL_LCD2;
break;
default:
BUG();
}
if (mc->x > oi->pos_x) {
x = 0;
outw -= (mc->x - oi->pos_x);
paddr += (mc->x - oi->pos_x) *
scale_x_m / scale_x_d * bpp / 8;
} else {
x = oi->pos_x - mc->x;
}
if (mc->y > oi->pos_y) {
y = 0;
outh -= (mc->y - oi->pos_y);
paddr += (mc->y - oi->pos_y) *
scale_y_m / scale_y_d *
oi->screen_width * bpp / 8;
} else {
y = oi->pos_y - mc->y;
}
if (mc->w < (x + outw))
outw -= (x + outw) - (mc->w);
if (mc->h < (y + outh))
outh -= (y + outh) - (mc->h);
w = w * outw / orig_outw;
h = h * outh / orig_outh;
/* YUV mode overlay's input width has to be even and the
* algorithm above may adjust the width to be odd.
*
* Here we adjust the width if needed, preferring to increase
* the width if the original width was bigger.
*/
if ((w & 1) &&
(oi->color_mode == OMAP_DSS_COLOR_YUV2 ||
oi->color_mode == OMAP_DSS_COLOR_UYVY)) {
if (orig_w > w)
w += 1;
else
w -= 1;
}
}
new_oi = *oi;
/* update new_oi members which could have been possibly updated */
new_oi.pos_x = x;
new_oi.pos_y = y;
new_oi.width = w;
new_oi.height = h;
new_oi.out_width = outw;
new_oi.out_height = outh;
new_oi.paddr = paddr;
r = dispc_ovl_setup(plane, &new_oi, c->ilace, c->channel,
c->replication, c->fifo_low, c->fifo_high);
if (r) {
/* this shouldn't happen */
DSSERR("dispc_ovl_setup failed for ovl %d\n", plane);
dispc_ovl_enable(plane, 0);
return r;
}
dispc_ovl_enable(plane, 1);
return 0;
}
static void configure_manager(enum omap_channel channel)
{
struct omap_overlay_manager_info *mi;
DSSDBGF("%d", channel);
/* picking info from the cache */
mi = &dss_cache.manager_cache[channel].info;
dispc_mgr_set_default_color(channel, mi->default_color);
dispc_mgr_set_trans_key(channel, mi->trans_key_type, mi->trans_key);
dispc_mgr_enable_trans_key(channel, mi->trans_enabled);
dispc_mgr_enable_alpha_fixed_zorder(channel, mi->partial_alpha_enabled);
if (dss_has_feature(FEAT_CPR)) {
dispc_mgr_enable_cpr(channel, mi->cpr_enable);
dispc_mgr_set_cpr_coef(channel, &mi->cpr_coefs);
}
}
/* configure_dispc() tries to write values from cache to shadow registers.
* It writes only to those managers/overlays that are not busy.
* returns 0 if everything could be written to shadow registers.
* returns 1 if not everything could be written to shadow registers. */
static int configure_dispc(void)
{
struct overlay_cache_data *oc;
struct manager_cache_data *mc;
const int num_ovls = dss_feat_get_num_ovls();
const int num_mgrs = dss_feat_get_num_mgrs();
int i;
int r;
bool mgr_busy[MAX_DSS_MANAGERS];
bool mgr_go[MAX_DSS_MANAGERS];
bool busy;
r = 0;
busy = false;
for (i = 0; i < num_mgrs; i++) {
mgr_busy[i] = dispc_mgr_go_busy(i);
mgr_go[i] = false;
}
/* Commit overlay settings */
for (i = 0; i < num_ovls; ++i) {
oc = &dss_cache.overlay_cache[i];
mc = &dss_cache.manager_cache[oc->channel];
mgr->set_device = &dss_mgr_set_device;
mgr->unset_device = &dss_mgr_unset_device;
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;
if (!oc->dirty)
continue;
mgr->caps = 0;
mgr->supported_displays =
dss_feat_get_supported_displays(mgr->id);
if (mc->manual_update && !mc->do_manual_update)
continue;
INIT_LIST_HEAD(&mgr->overlays);
if (mgr_busy[oc->channel]) {
busy = true;
continue;
}
r = kobject_init_and_add(&mgr->kobj, &manager_ktype,
&pdev->dev.kobj, "manager%d", i);
r = configure_overlay(i);
if (r)
DSSERR("configure_overlay %d failed\n", i);
oc->dirty = false;
oc->shadow_dirty = true;
mgr_go[oc->channel] = true;
}
/* Commit manager settings */
for (i = 0; i < num_mgrs; ++i) {
mc = &dss_cache.manager_cache[i];
if (!mc->dirty)
continue;
if (mc->manual_update && !mc->do_manual_update)
continue;
if (mgr_busy[i]) {
busy = true;
continue;
}
configure_manager(i);
mc->dirty = false;
mc->shadow_dirty = true;
mgr_go[i] = true;
}
/* set GO */
for (i = 0; i < num_mgrs; ++i) {
mc = &dss_cache.manager_cache[i];
if (!mgr_go[i])
continue;
/* We don't need GO with manual update display. LCD iface will
* always be turned off after frame, and new settings will be
* taken in to use at next update */
if (!mc->manual_update)
dispc_mgr_go(i);
}
if (busy)
r = 1;
else
r = 0;
return r;
}
/* Make the coordinates even. There are some strange problems with OMAP and
* partial DSI update when the update widths are odd. */
static void make_even(u16 *x, u16 *w)
{
u16 x1, x2;
x1 = *x;
x2 = *x + *w;
x1 &= ~1;
x2 = ALIGN(x2, 2);
*x = x1;
*w = x2 - x1;
}
/* Configure dispc for partial update. Return possibly modified update
* area */
void dss_setup_partial_planes(struct omap_dss_device *dssdev,
u16 *xi, u16 *yi, u16 *wi, u16 *hi, bool enlarge_update_area)
{
struct overlay_cache_data *oc;
struct manager_cache_data *mc;
struct omap_overlay_info *oi;
const int num_ovls = dss_feat_get_num_ovls();
struct omap_overlay_manager *mgr;
int i;
u16 x, y, w, h;
unsigned long flags;
bool area_changed;
x = *xi;
y = *yi;
w = *wi;
h = *hi;
DSSDBG("dispc_setup_partial_planes %d,%d %dx%d\n",
*xi, *yi, *wi, *hi);
mgr = dssdev->manager;
if (!mgr) {
DSSDBG("no manager\n");
return;
DSSERR("failed to create sysfs file\n");
}
make_even(&x, &w);
spin_lock_irqsave(&dss_cache.lock, flags);
/*
* Execute the outer loop until the inner loop has completed
* once without increasing the update area. This will ensure that
* all scaled overlays end up completely within the update area.
*/
do {
area_changed = false;
/* We need to show the whole overlay if it is scaled. So look
* for those, and make the update area larger if found.
* Also mark the overlay cache dirty */
for (i = 0; i < num_ovls; ++i) {
unsigned x1, y1, x2, y2;
unsigned outw, outh;
oc = &dss_cache.overlay_cache[i];
oi = &oc->info;
if (oc->channel != mgr->id)
continue;
oc->dirty = true;
if (!enlarge_update_area)
continue;
if (!oc->enabled)
continue;
if (!dispc_is_overlay_scaled(oc))
continue;
outw = oi->out_width == 0 ?
oi->width : oi->out_width;
outh = oi->out_height == 0 ?
oi->height : oi->out_height;
/* is the overlay outside the update region? */
if (!rectangle_intersects(x, y, w, h,
oi->pos_x, oi->pos_y,
outw, outh))
continue;
/* if the overlay totally inside the update region? */
if (rectangle_subset(oi->pos_x, oi->pos_y, outw, outh,
x, y, w, h))
continue;
if (x > oi->pos_x)
x1 = oi->pos_x;
else
x1 = x;
if (y > oi->pos_y)
y1 = oi->pos_y;
else
y1 = y;
if ((x + w) < (oi->pos_x + outw))
x2 = oi->pos_x + outw;
else
x2 = x + w;
if ((y + h) < (oi->pos_y + outh))
y2 = oi->pos_y + outh;
else
y2 = y + h;
x = x1;
y = y1;
w = x2 - x1;
h = y2 - y1;
make_even(&x, &w);
DSSDBG("changing upd area due to ovl(%d) "
"scaling %d,%d %dx%d\n",
i, x, y, w, h);
area_changed = true;
}
} while (area_changed);
mc = &dss_cache.manager_cache[mgr->id];
mc->do_manual_update = true;
mc->enlarge_update_area = enlarge_update_area;
mc->x = x;
mc->y = y;
mc->w = w;
mc->h = h;
configure_dispc();
mc->do_manual_update = false;
spin_unlock_irqrestore(&dss_cache.lock, flags);
*xi = x;
*yi = y;
*wi = w;
*hi = h;
return 0;
}
void dss_start_update(struct omap_dss_device *dssdev)
void dss_uninit_overlay_managers(struct platform_device *pdev)
{
struct manager_cache_data *mc;
struct overlay_cache_data *oc;
const int num_ovls = dss_feat_get_num_ovls();
const int num_mgrs = dss_feat_get_num_mgrs();
struct omap_overlay_manager *mgr;
int i;
mgr = dssdev->manager;
for (i = 0; i < num_managers; ++i) {
struct omap_overlay_manager *mgr = &managers[i];
for (i = 0; i < num_ovls; ++i) {
oc = &dss_cache.overlay_cache[i];
if (oc->channel != mgr->id)
continue;
oc->shadow_dirty = false;
}
for (i = 0; i < num_mgrs; ++i) {
mc = &dss_cache.manager_cache[i];
if (mgr->id != i)
continue;
mc->shadow_dirty = false;
kobject_del(&mgr->kobj);
kobject_put(&mgr->kobj);
}
dssdev->manager->enable(dssdev->manager);
kfree(managers);
managers = NULL;
num_managers = 0;
}
static void dss_apply_irq_handler(void *data, u32 mask)
int omap_dss_get_num_overlay_managers(void)
{
struct manager_cache_data *mc;
struct overlay_cache_data *oc;
const int num_ovls = dss_feat_get_num_ovls();
const int num_mgrs = dss_feat_get_num_mgrs();
int i, r;
bool mgr_busy[MAX_DSS_MANAGERS];
u32 irq_mask;
for (i = 0; i < num_mgrs; i++)
mgr_busy[i] = dispc_mgr_go_busy(i);
spin_lock(&dss_cache.lock);
for (i = 0; i < num_ovls; ++i) {
oc = &dss_cache.overlay_cache[i];
if (!mgr_busy[oc->channel])
oc->shadow_dirty = false;
}
for (i = 0; i < num_mgrs; ++i) {
mc = &dss_cache.manager_cache[i];
if (!mgr_busy[i])
mc->shadow_dirty = false;
}
r = configure_dispc();
if (r == 1)
goto end;
/* re-read busy flags */
for (i = 0; i < num_mgrs; i++)
mgr_busy[i] = dispc_mgr_go_busy(i);
/* keep running as long as there are busy managers, so that
* we can collect overlay-applied information */
for (i = 0; i < num_mgrs; ++i) {
if (mgr_busy[i])
goto end;
}
irq_mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD |
DISPC_IRQ_EVSYNC_EVEN;
if (dss_has_feature(FEAT_MGR_LCD2))
irq_mask |= DISPC_IRQ_VSYNC2;
omap_dispc_unregister_isr(dss_apply_irq_handler, NULL, irq_mask);
dss_cache.irq_enabled = false;
end:
spin_unlock(&dss_cache.lock);
return num_managers;
}
EXPORT_SYMBOL(omap_dss_get_num_overlay_managers);
static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
struct omap_overlay_manager *omap_dss_get_overlay_manager(int num)
{
struct overlay_cache_data *oc;
struct manager_cache_data *mc;
int i;
struct omap_overlay *ovl;
int num_planes_enabled = 0;
bool use_fifomerge;
unsigned long flags;
int r;
DSSDBG("omap_dss_mgr_apply(%s)\n", mgr->name);
r = dispc_runtime_get();
if (r)
return r;
spin_lock_irqsave(&dss_cache.lock, flags);
/* Configure overlays */
for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
struct omap_dss_device *dssdev;
ovl = omap_dss_get_overlay(i);
oc = &dss_cache.overlay_cache[ovl->id];
if (ovl->manager_changed) {
ovl->manager_changed = false;
ovl->info_dirty = true;
}
if (!overlay_enabled(ovl)) {
if (oc->enabled) {
oc->enabled = false;
oc->dirty = true;
}
continue;
}
if (!ovl->info_dirty) {
if (oc->enabled)
++num_planes_enabled;
continue;
}
dssdev = ovl->manager->device;
if (dss_check_overlay(ovl, dssdev)) {
if (oc->enabled) {
oc->enabled = false;
oc->dirty = true;
}
continue;
}
ovl->info_dirty = false;
oc->dirty = true;
oc->info = ovl->info;
oc->replication =
dss_use_replication(dssdev, ovl->info.color_mode);
oc->ilace = dssdev->type == OMAP_DISPLAY_TYPE_VENC;
oc->channel = ovl->manager->id;
oc->enabled = true;
++num_planes_enabled;
}
/* Configure managers */
list_for_each_entry(mgr, &manager_list, list) {
struct omap_dss_device *dssdev;
if (num >= num_managers)
return NULL;
mc = &dss_cache.manager_cache[mgr->id];
if (mgr->device_changed) {
mgr->device_changed = false;
mgr->info_dirty = true;
}
if (!mgr->info_dirty)
continue;
if (!mgr->device)
continue;
dssdev = mgr->device;
mgr->info_dirty = false;
mc->dirty = true;
mc->info = mgr->info;
mc->manual_update =
dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
}
/* XXX TODO: Try to get fifomerge working. The problem is that it
* affects both managers, not individually but at the same time. This
* means the change has to be well synchronized. I guess the proper way
* is to have a two step process for fifo merge:
* fifomerge enable:
* 1. disable other planes, leaving one plane enabled
* 2. wait until the planes are disabled on HW
* 3. config merged fifo thresholds, enable fifomerge
* fifomerge disable:
* 1. config unmerged fifo thresholds, disable fifomerge
* 2. wait until fifo changes are in HW
* 3. enable planes
*/
use_fifomerge = false;
/* Configure overlay fifos */
for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
struct omap_dss_device *dssdev;
u32 size, burst_size;
ovl = omap_dss_get_overlay(i);
oc = &dss_cache.overlay_cache[ovl->id];
if (!oc->enabled)
continue;
dssdev = ovl->manager->device;
size = dispc_ovl_get_fifo_size(ovl->id);
if (use_fifomerge)
size *= 3;
burst_size = dispc_ovl_get_burst_size(ovl->id);
switch (dssdev->type) {
case OMAP_DISPLAY_TYPE_DPI:
case OMAP_DISPLAY_TYPE_DBI:
case OMAP_DISPLAY_TYPE_SDI:
case OMAP_DISPLAY_TYPE_VENC:
case OMAP_DISPLAY_TYPE_HDMI:
default_get_overlay_fifo_thresholds(ovl->id, size,
burst_size, &oc->fifo_low,
&oc->fifo_high);
break;
#ifdef CONFIG_OMAP2_DSS_DSI
case OMAP_DISPLAY_TYPE_DSI:
dsi_get_overlay_fifo_thresholds(ovl->id, size,
burst_size, &oc->fifo_low,
&oc->fifo_high);
break;
#endif
default:
BUG();
}
}
r = 0;
if (!dss_cache.irq_enabled) {
u32 mask;
mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD |
DISPC_IRQ_EVSYNC_EVEN;
if (dss_has_feature(FEAT_MGR_LCD2))
mask |= DISPC_IRQ_VSYNC2;
r = omap_dispc_register_isr(dss_apply_irq_handler, NULL, mask);
dss_cache.irq_enabled = true;
}
configure_dispc();
spin_unlock_irqrestore(&dss_cache.lock, flags);
dispc_runtime_put();
return r;
return &managers[num];
}
EXPORT_SYMBOL(omap_dss_get_overlay_manager);
static int dss_check_manager(struct omap_overlay_manager *mgr)
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
* Alpha Mode.
*/
if (mgr->info.partial_alpha_enabled && mgr->info.trans_enabled
&& mgr->info.trans_key_type !=
OMAP_DSS_COLOR_KEY_GFX_DST)
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 omap_dss_mgr_set_info(struct omap_overlay_manager *mgr,
struct omap_overlay_manager_info *info)
{
int r;
struct omap_overlay_manager_info old_info;
old_info = mgr->info;
mgr->info = *info;
r = dss_check_manager(mgr);
if (r) {
mgr->info = old_info;
return r;
}
mgr->info_dirty = true;
return 0;
}
static void omap_dss_mgr_get_info(struct omap_overlay_manager *mgr,
struct omap_overlay_manager_info *info)
static int dss_mgr_check_zorder(struct omap_overlay_manager *mgr,
struct omap_overlay_info **overlay_infos)
{
*info = mgr->info;
}
struct omap_overlay *ovl1, *ovl2;
struct omap_overlay_info *info1, *info2;
static int dss_mgr_enable(struct omap_overlay_manager *mgr)
{
dispc_mgr_enable(mgr->id, 1);
return 0;
}
list_for_each_entry(ovl1, &mgr->overlays, list) {
info1 = overlay_infos[ovl1->id];
static int dss_mgr_disable(struct omap_overlay_manager *mgr)
{
dispc_mgr_enable(mgr->id, 0);
return 0;
}
static void omap_dss_add_overlay_manager(struct omap_overlay_manager *manager)
{
++num_managers;
list_add_tail(&manager->list, &manager_list);
}
int dss_init_overlay_managers(struct platform_device *pdev)
{
int i, r;
spin_lock_init(&dss_cache.lock);
INIT_LIST_HEAD(&manager_list);
num_managers = 0;
for (i = 0; i < dss_feat_get_num_mgrs(); ++i) {
struct omap_overlay_manager *mgr;
mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
BUG_ON(mgr == NULL);
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;
}
mgr->set_device = &omap_dss_set_device;
mgr->unset_device = &omap_dss_unset_device;
mgr->apply = &omap_dss_mgr_apply;
mgr->set_manager_info = &omap_dss_mgr_set_info;
mgr->get_manager_info = &omap_dss_mgr_get_info;
mgr->wait_for_go = &dss_mgr_wait_for_go;
mgr->wait_for_vsync = &dss_mgr_wait_for_vsync;
mgr->enable = &dss_mgr_enable;
mgr->disable = &dss_mgr_disable;
mgr->caps = 0;
mgr->supported_displays =
dss_feat_get_supported_displays(mgr->id);
if (info1 == NULL)
continue;
dss_overlay_setup_dispc_manager(mgr);
list_for_each_entry(ovl2, &mgr->overlays, list) {
if (ovl1 == ovl2)
continue;
omap_dss_add_overlay_manager(mgr);
info2 = overlay_infos[ovl2->id];
r = kobject_init_and_add(&mgr->kobj, &manager_ktype,
&pdev->dev.kobj, "manager%d", i);
if (info2 == NULL)
continue;
if (r) {
DSSERR("failed to create sysfs file\n");
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;
}
void dss_uninit_overlay_managers(struct platform_device *pdev)
int dss_mgr_check(struct omap_overlay_manager *mgr,
struct omap_dss_device *dssdev,
struct omap_overlay_manager_info *info,
struct omap_overlay_info **overlay_infos)
{
struct omap_overlay_manager *mgr;
struct omap_overlay *ovl;
int r;
while (!list_empty(&manager_list)) {
mgr = list_first_entry(&manager_list,
struct omap_overlay_manager, list);
list_del(&mgr->list);
kobject_del(&mgr->kobj);
kobject_put(&mgr->kobj);
kfree(mgr);
if (dss_has_feature(FEAT_ALPHA_FREE_ZORDER)) {
r = dss_mgr_check_zorder(mgr, overlay_infos);
if (r)
return r;
}
num_managers = 0;
}
list_for_each_entry(ovl, &mgr->overlays, list) {
struct omap_overlay_info *oi;
int r;
int omap_dss_get_num_overlay_managers(void)
{
return num_managers;
}
EXPORT_SYMBOL(omap_dss_get_num_overlay_managers);
oi = overlay_infos[ovl->id];
struct omap_overlay_manager *omap_dss_get_overlay_manager(int num)
{
int i = 0;
struct omap_overlay_manager *mgr;
if (oi == NULL)
continue;
list_for_each_entry(mgr, &manager_list, list) {
if (i++ == num)
return mgr;
r = dss_ovl_check(ovl, oi, dssdev);
if (r)
return r;
}
return NULL;
return 0;
}
EXPORT_SYMBOL(omap_dss_get_overlay_manager);
......@@ -38,7 +38,7 @@
#include "dss_features.h"
static int num_overlays;
static struct list_head overlay_list;
static struct omap_overlay *overlays;
static ssize_t overlay_name_show(struct omap_overlay *ovl, char *buf)
{
......@@ -124,19 +124,31 @@ static ssize_t overlay_manager_store(struct omap_overlay *ovl, const char *buf,
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",
ovl->info.width, ovl->info.height);
info.width, info.height);
}
static ssize_t overlay_screen_width_show(struct omap_overlay *ovl, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%d\n", ovl->info.screen_width);
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",
ovl->info.pos_x, ovl->info.pos_y);
info.pos_x, info.pos_y);
}
static ssize_t overlay_position_store(struct omap_overlay *ovl,
......@@ -170,8 +182,12 @@ static ssize_t overlay_position_store(struct omap_overlay *ovl,
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",
ovl->info.out_width, ovl->info.out_height);
info.out_width, info.out_height);
}
static ssize_t overlay_output_size_store(struct omap_overlay *ovl,
......@@ -205,7 +221,7 @@ static ssize_t overlay_output_size_store(struct omap_overlay *ovl,
static ssize_t overlay_enabled_show(struct omap_overlay *ovl, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%d\n", ovl->info.enabled);
return snprintf(buf, PAGE_SIZE, "%d\n", ovl->is_enabled(ovl));
}
static ssize_t overlay_enabled_store(struct omap_overlay *ovl, const char *buf,
......@@ -213,33 +229,30 @@ static ssize_t overlay_enabled_store(struct omap_overlay *ovl, const char *buf,
{
int r;
bool enable;
struct omap_overlay_info info;
ovl->get_overlay_info(ovl, &info);
r = strtobool(buf, &enable);
if (r)
return r;
info.enabled = enable;
if (enable)
r = ovl->enable(ovl);
else
r = ovl->disable(ovl);
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_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",
ovl->info.global_alpha);
info.global_alpha);
}
static ssize_t overlay_global_alpha_store(struct omap_overlay *ovl,
......@@ -276,8 +289,12 @@ static ssize_t overlay_global_alpha_store(struct omap_overlay *ovl,
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",
ovl->info.pre_mult_alpha);
info.pre_mult_alpha);
}
static ssize_t overlay_pre_mult_alpha_store(struct omap_overlay *ovl,
......@@ -313,7 +330,11 @@ static ssize_t overlay_pre_mult_alpha_store(struct omap_overlay *ovl,
static ssize_t overlay_zorder_show(struct omap_overlay *ovl, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%d\n", ovl->info.zorder);
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,
......@@ -430,183 +451,6 @@ static struct kobj_type overlay_ktype = {
.default_attrs = overlay_sysfs_attrs,
};
/* Check if overlay parameters are compatible with display */
int dss_check_overlay(struct omap_overlay *ovl, struct omap_dss_device *dssdev)
{
struct omap_overlay_info *info;
u16 outw, outh;
u16 dw, dh;
int i;
if (!dssdev)
return 0;
if (!ovl->info.enabled)
return 0;
info = &ovl->info;
if (info->paddr == 0) {
DSSDBG("check_overlay failed: paddr 0\n");
return -EINVAL;
}
dssdev->driver->get_resolution(dssdev, &dw, &dh);
DSSDBG("check_overlay %d: (%d,%d %dx%d -> %dx%d) disp (%dx%d)\n",
ovl->id,
info->pos_x, info->pos_y,
info->width, info->height,
info->out_width, info->out_height,
dw, dh);
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) {
DSSDBG("check_overlay failed 1: %d < %d + %d\n",
dw, info->pos_x, outw);
return -EINVAL;
}
if (dh < info->pos_y + outh) {
DSSDBG("check_overlay failed 2: %d < %d + %d\n",
dh, info->pos_y, outh);
return -EINVAL;
}
if ((ovl->supported_modes & info->color_mode) == 0) {
DSSERR("overlay doesn't support mode %d\n", info->color_mode);
return -EINVAL;
}
if (ovl->caps & OMAP_DSS_OVL_CAP_ZORDER) {
if (info->zorder < 0 || info->zorder > 3) {
DSSERR("zorder out of range: %d\n",
info->zorder);
return -EINVAL;
}
/*
* Check that zorder doesn't match with zorder of any other
* overlay which is enabled and is also connected to the same
* manager
*/
for (i = 0; i < omap_dss_get_num_overlays(); i++) {
struct omap_overlay *tmp_ovl = omap_dss_get_overlay(i);
if (tmp_ovl->id != ovl->id &&
tmp_ovl->manager == ovl->manager &&
tmp_ovl->info.enabled == true &&
tmp_ovl->info.zorder == info->zorder) {
DSSERR("%s and %s have same zorder: %d\n",
ovl->name, tmp_ovl->name, info->zorder);
return -EINVAL;
}
}
}
return 0;
}
static int dss_ovl_set_overlay_info(struct omap_overlay *ovl,
struct omap_overlay_info *info)
{
int r;
struct omap_overlay_info old_info;
old_info = ovl->info;
ovl->info = *info;
if (ovl->manager) {
r = dss_check_overlay(ovl, ovl->manager->device);
if (r) {
ovl->info = old_info;
return r;
}
}
ovl->info_dirty = true;
return 0;
}
static void dss_ovl_get_overlay_info(struct omap_overlay *ovl,
struct omap_overlay_info *info)
{
*info = ovl->info;
}
static int dss_ovl_wait_for_go(struct omap_overlay *ovl)
{
return dss_mgr_wait_for_go_ovl(ovl);
}
static int omap_dss_set_manager(struct omap_overlay *ovl,
struct omap_overlay_manager *mgr)
{
if (!mgr)
return -EINVAL;
if (ovl->manager) {
DSSERR("overlay '%s' already has a manager '%s'\n",
ovl->name, ovl->manager->name);
return -EINVAL;
}
if (ovl->info.enabled) {
DSSERR("overlay has to be disabled to change the manager\n");
return -EINVAL;
}
ovl->manager = mgr;
ovl->manager_changed = true;
/* XXX: When there is an overlay on a DSI manual update display, and
* the overlay is first disabled, then moved to tv, and enabled, we
* seem to get SYNC_LOST_DIGIT error.
*
* Waiting doesn't seem to help, but updating the manual update display
* after disabling the overlay seems to fix this. This hints that the
* overlay is perhaps somehow tied to the LCD output until the output
* is updated.
*
* Userspace workaround for this is to update the LCD after disabling
* the overlay, but before moving the overlay to TV.
*/
return 0;
}
static int omap_dss_unset_manager(struct omap_overlay *ovl)
{
if (!ovl->manager) {
DSSERR("failed to detach overlay: manager not set\n");
return -EINVAL;
}
if (ovl->info.enabled) {
DSSERR("overlay has to be disabled to unset the manager\n");
return -EINVAL;
}
ovl->manager = NULL;
ovl->manager_changed = true;
return 0;
}
int omap_dss_get_num_overlays(void)
{
return num_overlays;
......@@ -615,134 +459,65 @@ EXPORT_SYMBOL(omap_dss_get_num_overlays);
struct omap_overlay *omap_dss_get_overlay(int num)
{
int i = 0;
struct omap_overlay *ovl;
if (num >= num_overlays)
return NULL;
list_for_each_entry(ovl, &overlay_list, list) {
if (i++ == num)
return ovl;
}
return NULL;
return &overlays[num];
}
EXPORT_SYMBOL(omap_dss_get_overlay);
static void omap_dss_add_overlay(struct omap_overlay *overlay)
{
++num_overlays;
list_add_tail(&overlay->list, &overlay_list);
}
static struct omap_overlay *dispc_overlays[MAX_DSS_OVERLAYS];
void dss_overlay_setup_dispc_manager(struct omap_overlay_manager *mgr)
{
mgr->num_overlays = dss_feat_get_num_ovls();
mgr->overlays = dispc_overlays;
}
#ifdef L4_EXAMPLE
static struct omap_overlay *l4_overlays[1];
void dss_overlay_setup_l4_manager(struct omap_overlay_manager *mgr)
{
mgr->num_overlays = 1;
mgr->overlays = l4_overlays;
}
#endif
void dss_init_overlays(struct platform_device *pdev)
{
int i, r;
INIT_LIST_HEAD(&overlay_list);
num_overlays = dss_feat_get_num_ovls();
num_overlays = 0;
overlays = kzalloc(sizeof(struct omap_overlay) * num_overlays,
GFP_KERNEL);
for (i = 0; i < dss_feat_get_num_ovls(); ++i) {
struct omap_overlay *ovl;
ovl = kzalloc(sizeof(*ovl), GFP_KERNEL);
BUG_ON(overlays == NULL);
BUG_ON(ovl == 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;
ovl->info.global_alpha = 255;
ovl->info.zorder = 0;
break;
case 1:
ovl->name = "vid1";
ovl->id = OMAP_DSS_VIDEO1;
ovl->info.global_alpha = 255;
ovl->info.zorder =
dss_has_feature(FEAT_ALPHA_FREE_ZORDER) ? 3 : 0;
break;
case 2:
ovl->name = "vid2";
ovl->id = OMAP_DSS_VIDEO2;
ovl->info.global_alpha = 255;
ovl->info.zorder =
dss_has_feature(FEAT_ALPHA_FREE_ZORDER) ? 2 : 0;
break;
case 3:
ovl->name = "vid3";
ovl->id = OMAP_DSS_VIDEO3;
ovl->info.global_alpha = 255;
ovl->info.zorder =
dss_has_feature(FEAT_ALPHA_FREE_ZORDER) ? 1 : 0;
break;
}
ovl->set_manager = &omap_dss_set_manager;
ovl->unset_manager = &omap_dss_unset_manager;
ovl->set_overlay_info = &dss_ovl_set_overlay_info;
ovl->get_overlay_info = &dss_ovl_get_overlay_info;
ovl->wait_for_go = &dss_ovl_wait_for_go;
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->caps = dss_feat_get_overlay_caps(ovl->id);
ovl->supported_modes =
dss_feat_get_supported_color_modes(ovl->id);
omap_dss_add_overlay(ovl);
r = kobject_init_and_add(&ovl->kobj, &overlay_ktype,
&pdev->dev.kobj, "overlay%d", i);
if (r) {
DSSERR("failed to create sysfs file\n");
continue;
}
dispc_overlays[i] = ovl;
}
#ifdef L4_EXAMPLE
{
struct omap_overlay *ovl;
ovl = kzalloc(sizeof(*ovl), GFP_KERNEL);
BUG_ON(ovl == NULL);
ovl->name = "l4";
ovl->supported_modes = OMAP_DSS_COLOR_RGB24U;
ovl->set_manager = &omap_dss_set_manager;
ovl->unset_manager = &omap_dss_unset_manager;
ovl->set_overlay_info = &dss_ovl_set_overlay_info;
ovl->get_overlay_info = &dss_ovl_get_overlay_info;
omap_dss_add_overlay(ovl);
r = kobject_init_and_add(&ovl->kobj, &overlay_ktype,
&pdev->dev.kobj, "overlayl4");
if (r)
DSSERR("failed to create sysfs file\n");
l4_overlays[0] = ovl;
}
#endif
}
/* connect overlays to the new device, if not already connected. if force
......@@ -795,8 +570,8 @@ void dss_recheck_connections(struct omap_dss_device *dssdev, bool force)
ovl = omap_dss_get_overlay(i);
if (!ovl->manager || force) {
if (ovl->manager)
omap_dss_unset_manager(ovl);
omap_dss_set_manager(ovl, mgr);
ovl->unset_manager(ovl);
ovl->set_manager(ovl, mgr);
}
}
......@@ -806,17 +581,95 @@ void dss_recheck_connections(struct omap_dss_device *dssdev, bool force)
void dss_uninit_overlays(struct platform_device *pdev)
{
struct omap_overlay *ovl;
int i;
for (i = 0; i < num_overlays; ++i) {
struct omap_overlay *ovl = &overlays[i];
while (!list_empty(&overlay_list)) {
ovl = list_first_entry(&overlay_list,
struct omap_overlay, list);
list_del(&ovl->list);
kobject_del(&ovl->kobj);
kobject_put(&ovl->kobj);
kfree(ovl);
}
kfree(overlays);
overlays = NULL;
num_overlays = 0;
}
int dss_ovl_simple_check(struct omap_overlay *ovl,
const struct omap_overlay_info *info)
{
if (info->paddr == 0) {
DSSERR("check_overlay: paddr cannot be 0\n");
return -EINVAL;
}
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;
}
return 0;
}
int dss_ovl_check(struct omap_overlay *ovl,
struct omap_overlay_info *info, struct omap_dss_device *dssdev)
{
u16 outw, outh;
u16 dw, dh;
if (dssdev == NULL)
return 0;
dssdev->driver->get_resolution(dssdev, &dw, &dh);
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;
}
......@@ -784,7 +784,6 @@ int omap_rfbi_prepare_update(struct omap_dss_device *dssdev,
if (*w == 0 || *h == 0)
return -EINVAL;
dss_setup_partial_planes(dssdev, x, y, w, h, true);
dispc_mgr_set_lcd_size(dssdev->manager->id, *w, *h);
return 0;
......
......@@ -123,10 +123,14 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev)
goto err_sdi_enable;
mdelay(2);
dssdev->manager->enable(dssdev->manager);
r = dss_mgr_enable(dssdev->manager);
if (r)
goto err_mgr_enable;
return 0;
err_mgr_enable:
dss_sdi_disable();
err_sdi_enable:
err_set_dispc_clock_div:
err_set_dss_clock_div:
......@@ -145,7 +149,7 @@ EXPORT_SYMBOL(omapdss_sdi_display_enable);
void omapdss_sdi_display_disable(struct omap_dss_device *dssdev)
{
dssdev->manager->disable(dssdev->manager);
dss_mgr_disable(dssdev->manager);
dss_sdi_disable();
......
......@@ -110,6 +110,11 @@ struct ti_hdmi_ip_ops {
void (*dump_phy)(struct hdmi_ip_data *ip_data, struct seq_file *s);
#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \
defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE)
void (*audio_enable)(struct hdmi_ip_data *ip_data, bool start);
#endif
};
struct hdmi_ip_data {
......@@ -134,5 +139,8 @@ void ti_hdmi_4xxx_wp_dump(struct hdmi_ip_data *ip_data, struct seq_file *s);
void ti_hdmi_4xxx_pll_dump(struct hdmi_ip_data *ip_data, struct seq_file *s);
void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s);
void ti_hdmi_4xxx_phy_dump(struct hdmi_ip_data *ip_data, struct seq_file *s);
#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \
defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE)
void ti_hdmi_4xxx_wp_audio_enable(struct hdmi_ip_data *ip_data, bool enable);
#endif
#endif
......@@ -1204,36 +1204,13 @@ int hdmi_config_audio_acr(struct hdmi_ip_data *ip_data,
return 0;
}
int hdmi_audio_trigger(struct hdmi_ip_data *ip_data,
struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
void ti_hdmi_4xxx_wp_audio_enable(struct hdmi_ip_data *ip_data, bool enable)
{
int err = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
REG_FLD_MOD(hdmi_av_base(ip_data),
HDMI_CORE_AV_AUD_MODE, 1, 0, 0);
REG_FLD_MOD(hdmi_wp_base(ip_data),
HDMI_WP_AUDIO_CTRL, 1, 31, 31);
REG_FLD_MOD(hdmi_wp_base(ip_data),
HDMI_WP_AUDIO_CTRL, 1, 30, 30);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
REG_FLD_MOD(hdmi_av_base(ip_data),
HDMI_CORE_AV_AUD_MODE, 0, 0, 0);
REG_FLD_MOD(hdmi_wp_base(ip_data),
HDMI_WP_AUDIO_CTRL, 0, 30, 30);
REG_FLD_MOD(hdmi_wp_base(ip_data),
HDMI_WP_AUDIO_CTRL, 0, 31, 31);
break;
default:
err = -EINVAL;
}
return err;
REG_FLD_MOD(hdmi_av_base(ip_data),
HDMI_CORE_AV_AUD_MODE, enable, 0, 0);
REG_FLD_MOD(hdmi_wp_base(ip_data),
HDMI_WP_AUDIO_CTRL, enable, 31, 31);
REG_FLD_MOD(hdmi_wp_base(ip_data),
HDMI_WP_AUDIO_CTRL, enable, 30, 30);
}
#endif
......@@ -576,9 +576,6 @@ struct hdmi_core_audio_config {
#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \
defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE)
int hdmi_audio_trigger(struct hdmi_ip_data *ip_data,
struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai);
int hdmi_config_audio_acr(struct hdmi_ip_data *ip_data,
u32 sample_freq, u32 *n, u32 *cts);
void hdmi_core_audio_infoframe_config(struct hdmi_ip_data *ip_data,
......
......@@ -417,9 +417,10 @@ static const struct venc_config *venc_timings_to_config(
BUG();
}
static void venc_power_on(struct omap_dss_device *dssdev)
static int venc_power_on(struct omap_dss_device *dssdev)
{
u32 l;
int r;
venc_reset();
venc_write_config(venc_timings_to_config(&dssdev->panel.timings));
......@@ -447,7 +448,22 @@ static void venc_power_on(struct omap_dss_device *dssdev)
if (dssdev->platform_enable)
dssdev->platform_enable(dssdev);
dssdev->manager->enable(dssdev->manager);
r = dss_mgr_enable(dssdev->manager);
if (r)
goto err;
return 0;
err:
venc_write_reg(VENC_OUTPUT_CONTROL, 0);
dss_set_dac_pwrdn_bgz(0);
if (dssdev->platform_disable)
dssdev->platform_disable(dssdev);
regulator_disable(venc.vdda_dac_reg);
return r;
}
static void venc_power_off(struct omap_dss_device *dssdev)
......@@ -455,7 +471,7 @@ static void venc_power_off(struct omap_dss_device *dssdev)
venc_write_reg(VENC_OUTPUT_CONTROL, 0);
dss_set_dac_pwrdn_bgz(0);
dssdev->manager->disable(dssdev->manager);
dss_mgr_disable(dssdev->manager);
if (dssdev->platform_disable)
dssdev->platform_disable(dssdev);
......@@ -504,7 +520,9 @@ static int venc_panel_enable(struct omap_dss_device *dssdev)
if (r)
goto err1;
venc_power_on(dssdev);
r = venc_power_on(dssdev);
if (r)
goto err2;
venc.wss_data = 0;
......@@ -512,6 +530,8 @@ static int venc_panel_enable(struct omap_dss_device *dssdev)
mutex_unlock(&venc.venc_lock);
return 0;
err2:
venc_runtime_put();
err1:
omap_dss_stop_device(dssdev);
err0:
......
......@@ -111,28 +111,22 @@ static int omapfb_setup_plane(struct fb_info *fbi, struct omapfb_plane_info *pi)
set_fb_fix(fbi);
}
if (pi->enabled) {
struct omap_overlay_info info;
if (!pi->enabled) {
r = ovl->disable(ovl);
if (r)
goto undo;
}
if (pi->enabled) {
r = omapfb_setup_overlay(fbi, ovl, pi->pos_x, pi->pos_y,
pi->out_width, pi->out_height);
if (r)
goto undo;
ovl->get_overlay_info(ovl, &info);
if (!info.enabled) {
info.enabled = pi->enabled;
r = ovl->set_overlay_info(ovl, &info);
if (r)
goto undo;
}
} else {
struct omap_overlay_info info;
ovl->get_overlay_info(ovl, &info);
info.enabled = pi->enabled;
info.pos_x = pi->pos_x;
info.pos_y = pi->pos_y;
info.out_width = pi->out_width;
......@@ -146,6 +140,12 @@ static int omapfb_setup_plane(struct fb_info *fbi, struct omapfb_plane_info *pi)
if (ovl->manager)
ovl->manager->apply(ovl->manager);
if (pi->enabled) {
r = ovl->enable(ovl);
if (r)
goto undo;
}
/* Release the locks in a specific order to keep lockdep happy */
if (old_rg->id > new_rg->id) {
omapfb_put_mem_region(old_rg);
......@@ -189,19 +189,19 @@ static int omapfb_query_plane(struct fb_info *fbi, struct omapfb_plane_info *pi)
memset(pi, 0, sizeof(*pi));
} else {
struct omap_overlay *ovl;
struct omap_overlay_info *ovli;
struct omap_overlay_info ovli;
ovl = ofbi->overlays[0];
ovli = &ovl->info;
ovl->get_overlay_info(ovl, &ovli);
pi->pos_x = ovli->pos_x;
pi->pos_y = ovli->pos_y;
pi->enabled = ovli->enabled;
pi->pos_x = ovli.pos_x;
pi->pos_y = ovli.pos_y;
pi->enabled = ovl->is_enabled(ovl);
pi->channel_out = 0; /* xxx */
pi->mirror = 0;
pi->mem_idx = get_mem_idx(ofbi);
pi->out_width = ovli->out_width;
pi->out_height = ovli->out_height;
pi->out_width = ovli.out_width;
pi->out_height = ovli.out_height;
}
return 0;
......@@ -238,7 +238,9 @@ static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)
continue;
for (j = 0; j < ofbi2->num_overlays; j++) {
if (ofbi2->overlays[j]->info.enabled) {
struct omap_overlay *ovl;
ovl = ofbi2->overlays[j];
if (ovl->is_enabled(ovl)) {
r = -EBUSY;
goto out;
}
......
......@@ -970,16 +970,20 @@ int omapfb_apply_changes(struct fb_info *fbi, int init)
outh = var->yres;
}
} else {
outw = ovl->info.out_width;
outh = ovl->info.out_height;
struct omap_overlay_info info;
ovl->get_overlay_info(ovl, &info);
outw = info.out_width;
outh = info.out_height;
}
if (init) {
posx = 0;
posy = 0;
} else {
posx = ovl->info.pos_x;
posy = ovl->info.pos_y;
struct omap_overlay_info info;
ovl->get_overlay_info(ovl, &info);
posx = info.pos_x;
posy = info.pos_y;
}
r = omapfb_setup_overlay(fbi, ovl, posx, posy, outw, outh);
......@@ -2067,6 +2071,8 @@ static int omapfb_create_framebuffers(struct omapfb2_device *fbdev)
if (ofbi->num_overlays > 0) {
struct omap_overlay *ovl = ofbi->overlays[0];
ovl->manager->apply(ovl->manager);
r = omapfb_overlay_enable(ovl, 1);
if (r) {
......
......@@ -473,7 +473,9 @@ static ssize_t store_size(struct device *dev, struct device_attribute *attr,
continue;
for (j = 0; j < ofbi2->num_overlays; j++) {
if (ofbi2->overlays[j]->info.enabled) {
struct omap_overlay *ovl;
ovl = ofbi2->overlays[j];
if (ovl->is_enabled(ovl)) {
r = -EBUSY;
goto out;
}
......
......@@ -181,13 +181,10 @@ static inline void omapfb_unlock(struct omapfb2_device *fbdev)
static inline int omapfb_overlay_enable(struct omap_overlay *ovl,
int enable)
{
struct omap_overlay_info info;
ovl->get_overlay_info(ovl, &info);
if (info.enabled == enable)
return 0;
info.enabled = enable;
return ovl->set_overlay_info(ovl, &info);
if (enable)
return ovl->enable(ovl);
else
return ovl->disable(ovl);
}
static inline struct omapfb2_mem_region *
......
......@@ -200,6 +200,10 @@ enum omap_dss_clk_source {
OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DSI, /* OMAP4: PLL2_CLK2 */
};
enum omap_hdmi_flags {
OMAP_HDMI_SDA_SCL_EXTERNAL_PULLUP = 1 << 0,
};
/* RFBI */
struct rfbi_timings {
......@@ -294,8 +298,8 @@ int dsi_vc_set_max_rx_packet_size(struct omap_dss_device *dssdev, int channel,
u16 len);
int dsi_vc_send_null(struct omap_dss_device *dssdev, int channel);
int dsi_vc_send_bta_sync(struct omap_dss_device *dssdev, int channel);
int dsi_video_mode_enable(struct omap_dss_device *dssdev, int channel);
void dsi_video_mode_disable(struct omap_dss_device *dssdev, int channel);
int dsi_enable_video_output(struct omap_dss_device *dssdev, int channel);
void dsi_disable_video_output(struct omap_dss_device *dssdev, int channel);
/* Board specific data */
struct omap_dss_board_info {
......@@ -309,6 +313,8 @@ 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_display_platform_data {
struct omap_dss_board_info *board_data;
......@@ -352,8 +358,6 @@ struct omap_dss_cpr_coefs {
};
struct omap_overlay_info {
bool enabled;
u32 paddr;
u32 p_uv_addr; /* for NV12 format */
u16 screen_width;
......@@ -385,11 +389,21 @@ struct omap_overlay {
/* dynamic fields */
struct omap_overlay_manager *manager;
struct omap_overlay_info info;
bool manager_changed;
/* if true, info has been changed, but not applied() yet */
bool info_dirty;
/*
* The following functions do not block:
*
* is_enabled
* set_overlay_info
* get_overlay_info
*
* The rest of the functions may block and cannot be called from
* interrupt context
*/
int (*enable)(struct omap_overlay *ovl);
int (*disable)(struct omap_overlay *ovl);
bool (*is_enabled)(struct omap_overlay *ovl);
int (*set_manager)(struct omap_overlay *ovl,
struct omap_overlay_manager *mgr);
......@@ -418,23 +432,27 @@ struct omap_overlay_manager_info {
struct omap_overlay_manager {
struct kobject kobj;
struct list_head list;
/* static fields */
const char *name;
enum omap_channel id;
enum omap_overlay_manager_caps caps;
int num_overlays;
struct omap_overlay **overlays;
struct list_head overlays;
enum omap_display_type supported_displays;
/* dynamic fields */
struct omap_dss_device *device;
struct omap_overlay_manager_info info;
bool device_changed;
/* if true, info has been changed but not applied() yet */
bool info_dirty;
/*
* The following functions do not block:
*
* set_manager_info
* get_manager_info
* apply
*
* The rest of the functions may block and cannot be called from
* interrupt context
*/
int (*set_device)(struct omap_overlay_manager *mgr,
struct omap_dss_device *dssdev);
......@@ -448,9 +466,6 @@ struct omap_overlay_manager {
int (*apply)(struct omap_overlay_manager *mgr);
int (*wait_for_go)(struct omap_overlay_manager *mgr);
int (*wait_for_vsync)(struct omap_overlay_manager *mgr);
int (*enable)(struct omap_overlay_manager *mgr);
int (*disable)(struct omap_overlay_manager *mgr);
};
struct omap_dss_device {
......@@ -662,12 +677,7 @@ void omapdss_dsi_vc_enable_hs(struct omap_dss_device *dssdev, int channel,
bool enable);
int omapdss_dsi_enable_te(struct omap_dss_device *dssdev, bool enable);
int omap_dsi_prepare_update(struct omap_dss_device *dssdev,
u16 *x, u16 *y, u16 *w, u16 *h,
bool enlarge_update_area);
int omap_dsi_update(struct omap_dss_device *dssdev,
int channel,
u16 x, u16 y, u16 w, u16 h,
int omap_dsi_update(struct omap_dss_device *dssdev, int channel,
void (*callback)(int, void *), void *data);
int omap_dsi_request_vc(struct omap_dss_device *dssdev, int *channel);
int omap_dsi_set_vc_id(struct omap_dss_device *dssdev, int channel, int vc_id);
......
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