diff options
Diffstat (limited to 'drivers/video/omap2')
36 files changed, 3811 insertions, 2912 deletions
diff --git a/drivers/video/omap2/displays/Kconfig b/drivers/video/omap2/displays/Kconfig index 8d8e1fe1901c..408a9927be92 100644 --- a/drivers/video/omap2/displays/Kconfig +++ b/drivers/video/omap2/displays/Kconfig @@ -12,7 +12,7 @@ config PANEL_GENERIC_DPI config PANEL_DVI tristate "DVI output" - depends on OMAP2_DSS_DPI + depends on OMAP2_DSS_DPI && I2C help Driver for external monitors, connected via DVI. The driver uses i2c to read EDID information from the monitor. @@ -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/ diff --git a/drivers/video/omap2/displays/panel-acx565akm.c b/drivers/video/omap2/displays/panel-acx565akm.c index dbd59b8e5b36..d26f37ac69d8 100644 --- a/drivers/video/omap2/displays/panel-acx565akm.c +++ b/drivers/video/omap2/displays/panel-acx565akm.c @@ -803,25 +803,13 @@ static int acx565akm_spi_remove(struct spi_device *spi) static struct spi_driver acx565akm_spi_driver = { .driver = { .name = "acx565akm", - .bus = &spi_bus_type, .owner = THIS_MODULE, }, .probe = acx565akm_spi_probe, .remove = __devexit_p(acx565akm_spi_remove), }; -static int __init acx565akm_init(void) -{ - return spi_register_driver(&acx565akm_spi_driver); -} - -static void __exit acx565akm_exit(void) -{ - spi_unregister_driver(&acx565akm_spi_driver); -} - -module_init(acx565akm_init); -module_exit(acx565akm_exit); +module_spi_driver(acx565akm_spi_driver); MODULE_AUTHOR("Nokia Corporation"); MODULE_DESCRIPTION("acx565akm LCD Driver"); diff --git a/drivers/video/omap2/displays/panel-generic-dpi.c b/drivers/video/omap2/displays/panel-generic-dpi.c index 519c47d2057f..30fe4dfeb227 100644 --- a/drivers/video/omap2/displays/panel-generic-dpi.c +++ b/drivers/video/omap2/displays/panel-generic-dpi.c @@ -297,6 +297,95 @@ 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", + }, + + /* Innolux AT080TN52 */ + { + { + .x_res = 800, + .y_res = 600, + + .pixel_clock = 41142, + + .hsw = 20, + .hfp = 210, + .hbp = 46, + + .vsw = 10, + .vfp = 12, + .vbp = 23, + }, + .acb = 0x0, + .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | + OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IEO, + + .name = "innolux_at080tn52", + }, }; struct panel_drv_data { diff --git a/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c b/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c index e0eb35be303e..0841cc2b3f77 100644 --- a/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c +++ b/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c @@ -264,16 +264,6 @@ static struct spi_driver lb035q02_spi_driver = { .remove = __devexit_p(lb035q02_panel_spi_remove), }; -static int __init lb035q02_panel_drv_init(void) -{ - return spi_register_driver(&lb035q02_spi_driver); -} - -static void __exit lb035q02_panel_drv_exit(void) -{ - spi_unregister_driver(&lb035q02_spi_driver); -} +module_spi_driver(lb035q02_spi_driver); -module_init(lb035q02_panel_drv_init); -module_exit(lb035q02_panel_drv_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/displays/panel-n8x0.c b/drivers/video/omap2/displays/panel-n8x0.c index 150e8bae35a1..dc9408dc93d1 100644 --- a/drivers/video/omap2/displays/panel-n8x0.c +++ b/drivers/video/omap2/displays/panel-n8x0.c @@ -708,7 +708,6 @@ static int mipid_spi_remove(struct spi_device *spi) static struct spi_driver mipid_spi_driver = { .driver = { .name = "lcd_mipid", - .bus = &spi_bus_type, .owner = THIS_MODULE, }, .probe = mipid_spi_probe, diff --git a/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c b/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c index 2ba9d0ca187c..8b38b39213f4 100644 --- a/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c +++ b/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c @@ -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) @@ -303,23 +346,12 @@ static struct spi_driver nec_8048_spi_driver = { .resume = nec_8048_spi_resume, .driver = { .name = "nec_8048_spi", - .bus = &spi_bus_type, .owner = THIS_MODULE, }, }; -static int __init nec_8048_lcd_init(void) -{ - return spi_register_driver(&nec_8048_spi_driver); -} - -static void __exit nec_8048_lcd_exit(void) -{ - return spi_unregister_driver(&nec_8048_spi_driver); -} +module_spi_driver(nec_8048_spi_driver); -module_init(nec_8048_lcd_init); -module_exit(nec_8048_lcd_exit); MODULE_AUTHOR("Erik Gilling <konkers@android.com>"); MODULE_DESCRIPTION("NEC-nl8048hl11-01b Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/displays/panel-taal.c b/drivers/video/omap2/displays/panel-taal.c index 80c3f6ab1a94..0f21fa5a16ae 100644 --- a/drivers/video/omap2/displays/panel-taal.c +++ b/drivers/video/omap2/displays/panel-taal.c @@ -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; @@ -1025,14 +1019,12 @@ static int taal_probe(struct omap_dss_device *dssdev) if (panel_data->use_ext_te) { int gpio = panel_data->ext_te_gpio; - r = gpio_request(gpio, "taal irq"); + r = gpio_request_one(gpio, GPIOF_IN, "taal irq"); if (r) { dev_err(&dssdev->dev, "GPIO request failed\n"); goto err_gpio; } - gpio_direction_input(gpio); - r = request_irq(gpio_to_irq(gpio), taal_te_isr, IRQF_TRIGGER_RISING, "taal vsync", dssdev); @@ -1188,6 +1180,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 +1213,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 +1392,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 +1438,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; } diff --git a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c index 2462b9ec6662..32f3fcd7f0f0 100644 --- a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c +++ b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c @@ -47,16 +47,20 @@ TPO_R03_EN_PRE_CHARGE | TPO_R03_SOFTWARE_CTL) static const u16 tpo_td043_def_gamma[12] = { - 106, 200, 289, 375, 460, 543, 625, 705, 785, 864, 942, 1020 + 105, 315, 381, 431, 490, 537, 579, 686, 780, 837, 880, 1023 }; struct tpo_td043_device { struct spi_device *spi; struct regulator *vcc_reg; + int nreset_gpio; u16 gamma[12]; u32 mode; u32 hmirror:1; u32 vmirror:1; + u32 powered_on:1; + u32 spi_suspended:1; + u32 power_on_resume:1; }; static int tpo_td043_write(struct spi_device *spi, u8 addr, u8 data) @@ -265,28 +269,16 @@ static const struct omap_video_timings tpo_td043_timings = { .vbp = 34, }; -static int tpo_td043_power_on(struct omap_dss_device *dssdev) +static int tpo_td043_power_on(struct tpo_td043_device *tpo_td043) { - struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev); - int nreset_gpio = dssdev->reset_gpio; - int r; + int nreset_gpio = tpo_td043->nreset_gpio; - if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) + if (tpo_td043->powered_on) return 0; - r = omapdss_dpi_display_enable(dssdev); - if (r) - goto err0; - - if (dssdev->platform_enable) { - r = dssdev->platform_enable(dssdev); - if (r) - goto err1; - } - regulator_enable(tpo_td043->vcc_reg); - /* wait for power up */ + /* wait for regulator to stabilize */ msleep(160); if (gpio_is_valid(nreset_gpio)) @@ -301,19 +293,15 @@ static int tpo_td043_power_on(struct omap_dss_device *dssdev) tpo_td043->vmirror); tpo_td043_write_gamma(tpo_td043->spi, tpo_td043->gamma); + tpo_td043->powered_on = 1; return 0; -err1: - omapdss_dpi_display_disable(dssdev); -err0: - return r; } -static void tpo_td043_power_off(struct omap_dss_device *dssdev) +static void tpo_td043_power_off(struct tpo_td043_device *tpo_td043) { - struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev); - int nreset_gpio = dssdev->reset_gpio; + int nreset_gpio = tpo_td043->nreset_gpio; - if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) + if (!tpo_td043->powered_on) return; tpo_td043_write(tpo_td043->spi, 3, @@ -329,54 +317,94 @@ static void tpo_td043_power_off(struct omap_dss_device *dssdev) regulator_disable(tpo_td043->vcc_reg); + tpo_td043->powered_on = 0; +} + +static int tpo_td043_enable_dss(struct omap_dss_device *dssdev) +{ + struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev); + int r; + + 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) + goto err1; + } + + /* + * If we are resuming from system suspend, SPI clocks might not be + * enabled yet, so we'll program the LCD from SPI PM resume callback. + */ + if (!tpo_td043->spi_suspended) { + r = tpo_td043_power_on(tpo_td043); + if (r) + goto err1; + } + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + return 0; +err1: + omapdss_dpi_display_disable(dssdev); +err0: + return r; +} + +static void tpo_td043_disable_dss(struct omap_dss_device *dssdev) +{ + struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev); + + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) + return; + if (dssdev->platform_disable) dssdev->platform_disable(dssdev); omapdss_dpi_display_disable(dssdev); + + if (!tpo_td043->spi_suspended) + tpo_td043_power_off(tpo_td043); } static int tpo_td043_enable(struct omap_dss_device *dssdev) { - int ret; - dev_dbg(&dssdev->dev, "enable\n"); - ret = tpo_td043_power_on(dssdev); - if (ret) - return ret; - - dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; - - return 0; + return tpo_td043_enable_dss(dssdev); } static void tpo_td043_disable(struct omap_dss_device *dssdev) { dev_dbg(&dssdev->dev, "disable\n"); - tpo_td043_power_off(dssdev); + tpo_td043_disable_dss(dssdev); dssdev->state = OMAP_DSS_DISPLAY_DISABLED; } static int tpo_td043_suspend(struct omap_dss_device *dssdev) { - tpo_td043_power_off(dssdev); + dev_dbg(&dssdev->dev, "suspend\n"); + + tpo_td043_disable_dss(dssdev); + dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; + return 0; } static int tpo_td043_resume(struct omap_dss_device *dssdev) { - int r = 0; - - r = tpo_td043_power_on(dssdev); - if (r) - return r; + dev_dbg(&dssdev->dev, "resume\n"); - dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; - - return 0; + return tpo_td043_enable_dss(dssdev); } static int tpo_td043_probe(struct omap_dss_device *dssdev) @@ -408,17 +436,12 @@ static int tpo_td043_probe(struct omap_dss_device *dssdev) } if (gpio_is_valid(nreset_gpio)) { - ret = gpio_request(nreset_gpio, "lcd reset"); + ret = gpio_request_one(nreset_gpio, GPIOF_OUT_INIT_LOW, + "lcd reset"); if (ret < 0) { dev_err(&dssdev->dev, "couldn't request reset GPIO\n"); goto fail_gpio_req; } - - ret = gpio_direction_output(nreset_gpio, 0); - if (ret < 0) { - dev_err(&dssdev->dev, "couldn't set GPIO direction\n"); - goto fail_gpio_direction; - } } ret = sysfs_create_group(&dssdev->dev.kobj, &tpo_td043_attr_group); @@ -427,8 +450,6 @@ static int tpo_td043_probe(struct omap_dss_device *dssdev) return 0; -fail_gpio_direction: - gpio_free(nreset_gpio); fail_gpio_req: regulator_put(tpo_td043->vcc_reg); fail_regulator: @@ -491,6 +512,7 @@ static int tpo_td043_spi_probe(struct spi_device *spi) return -ENOMEM; tpo_td043->spi = spi; + tpo_td043->nreset_gpio = dssdev->reset_gpio; dev_set_drvdata(&spi->dev, tpo_td043); dev_set_drvdata(&dssdev->dev, tpo_td043); @@ -509,28 +531,52 @@ static int __devexit tpo_td043_spi_remove(struct spi_device *spi) return 0; } +#ifdef CONFIG_PM_SLEEP +static int tpo_td043_spi_suspend(struct device *dev) +{ + struct tpo_td043_device *tpo_td043 = dev_get_drvdata(dev); + + dev_dbg(dev, "tpo_td043_spi_suspend, tpo %p\n", tpo_td043); + + tpo_td043->power_on_resume = tpo_td043->powered_on; + tpo_td043_power_off(tpo_td043); + tpo_td043->spi_suspended = 1; + + return 0; +} + +static int tpo_td043_spi_resume(struct device *dev) +{ + struct tpo_td043_device *tpo_td043 = dev_get_drvdata(dev); + int ret; + + dev_dbg(dev, "tpo_td043_spi_resume\n"); + + if (tpo_td043->power_on_resume) { + ret = tpo_td043_power_on(tpo_td043); + if (ret) + return ret; + } + tpo_td043->spi_suspended = 0; + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(tpo_td043_spi_pm, + tpo_td043_spi_suspend, tpo_td043_spi_resume); + static struct spi_driver tpo_td043_spi_driver = { .driver = { .name = "tpo_td043mtea1_panel_spi", - .bus = &spi_bus_type, .owner = THIS_MODULE, + .pm = &tpo_td043_spi_pm, }, .probe = tpo_td043_spi_probe, .remove = __devexit_p(tpo_td043_spi_remove), }; -static int __init tpo_td043_init(void) -{ - return spi_register_driver(&tpo_td043_spi_driver); -} - -static void __exit tpo_td043_exit(void) -{ - spi_unregister_driver(&tpo_td043_spi_driver); -} - -module_init(tpo_td043_init); -module_exit(tpo_td043_exit); +module_spi_driver(tpo_td043_spi_driver); MODULE_AUTHOR("GraÅžvydas Ignotas <notasas@gmail.com>"); MODULE_DESCRIPTION("TPO TD043MTEA1 LCD Driver"); diff --git a/drivers/video/omap2/dss/Makefile b/drivers/video/omap2/dss/Makefile index bd34ac5b2026..5c450b0f94d0 100644 --- a/drivers/video/omap2/dss/Makefile +++ b/drivers/video/omap2/dss/Makefile @@ -1,5 +1,6 @@ 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 diff --git a/drivers/video/omap2/dss/apply.c b/drivers/video/omap2/dss/apply.c new file mode 100644 index 000000000000..b10b3bc1931e --- /dev/null +++ b/drivers/video/omap2/dss/apply.c @@ -0,0 +1,1497 @@ +/* + * 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 fifo_merge_dirty; + bool fifo_merge; + + 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; + int r; + + spin_lock_irqsave(&data_lock, flags); + + updating = extra_info_update_ongoing(); + + if (!updating) { + spin_unlock_irqrestore(&data_lock, flags); + return; + } + + init_completion(&extra_updated_completion); + + spin_unlock_irqrestore(&data_lock, flags); + + t = msecs_to_jiffies(500); + r = wait_for_completion_timeout(&extra_updated_completion, t); + if (r == 0) + DSSWARN("timeout in wait_pending_extra_info_updates\n"); + else if (r < 0) + DSSERR("wait_pending_extra_info_updates failed: %d\n", r); +} + +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; + + r = dispc_runtime_get(); + if (r) + return r; + + 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; + } + } + + dispc_runtime_put(); + + 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; + + r = dispc_runtime_get(); + if (r) + return r; + + irq = dispc_mgr_get_vsync_irq(ovl->manager->id); + + op = get_ovl_priv(ovl); + i = 0; + while (1) { + 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; + } + } + + dispc_runtime_put(); + + return r; +} + +static void dss_ovl_write_regs(struct omap_overlay *ovl) +{ + struct ovl_priv_data *op = get_ovl_priv(ovl); + struct omap_overlay_info *oi; + bool 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_common(void) +{ + const int num_mgrs = omap_dss_get_num_overlay_managers(); + int i; + + if (!dss_data.fifo_merge_dirty) + return; + + 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) { + if (dss_data.fifo_merge_dirty) { + dispc_enable_fifomerge(dss_data.fifo_merge); + dss_data.fifo_merge_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; + + dss_write_regs_common(); + + 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); + } + +} + +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; + } +} + +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); + + dss_write_regs_common(); + + mp->updating = true; + + if (!dss_data.irq_enabled && need_isr()) + dss_register_vsync_isr(); + + dispc_mgr_enable(mgr->id, true); + + mgr_clear_shadow_dirty(mgr); + + spin_unlock_irqrestore(&data_lock, flags); +} + +static void dss_apply_irq_handler(void *data, u32 mask); + +static void dss_register_vsync_isr(void) +{ + const int num_mgrs = dss_feat_get_num_mgrs(); + u32 mask; + int r, i; + + mask = 0; + for (i = 0; i < num_mgrs; ++i) + mask |= dispc_mgr_get_vsync_irq(i); + + for (i = 0; i < num_mgrs; ++i) + mask |= dispc_mgr_get_framedone_irq(i); + + r = omap_dispc_register_isr(dss_apply_irq_handler, NULL, mask); + WARN_ON(r); + + dss_data.irq_enabled = true; +} + +static void dss_unregister_vsync_isr(void) +{ + const int num_mgrs = dss_feat_get_num_mgrs(); + u32 mask; + int r, i; + + mask = 0; + for (i = 0; i < num_mgrs; ++i) + mask |= dispc_mgr_get_vsync_irq(i); + + for (i = 0; i < num_mgrs; ++i) + mask |= dispc_mgr_get_framedone_irq(i); + + r = omap_dispc_unregister_isr(dss_apply_irq_handler, NULL, mask); + WARN_ON(r); + + dss_data.irq_enabled = false; +} + +static void dss_apply_irq_handler(void *data, u32 mask) +{ + const int num_mgrs = dss_feat_get_num_mgrs(); + int i; + bool extra_updating; + + spin_lock(&data_lock); + + /* clear busy, updating flags, shadow_dirty flags */ + for (i = 0; i < num_mgrs; i++) { + struct omap_overlay_manager *mgr; + struct mgr_priv_data *mp; + 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); + } + } + + 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_apply_fifo_merge(bool use_fifo_merge) +{ + if (dss_data.fifo_merge == use_fifo_merge) + return; + + dss_data.fifo_merge = use_fifo_merge; + dss_data.fifo_merge_dirty = true; +} + +static void dss_ovl_setup_fifo(struct omap_overlay *ovl, + bool use_fifo_merge) +{ + struct ovl_priv_data *op = get_ovl_priv(ovl); + struct omap_dss_device *dssdev; + u32 fifo_low, fifo_high; + + if (!op->enabled && !op->enabling) + return; + + dssdev = ovl->manager->device; + + dispc_ovl_compute_fifo_thresholds(ovl->id, &fifo_low, &fifo_high, + use_fifo_merge); + + dss_apply_ovl_fifo_thresholds(ovl, fifo_low, fifo_high); +} + +static void dss_mgr_setup_fifos(struct omap_overlay_manager *mgr, + bool use_fifo_merge) +{ + 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, use_fifo_merge); +} + +static void dss_setup_fifos(bool use_fifo_merge) +{ + 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, use_fifo_merge); + } +} + +static int get_num_used_managers(void) +{ + const int num_mgrs = omap_dss_get_num_overlay_managers(); + struct omap_overlay_manager *mgr; + struct mgr_priv_data *mp; + int i; + int enabled_mgrs; + + enabled_mgrs = 0; + + for (i = 0; i < num_mgrs; ++i) { + mgr = omap_dss_get_overlay_manager(i); + mp = get_mgr_priv(mgr); + + if (!mp->enabled) + continue; + + enabled_mgrs++; + } + + return enabled_mgrs; +} + +static int get_num_used_overlays(void) +{ + const int num_ovls = omap_dss_get_num_overlays(); + struct omap_overlay *ovl; + struct ovl_priv_data *op; + struct mgr_priv_data *mp; + int i; + int enabled_ovls; + + enabled_ovls = 0; + + for (i = 0; i < num_ovls; ++i) { + ovl = omap_dss_get_overlay(i); + op = get_ovl_priv(ovl); + + if (!op->enabled && !op->enabling) + continue; + + mp = get_mgr_priv(ovl->manager); + + if (!mp->enabled) + continue; + + enabled_ovls++; + } + + return enabled_ovls; +} + +static bool get_use_fifo_merge(void) +{ + int enabled_mgrs = get_num_used_managers(); + int enabled_ovls = get_num_used_overlays(); + + if (!dss_has_feature(FEAT_FIFO_MERGE)) + return false; + + /* + * In theory the only requirement for fifomerge is enabled_ovls <= 1. + * However, if we have two managers enabled and set/unset the fifomerge, + * we need to set the GO bits in particular sequence for the managers, + * and wait in between. + * + * This is rather difficult as new apply calls can happen at any time, + * so we simplify the problem by requiring also that enabled_mgrs <= 1. + * In practice this shouldn't matter, because when only one overlay is + * enabled, most likely only one output is enabled. + */ + + return enabled_mgrs <= 1 && enabled_ovls <= 1; +} + +int dss_mgr_enable(struct omap_overlay_manager *mgr) +{ + struct mgr_priv_data *mp = get_mgr_priv(mgr); + unsigned long flags; + int r; + bool fifo_merge; + + 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; + } + + /* step 1: setup fifos/fifomerge before enabling the manager */ + + fifo_merge = get_use_fifo_merge(); + dss_setup_fifos(fifo_merge); + dss_apply_fifo_merge(fifo_merge); + + dss_write_regs(); + dss_set_go_bits(); + + spin_unlock_irqrestore(&data_lock, flags); + + /* wait until fifo config is in */ + wait_pending_extra_info_updates(); + + /* step 2: enable the manager */ + spin_lock_irqsave(&data_lock, flags); + + 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; + bool fifo_merge; + + 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; + + fifo_merge = get_use_fifo_merge(); + dss_setup_fifos(fifo_merge); + dss_apply_fifo_merge(fifo_merge); + + dss_write_regs(); + dss_set_go_bits(); + + spin_unlock_irqrestore(&data_lock, flags); + + wait_pending_extra_info_updates(); +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; + bool fifo_merge; + 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; + } + + /* step 1: configure fifos/fifomerge for currently enabled ovls */ + + fifo_merge = get_use_fifo_merge(); + dss_setup_fifos(fifo_merge); + dss_apply_fifo_merge(fifo_merge); + + dss_write_regs(); + dss_set_go_bits(); + + spin_unlock_irqrestore(&data_lock, flags); + + /* wait for fifo configs to go in */ + wait_pending_extra_info_updates(); + + /* step 2: enable the overlay */ + spin_lock_irqsave(&data_lock, flags); + + op->enabling = false; + dss_apply_ovl_enable(ovl, true); + + dss_write_regs(); + dss_set_go_bits(); + + spin_unlock_irqrestore(&data_lock, flags); + + /* wait for overlay to be enabled */ + wait_pending_extra_info_updates(); + + 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; + bool fifo_merge; + 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; + } + + /* step 1: disable the overlay */ + 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); + + /* wait for the overlay to be disabled */ + wait_pending_extra_info_updates(); + + /* step 2: configure fifos/fifomerge */ + spin_lock_irqsave(&data_lock, flags); + + fifo_merge = get_use_fifo_merge(); + dss_setup_fifos(fifo_merge); + dss_apply_fifo_merge(fifo_merge); + + dss_write_regs(); + dss_set_go_bits(); + + spin_unlock_irqrestore(&data_lock, flags); + + /* wait for fifo config to go in */ + wait_pending_extra_info_updates(); + + mutex_unlock(&apply_lock); + + return 0; + +err: + mutex_unlock(&apply_lock); + return r; +} + diff --git a/drivers/video/omap2/dss/core.c b/drivers/video/omap2/dss/core.c index 86ec12e16c7c..e8a120771ac6 100644 --- a/drivers/video/omap2/dss/core.c +++ b/drivers/video/omap2/dss/core.c @@ -50,7 +50,7 @@ module_param_named(def_disp, def_disp_name, charp, 0); MODULE_PARM_DESC(def_disp, "default display name"); #ifdef DEBUG -unsigned int dss_debug; +bool dss_debug; module_param_named(debug, dss_debug, bool, 0644); #endif @@ -178,45 +178,11 @@ static int omap_dss_probe(struct platform_device *pdev) dss_features_init(); + dss_apply_init(); + dss_init_overlay_managers(pdev); dss_init_overlays(pdev); - r = dss_init_platform_driver(); - if (r) { - DSSERR("Failed to initialize DSS platform driver\n"); - goto err_dss; - } - - r = dispc_init_platform_driver(); - if (r) { - DSSERR("Failed to initialize dispc platform driver\n"); - goto err_dispc; - } - - r = rfbi_init_platform_driver(); - if (r) { - DSSERR("Failed to initialize rfbi platform driver\n"); - goto err_rfbi; - } - - r = venc_init_platform_driver(); - if (r) { - DSSERR("Failed to initialize venc platform driver\n"); - goto err_venc; - } - - r = dsi_init_platform_driver(); - if (r) { - DSSERR("Failed to initialize DSI platform driver\n"); - goto err_dsi; - } - - r = hdmi_init_platform_driver(); - if (r) { - DSSERR("Failed to initialize hdmi\n"); - goto err_hdmi; - } - r = dss_initialize_debugfs(); if (r) goto err_debugfs; @@ -244,18 +210,6 @@ static int omap_dss_probe(struct platform_device *pdev) err_register: dss_uninitialize_debugfs(); err_debugfs: - hdmi_uninit_platform_driver(); -err_hdmi: - dsi_uninit_platform_driver(); -err_dsi: - venc_uninit_platform_driver(); -err_venc: - dispc_uninit_platform_driver(); -err_dispc: - rfbi_uninit_platform_driver(); -err_rfbi: - dss_uninit_platform_driver(); -err_dss: return r; } @@ -267,13 +221,6 @@ static int omap_dss_remove(struct platform_device *pdev) dss_uninitialize_debugfs(); - hdmi_uninit_platform_driver(); - dsi_uninit_platform_driver(); - venc_uninit_platform_driver(); - rfbi_uninit_platform_driver(); - dispc_uninit_platform_driver(); - dss_uninit_platform_driver(); - dss_uninit_overlays(pdev); dss_uninit_overlay_managers(pdev); @@ -523,6 +470,80 @@ static int omap_dss_bus_register(void) /* INIT */ +static int __init omap_dss_register_drivers(void) +{ + int r; + + r = platform_driver_register(&omap_dss_driver); + if (r) + return r; + + r = dss_init_platform_driver(); + if (r) { + DSSERR("Failed to initialize DSS platform driver\n"); + goto err_dss; + } + + r = dispc_init_platform_driver(); + if (r) { + DSSERR("Failed to initialize dispc platform driver\n"); + goto err_dispc; + } + + r = rfbi_init_platform_driver(); + if (r) { + DSSERR("Failed to initialize rfbi platform driver\n"); + goto err_rfbi; + } + + r = venc_init_platform_driver(); + if (r) { + DSSERR("Failed to initialize venc platform driver\n"); + goto err_venc; + } + + r = dsi_init_platform_driver(); + if (r) { + DSSERR("Failed to initialize DSI platform driver\n"); + goto err_dsi; + } + + r = hdmi_init_platform_driver(); + if (r) { + DSSERR("Failed to initialize hdmi\n"); + goto err_hdmi; + } + + return 0; + +err_hdmi: + dsi_uninit_platform_driver(); +err_dsi: + venc_uninit_platform_driver(); +err_venc: + rfbi_uninit_platform_driver(); +err_rfbi: + dispc_uninit_platform_driver(); +err_dispc: + dss_uninit_platform_driver(); +err_dss: + platform_driver_unregister(&omap_dss_driver); + + return r; +} + +static void __exit omap_dss_unregister_drivers(void) +{ + hdmi_uninit_platform_driver(); + dsi_uninit_platform_driver(); + venc_uninit_platform_driver(); + rfbi_uninit_platform_driver(); + dispc_uninit_platform_driver(); + dss_uninit_platform_driver(); + + platform_driver_unregister(&omap_dss_driver); +} + #ifdef CONFIG_OMAP2_DSS_MODULE static void omap_dss_bus_unregister(void) { @@ -539,7 +560,7 @@ static int __init omap_dss_init(void) if (r) return r; - r = platform_driver_register(&omap_dss_driver); + r = omap_dss_register_drivers(); if (r) { omap_dss_bus_unregister(); return r; @@ -560,7 +581,7 @@ static void __exit omap_dss_exit(void) core.vdds_sdi_reg = NULL; } - platform_driver_unregister(&omap_dss_driver); + omap_dss_unregister_drivers(); omap_dss_bus_unregister(); } @@ -575,7 +596,7 @@ static int __init omap_dss_init(void) static int __init omap_dss_init2(void) { - return platform_driver_register(&omap_dss_driver); + return omap_dss_register_drivers(); } core_initcall(omap_dss_init); diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c index 5c81533eacaa..ee30937482e1 100644 --- a/drivers/video/omap2/dss/dispc.c +++ b/drivers/video/omap2/dss/dispc.c @@ -37,7 +37,6 @@ #include <linux/platform_device.h> #include <linux/pm_runtime.h> -#include <plat/sram.h> #include <plat/clock.h> #include <video/omapdss.h> @@ -64,22 +63,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, @@ -417,7 +400,7 @@ void dispc_runtime_put(void) DSSDBG("dispc_runtime_put\n"); - r = pm_runtime_put(&dispc.pdev->dev); + r = pm_runtime_put_sync(&dispc.pdev->dev); WARN_ON(r < 0); } @@ -438,6 +421,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 +544,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 +579,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 @@ -802,11 +735,11 @@ static void dispc_ovl_set_color_mode(enum omap_plane plane, switch (color_mode) { case OMAP_DSS_COLOR_NV12: m = 0x0; break; - case OMAP_DSS_COLOR_RGB12U: + case OMAP_DSS_COLOR_RGBX16: m = 0x1; break; case OMAP_DSS_COLOR_RGBA16: m = 0x2; break; - case OMAP_DSS_COLOR_RGBX16: + case OMAP_DSS_COLOR_RGB12U: m = 0x4; break; case OMAP_DSS_COLOR_ARGB16: m = 0x5; break; @@ -855,9 +788,9 @@ static void dispc_ovl_set_color_mode(enum omap_plane plane, m = 0x8; break; case OMAP_DSS_COLOR_RGB24P: m = 0x9; break; - case OMAP_DSS_COLOR_YUV2: + case OMAP_DSS_COLOR_RGBX16: m = 0xa; break; - case OMAP_DSS_COLOR_UYVY: + case OMAP_DSS_COLOR_RGBA16: m = 0xb; break; case OMAP_DSS_COLOR_ARGB32: m = 0xc; break; @@ -875,8 +808,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 +855,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) { @@ -943,7 +908,7 @@ static void dispc_configure_burst_sizes(void) dispc_ovl_set_burst_size(i, burst_size); } -u32 dispc_ovl_get_burst_size(enum omap_plane plane) +static u32 dispc_ovl_get_burst_size(enum omap_plane plane) { unsigned unit = dss_feat_get_burst_size_unit(); /* burst multiplier is always x8 (see dispc_configure_burst_sizes()) */ @@ -964,7 +929,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 +943,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; @@ -1052,13 +1017,12 @@ static void dispc_read_plane_fifo_sizes(void) } } -u32 dispc_ovl_get_fifo_size(enum omap_plane plane) +static 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; @@ -1074,13 +1038,13 @@ static void dispc_ovl_set_fifo_threshold(enum omap_plane plane, u32 low, dss_feat_get_reg_field(FEAT_REG_FIFOHIGHTHRESHOLD, &hi_start, &hi_end); dss_feat_get_reg_field(FEAT_REG_FIFOLOWTHRESHOLD, &lo_start, &lo_end); - DSSDBG("fifo(%d) low/high old %u/%u, new %u/%u\n", + DSSDBG("fifo(%d) threshold (bytes), old %u/%u, new %u/%u\n", plane, REG_GET(DISPC_OVL_FIFO_THRESHOLD(plane), - lo_start, lo_end), + lo_start, lo_end) * unit, REG_GET(DISPC_OVL_FIFO_THRESHOLD(plane), - hi_start, hi_end), - low, high); + hi_start, hi_end) * unit, + low * unit, high * unit); dispc_write_reg(DISPC_OVL_FIFO_THRESHOLD(plane), FLD_VAL(high, hi_start, hi_end) | @@ -1089,10 +1053,53 @@ static void dispc_ovl_set_fifo_threshold(enum omap_plane plane, u32 low, void dispc_enable_fifomerge(bool enable) { + if (!dss_has_feature(FEAT_FIFO_MERGE)) { + WARN_ON(enable); + return; + } + DSSDBG("FIFO merge %s\n", enable ? "enabled" : "disabled"); REG_FLD_MOD(DISPC_CONFIG, enable ? 1 : 0, 14, 14); } +void dispc_ovl_compute_fifo_thresholds(enum omap_plane plane, + u32 *fifo_low, u32 *fifo_high, bool use_fifomerge) +{ + /* + * All sizes are in bytes. Both the buffer and burst are made of + * buffer_units, and the fifo thresholds must be buffer_unit aligned. + */ + + unsigned buf_unit = dss_feat_get_buffer_size_unit(); + unsigned ovl_fifo_size, total_fifo_size, burst_size; + int i; + + burst_size = dispc_ovl_get_burst_size(plane); + ovl_fifo_size = dispc_ovl_get_fifo_size(plane); + + if (use_fifomerge) { + total_fifo_size = 0; + for (i = 0; i < omap_dss_get_num_overlays(); ++i) + total_fifo_size += dispc_ovl_get_fifo_size(i); + } else { + total_fifo_size = ovl_fifo_size; + } + + /* + * We use the same low threshold for both fifomerge and non-fifomerge + * cases, but for fifomerge we calculate the high threshold using the + * combined fifo size + */ + + if (dss_has_feature(FEAT_OMAP3_DSI_FIFO_BUG)) { + *fifo_low = ovl_fifo_size - burst_size * 2; + *fifo_high = total_fifo_size - burst_size; + } else { + *fifo_low = ovl_fifo_size - burst_size; + *fifo_high = total_fifo_size - buf_unit; + } +} + static void dispc_ovl_set_fir(enum omap_plane plane, int hinc, int vinc, enum omap_color_component color_comp) @@ -1169,17 +1176,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 +1656,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; @@ -1688,6 +1693,7 @@ static unsigned long calc_fclk(enum omap_channel channel, u16 width, u16 height, u16 out_width, u16 out_height) { unsigned int hf, vf; + unsigned long pclk = dispc_mgr_pclk_rate(channel); /* * FIXME how to determine the 'A' factor @@ -1708,7 +1714,19 @@ 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 pclk * 4; + else + return pclk * 2; + } else if (cpu_is_omap34xx()) { + return pclk * vf * hf; + } else { + if (hf > 1) + return DIV_ROUND_UP(pclk, out_width) * width; + else + return pclk; + } } static int dispc_ovl_calc_scaling(enum omap_plane plane, @@ -1718,6 +1736,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 +1754,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 +1803,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 +1814,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 +1868,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 +1907,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 +1915,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 +1929,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 +1951,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 +2006,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 +2165,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 +2184,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 +2194,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 +2206,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) +void dispc_mgr_setup(enum omap_channel channel, + struct omap_overlay_manager_info *info) { - 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) -{ - 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 +3174,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); } } @@ -3327,11 +3318,6 @@ static void _omap_dispc_initial_config(void) if (dss_has_feature(FEAT_FUNCGATED)) REG_FLD_MOD(DISPC_CONFIG, 1, 9, 9); - /* L3 firewall setting: enable access to OCM RAM */ - /* XXX this should be somewhere in plat-omap */ - if (cpu_is_omap24xx()) - __raw_writel(0x402000b0, OMAP2_L3_IO_ADDRESS(0x680050a0)); - _dispc_setup_color_conv_coef(); dispc_set_loadmode(OMAP_DSS_LOAD_FRAME_ONLY); @@ -3353,15 +3339,6 @@ static int omap_dispchw_probe(struct platform_device *pdev) dispc.pdev = pdev; - clk = clk_get(&pdev->dev, "fck"); - if (IS_ERR(clk)) { - DSSERR("can't get fck\n"); - r = PTR_ERR(clk); - goto err_get_clk; - } - - dispc.dss_clk = clk; - spin_lock_init(&dispc.irq_lock); #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS @@ -3374,29 +3351,38 @@ static int omap_dispchw_probe(struct platform_device *pdev) dispc_mem = platform_get_resource(dispc.pdev, IORESOURCE_MEM, 0); if (!dispc_mem) { DSSERR("can't get IORESOURCE_MEM DISPC\n"); - r = -EINVAL; - goto err_ioremap; + return -EINVAL; } - dispc.base = ioremap(dispc_mem->start, resource_size(dispc_mem)); + + dispc.base = devm_ioremap(&pdev->dev, dispc_mem->start, + resource_size(dispc_mem)); if (!dispc.base) { DSSERR("can't ioremap DISPC\n"); - r = -ENOMEM; - goto err_ioremap; + return -ENOMEM; } + dispc.irq = platform_get_irq(dispc.pdev, 0); if (dispc.irq < 0) { DSSERR("platform_get_irq failed\n"); - r = -ENODEV; - goto err_irq; + return -ENODEV; } - r = request_irq(dispc.irq, omap_dispc_irq_handler, IRQF_SHARED, - "OMAP DISPC", dispc.pdev); + r = devm_request_irq(&pdev->dev, dispc.irq, omap_dispc_irq_handler, + IRQF_SHARED, "OMAP DISPC", dispc.pdev); if (r < 0) { DSSERR("request_irq failed\n"); - goto err_irq; + return r; + } + + clk = clk_get(&pdev->dev, "fck"); + if (IS_ERR(clk)) { + DSSERR("can't get fck\n"); + r = PTR_ERR(clk); + return r; } + dispc.dss_clk = clk; + pm_runtime_enable(&pdev->dev); r = dispc_runtime_get(); @@ -3417,12 +3403,7 @@ static int omap_dispchw_probe(struct platform_device *pdev) err_runtime_get: pm_runtime_disable(&pdev->dev); - free_irq(dispc.irq, dispc.pdev); -err_irq: - iounmap(dispc.base); -err_ioremap: clk_put(dispc.dss_clk); -err_get_clk: return r; } @@ -3432,8 +3413,6 @@ static int omap_dispchw_remove(struct platform_device *pdev) clk_put(dispc.dss_clk); - free_irq(dispc.irq, dispc.pdev); - iounmap(dispc.base); return 0; } diff --git a/drivers/video/omap2/dss/dispc.h b/drivers/video/omap2/dss/dispc.h index c06efc38983e..5836bd1650f9 100644 --- a/drivers/video/omap2/dss/dispc.h +++ b/drivers/video/omap2/dss/dispc.h @@ -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) { diff --git a/drivers/video/omap2/dss/dispc_coefs.c b/drivers/video/omap2/dss/dispc_coefs.c new file mode 100644 index 000000000000..038c15b04215 --- /dev/null +++ b/drivers/video/omap2/dss/dispc_coefs.c @@ -0,0 +1,325 @@ +/* + * linux/drivers/video/omap2/dss/dispc_coefs.c + * + * Copyright (C) 2011 Texas Instruments + * Author: Chandrabhanu Mahapatra <cmahapatra@ti.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/kernel.h> +#include <video/omapdss.h> + +#include "dispc.h" + +static const struct dispc_coef coef3_M8[8] = { + { 0, 0, 128, 0, 0 }, + { 0, -4, 123, 9, 0 }, + { 0, -4, 108, 24, 0 }, + { 0, -2, 87, 43, 0 }, + { 0, 64, 64, 0, 0 }, + { 0, 43, 87, -2, 0 }, + { 0, 24, 108, -4, 0 }, + { 0, 9, 123, -4, 0 }, +}; + +static const struct dispc_coef coef3_M9[8] = { + { 0, 6, 116, 6, 0 }, + { 0, 0, 112, 16, 0 }, + { 0, -2, 100, 30, 0 }, + { 0, -2, 83, 47, 0 }, + { 0, 64, 64, 0, 0 }, + { 0, 47, 83, -2, 0 }, + { 0, 30, 100, -2, 0 }, + { 0, 16, 112, 0, 0 }, +}; + +static const struct dispc_coef coef3_M10[8] = { + { 0, 10, 108, 10, 0 }, + { 0, 3, 104, 21, 0 }, + { 0, 0, 94, 34, 0 }, + { 0, -1, 80, 49, 0 }, + { 0, 64, 64, 0, 0 }, + { 0, 49, 80, -1, 0 }, + { 0, 34, 94, 0, 0 }, + { 0, 21, 104, 3, 0 }, +}; + +static const struct dispc_coef coef3_M11[8] = { + { 0, 14, 100, 14, 0 }, + { 0, 6, 98, 24, 0 }, + { 0, 2, 90, 36, 0 }, + { 0, 0, 78, 50, 0 }, + { 0, 64, 64, 0, 0 }, + { 0, 50, 78, 0, 0 }, + { 0, 36, 90, 2, 0 }, + { 0, 24, 98, 6, 0 }, +}; + +static const struct dispc_coef coef3_M12[8] = { + { 0, 16, 96, 16, 0 }, + { 0, 9, 93, 26, 0 }, + { 0, 4, 86, 38, 0 }, + { 0, 1, 76, 51, 0 }, + { 0, 64, 64, 0, 0 }, + { 0, 51, 76, 1, 0 }, + { 0, 38, 86, 4, 0 }, + { 0, 26, 93, 9, 0 }, +}; + +static const struct dispc_coef coef3_M13[8] = { + { 0, 18, 92, 18, 0 }, + { 0, 10, 90, 28, 0 }, + { 0, 5, 83, 40, 0 }, + { 0, 1, 75, 52, 0 }, + { 0, 64, 64, 0, 0 }, + { 0, 52, 75, 1, 0 }, + { 0, 40, 83, 5, 0 }, + { 0, 28, 90, 10, 0 }, +}; + +static const struct dispc_coef coef3_M14[8] = { + { 0, 20, 88, 20, 0 }, + { 0, 12, 86, 30, 0 }, + { 0, 6, 81, 41, 0 }, + { 0, 2, 74, 52, 0 }, + { 0, 64, 64, 0, 0 }, + { 0, 52, 74, 2, 0 }, + { 0, 41, 81, 6, 0 }, + { 0, 30, 86, 12, 0 }, +}; + +static const struct dispc_coef coef3_M16[8] = { + { 0, 22, 84, 22, 0 }, + { 0, 14, 82, 32, 0 }, + { 0, 8, 78, 42, 0 }, + { 0, 3, 72, 53, 0 }, + { 0, 64, 64, 0, 0 }, + { 0, 53, 72, 3, 0 }, + { 0, 42, 78, 8, 0 }, + { 0, 32, 82, 14, 0 }, +}; + +static const struct dispc_coef coef3_M19[8] = { + { 0, 24, 80, 24, 0 }, + { 0, 16, 79, 33, 0 }, + { 0, 9, 76, 43, 0 }, + { 0, 4, 70, 54, 0 }, + { 0, 64, 64, 0, 0 }, + { 0, 54, 70, 4, 0 }, + { 0, 43, 76, 9, 0 }, + { 0, 33, 79, 16, 0 }, +}; + +static const struct dispc_coef coef3_M22[8] = { + { 0, 25, 78, 25, 0 }, + { 0, 17, 77, 34, 0 }, + { 0, 10, 74, 44, 0 }, + { 0, 5, 69, 54, 0 }, + { 0, 64, 64, 0, 0 }, + { 0, 54, 69, 5, 0 }, + { 0, 44, 74, 10, 0 }, + { 0, 34, 77, 17, 0 }, +}; + +static const struct dispc_coef coef3_M26[8] = { + { 0, 26, 76, 26, 0 }, + { 0, 19, 74, 35, 0 }, + { 0, 11, 72, 45, 0 }, + { 0, 5, 69, 54, 0 }, + { 0, 64, 64, 0, 0 }, + { 0, 54, 69, 5, 0 }, + { 0, 45, 72, 11, 0 }, + { 0, 35, 74, 19, 0 }, +}; + +static const struct dispc_coef coef3_M32[8] = { + { 0, 27, 74, 27, 0 }, + { 0, 19, 73, 36, 0 }, + { 0, 12, 71, 45, 0 }, + { 0, 6, 68, 54, 0 }, + { 0, 64, 64, 0, 0 }, + { 0, 54, 68, 6, 0 }, + { 0, 45, 71, 12, 0 }, + { 0, 36, 73, 19, 0 }, +}; + +static const struct dispc_coef coef5_M8[8] = { + { 0, 0, 128, 0, 0 }, + { -2, 14, 125, -10, 1 }, + { -6, 33, 114, -15, 2 }, + { -10, 55, 98, -16, 1 }, + { 0, -14, 78, 78, -14 }, + { 1, -16, 98, 55, -10 }, + { 2, -15, 114, 33, -6 }, + { 1, -10, 125, 14, -2 }, +}; + +static const struct dispc_coef coef5_M9[8] = { + { -3, 10, 114, 10, -3 }, + { -6, 24, 111, 0, -1 }, + { -8, 40, 103, -7, 0 }, + { -11, 58, 91, -11, 1 }, + { 0, -12, 76, 76, -12 }, + { 1, -11, 91, 58, -11 }, + { 0, -7, 103, 40, -8 }, + { -1, 0, 111, 24, -6 }, +}; + +static const struct dispc_coef coef5_M10[8] = { + { -4, 18, 100, 18, -4 }, + { -6, 30, 99, 8, -3 }, + { -8, 44, 93, 0, -1 }, + { -9, 58, 84, -5, 0 }, + { 0, -8, 72, 72, -8 }, + { 0, -5, 84, 58, -9 }, + { -1, 0, 93, 44, -8 }, + { -3, 8, 99, 30, -6 }, +}; + +static const struct dispc_coef coef5_M11[8] = { + { -5, 23, 92, 23, -5 }, + { -6, 34, 90, 13, -3 }, + { -6, 45, 85, 6, -2 }, + { -6, 57, 78, 0, -1 }, + { 0, -4, 68, 68, -4 }, + { -1, 0, 78, 57, -6 }, + { -2, 6, 85, 45, -6 }, + { -3, 13, 90, 34, -6 }, +}; + +static const struct dispc_coef coef5_M12[8] = { + { -4, 26, 84, 26, -4 }, + { -5, 36, 82, 18, -3 }, + { -4, 46, 78, 10, -2 }, + { -3, 55, 72, 5, -1 }, + { 0, 0, 64, 64, 0 }, + { -1, 5, 72, 55, -3 }, + { -2, 10, 78, 46, -4 }, + { -3, 18, 82, 36, -5 }, +}; + +static const struct dispc_coef coef5_M13[8] = { + { -3, 28, 78, 28, -3 }, + { -3, 37, 76, 21, -3 }, + { -2, 45, 73, 14, -2 }, + { 0, 53, 68, 8, -1 }, + { 0, 3, 61, 61, 3 }, + { -1, 8, 68, 53, 0 }, + { -2, 14, 73, 45, -2 }, + { -3, 21, 76, 37, -3 }, +}; + +static const struct dispc_coef coef5_M14[8] = { + { -2, 30, 72, 30, -2 }, + { -1, 37, 71, 23, -2 }, + { 0, 45, 69, 16, -2 }, + { 3, 52, 64, 10, -1 }, + { 0, 6, 58, 58, 6 }, + { -1, 10, 64, 52, 3 }, + { -2, 16, 69, 45, 0 }, + { -2, 23, 71, 37, -1 }, +}; + +static const struct dispc_coef coef5_M16[8] = { + { 0, 31, 66, 31, 0 }, + { 1, 38, 65, 25, -1 }, + { 3, 44, 62, 20, -1 }, + { 6, 49, 59, 14, 0 }, + { 0, 10, 54, 54, 10 }, + { 0, 14, 59, 49, 6 }, + { -1, 20, 62, 44, 3 }, + { -1, 25, 65, 38, 1 }, +}; + +static const struct dispc_coef coef5_M19[8] = { + { 3, 32, 58, 32, 3 }, + { 4, 38, 58, 27, 1 }, + { 7, 42, 55, 23, 1 }, + { 10, 46, 54, 18, 0 }, + { 0, 14, 50, 50, 14 }, + { 0, 18, 54, 46, 10 }, + { 1, 23, 55, 42, 7 }, + { 1, 27, 58, 38, 4 }, +}; + +static const struct dispc_coef coef5_M22[8] = { + { 4, 33, 54, 33, 4 }, + { 6, 37, 54, 28, 3 }, + { 9, 41, 53, 24, 1 }, + { 12, 45, 51, 20, 0 }, + { 0, 16, 48, 48, 16 }, + { 0, 20, 51, 45, 12 }, + { 1, 24, 53, 41, 9 }, + { 3, 28, 54, 37, 6 }, +}; + +static const struct dispc_coef coef5_M26[8] = { + { 6, 33, 50, 33, 6 }, + { 8, 36, 51, 29, 4 }, + { 11, 40, 50, 25, 2 }, + { 14, 43, 48, 22, 1 }, + { 0, 18, 46, 46, 18 }, + { 1, 22, 48, 43, 14 }, + { 2, 25, 50, 40, 11 }, + { 4, 29, 51, 36, 8 }, +}; + +static const struct dispc_coef coef5_M32[8] = { + { 7, 33, 48, 33, 7 }, + { 10, 36, 48, 29, 5 }, + { 13, 39, 47, 26, 3 }, + { 16, 42, 46, 23, 1 }, + { 0, 19, 45, 45, 19 }, + { 1, 23, 46, 42, 16 }, + { 3, 26, 47, 39, 13 }, + { 5, 29, 48, 36, 10 }, +}; + +const struct dispc_coef *dispc_ovl_get_scale_coef(int inc, int five_taps) +{ + int i; + static const struct { + int Mmin; + int Mmax; + const struct dispc_coef *coef_3; + const struct dispc_coef *coef_5; + } coefs[] = { + { 27, 32, coef3_M32, coef5_M32 }, + { 23, 26, coef3_M26, coef5_M26 }, + { 20, 22, coef3_M22, coef5_M22 }, + { 17, 19, coef3_M19, coef5_M19 }, + { 15, 16, coef3_M16, coef5_M16 }, + { 14, 14, coef3_M14, coef5_M14 }, + { 13, 13, coef3_M13, coef5_M13 }, + { 12, 12, coef3_M12, coef5_M12 }, + { 11, 11, coef3_M11, coef5_M11 }, + { 10, 10, coef3_M10, coef5_M10 }, + { 9, 9, coef3_M9, coef5_M9 }, + { 4, 8, coef3_M8, coef5_M8 }, + /* + * When upscaling more than two times, blockiness and outlines + * around the image are observed when M8 tables are used. M11, + * M16 and M19 tables are used to prevent this. + */ + { 3, 3, coef3_M11, coef5_M11 }, + { 2, 2, coef3_M16, coef5_M16 }, + { 0, 1, coef3_M19, coef5_M19 }, + }; + + inc /= 128; + for (i = 0; i < ARRAY_SIZE(coefs); ++i) + if (inc >= coefs[i].Mmin && inc <= coefs[i].Mmax) + return five_taps ? coefs[i].coef_5 : coefs[i].coef_3; + return NULL; +} diff --git a/drivers/video/omap2/dss/display.c b/drivers/video/omap2/dss/display.c index be331dc5a61b..4424c198dbcd 100644 --- a/drivers/video/omap2/dss/display.c +++ b/drivers/video/omap2/dss/display.c @@ -279,16 +279,6 @@ void omapdss_default_get_resolution(struct omap_dss_device *dssdev, } EXPORT_SYMBOL(omapdss_default_get_resolution); -void default_get_overlay_fifo_thresholds(enum omap_plane plane, - u32 fifo_size, u32 burst_size, - u32 *fifo_low, u32 *fifo_high) -{ - unsigned buf_unit = dss_feat_get_buffer_size_unit(); - - *fifo_high = fifo_size - buf_unit; - *fifo_low = fifo_size - burst_size; -} - int omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev) { switch (dssdev->type) { diff --git a/drivers/video/omap2/dss/dpi.c b/drivers/video/omap2/dss/dpi.c index 976ac23dcd0c..faaf305fda27 100644 --- a/drivers/video/omap2/dss/dpi.c +++ b/drivers/video/omap2/dss/dpi.c @@ -180,6 +180,11 @@ int omapdss_dpi_display_enable(struct omap_dss_device *dssdev) { int r; + if (cpu_is_omap34xx() && !dpi.vdds_dsi_reg) { + DSSERR("no VDSS_DSI regulator\n"); + return -ENODEV; + } + if (dssdev->manager == NULL) { DSSERR("failed to enable display: no manager\n"); return -ENODEV; @@ -223,10 +228,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 +257,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); diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c index 5abf8e7e7456..662d14f8c2c3 100644 --- a/drivers/video/omap2/dss/dsi.c +++ b/drivers/video/omap2/dss/dsi.c @@ -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; }; @@ -340,8 +342,8 @@ struct dsi_packet_sent_handler_data { static struct platform_device *dsi_pdev_map[MAX_NUM_DSI]; #ifdef DEBUG -static unsigned int dsi_perf; -module_param_named(dsi_perf, dsi_perf, bool, 0644); +static bool dsi_perf; +module_param(dsi_perf, bool, 0644); #endif static inline struct dsi_data *dsi_get_dsidrv_data(struct platform_device *dsidev) @@ -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; + + /* 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; + } - while (REG_GET(dsidev, idx, bitnum, bitnum) != value) { - if (--t == 0) - 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; + 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", @@ -1065,7 +1079,7 @@ void dsi_runtime_put(struct platform_device *dsidev) DSSDBG("dsi_runtime_put\n"); - r = pm_runtime_put(&dsi->pdev->dev); + r = pm_runtime_put_sync(&dsi->pdev->dev); WARN_ON(r < 0); } @@ -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; + + 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; - 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[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; @@ -4509,19 +4524,10 @@ int omapdss_dsi_enable_te(struct omap_dss_device *dssdev, bool enable) } EXPORT_SYMBOL(omapdss_dsi_enable_te); -void dsi_get_overlay_fifo_thresholds(enum omap_plane plane, - u32 fifo_size, u32 burst_size, - u32 *fifo_low, u32 *fifo_high) -{ - *fifo_high = fifo_size - burst_size; - *fifo_low = fifo_size - burst_size * 2; -} - 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 +4549,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; } @@ -4687,11 +4687,9 @@ static int omap_dsihw_probe(struct platform_device *dsidev) struct resource *dsi_mem; struct dsi_data *dsi; - dsi = kzalloc(sizeof(*dsi), GFP_KERNEL); - if (!dsi) { - r = -ENOMEM; - goto err_alloc; - } + dsi = devm_kzalloc(&dsidev->dev, sizeof(*dsi), GFP_KERNEL); + if (!dsi) + return -ENOMEM; dsi->pdev = dsidev; dsi_pdev_map[dsi_module] = dsidev; @@ -4714,12 +4712,6 @@ static int omap_dsihw_probe(struct platform_device *dsidev) mutex_init(&dsi->lock); sema_init(&dsi->bus_lock, 1); - r = dsi_get_clocks(dsidev); - if (r) - goto err_get_clk; - - pm_runtime_enable(&dsidev->dev); - INIT_DELAYED_WORK_DEFERRABLE(&dsi->framedone_timeout_work, dsi_framedone_timeout_work_callback); @@ -4731,27 +4723,27 @@ static int omap_dsihw_probe(struct platform_device *dsidev) dsi_mem = platform_get_resource(dsi->pdev, IORESOURCE_MEM, 0); if (!dsi_mem) { DSSERR("can't get IORESOURCE_MEM DSI\n"); - r = -EINVAL; - goto err_ioremap; + return -EINVAL; } - dsi->base = ioremap(dsi_mem->start, resource_size(dsi_mem)); + + dsi->base = devm_ioremap(&dsidev->dev, dsi_mem->start, + resource_size(dsi_mem)); if (!dsi->base) { DSSERR("can't ioremap DSI\n"); - r = -ENOMEM; - goto err_ioremap; + return -ENOMEM; } + dsi->irq = platform_get_irq(dsi->pdev, 0); if (dsi->irq < 0) { DSSERR("platform_get_irq failed\n"); - r = -ENODEV; - goto err_get_irq; + return -ENODEV; } - r = request_irq(dsi->irq, omap_dsi_irq_handler, IRQF_SHARED, - dev_name(&dsidev->dev), dsi->pdev); + r = devm_request_irq(&dsidev->dev, dsi->irq, omap_dsi_irq_handler, + IRQF_SHARED, dev_name(&dsidev->dev), dsi->pdev); if (r < 0) { DSSERR("request_irq failed\n"); - goto err_get_irq; + return r; } /* DSI VCs initialization */ @@ -4763,29 +4755,35 @@ static int omap_dsihw_probe(struct platform_device *dsidev) dsi_calc_clock_param_ranges(dsidev); + r = dsi_get_clocks(dsidev); + if (r) + return r; + + pm_runtime_enable(&dsidev->dev); + r = dsi_runtime_get(dsidev); if (r) - goto err_get_dsi; + goto err_runtime_get; rev = dsi_read_reg(dsidev, DSI_REVISION); 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); return 0; -err_get_dsi: - free_irq(dsi->irq, dsi->pdev); -err_get_irq: - iounmap(dsi->base); -err_ioremap: +err_runtime_get: pm_runtime_disable(&dsidev->dev); -err_get_clk: - kfree(dsi); -err_alloc: + dsi_put_clocks(dsidev); return r; } @@ -4809,11 +4807,6 @@ static int omap_dsihw_remove(struct platform_device *dsidev) dsi->vdds_dsi_reg = NULL; } - free_irq(dsi->irq, dsi->pdev); - iounmap(dsi->base); - - kfree(dsi); - return 0; } diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c index 17033457ee89..bd2d5e159463 100644 --- a/drivers/video/omap2/dss/dss.c +++ b/drivers/video/omap2/dss/dss.c @@ -33,7 +33,10 @@ #include <linux/pm_runtime.h> #include <video/omapdss.h> + +#include <plat/cpu.h> #include <plat/clock.h> + #include "dss.h" #include "dss_features.h" @@ -720,7 +723,7 @@ void dss_runtime_put(void) DSSDBG("dss_runtime_put\n"); - r = pm_runtime_put(&dss.pdev->dev); + r = pm_runtime_put_sync(&dss.pdev->dev); WARN_ON(r < 0); } @@ -748,19 +751,19 @@ static int omap_dsshw_probe(struct platform_device *pdev) dss_mem = platform_get_resource(dss.pdev, IORESOURCE_MEM, 0); if (!dss_mem) { DSSERR("can't get IORESOURCE_MEM DSS\n"); - r = -EINVAL; - goto err_ioremap; + return -EINVAL; } - dss.base = ioremap(dss_mem->start, resource_size(dss_mem)); + + dss.base = devm_ioremap(&pdev->dev, dss_mem->start, + resource_size(dss_mem)); if (!dss.base) { DSSERR("can't ioremap DSS\n"); - r = -ENOMEM; - goto err_ioremap; + return -ENOMEM; } r = dss_get_clocks(); if (r) - goto err_clocks; + return r; pm_runtime_enable(&pdev->dev); @@ -808,9 +811,6 @@ err_dpi: err_runtime_get: pm_runtime_disable(&pdev->dev); dss_put_clocks(); -err_clocks: - iounmap(dss.base); -err_ioremap: return r; } @@ -819,8 +819,6 @@ static int omap_dsshw_remove(struct platform_device *pdev) dpi_exit(); sdi_exit(); - iounmap(dss.base); - pm_runtime_disable(&pdev->dev); dss_put_clocks(); diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h index 6308fc59fc9e..d4b3dff2ead3 100644 --- a/drivers/video/omap2/dss/dss.h +++ b/drivers/video/omap2/dss/dss.h @@ -28,7 +28,7 @@ #endif #ifdef DEBUG -extern unsigned int dss_debug; +extern bool dss_debug; #ifdef DSS_SUBSYS_NAME #define DSSDBG(format, ...) \ if (dss_debug) \ @@ -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); @@ -174,28 +202,26 @@ void dss_uninit_device(struct platform_device *pdev, struct omap_dss_device *dssdev); bool dss_use_replication(struct omap_dss_device *dssdev, enum omap_color_mode mode); -void default_get_overlay_fifo_thresholds(enum omap_plane plane, - u32 fifo_size, u32 burst_size, - u32 *fifo_low, u32 *fifo_high); /* 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); @@ -284,9 +310,6 @@ int dsi_pll_calc_clock_div_pck(struct platform_device *dsidev, bool is_tft, int dsi_pll_init(struct platform_device *dsidev, bool enable_hsclk, bool enable_hsdiv); void dsi_pll_uninit(struct platform_device *dsidev, bool disconnect_lanes); -void dsi_get_overlay_fifo_thresholds(enum omap_plane plane, - u32 fifo_size, u32 burst_size, - u32 *fifo_low, u32 *fifo_high); void dsi_wait_pll_hsdiv_dispc_active(struct platform_device *dsidev); void dsi_wait_pll_hsdiv_dsi_active(struct platform_device *dsidev); struct platform_device *dsi_get_dsidev_from_id(int module); @@ -399,21 +422,22 @@ int dispc_calc_clock_rates(unsigned long dispc_fclk_rate, struct dispc_clock_info *cinfo); -u32 dispc_ovl_get_fifo_size(enum omap_plane plane); -u32 dispc_ovl_get_burst_size(enum omap_plane plane); +void dispc_ovl_set_fifo_threshold(enum omap_plane plane, u32 low, u32 high); +void dispc_ovl_compute_fifo_thresholds(enum omap_plane plane, + u32 *fifo_low, u32 *fifo_high, bool use_fifomerge); 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 +445,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 +455,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 diff --git a/drivers/video/omap2/dss/dss_features.c b/drivers/video/omap2/dss/dss_features.c index b402699168a5..ce14aa6dd672 100644 --- a/drivers/video/omap2/dss/dss_features.c +++ b/drivers/video/omap2/dss/dss_features.c @@ -41,7 +41,8 @@ struct omap_dss_features { const struct dss_reg_field *reg_fields; const int num_reg_fields; - const u32 has_feature; + const enum dss_feat_id *features; + const int num_features; const int num_mgrs; const int num_ovls; @@ -189,7 +190,8 @@ static const enum omap_color_mode omap4_dss_supported_color_modes[] = { OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U | OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_ARGB32 | OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_RGBX32 | - OMAP_DSS_COLOR_ARGB16_1555, + OMAP_DSS_COLOR_ARGB16_1555 | OMAP_DSS_COLOR_RGBX16 | + OMAP_DSS_COLOR_RGBA16 | OMAP_DSS_COLOR_XRGB16_1555, /* OMAP_DSS_VIDEO1 */ OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB12U | @@ -304,6 +306,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 +323,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 +336,104 @@ 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 }, +}; + +static const enum dss_feat_id omap2_dss_feat_list[] = { + FEAT_LCDENABLEPOL, + FEAT_LCDENABLESIGNAL, + FEAT_PCKFREEENABLE, + FEAT_FUNCGATED, + FEAT_ROWREPEATENABLE, + FEAT_RESIZECONF, +}; + +static const enum dss_feat_id omap3430_dss_feat_list[] = { + FEAT_LCDENABLEPOL, + FEAT_LCDENABLESIGNAL, + FEAT_PCKFREEENABLE, + FEAT_FUNCGATED, + FEAT_LINEBUFFERSPLIT, + FEAT_ROWREPEATENABLE, + FEAT_RESIZECONF, + FEAT_DSI_PLL_FREQSEL, + FEAT_DSI_REVERSE_TXCLKESC, + FEAT_VENC_REQUIRES_TV_DAC_CLK, + FEAT_CPR, + FEAT_PRELOAD, + FEAT_FIR_COEF_V, + FEAT_ALPHA_FIXED_ZORDER, + FEAT_FIFO_MERGE, + FEAT_OMAP3_DSI_FIFO_BUG, +}; + +static const enum dss_feat_id omap3630_dss_feat_list[] = { + FEAT_LCDENABLEPOL, + FEAT_LCDENABLESIGNAL, + FEAT_PCKFREEENABLE, + FEAT_FUNCGATED, + FEAT_LINEBUFFERSPLIT, + FEAT_ROWREPEATENABLE, + FEAT_RESIZECONF, + FEAT_DSI_PLL_PWR_BUG, + FEAT_DSI_PLL_FREQSEL, + FEAT_CPR, + FEAT_PRELOAD, + FEAT_FIR_COEF_V, + FEAT_ALPHA_FIXED_ZORDER, + FEAT_FIFO_MERGE, + FEAT_OMAP3_DSI_FIFO_BUG, +}; + +static const enum dss_feat_id omap4430_es1_0_dss_feat_list[] = { + FEAT_MGR_LCD2, + FEAT_CORE_CLK_DIV, + FEAT_LCD_CLK_SRC, + FEAT_DSI_DCS_CMD_CONFIG_VC, + FEAT_DSI_VC_OCP_WIDTH, + FEAT_DSI_GNQ, + FEAT_HANDLE_UV_SEPARATE, + FEAT_ATTR2, + FEAT_CPR, + FEAT_PRELOAD, + FEAT_FIR_COEF_V, + FEAT_ALPHA_FREE_ZORDER, + FEAT_FIFO_MERGE, +}; + +static const enum dss_feat_id omap4430_es2_0_1_2_dss_feat_list[] = { + FEAT_MGR_LCD2, + FEAT_CORE_CLK_DIV, + FEAT_LCD_CLK_SRC, + FEAT_DSI_DCS_CMD_CONFIG_VC, + FEAT_DSI_VC_OCP_WIDTH, + FEAT_DSI_GNQ, + FEAT_HDMI_CTS_SWMODE, + FEAT_HANDLE_UV_SEPARATE, + FEAT_ATTR2, + FEAT_CPR, + FEAT_PRELOAD, + FEAT_FIR_COEF_V, + FEAT_ALPHA_FREE_ZORDER, + FEAT_FIFO_MERGE, +}; + +static const enum dss_feat_id omap4_dss_feat_list[] = { + FEAT_MGR_LCD2, + FEAT_CORE_CLK_DIV, + FEAT_LCD_CLK_SRC, + FEAT_DSI_DCS_CMD_CONFIG_VC, + FEAT_DSI_VC_OCP_WIDTH, + FEAT_DSI_GNQ, + FEAT_HDMI_CTS_SWMODE, + FEAT_HDMI_AUDIO_USE_MCLK, + FEAT_HANDLE_UV_SEPARATE, + FEAT_ATTR2, + FEAT_CPR, + FEAT_PRELOAD, + FEAT_FIR_COEF_V, + FEAT_ALPHA_FREE_ZORDER, + FEAT_FIFO_MERGE, }; /* OMAP2 DSS Features */ @@ -335,10 +441,8 @@ static const struct omap_dss_features omap2_dss_features = { .reg_fields = omap2_dss_reg_fields, .num_reg_fields = ARRAY_SIZE(omap2_dss_reg_fields), - .has_feature = - FEAT_LCDENABLEPOL | FEAT_LCDENABLESIGNAL | - FEAT_PCKFREEENABLE | FEAT_FUNCGATED | - FEAT_ROWREPEATENABLE | FEAT_RESIZECONF, + .features = omap2_dss_feat_list, + .num_features = ARRAY_SIZE(omap2_dss_feat_list), .num_mgrs = 2, .num_ovls = 3, @@ -356,14 +460,8 @@ static const struct omap_dss_features omap3430_dss_features = { .reg_fields = omap3_dss_reg_fields, .num_reg_fields = ARRAY_SIZE(omap3_dss_reg_fields), - .has_feature = - FEAT_LCDENABLEPOL | - FEAT_LCDENABLESIGNAL | FEAT_PCKFREEENABLE | - FEAT_FUNCGATED | FEAT_ROWREPEATENABLE | - FEAT_LINEBUFFERSPLIT | FEAT_RESIZECONF | - FEAT_DSI_PLL_FREQSEL | FEAT_DSI_REVERSE_TXCLKESC | - FEAT_VENC_REQUIRES_TV_DAC_CLK | FEAT_CPR | FEAT_PRELOAD | - FEAT_FIR_COEF_V | FEAT_ALPHA_FIXED_ZORDER, + .features = omap3430_dss_feat_list, + .num_features = ARRAY_SIZE(omap3430_dss_feat_list), .num_mgrs = 2, .num_ovls = 3, @@ -380,14 +478,8 @@ static const struct omap_dss_features omap3630_dss_features = { .reg_fields = omap3_dss_reg_fields, .num_reg_fields = ARRAY_SIZE(omap3_dss_reg_fields), - .has_feature = - FEAT_LCDENABLEPOL | - FEAT_LCDENABLESIGNAL | FEAT_PCKFREEENABLE | - FEAT_FUNCGATED | - FEAT_ROWREPEATENABLE | FEAT_LINEBUFFERSPLIT | - FEAT_RESIZECONF | FEAT_DSI_PLL_PWR_BUG | - FEAT_DSI_PLL_FREQSEL | FEAT_CPR | FEAT_PRELOAD | - FEAT_FIR_COEF_V | FEAT_ALPHA_FIXED_ZORDER, + .features = omap3630_dss_feat_list, + .num_features = ARRAY_SIZE(omap3630_dss_feat_list), .num_mgrs = 2, .num_ovls = 3, @@ -406,13 +498,27 @@ static const struct omap_dss_features omap4430_es1_0_dss_features = { .reg_fields = omap4_dss_reg_fields, .num_reg_fields = ARRAY_SIZE(omap4_dss_reg_fields), - .has_feature = - FEAT_MGR_LCD2 | - FEAT_CORE_CLK_DIV | FEAT_LCD_CLK_SRC | - FEAT_DSI_DCS_CMD_CONFIG_VC | FEAT_DSI_VC_OCP_WIDTH | - FEAT_DSI_GNQ | FEAT_HANDLE_UV_SEPARATE | FEAT_ATTR2 | - FEAT_CPR | FEAT_PRELOAD | FEAT_FIR_COEF_V | - FEAT_ALPHA_FREE_ZORDER, + .features = omap4430_es1_0_dss_feat_list, + .num_features = ARRAY_SIZE(omap4430_es1_0_dss_feat_list), + + .num_mgrs = 3, + .num_ovls = 4, + .supported_displays = omap4_dss_supported_displays, + .supported_color_modes = omap4_dss_supported_color_modes, + .overlay_caps = omap4_dss_overlay_caps, + .clksrc_names = omap4_dss_clk_source_names, + .dss_params = omap4_dss_param_range, + .buffer_size_unit = 16, + .burst_size_unit = 16, +}; + +/* For OMAP4430 ES 2.0, 2.1 and 2.2 revisions */ +static const struct omap_dss_features omap4430_es2_0_1_2_dss_features = { + .reg_fields = omap4_dss_reg_fields, + .num_reg_fields = ARRAY_SIZE(omap4_dss_reg_fields), + + .features = omap4430_es2_0_1_2_dss_feat_list, + .num_features = ARRAY_SIZE(omap4430_es2_0_1_2_dss_feat_list), .num_mgrs = 3, .num_ovls = 4, @@ -430,13 +536,8 @@ static const struct omap_dss_features omap4_dss_features = { .reg_fields = omap4_dss_reg_fields, .num_reg_fields = ARRAY_SIZE(omap4_dss_reg_fields), - .has_feature = - FEAT_MGR_LCD2 | - FEAT_CORE_CLK_DIV | FEAT_LCD_CLK_SRC | - FEAT_DSI_DCS_CMD_CONFIG_VC | FEAT_DSI_VC_OCP_WIDTH | - FEAT_DSI_GNQ | FEAT_HDMI_CTS_SWMODE | - FEAT_HANDLE_UV_SEPARATE | FEAT_ATTR2 | FEAT_CPR | - FEAT_PRELOAD | FEAT_FIR_COEF_V | FEAT_ALPHA_FREE_ZORDER, + .features = omap4_dss_feat_list, + .num_features = ARRAY_SIZE(omap4_dss_feat_list), .num_mgrs = 3, .num_ovls = 4, @@ -465,6 +566,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 }; @@ -536,7 +641,16 @@ u32 dss_feat_get_burst_size_unit(void) /* DSS has_feature check */ bool dss_has_feature(enum dss_feat_id id) { - return omap_current_dss_features->has_feature & id; + int i; + const enum dss_feat_id *features = omap_current_dss_features->features; + const int num_features = omap_current_dss_features->num_features; + + for (i = 0; i < num_features; i++) { + if (features[i] == id) + return true; + } + + return false; } void dss_feat_get_reg_field(enum dss_feat_reg_field id, u8 *start, u8 *end) @@ -558,6 +672,10 @@ void dss_features_init(void) omap_current_dss_features = &omap3430_dss_features; else if (omap_rev() == OMAP4430_REV_ES1_0) omap_current_dss_features = &omap4430_es1_0_dss_features; + else if (omap_rev() == OMAP4430_REV_ES2_0 || + omap_rev() == OMAP4430_REV_ES2_1 || + omap_rev() == OMAP4430_REV_ES2_2) + omap_current_dss_features = &omap4430_es2_0_1_2_dss_features; else if (cpu_is_omap44xx()) omap_current_dss_features = &omap4_dss_features; else diff --git a/drivers/video/omap2/dss/dss_features.h b/drivers/video/omap2/dss/dss_features.h index 6a6c05dd45ce..c332e7ddfce1 100644 --- a/drivers/video/omap2/dss/dss_features.h +++ b/drivers/video/omap2/dss/dss_features.h @@ -31,33 +31,37 @@ /* DSS has feature id */ enum dss_feat_id { - FEAT_LCDENABLEPOL = 1 << 3, - FEAT_LCDENABLESIGNAL = 1 << 4, - FEAT_PCKFREEENABLE = 1 << 5, - FEAT_FUNCGATED = 1 << 6, - FEAT_MGR_LCD2 = 1 << 7, - FEAT_LINEBUFFERSPLIT = 1 << 8, - FEAT_ROWREPEATENABLE = 1 << 9, - FEAT_RESIZECONF = 1 << 10, + FEAT_LCDENABLEPOL, + FEAT_LCDENABLESIGNAL, + FEAT_PCKFREEENABLE, + FEAT_FUNCGATED, + FEAT_MGR_LCD2, + FEAT_LINEBUFFERSPLIT, + FEAT_ROWREPEATENABLE, + FEAT_RESIZECONF, /* Independent core clk divider */ - FEAT_CORE_CLK_DIV = 1 << 11, - FEAT_LCD_CLK_SRC = 1 << 12, + FEAT_CORE_CLK_DIV, + FEAT_LCD_CLK_SRC, /* DSI-PLL power command 0x3 is not working */ - FEAT_DSI_PLL_PWR_BUG = 1 << 13, - FEAT_DSI_PLL_FREQSEL = 1 << 14, - FEAT_DSI_DCS_CMD_CONFIG_VC = 1 << 15, - FEAT_DSI_VC_OCP_WIDTH = 1 << 16, - FEAT_DSI_REVERSE_TXCLKESC = 1 << 17, - FEAT_DSI_GNQ = 1 << 18, - FEAT_HDMI_CTS_SWMODE = 1 << 19, - FEAT_HANDLE_UV_SEPARATE = 1 << 20, - FEAT_ATTR2 = 1 << 21, - FEAT_VENC_REQUIRES_TV_DAC_CLK = 1 << 22, - FEAT_CPR = 1 << 23, - FEAT_PRELOAD = 1 << 24, - FEAT_FIR_COEF_V = 1 << 25, - FEAT_ALPHA_FIXED_ZORDER = 1 << 26, - FEAT_ALPHA_FREE_ZORDER = 1 << 27, + FEAT_DSI_PLL_PWR_BUG, + FEAT_DSI_PLL_FREQSEL, + FEAT_DSI_DCS_CMD_CONFIG_VC, + FEAT_DSI_VC_OCP_WIDTH, + FEAT_DSI_REVERSE_TXCLKESC, + FEAT_DSI_GNQ, + FEAT_HDMI_CTS_SWMODE, + FEAT_HDMI_AUDIO_USE_MCLK, + FEAT_HANDLE_UV_SEPARATE, + FEAT_ATTR2, + FEAT_VENC_REQUIRES_TV_DAC_CLK, + FEAT_CPR, + FEAT_PRELOAD, + FEAT_FIR_COEF_V, + FEAT_ALPHA_FIXED_ZORDER, + FEAT_ALPHA_FREE_ZORDER, + FEAT_FIFO_MERGE, + /* An unknown HW bug causing the normal FIFO thresholds not to work */ + FEAT_OMAP3_DSI_FIFO_BUG, }; /* DSS register field id */ @@ -86,6 +90,7 @@ enum dss_range_param { FEAT_PARAM_DSIPLL_FINT, FEAT_PARAM_DSIPLL_LPDIV, FEAT_PARAM_DOWNSCALE, + FEAT_PARAM_LINEWIDTH, }; /* DSS Feature Functions */ diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c index c56378c555b0..c4b4f6950a92 100644 --- a/drivers/video/omap2/dss/hdmi.c +++ b/drivers/video/omap2/dss/hdmi.c @@ -58,8 +58,6 @@ #define EDID_SIZE_BLOCK0_TIMING_DESCRIPTOR 4 #define EDID_SIZE_BLOCK1_TIMING_DESCRIPTOR 4 -#define OMAP_HDMI_TIMINGS_NB 34 - #define HDMI_DEFAULT_REGN 16 #define HDMI_DEFAULT_REGM2 1 @@ -68,8 +66,6 @@ static struct { struct omap_display_platform_data *pdata; struct platform_device *pdev; struct hdmi_ip_data ip_data; - int code; - int mode; struct clk *sys_clk; } hdmi; @@ -88,86 +84,71 @@ static struct { * map it to corresponding CEA or VESA index. */ -static const struct hdmi_timings cea_vesa_timings[OMAP_HDMI_TIMINGS_NB] = { - { {640, 480, 25200, 96, 16, 48, 2, 10, 33} , 0 , 0}, - { {1280, 720, 74250, 40, 440, 220, 5, 5, 20}, 1, 1}, - { {1280, 720, 74250, 40, 110, 220, 5, 5, 20}, 1, 1}, - { {720, 480, 27027, 62, 16, 60, 6, 9, 30}, 0, 0}, - { {2880, 576, 108000, 256, 48, 272, 5, 5, 39}, 0, 0}, - { {1440, 240, 27027, 124, 38, 114, 3, 4, 15}, 0, 0}, - { {1440, 288, 27000, 126, 24, 138, 3, 2, 19}, 0, 0}, - { {1920, 540, 74250, 44, 528, 148, 5, 2, 15}, 1, 1}, - { {1920, 540, 74250, 44, 88, 148, 5, 2, 15}, 1, 1}, - { {1920, 1080, 148500, 44, 88, 148, 5, 4, 36}, 1, 1}, - { {720, 576, 27000, 64, 12, 68, 5, 5, 39}, 0, 0}, - { {1440, 576, 54000, 128, 24, 136, 5, 5, 39}, 0, 0}, - { {1920, 1080, 148500, 44, 528, 148, 5, 4, 36}, 1, 1}, - { {2880, 480, 108108, 248, 64, 240, 6, 9, 30}, 0, 0}, - { {1920, 1080, 74250, 44, 638, 148, 5, 4, 36}, 1, 1}, - /* VESA From Here */ - { {640, 480, 25175, 96, 16, 48, 2 , 11, 31}, 0, 0}, - { {800, 600, 40000, 128, 40, 88, 4 , 1, 23}, 1, 1}, - { {848, 480, 33750, 112, 16, 112, 8 , 6, 23}, 1, 1}, - { {1280, 768, 79500, 128, 64, 192, 7 , 3, 20}, 1, 0}, - { {1280, 800, 83500, 128, 72, 200, 6 , 3, 22}, 1, 0}, - { {1360, 768, 85500, 112, 64, 256, 6 , 3, 18}, 1, 1}, - { {1280, 960, 108000, 112, 96, 312, 3 , 1, 36}, 1, 1}, - { {1280, 1024, 108000, 112, 48, 248, 3 , 1, 38}, 1, 1}, - { {1024, 768, 65000, 136, 24, 160, 6, 3, 29}, 0, 0}, - { {1400, 1050, 121750, 144, 88, 232, 4, 3, 32}, 1, 0}, - { {1440, 900, 106500, 152, 80, 232, 6, 3, 25}, 1, 0}, - { {1680, 1050, 146250, 176 , 104, 280, 6, 3, 30}, 1, 0}, - { {1366, 768, 85500, 143, 70, 213, 3, 3, 24}, 1, 1}, - { {1920, 1080, 148500, 44, 148, 80, 5, 4, 36}, 1, 1}, - { {1280, 768, 68250, 32, 48, 80, 7, 3, 12}, 0, 1}, - { {1400, 1050, 101000, 32, 48, 80, 4, 3, 23}, 0, 1}, - { {1680, 1050, 119000, 32, 48, 80, 6, 3, 21}, 0, 1}, - { {1280, 800, 79500, 32, 48, 80, 6, 3, 14}, 0, 1}, - { {1280, 720, 74250, 40, 110, 220, 5, 5, 20}, 1, 1} +static const struct hdmi_config cea_timings[] = { +{ {640, 480, 25200, 96, 16, 48, 2, 10, 33, 0, 0, 0}, {1, HDMI_HDMI} }, +{ {720, 480, 27027, 62, 16, 60, 6, 9, 30, 0, 0, 0}, {2, HDMI_HDMI} }, +{ {1280, 720, 74250, 40, 110, 220, 5, 5, 20, 1, 1, 0}, {4, HDMI_HDMI} }, +{ {1920, 540, 74250, 44, 88, 148, 5, 2, 15, 1, 1, 1}, {5, HDMI_HDMI} }, +{ {1440, 240, 27027, 124, 38, 114, 3, 4, 15, 0, 0, 1}, {6, HDMI_HDMI} }, +{ {1920, 1080, 148500, 44, 88, 148, 5, 4, 36, 1, 1, 0}, {16, HDMI_HDMI} }, +{ {720, 576, 27000, 64, 12, 68, 5, 5, 39, 0, 0, 0}, {17, HDMI_HDMI} }, +{ {1280, 720, 74250, 40, 440, 220, 5, 5, 20, 1, 1, 0}, {19, HDMI_HDMI} }, +{ {1920, 540, 74250, 44, 528, 148, 5, 2, 15, 1, 1, 1}, {20, HDMI_HDMI} }, +{ {1440, 288, 27000, 126, 24, 138, 3, 2, 19, 0, 0, 1}, {21, HDMI_HDMI} }, +{ {1440, 576, 54000, 128, 24, 136, 5, 5, 39, 0, 0, 0}, {29, HDMI_HDMI} }, +{ {1920, 1080, 148500, 44, 528, 148, 5, 4, 36, 1, 1, 0}, {31, HDMI_HDMI} }, +{ {1920, 1080, 74250, 44, 638, 148, 5, 4, 36, 1, 1, 0}, {32, HDMI_HDMI} }, +{ {2880, 480, 108108, 248, 64, 240, 6, 9, 30, 0, 0, 0}, {35, HDMI_HDMI} }, +{ {2880, 576, 108000, 256, 48, 272, 5, 5, 39, 0, 0, 0}, {37, HDMI_HDMI} }, }; - -/* - * This is a static mapping array which maps the timing values - * with corresponding CEA / VESA code - */ -static const int code_index[OMAP_HDMI_TIMINGS_NB] = { - 1, 19, 4, 2, 37, 6, 21, 20, 5, 16, 17, 29, 31, 35, 32, - /* <--15 CEA 17--> vesa*/ - 4, 9, 0xE, 0x17, 0x1C, 0x27, 0x20, 0x23, 0x10, 0x2A, - 0X2F, 0x3A, 0X51, 0X52, 0x16, 0x29, 0x39, 0x1B +static const struct hdmi_config vesa_timings[] = { +/* VESA From Here */ +{ {640, 480, 25175, 96, 16, 48, 2 , 11, 31, 0, 0, 0}, {4, HDMI_DVI} }, +{ {800, 600, 40000, 128, 40, 88, 4 , 1, 23, 1, 1, 0}, {9, HDMI_DVI} }, +{ {848, 480, 33750, 112, 16, 112, 8 , 6, 23, 1, 1, 0}, {0xE, HDMI_DVI} }, +{ {1280, 768, 79500, 128, 64, 192, 7 , 3, 20, 1, 0, 0}, {0x17, HDMI_DVI} }, +{ {1280, 800, 83500, 128, 72, 200, 6 , 3, 22, 1, 0, 0}, {0x1C, HDMI_DVI} }, +{ {1360, 768, 85500, 112, 64, 256, 6 , 3, 18, 1, 1, 0}, {0x27, HDMI_DVI} }, +{ {1280, 960, 108000, 112, 96, 312, 3 , 1, 36, 1, 1, 0}, {0x20, HDMI_DVI} }, +{ {1280, 1024, 108000, 112, 48, 248, 3 , 1, 38, 1, 1, 0}, {0x23, HDMI_DVI} }, +{ {1024, 768, 65000, 136, 24, 160, 6, 3, 29, 0, 0, 0}, {0x10, HDMI_DVI} }, +{ {1400, 1050, 121750, 144, 88, 232, 4, 3, 32, 1, 0, 0}, {0x2A, HDMI_DVI} }, +{ {1440, 900, 106500, 152, 80, 232, 6, 3, 25, 1, 0, 0}, {0x2F, HDMI_DVI} }, +{ {1680, 1050, 146250, 176 , 104, 280, 6, 3, 30, 1, 0, 0}, {0x3A, HDMI_DVI} }, +{ {1366, 768, 85500, 143, 70, 213, 3, 3, 24, 1, 1, 0}, {0x51, HDMI_DVI} }, +{ {1920, 1080, 148500, 44, 148, 80, 5, 4, 36, 1, 1, 0}, {0x52, HDMI_DVI} }, +{ {1280, 768, 68250, 32, 48, 80, 7, 3, 12, 0, 1, 0}, {0x16, HDMI_DVI} }, +{ {1400, 1050, 101000, 32, 48, 80, 4, 3, 23, 0, 1, 0}, {0x29, HDMI_DVI} }, +{ {1680, 1050, 119000, 32, 48, 80, 6, 3, 21, 0, 1, 0}, {0x39, HDMI_DVI} }, +{ {1280, 800, 79500, 32, 48, 80, 6, 3, 14, 0, 1, 0}, {0x1B, HDMI_DVI} }, +{ {1280, 720, 74250, 40, 110, 220, 5, 5, 20, 1, 1, 0}, {0x55, HDMI_DVI} } }; -/* - * This is reverse static mapping which maps the CEA / VESA code - * to the corresponding timing values - */ -static const int code_cea[39] = { - -1, 0, 3, 3, 2, 8, 5, 5, -1, -1, - -1, -1, -1, -1, -1, -1, 9, 10, 10, 1, - 7, 6, 6, -1, -1, -1, -1, -1, -1, 11, - 11, 12, 14, -1, -1, 13, 13, 4, 4 -}; - -static const int code_vesa[85] = { - -1, -1, -1, -1, 15, -1, -1, -1, -1, 16, - -1, -1, -1, -1, 17, -1, 23, -1, -1, -1, - -1, -1, 29, 18, -1, -1, -1, 32, 19, -1, - -1, -1, 21, -1, -1, 22, -1, -1, -1, 20, - -1, 30, 24, -1, -1, -1, -1, 25, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 31, 26, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 27, 28, -1, 33}; - static int hdmi_runtime_get(void) { int r; DSSDBG("hdmi_runtime_get\n"); + /* + * HACK: Add dss_runtime_get() to ensure DSS clock domain is enabled. + * This should be removed later. + */ + r = dss_runtime_get(); + if (r < 0) + goto err_get_dss; + r = pm_runtime_get_sync(&hdmi.pdev->dev); WARN_ON(r < 0); - return r < 0 ? r : 0; + if (r < 0) + goto err_get_hdmi; + + return 0; + +err_get_hdmi: + dss_runtime_put(); +err_get_dss: + return r; } static void hdmi_runtime_put(void) @@ -176,8 +157,14 @@ static void hdmi_runtime_put(void) DSSDBG("hdmi_runtime_put\n"); - r = pm_runtime_put(&hdmi.pdev->dev); + r = pm_runtime_put_sync(&hdmi.pdev->dev); WARN_ON(r < 0); + + /* + * HACK: This is added to complement the dss_runtime_get() call in + * hdmi_runtime_get(). This should be removed later. + */ + dss_runtime_put(); } int hdmi_init_display(struct omap_dss_device *dssdev) @@ -188,88 +175,89 @@ int hdmi_init_display(struct omap_dss_device *dssdev) return 0; } -static int get_timings_index(void) +static const struct hdmi_config *hdmi_find_timing( + const struct hdmi_config *timings_arr, + int len) { - int code; + int i; - if (hdmi.mode == 0) - code = code_vesa[hdmi.code]; - else - code = code_cea[hdmi.code]; + for (i = 0; i < len; i++) { + if (timings_arr[i].cm.code == hdmi.ip_data.cfg.cm.code) + return &timings_arr[i]; + } + return NULL; +} - if (code == -1) { - /* HDMI code 4 corresponds to 640 * 480 VGA */ - hdmi.code = 4; - /* DVI mode 1 corresponds to HDMI 0 to DVI */ - hdmi.mode = HDMI_DVI; +static const struct hdmi_config *hdmi_get_timings(void) +{ + const struct hdmi_config *arr; + int len; + + if (hdmi.ip_data.cfg.cm.mode == HDMI_DVI) { + arr = vesa_timings; + len = ARRAY_SIZE(vesa_timings); + } else { + arr = cea_timings; + len = ARRAY_SIZE(cea_timings); + } + + return hdmi_find_timing(arr, len); +} + +static bool hdmi_timings_compare(struct omap_video_timings *timing1, + const struct hdmi_video_timings *timing2) +{ + int timing1_vsync, timing1_hsync, timing2_vsync, timing2_hsync; + + if ((timing2->pixel_clock == timing1->pixel_clock) && + (timing2->x_res == timing1->x_res) && + (timing2->y_res == timing1->y_res)) { - code = code_vesa[hdmi.code]; + timing2_hsync = timing2->hfp + timing2->hsw + timing2->hbp; + timing1_hsync = timing1->hfp + timing1->hsw + timing1->hbp; + timing2_vsync = timing2->vfp + timing2->vsw + timing2->vbp; + timing1_vsync = timing2->vfp + timing2->vsw + timing2->vbp; + + DSSDBG("timing1_hsync = %d timing1_vsync = %d"\ + "timing2_hsync = %d timing2_vsync = %d\n", + timing1_hsync, timing1_vsync, + timing2_hsync, timing2_vsync); + + if ((timing1_hsync == timing2_hsync) && + (timing1_vsync == timing2_vsync)) { + return true; + } } - return code; + return false; } static struct hdmi_cm hdmi_get_code(struct omap_video_timings *timing) { - int i = 0, code = -1, temp_vsync = 0, temp_hsync = 0; - int timing_vsync = 0, timing_hsync = 0; - struct hdmi_video_timings temp; + int i; struct hdmi_cm cm = {-1}; DSSDBG("hdmi_get_code\n"); - for (i = 0; i < OMAP_HDMI_TIMINGS_NB; i++) { - temp = cea_vesa_timings[i].timings; - if ((temp.pixel_clock == timing->pixel_clock) && - (temp.x_res == timing->x_res) && - (temp.y_res == timing->y_res)) { - - temp_hsync = temp.hfp + temp.hsw + temp.hbp; - timing_hsync = timing->hfp + timing->hsw + timing->hbp; - temp_vsync = temp.vfp + temp.vsw + temp.vbp; - timing_vsync = timing->vfp + timing->vsw + timing->vbp; - - DSSDBG("temp_hsync = %d , temp_vsync = %d" - "timing_hsync = %d, timing_vsync = %d\n", - temp_hsync, temp_hsync, - timing_hsync, timing_vsync); - - if ((temp_hsync == timing_hsync) && - (temp_vsync == timing_vsync)) { - code = i; - cm.code = code_index[i]; - if (code < 14) - cm.mode = HDMI_HDMI; - else - cm.mode = HDMI_DVI; - DSSDBG("Hdmi_code = %d mode = %d\n", - cm.code, cm.mode); - break; - } + for (i = 0; i < ARRAY_SIZE(cea_timings); i++) { + if (hdmi_timings_compare(timing, &cea_timings[i].timings)) { + cm = cea_timings[i].cm; + goto end; + } + } + for (i = 0; i < ARRAY_SIZE(vesa_timings); i++) { + if (hdmi_timings_compare(timing, &vesa_timings[i].timings)) { + cm = vesa_timings[i].cm; + goto end; } } - return cm; -} +end: return cm; -static void update_hdmi_timings(struct hdmi_config *cfg, - struct omap_video_timings *timings, int code) -{ - cfg->timings.timings.x_res = timings->x_res; - cfg->timings.timings.y_res = timings->y_res; - cfg->timings.timings.hbp = timings->hbp; - cfg->timings.timings.hfp = timings->hfp; - cfg->timings.timings.hsw = timings->hsw; - cfg->timings.timings.vbp = timings->vbp; - cfg->timings.timings.vfp = timings->vfp; - cfg->timings.timings.vsw = timings->vsw; - cfg->timings.timings.pixel_clock = timings->pixel_clock; - cfg->timings.vsync_pol = cea_vesa_timings[code].vsync_pol; - cfg->timings.hsync_pol = cea_vesa_timings[code].hsync_pol; } unsigned long hdmi_get_pixel_clock(void) { /* HDMI Pixel Clock in Mhz */ - return hdmi.ip_data.cfg.timings.timings.pixel_clock * 1000; + return hdmi.ip_data.cfg.timings.pixel_clock * 1000; } static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy, @@ -290,24 +278,24 @@ static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy, refclk = clkin / pi->regn; - /* - * multiplier is pixel_clk/ref_clk - * Multiplying by 100 to avoid fractional part removal - */ - pi->regm = (phy * 100 / (refclk)) / 100; - if (dssdev->clocks.hdmi.regm2 == 0) pi->regm2 = HDMI_DEFAULT_REGM2; else pi->regm2 = dssdev->clocks.hdmi.regm2; /* + * multiplier is pixel_clk/ref_clk + * Multiplying by 100 to avoid fractional part removal + */ + pi->regm = phy * pi->regm2 / refclk; + + /* * fractional multiplier is remainder of the difference between * multiplier and actual phy(required pixel clock thus should be * multiplied by 2^18(262144) divided by the reference clock */ - mf = (phy - pi->regm * refclk) * 262144; - pi->regmf = mf / (refclk); + mf = (phy - pi->regm / pi->regm2 * refclk) * 262144; + pi->regmf = pi->regm2 * mf / refclk; /* * Dcofreq should be set to 1 if required pixel clock @@ -325,7 +313,8 @@ static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy, static int hdmi_power_on(struct omap_dss_device *dssdev) { - int r, code = 0; + int r; + const struct hdmi_config *timing; struct omap_video_timings *p; unsigned long phy; @@ -333,7 +322,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; @@ -341,9 +330,16 @@ static int hdmi_power_on(struct omap_dss_device *dssdev) dssdev->panel.timings.x_res, dssdev->panel.timings.y_res); - code = get_timings_index(); - update_hdmi_timings(&hdmi.ip_data.cfg, p, code); - + timing = hdmi_get_timings(); + if (timing == NULL) { + /* HDMI code 4 corresponds to 640 * 480 VGA */ + hdmi.ip_data.cfg.cm.code = 4; + /* DVI mode 1 corresponds to HDMI 0 to DVI */ + hdmi.ip_data.cfg.cm.mode = HDMI_DVI; + hdmi.ip_data.cfg = vesa_timings[0]; + } else { + hdmi.ip_data.cfg = *timing; + } phy = p->pixel_clock; hdmi_compute_pll(dssdev, phy, &hdmi.ip_data.pll_data); @@ -363,8 +359,6 @@ static int hdmi_power_on(struct omap_dss_device *dssdev) goto err; } - hdmi.ip_data.cfg.cm.mode = hdmi.mode; - hdmi.ip_data.cfg.cm.code = hdmi.code; hdmi.ip_data.ops->video_configure(&hdmi.ip_data); /* Make selection of HDMI in DSS */ @@ -387,9 +381,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 +398,7 @@ err: 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); @@ -424,8 +425,8 @@ void omapdss_hdmi_display_set_timing(struct omap_dss_device *dssdev) struct hdmi_cm cm; cm = hdmi_get_code(&dssdev->panel.timings); - hdmi.code = cm.code; - hdmi.mode = cm.mode; + hdmi.ip_data.cfg.cm.code = cm.code; + hdmi.ip_data.cfg.cm.mode = cm.mode; if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { int r; @@ -490,6 +491,7 @@ bool omapdss_hdmi_detect(void) int omapdss_hdmi_display_enable(struct omap_dss_device *dssdev) { + struct omap_dss_hdmi_data *priv = dssdev->data; int r = 0; DSSDBG("ENTER hdmi_display_enable\n"); @@ -502,6 +504,8 @@ int omapdss_hdmi_display_enable(struct omap_dss_device *dssdev) goto err0; } + hdmi.ip_data.hpd_gpio = priv->hpd_gpio; + r = omap_dss_start_device(dssdev); if (r) { DSSERR("failed to start device\n"); @@ -554,11 +558,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; @@ -652,13 +689,15 @@ static int hdmi_audio_hw_params(struct hdmi_ip_data *ip_data, if (dss_has_feature(FEAT_HDMI_CTS_SWMODE)) { core_cfg.aud_par_busclk = 0; core_cfg.cts_mode = HDMI_AUDIO_CTS_MODE_SW; - core_cfg.use_mclk = false; + core_cfg.use_mclk = dss_has_feature(FEAT_HDMI_AUDIO_USE_MCLK); } else { core_cfg.aud_par_busclk = (((128 * 31) - 1) << 8); core_cfg.cts_mode = HDMI_AUDIO_CTS_MODE_HW; core_cfg.use_mclk = true; - core_cfg.mclk_mode = HDMI_AUDIO_MCLK_128FS; } + + if (core_cfg.use_mclk) + core_cfg.mclk_mode = HDMI_AUDIO_MCLK_128FS; core_cfg.layout = HDMI_AUDIO_LAYOUT_2CH; core_cfg.en_spdif = false; /* Use sample frequency from channel status word */ @@ -691,14 +730,23 @@ static int hdmi_audio_hw_params(struct hdmi_ip_data *ip_data, static int hdmi_audio_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - if (!hdmi.mode) { + if (!hdmi.ip_data.cfg.cm.mode) { pr_err("Current video settings do not support audio.\n"); return -EIO; } 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 = { diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c index 6e63845cc7d7..e7364603f6a1 100644 --- a/drivers/video/omap2/dss/manager.c +++ b/drivers/video/omap2/dss/manager.c @@ -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 @@ put_device: 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,147 +490,15 @@ 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); u32 irq; + int r; + + r = dispc_runtime_get(); + if (r) + return r; if (mgr->device->type == OMAP_DISPLAY_TYPE_VENC) { irq = DISPC_IRQ_EVSYNC_ODD; @@ -621,1025 +510,177 @@ static int dss_mgr_wait_for_vsync(struct omap_overlay_manager *mgr) else irq = DISPC_IRQ_VSYNC2; } - 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; + r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout); - 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; - } - } + dispc_runtime_put(); 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) +int dss_init_overlay_managers(struct platform_device *pdev) { - 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; + int i, r; - return false; -} + num_managers = dss_feat_get_num_mgrs(); -static int configure_overlay(enum omap_plane plane) -{ - 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; + managers = kzalloc(sizeof(struct omap_overlay_manager) * num_managers, + GFP_KERNEL); - DSSDBGF("%d", plane); + BUG_ON(managers == NULL); - c = &dss_cache.overlay_cache[plane]; - oi = &c->info; + for (i = 0; i < num_managers; ++i) { + struct omap_overlay_manager *mgr = &managers[i]; - if (!c->enabled) { - dispc_ovl_enable(plane, 0); - return 0; - } - - 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; - } - - 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) -{ - *info = mgr->info; -} - -static int dss_mgr_enable(struct omap_overlay_manager *mgr) +static int dss_mgr_check_zorder(struct omap_overlay_manager *mgr, + struct omap_overlay_info **overlay_infos) { - dispc_mgr_enable(mgr->id, 1); - return 0; -} - -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); + struct omap_overlay *ovl1, *ovl2; + struct omap_overlay_info *info1, *info2; - INIT_LIST_HEAD(&manager_list); + list_for_each_entry(ovl1, &mgr->overlays, list) { + info1 = overlay_infos[ovl1->id]; - 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); - diff --git a/drivers/video/omap2/dss/overlay.c b/drivers/video/omap2/dss/overlay.c index ab8e40e48759..6e821810deec 100644 --- a/drivers/video/omap2/dss/overlay.c +++ b/drivers/video/omap2/dss/overlay.c @@ -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 @@ err: 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; +} diff --git a/drivers/video/omap2/dss/rfbi.c b/drivers/video/omap2/dss/rfbi.c index 1130c608a561..788a0ef6323a 100644 --- a/drivers/video/omap2/dss/rfbi.c +++ b/drivers/video/omap2/dss/rfbi.c @@ -140,7 +140,7 @@ static void rfbi_runtime_put(void) DSSDBG("rfbi_runtime_put\n"); - r = pm_runtime_put(&rfbi.pdev->dev); + r = pm_runtime_put_sync(&rfbi.pdev->dev); WARN_ON(r < 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; @@ -923,35 +922,34 @@ static int omap_rfbihw_probe(struct platform_device *pdev) rfbi_mem = platform_get_resource(rfbi.pdev, IORESOURCE_MEM, 0); if (!rfbi_mem) { DSSERR("can't get IORESOURCE_MEM RFBI\n"); - r = -EINVAL; - goto err_ioremap; + return -EINVAL; } - rfbi.base = ioremap(rfbi_mem->start, resource_size(rfbi_mem)); + + rfbi.base = devm_ioremap(&pdev->dev, rfbi_mem->start, + resource_size(rfbi_mem)); if (!rfbi.base) { DSSERR("can't ioremap RFBI\n"); - r = -ENOMEM; - goto err_ioremap; + return -ENOMEM; } - pm_runtime_enable(&pdev->dev); - - r = rfbi_runtime_get(); - if (r) - goto err_get_rfbi; - - msleep(10); - clk = clk_get(&pdev->dev, "ick"); if (IS_ERR(clk)) { DSSERR("can't get ick\n"); - r = PTR_ERR(clk); - goto err_get_ick; + return PTR_ERR(clk); } rfbi.l4_khz = clk_get_rate(clk) / 1000; clk_put(clk); + pm_runtime_enable(&pdev->dev); + + r = rfbi_runtime_get(); + if (r) + goto err_runtime_get; + + msleep(10); + rev = rfbi_read_reg(RFBI_REVISION); dev_dbg(&pdev->dev, "OMAP RFBI rev %d.%d\n", FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); @@ -960,19 +958,14 @@ static int omap_rfbihw_probe(struct platform_device *pdev) return 0; -err_get_ick: - rfbi_runtime_put(); -err_get_rfbi: +err_runtime_get: pm_runtime_disable(&pdev->dev); - iounmap(rfbi.base); -err_ioremap: return r; } static int omap_rfbihw_remove(struct platform_device *pdev) { pm_runtime_disable(&pdev->dev); - iounmap(rfbi.base); return 0; } diff --git a/drivers/video/omap2/dss/sdi.c b/drivers/video/omap2/dss/sdi.c index 40305ad7841e..8266ca0d666b 100644 --- a/drivers/video/omap2/dss/sdi.c +++ b/drivers/video/omap2/dss/sdi.c @@ -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(); diff --git a/drivers/video/omap2/dss/ti_hdmi.h b/drivers/video/omap2/dss/ti_hdmi.h index 2c3443dabb14..1f58b84d6901 100644 --- a/drivers/video/omap2/dss/ti_hdmi.h +++ b/drivers/video/omap2/dss/ti_hdmi.h @@ -42,6 +42,7 @@ enum hdmi_clk_refsel { HDMI_REFSEL_SYSCLK = 3 }; +/* HDMI timing structure */ struct hdmi_video_timings { u16 x_res; u16 y_res; @@ -53,13 +54,9 @@ struct hdmi_video_timings { u16 vsw; u16 vfp; u16 vbp; -}; - -/* HDMI timing structure */ -struct hdmi_timings { - struct hdmi_video_timings timings; - int vsync_pol; - int hsync_pol; + bool vsync_pol; + bool hsync_pol; + bool interlace; }; struct hdmi_cm { @@ -68,8 +65,7 @@ struct hdmi_cm { }; struct hdmi_config { - struct hdmi_timings timings; - u16 interlace; + struct hdmi_video_timings timings; struct hdmi_cm cm; }; @@ -110,6 +106,52 @@ 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 + +}; + +/* + * Refer to section 8.2 in HDMI 1.3 specification for + * details about infoframe databytes + */ +struct hdmi_core_infoframe_avi { + /* Y0, Y1 rgb,yCbCr */ + u8 db1_format; + /* A0 Active information Present */ + u8 db1_active_info; + /* B0, B1 Bar info data valid */ + u8 db1_bar_info_dv; + /* S0, S1 scan information */ + u8 db1_scan_info; + /* C0, C1 colorimetry */ + u8 db2_colorimetry; + /* M0, M1 Aspect ratio (4:3, 16:9) */ + u8 db2_aspect_ratio; + /* R0...R3 Active format aspect ratio */ + u8 db2_active_fmt_ar; + /* ITC IT content. */ + u8 db3_itc; + /* EC0, EC1, EC2 Extended colorimetry */ + u8 db3_ec; + /* Q1, Q0 Quantization range */ + u8 db3_q_range; + /* SC1, SC0 Non-uniform picture scaling */ + u8 db3_nup_scaling; + /* VIC0..6 Video format identification */ + u8 db4_videocode; + /* PR0..PR3 Pixel repetition factor */ + u8 db5_pixel_repeat; + /* Line number end of top bar */ + u16 db6_7_line_eoftop; + /* Line number start of bottom bar */ + u16 db8_9_line_sofbottom; + /* Pixel number end of left bar */ + u16 db10_11_pixel_eofleft; + /* Pixel number start of right bar */ + u16 db12_13_pixel_sofright; }; struct hdmi_ip_data { @@ -121,6 +163,11 @@ struct hdmi_ip_data { const struct ti_hdmi_ip_ops *ops; struct hdmi_config cfg; struct hdmi_pll_info pll_data; + struct hdmi_core_infoframe_avi avi_cfg; + + /* ti_hdmi_4xxx_ip private data. These should be in a separate struct */ + int hpd_gpio; + bool phy_tx_enabled; }; int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data); void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data); @@ -134,5 +181,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 diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c index e1a6ce518af6..bfe6fe65c8be 100644 --- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c @@ -28,6 +28,7 @@ #include <linux/delay.h> #include <linux/string.h> #include <linux/seq_file.h> +#include <linux/gpio.h> #include "ti_hdmi_4xxx_ip.h" #include "dss.h" @@ -223,6 +224,49 @@ void ti_hdmi_4xxx_pll_disable(struct hdmi_ip_data *ip_data) hdmi_set_pll_pwr(ip_data, HDMI_PLLPWRCMD_ALLOFF); } +static int hdmi_check_hpd_state(struct hdmi_ip_data *ip_data) +{ + unsigned long flags; + bool hpd; + int r; + /* this should be in ti_hdmi_4xxx_ip private data */ + static DEFINE_SPINLOCK(phy_tx_lock); + + spin_lock_irqsave(&phy_tx_lock, flags); + + hpd = gpio_get_value(ip_data->hpd_gpio); + + if (hpd == ip_data->phy_tx_enabled) { + spin_unlock_irqrestore(&phy_tx_lock, flags); + return 0; + } + + if (hpd) + r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_TXON); + else + r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_LDOON); + + if (r) { + DSSERR("Failed to %s PHY TX power\n", + hpd ? "enable" : "disable"); + goto err; + } + + ip_data->phy_tx_enabled = hpd; +err: + spin_unlock_irqrestore(&phy_tx_lock, flags); + return r; +} + +static irqreturn_t hpd_irq_handler(int irq, void *data) +{ + struct hdmi_ip_data *ip_data = data; + + hdmi_check_hpd_state(ip_data); + + return IRQ_HANDLED; +} + int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data) { u16 r = 0; @@ -232,10 +276,6 @@ int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data) if (r) return r; - r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_TXON); - if (r) - return r; - /* * Read address 0 in order to get the SCP reset done completed * Dummy access performed to make sure reset is done @@ -257,12 +297,32 @@ int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data) /* Write to phy address 3 to change the polarity control */ REG_FLD_MOD(phy_base, HDMI_TXPHY_PAD_CFG_CTRL, 0x1, 27, 27); + r = request_threaded_irq(gpio_to_irq(ip_data->hpd_gpio), + NULL, hpd_irq_handler, + IRQF_DISABLED | IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING, "hpd", ip_data); + if (r) { + DSSERR("HPD IRQ request failed\n"); + hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF); + return r; + } + + r = hdmi_check_hpd_state(ip_data); + if (r) { + free_irq(gpio_to_irq(ip_data->hpd_gpio), ip_data); + hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF); + return r; + } + return 0; } void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data) { + free_irq(gpio_to_irq(ip_data->hpd_gpio), ip_data); + hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF); + ip_data->phy_tx_enabled = false; } static int hdmi_core_ddc_init(struct hdmi_ip_data *ip_data) @@ -419,14 +479,7 @@ int ti_hdmi_4xxx_read_edid(struct hdmi_ip_data *ip_data, bool ti_hdmi_4xxx_detect(struct hdmi_ip_data *ip_data) { - int r; - - void __iomem *base = hdmi_core_sys_base(ip_data); - - /* HPD */ - r = REG_GET(base, HDMI_CORE_SYS_SYS_STAT, 1, 1); - - return r == 1; + return gpio_get_value(ip_data->hpd_gpio); } static void hdmi_core_init(struct hdmi_core_video_config *video_cfg, @@ -534,12 +587,12 @@ static void hdmi_core_video_config(struct hdmi_ip_data *ip_data, HDMI_CORE_SYS_TMDS_CTRL, cfg->tclk_sel_clkmult, 6, 5); } -static void hdmi_core_aux_infoframe_avi_config(struct hdmi_ip_data *ip_data, - struct hdmi_core_infoframe_avi info_avi) +static void hdmi_core_aux_infoframe_avi_config(struct hdmi_ip_data *ip_data) { u32 val; char sum = 0, checksum = 0; void __iomem *av_base = hdmi_av_base(ip_data); + struct hdmi_core_infoframe_avi info_avi = ip_data->avi_cfg; sum += 0x82 + 0x002 + 0x00D; hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_TYPE, 0x082); @@ -629,8 +682,7 @@ static void hdmi_core_av_packet_config(struct hdmi_ip_data *ip_data, } static void hdmi_wp_init(struct omap_video_timings *timings, - struct hdmi_video_format *video_fmt, - struct hdmi_video_interface *video_int) + struct hdmi_video_format *video_fmt) { pr_debug("Enter hdmi_wp_init\n"); @@ -645,12 +697,6 @@ static void hdmi_wp_init(struct omap_video_timings *timings, video_fmt->y_res = 0; video_fmt->x_res = 0; - video_int->vsp = 0; - video_int->hsp = 0; - - video_int->interlacing = 0; - video_int->tm = 0; /* HDMI_TIMING_SLAVE */ - } void ti_hdmi_4xxx_wp_video_start(struct hdmi_ip_data *ip_data, bool start) @@ -663,15 +709,15 @@ static void hdmi_wp_video_init_format(struct hdmi_video_format *video_fmt, { pr_debug("Enter hdmi_wp_video_init_format\n"); - video_fmt->y_res = param->timings.timings.y_res; - video_fmt->x_res = param->timings.timings.x_res; + video_fmt->y_res = param->timings.y_res; + video_fmt->x_res = param->timings.x_res; - timings->hbp = param->timings.timings.hbp; - timings->hfp = param->timings.timings.hfp; - timings->hsw = param->timings.timings.hsw; - timings->vbp = param->timings.timings.vbp; - timings->vfp = param->timings.timings.vfp; - timings->vsw = param->timings.timings.vsw; + timings->hbp = param->timings.hbp; + timings->hfp = param->timings.hfp; + timings->hsw = param->timings.hsw; + timings->vbp = param->timings.vbp; + timings->vfp = param->timings.vfp; + timings->vsw = param->timings.vsw; } static void hdmi_wp_video_config_format(struct hdmi_ip_data *ip_data, @@ -687,17 +733,16 @@ static void hdmi_wp_video_config_format(struct hdmi_ip_data *ip_data, hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_SIZE, l); } -static void hdmi_wp_video_config_interface(struct hdmi_ip_data *ip_data, - struct hdmi_video_interface *video_int) +static void hdmi_wp_video_config_interface(struct hdmi_ip_data *ip_data) { u32 r; pr_debug("Enter hdmi_wp_video_config_interface\n"); r = hdmi_read_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG); - r = FLD_MOD(r, video_int->vsp, 7, 7); - r = FLD_MOD(r, video_int->hsp, 6, 6); - r = FLD_MOD(r, video_int->interlacing, 3, 3); - r = FLD_MOD(r, video_int->tm, 1, 0); + r = FLD_MOD(r, ip_data->cfg.timings.vsync_pol, 7, 7); + r = FLD_MOD(r, ip_data->cfg.timings.hsync_pol, 6, 6); + r = FLD_MOD(r, ip_data->cfg.timings.interlace, 3, 3); + r = FLD_MOD(r, 1, 1, 0); /* HDMI_TIMING_MASTER_24BIT */ hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG, r); } @@ -725,15 +770,13 @@ void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data) /* HDMI */ struct omap_video_timings video_timing; struct hdmi_video_format video_format; - struct hdmi_video_interface video_interface; /* HDMI core */ - struct hdmi_core_infoframe_avi avi_cfg; + struct hdmi_core_infoframe_avi avi_cfg = ip_data->avi_cfg; struct hdmi_core_video_config v_core_cfg; struct hdmi_core_packet_enable_repeat repeat_cfg; struct hdmi_config *cfg = &ip_data->cfg; - hdmi_wp_init(&video_timing, &video_format, - &video_interface); + hdmi_wp_init(&video_timing, &video_format); hdmi_core_init(&v_core_cfg, &avi_cfg, @@ -748,12 +791,7 @@ void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data) hdmi_wp_video_config_format(ip_data, &video_format); - video_interface.vsp = cfg->timings.vsync_pol; - video_interface.hsp = cfg->timings.hsync_pol; - video_interface.interlacing = cfg->interlace; - video_interface.tm = 1 ; /* HDMI_TIMING_MASTER_24BIT */ - - hdmi_wp_video_config_interface(ip_data, &video_interface); + hdmi_wp_video_config_interface(ip_data); /* * configure core video part @@ -795,7 +833,7 @@ void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data) avi_cfg.db10_11_pixel_eofleft = 0; avi_cfg.db12_13_pixel_sofright = 0; - hdmi_core_aux_infoframe_avi_config(ip_data, avi_cfg); + hdmi_core_aux_infoframe_avi_config(ip_data); /* enable/repeat the infoframe */ repeat_cfg.avi_infoframe = HDMI_PACKETENABLE; @@ -1023,13 +1061,9 @@ void hdmi_core_audio_config(struct hdmi_ip_data *ip_data, u32 r; void __iomem *av_base = hdmi_av_base(ip_data); - /* audio clock recovery parameters */ - r = hdmi_read_reg(av_base, HDMI_CORE_AV_ACR_CTRL); - r = FLD_MOD(r, cfg->use_mclk, 2, 2); - r = FLD_MOD(r, cfg->en_acr_pkt, 1, 1); - r = FLD_MOD(r, cfg->cts_mode, 0, 0); - hdmi_write_reg(av_base, HDMI_CORE_AV_ACR_CTRL, r); - + /* + * Parameters for generation of Audio Clock Recovery packets + */ REG_FLD_MOD(av_base, HDMI_CORE_AV_N_SVAL1, cfg->n, 7, 0); REG_FLD_MOD(av_base, HDMI_CORE_AV_N_SVAL2, cfg->n >> 8, 7, 0); REG_FLD_MOD(av_base, HDMI_CORE_AV_N_SVAL3, cfg->n >> 16, 7, 0); @@ -1041,14 +1075,6 @@ void hdmi_core_audio_config(struct hdmi_ip_data *ip_data, REG_FLD_MOD(av_base, HDMI_CORE_AV_CTS_SVAL3, cfg->cts >> 16, 7, 0); } else { - /* - * HDMI IP uses this configuration to divide the MCLK to - * update CTS value. - */ - REG_FLD_MOD(av_base, - HDMI_CORE_AV_FREQ_SVAL, cfg->mclk_mode, 2, 0); - - /* Configure clock for audio packets */ REG_FLD_MOD(av_base, HDMI_CORE_AV_AUD_PAR_BUSCLK_1, cfg->aud_par_busclk, 7, 0); REG_FLD_MOD(av_base, HDMI_CORE_AV_AUD_PAR_BUSCLK_2, @@ -1057,6 +1083,25 @@ void hdmi_core_audio_config(struct hdmi_ip_data *ip_data, (cfg->aud_par_busclk >> 16), 7, 0); } + /* Set ACR clock divisor */ + REG_FLD_MOD(av_base, + HDMI_CORE_AV_FREQ_SVAL, cfg->mclk_mode, 2, 0); + + r = hdmi_read_reg(av_base, HDMI_CORE_AV_ACR_CTRL); + /* + * Use TMDS clock for ACR packets. For devices that use + * the MCLK, this is the first part of the MCLK initialization. + */ + r = FLD_MOD(r, 0, 2, 2); + + r = FLD_MOD(r, cfg->en_acr_pkt, 1, 1); + r = FLD_MOD(r, cfg->cts_mode, 0, 0); + hdmi_write_reg(av_base, HDMI_CORE_AV_ACR_CTRL, r); + + /* For devices using MCLK, this completes its initialization. */ + if (cfg->use_mclk) + REG_FLD_MOD(av_base, HDMI_CORE_AV_ACR_CTRL, 1, 2, 2); + /* Override of SPDIF sample frequency with value in I2S_CHST4 */ REG_FLD_MOD(av_base, HDMI_CORE_AV_SPDIF_CTRL, cfg->fs_override, 1, 1); @@ -1152,7 +1197,7 @@ int hdmi_config_audio_acr(struct hdmi_ip_data *ip_data, { u32 r; u32 deep_color = 0; - u32 pclk = ip_data->cfg.timings.timings.pixel_clock; + u32 pclk = ip_data->cfg.timings.pixel_clock; if (n == NULL || cts == NULL) return -EINVAL; @@ -1204,36 +1249,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 diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h index 204095632d27..a14d1a0e6e41 100644 --- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h @@ -450,46 +450,6 @@ struct hdmi_core_video_config { * Refer to section 8.2 in HDMI 1.3 specification for * details about infoframe databytes */ -struct hdmi_core_infoframe_avi { - /* Y0, Y1 rgb,yCbCr */ - u8 db1_format; - /* A0 Active information Present */ - u8 db1_active_info; - /* B0, B1 Bar info data valid */ - u8 db1_bar_info_dv; - /* S0, S1 scan information */ - u8 db1_scan_info; - /* C0, C1 colorimetry */ - u8 db2_colorimetry; - /* M0, M1 Aspect ratio (4:3, 16:9) */ - u8 db2_aspect_ratio; - /* R0...R3 Active format aspect ratio */ - u8 db2_active_fmt_ar; - /* ITC IT content. */ - u8 db3_itc; - /* EC0, EC1, EC2 Extended colorimetry */ - u8 db3_ec; - /* Q1, Q0 Quantization range */ - u8 db3_q_range; - /* SC1, SC0 Non-uniform picture scaling */ - u8 db3_nup_scaling; - /* VIC0..6 Video format identification */ - u8 db4_videocode; - /* PR0..PR3 Pixel repetition factor */ - u8 db5_pixel_repeat; - /* Line number end of top bar */ - u16 db6_7_line_eoftop; - /* Line number start of bottom bar */ - u16 db8_9_line_sofbottom; - /* Pixel number end of left bar */ - u16 db10_11_pixel_eofleft; - /* Pixel number start of right bar */ - u16 db12_13_pixel_sofright; -}; -/* - * Refer to section 8.2 in HDMI 1.3 specification for - * details about infoframe databytes - */ struct hdmi_core_infoframe_audio { u8 db1_coding_type; u8 db1_channel_count; @@ -517,13 +477,6 @@ struct hdmi_video_format { u32 x_res; /* pixel per line */ }; -struct hdmi_video_interface { - int vsp; /* Vsync polarity */ - int hsp; /* Hsync polarity */ - int interlacing; - int tm; /* Timing mode */ -}; - struct hdmi_audio_format { enum hdmi_stereo_channels stereo_channels; u8 active_chnnls_msk; @@ -576,9 +529,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, diff --git a/drivers/video/omap2/dss/venc.c b/drivers/video/omap2/dss/venc.c index 7533458ba4d2..9c3daf71750c 100644 --- a/drivers/video/omap2/dss/venc.c +++ b/drivers/video/omap2/dss/venc.c @@ -401,7 +401,7 @@ static void venc_runtime_put(void) DSSDBG("venc_runtime_put\n"); - r = pm_runtime_put(&venc.pdev->dev); + r = pm_runtime_put_sync(&venc.pdev->dev); WARN_ON(r < 0); } @@ -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: @@ -679,6 +699,11 @@ void venc_dump_regs(struct seq_file *s) { #define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, venc_read_reg(r)) + if (cpu_is_omap44xx()) { + seq_printf(s, "VENC currently disabled on OMAP44xx\n"); + return; + } + if (venc_runtime_get()) return; @@ -770,39 +795,41 @@ static int omap_venchw_probe(struct platform_device *pdev) venc_mem = platform_get_resource(venc.pdev, IORESOURCE_MEM, 0); if (!venc_mem) { DSSERR("can't get IORESOURCE_MEM VENC\n"); - r = -EINVAL; - goto err_ioremap; + return -EINVAL; } - venc.base = ioremap(venc_mem->start, resource_size(venc_mem)); + + venc.base = devm_ioremap(&pdev->dev, venc_mem->start, + resource_size(venc_mem)); if (!venc.base) { DSSERR("can't ioremap VENC\n"); - r = -ENOMEM; - goto err_ioremap; + return -ENOMEM; } r = venc_get_clocks(pdev); if (r) - goto err_get_clk; + return r; pm_runtime_enable(&pdev->dev); r = venc_runtime_get(); if (r) - goto err_get_venc; + goto err_runtime_get; rev_id = (u8)(venc_read_reg(VENC_REV_ID) & 0xff); dev_dbg(&pdev->dev, "OMAP VENC rev %d\n", rev_id); venc_runtime_put(); - return omap_dss_register_driver(&venc_driver); + r = omap_dss_register_driver(&venc_driver); + if (r) + goto err_reg_panel_driver; + + return 0; -err_get_venc: +err_reg_panel_driver: +err_runtime_get: pm_runtime_disable(&pdev->dev); venc_put_clocks(); -err_get_clk: - iounmap(venc.base); -err_ioremap: return r; } @@ -817,7 +844,6 @@ static int omap_venchw_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); venc_put_clocks(); - iounmap(venc.base); return 0; } diff --git a/drivers/video/omap2/omapfb/omapfb-ioctl.c b/drivers/video/omap2/omapfb/omapfb-ioctl.c index df7bcce5b107..6a09ef87e14f 100644 --- a/drivers/video/omap2/omapfb/omapfb-ioctl.c +++ b/drivers/video/omap2/omapfb/omapfb-ioctl.c @@ -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; @@ -215,7 +215,7 @@ static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi) int r = 0, i; size_t size; - if (mi->type > OMAPFB_MEMTYPE_MAX) + if (mi->type != OMAPFB_MEMTYPE_SDRAM) return -EINVAL; size = PAGE_ALIGN(mi->size); @@ -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; } diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c index 70aa47de7146..b00db4068d21 100644 --- a/drivers/video/omap2/omapfb/omapfb-main.c +++ b/drivers/video/omap2/omapfb/omapfb-main.c @@ -43,18 +43,18 @@ static char *def_mode; static char *def_vram; -static int def_vrfb; +static bool def_vrfb; static int def_rotate; -static int def_mirror; +static bool def_mirror; static bool auto_update; static unsigned int auto_update_freq; module_param(auto_update, bool, 0); module_param(auto_update_freq, uint, 0644); #ifdef DEBUG -unsigned int omapfb_debug; +bool omapfb_debug; module_param_named(debug, omapfb_debug, bool, 0644); -static unsigned int omapfb_test_pattern; +static bool omapfb_test_pattern; module_param_named(test, omapfb_test_pattern, bool, 0644); #endif @@ -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); @@ -1395,7 +1399,7 @@ static int omapfb_alloc_fbmem(struct fb_info *fbi, unsigned long size, if (!paddr) { DBG("allocating %lu bytes for fb %d\n", size, ofbi->id); - r = omap_vram_alloc(OMAP_VRAM_MEMTYPE_SDRAM, size, &paddr); + r = omap_vram_alloc(size, &paddr); } else { DBG("reserving %lu bytes at %lx for fb %d\n", size, paddr, ofbi->id); @@ -1483,60 +1487,6 @@ static int omapfb_alloc_fbmem_display(struct fb_info *fbi, unsigned long size, return omapfb_alloc_fbmem(fbi, size, paddr); } -static enum omap_color_mode fb_format_to_dss_mode(enum omapfb_color_format fmt) -{ - enum omap_color_mode mode; - - switch (fmt) { - case OMAPFB_COLOR_RGB565: - mode = OMAP_DSS_COLOR_RGB16; - break; - case OMAPFB_COLOR_YUV422: - mode = OMAP_DSS_COLOR_YUV2; - break; - case OMAPFB_COLOR_CLUT_8BPP: - mode = OMAP_DSS_COLOR_CLUT8; - break; - case OMAPFB_COLOR_CLUT_4BPP: - mode = OMAP_DSS_COLOR_CLUT4; - break; - case OMAPFB_COLOR_CLUT_2BPP: - mode = OMAP_DSS_COLOR_CLUT2; - break; - case OMAPFB_COLOR_CLUT_1BPP: - mode = OMAP_DSS_COLOR_CLUT1; - break; - case OMAPFB_COLOR_RGB444: - mode = OMAP_DSS_COLOR_RGB12U; - break; - case OMAPFB_COLOR_YUY422: - mode = OMAP_DSS_COLOR_UYVY; - break; - case OMAPFB_COLOR_ARGB16: - mode = OMAP_DSS_COLOR_ARGB16; - break; - case OMAPFB_COLOR_RGB24U: - mode = OMAP_DSS_COLOR_RGB24U; - break; - case OMAPFB_COLOR_RGB24P: - mode = OMAP_DSS_COLOR_RGB24P; - break; - case OMAPFB_COLOR_ARGB32: - mode = OMAP_DSS_COLOR_ARGB32; - break; - case OMAPFB_COLOR_RGBA32: - mode = OMAP_DSS_COLOR_RGBA32; - break; - case OMAPFB_COLOR_RGBX32: - mode = OMAP_DSS_COLOR_RGBX32; - break; - default: - mode = -EINVAL; - } - - return mode; -} - static int omapfb_parse_vram_param(const char *param, int max_entries, unsigned long *sizes, unsigned long *paddrs) { @@ -1610,23 +1560,6 @@ static int omapfb_allocate_all_fbs(struct omapfb2_device *fbdev) memset(&vram_paddrs, 0, sizeof(vram_paddrs)); } - if (fbdev->dev->platform_data) { - struct omapfb_platform_data *opd; - opd = fbdev->dev->platform_data; - for (i = 0; i < opd->mem_desc.region_cnt; ++i) { - if (!vram_sizes[i]) { - unsigned long size; - unsigned long paddr; - - size = opd->mem_desc.region[i].size; - paddr = opd->mem_desc.region[i].paddr; - - vram_sizes[i] = size; - vram_paddrs[i] = paddr; - } - } - } - for (i = 0; i < fbdev->num_fbs; i++) { /* allocate memory automatically only for fb0, or if * excplicitly defined with vram or plat data option */ @@ -1665,7 +1598,7 @@ int omapfb_realloc_fbmem(struct fb_info *fbi, unsigned long size, int type) int old_type = rg->type; int r; - if (type > OMAPFB_MEMTYPE_MAX) + if (type != OMAPFB_MEMTYPE_SDRAM) return -EINVAL; size = PAGE_ALIGN(size); @@ -1824,32 +1757,6 @@ static int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi) var->rotate = def_rotate; - /* - * Check if there is a default color format set in the board file, - * and use this format instead the default deducted from the - * display bpp. - */ - if (fbdev->dev->platform_data) { - struct omapfb_platform_data *opd; - int id = ofbi->id; - - opd = fbdev->dev->platform_data; - if (opd->mem_desc.region[id].format_used) { - enum omap_color_mode mode; - enum omapfb_color_format format; - - format = opd->mem_desc.region[id].format; - mode = fb_format_to_dss_mode(format); - if (mode < 0) { - r = mode; - goto err; - } - r = dss_mode_to_fb_mode(mode, var); - if (r < 0) - goto err; - } - } - if (display) { u16 w, h; int rotation = (var->rotate + ofbi->rotation[0]) % 4; @@ -2067,6 +1974,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) { diff --git a/drivers/video/omap2/omapfb/omapfb-sysfs.c b/drivers/video/omap2/omapfb/omapfb-sysfs.c index 1694d5148f32..e8d8cc76a435 100644 --- a/drivers/video/omap2/omapfb/omapfb-sysfs.c +++ b/drivers/video/omap2/omapfb/omapfb-sysfs.c @@ -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; } diff --git a/drivers/video/omap2/omapfb/omapfb.h b/drivers/video/omap2/omapfb/omapfb.h index fdf0edeccf4e..c0bdc9b54ecf 100644 --- a/drivers/video/omap2/omapfb/omapfb.h +++ b/drivers/video/omap2/omapfb/omapfb.h @@ -32,7 +32,7 @@ #include <video/omapdss.h> #ifdef DEBUG -extern unsigned int omapfb_debug; +extern bool omapfb_debug; #define DBG(format, ...) \ do { \ if (omapfb_debug) \ @@ -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 * diff --git a/drivers/video/omap2/vram.c b/drivers/video/omap2/vram.c index 9441e2eb3dee..87e421e25afe 100644 --- a/drivers/video/omap2/vram.c +++ b/drivers/video/omap2/vram.c @@ -33,7 +33,6 @@ #include <asm/setup.h> -#include <plat/sram.h> #include <plat/vram.h> #include <plat/dma.h> @@ -43,10 +42,6 @@ #define DBG(format, ...) #endif -#define OMAP2_SRAM_START 0x40200000 -/* Maximum size, in reality this is smaller if SRAM is partially locked. */ -#define OMAP2_SRAM_SIZE 0xa0000 /* 640k */ - /* postponed regions are used to temporarily store region information at boot * time when we cannot yet allocate the region list */ #define MAX_POSTPONED_REGIONS 10 @@ -74,15 +69,6 @@ struct vram_region { static DEFINE_MUTEX(region_mutex); static LIST_HEAD(region_list); -static inline int region_mem_type(unsigned long paddr) -{ - if (paddr >= OMAP2_SRAM_START && - paddr < OMAP2_SRAM_START + OMAP2_SRAM_SIZE) - return OMAP_VRAM_MEMTYPE_SRAM; - else - return OMAP_VRAM_MEMTYPE_SDRAM; -} - static struct vram_region *omap_vram_create_region(unsigned long paddr, unsigned pages) { @@ -212,9 +198,6 @@ static int _omap_vram_reserve(unsigned long paddr, unsigned pages) DBG("checking region %lx %d\n", rm->paddr, rm->pages); - if (region_mem_type(rm->paddr) != region_mem_type(paddr)) - continue; - start = rm->paddr; end = start + (rm->pages << PAGE_SHIFT) - 1; if (start > paddr || end < paddr + size - 1) @@ -320,7 +303,7 @@ err: return r; } -static int _omap_vram_alloc(int mtype, unsigned pages, unsigned long *paddr) +static int _omap_vram_alloc(unsigned pages, unsigned long *paddr) { struct vram_region *rm; struct vram_alloc *alloc; @@ -330,9 +313,6 @@ static int _omap_vram_alloc(int mtype, unsigned pages, unsigned long *paddr) DBG("checking region %lx %d\n", rm->paddr, rm->pages); - if (region_mem_type(rm->paddr) != mtype) - continue; - start = rm->paddr; list_for_each_entry(alloc, &rm->alloc_list, list) { @@ -365,21 +345,21 @@ found: return -ENOMEM; } -int omap_vram_alloc(int mtype, size_t size, unsigned long *paddr) +int omap_vram_alloc(size_t size, unsigned long *paddr) { unsigned pages; int r; - BUG_ON(mtype > OMAP_VRAM_MEMTYPE_MAX || !size); + BUG_ON(!size); - DBG("alloc mem type %d size %d\n", mtype, size); + DBG("alloc mem size %d\n", size); size = PAGE_ALIGN(size); pages = size >> PAGE_SHIFT; mutex_lock(®ion_mutex); - r = _omap_vram_alloc(mtype, pages, paddr); + r = _omap_vram_alloc(pages, paddr); mutex_unlock(®ion_mutex); @@ -501,10 +481,6 @@ arch_initcall(omap_vram_init); /* boottime vram alloc stuff */ /* set from board file */ -static u32 omap_vram_sram_start __initdata; -static u32 omap_vram_sram_size __initdata; - -/* set from board file */ static u32 omap_vram_sdram_start __initdata; static u32 omap_vram_sdram_size __initdata; @@ -587,73 +563,8 @@ void __init omap_vram_reserve_sdram_memblock(void) pr_info("Reserving %u bytes SDRAM for VRAM\n", size); } -/* - * Called at sram init time, before anything is pushed to the SRAM stack. - * Because of the stack scheme, we will allocate everything from the - * start of the lowest address region to the end of SRAM. This will also - * include padding for page alignment and possible holes between regions. - * - * As opposed to the SDRAM case, we'll also do any dynamic allocations at - * this point, since the driver built as a module would have problem with - * freeing / reallocating the regions. - */ -unsigned long __init omap_vram_reserve_sram(unsigned long sram_pstart, - unsigned long sram_vstart, - unsigned long sram_size, - unsigned long pstart_avail, - unsigned long size_avail) -{ - unsigned long pend_avail; - unsigned long reserved; - u32 paddr; - u32 size; - - paddr = omap_vram_sram_start; - size = omap_vram_sram_size; - - if (!size) - return 0; - - reserved = 0; - pend_avail = pstart_avail + size_avail; - - if (!paddr) { - /* Dynamic allocation */ - if ((size_avail & PAGE_MASK) < size) { - pr_err("Not enough SRAM for VRAM\n"); - return 0; - } - size_avail = (size_avail - size) & PAGE_MASK; - paddr = pstart_avail + size_avail; - } - - if (paddr < sram_pstart || - paddr + size > sram_pstart + sram_size) { - pr_err("Illegal SRAM region for VRAM\n"); - return 0; - } - - /* Reserve everything above the start of the region. */ - if (pend_avail - paddr > reserved) - reserved = pend_avail - paddr; - size_avail = pend_avail - reserved - pstart_avail; - - omap_vram_add_region(paddr, size); - - if (reserved) - pr_info("Reserving %lu bytes SRAM for VRAM\n", reserved); - - return reserved; -} - void __init omap_vram_set_sdram_vram(u32 size, u32 start) { omap_vram_sdram_start = start; omap_vram_sdram_size = size; } - -void __init omap_vram_set_sram_vram(u32 size, u32 start) -{ - omap_vram_sram_start = start; - omap_vram_sram_size = size; -} diff --git a/drivers/video/omap2/vrfb.c b/drivers/video/omap2/vrfb.c index fd2271600370..4e5b960c32c8 100644 --- a/drivers/video/omap2/vrfb.c +++ b/drivers/video/omap2/vrfb.c @@ -27,7 +27,6 @@ #include <linux/bitops.h> #include <linux/mutex.h> -#include <mach/io.h> #include <plat/vrfb.h> #include <plat/sdrc.h> |