diff options
Diffstat (limited to 'drivers/gpu/drm/omapdrm')
81 files changed, 10925 insertions, 17883 deletions
diff --git a/drivers/gpu/drm/omapdrm/Kconfig b/drivers/gpu/drm/omapdrm/Kconfig index b3d08c5f41d4..9d4016bd0f44 100644 --- a/drivers/gpu/drm/omapdrm/Kconfig +++ b/drivers/gpu/drm/omapdrm/Kconfig @@ -1,16 +1,137 @@ +# SPDX-License-Identifier: GPL-2.0-only config DRM_OMAP tristate "OMAP DRM" - depends on DRM - depends on ARCH_OMAP2PLUS || ARCH_MULTIPLATFORM - select OMAP2_DSS + depends on MMU + depends on DRM && OF + depends on ARCH_OMAP2PLUS || (COMPILE_TEST && PAGE_SIZE_LESS_THAN_64KB) + select DRM_CLIENT_SELECTION select DRM_KMS_HELPER + select DRM_DISPLAY_HELPER + select DRM_BRIDGE_CONNECTOR + select FB_DMAMEM_HELPERS_DEFERRED if DRM_FBDEV_EMULATION + select VIDEOMODE_HELPERS + select HDMI default n help DRM display driver for OMAP2/3/4 based boards. if DRM_OMAP -source "drivers/gpu/drm/omapdrm/dss/Kconfig" -source "drivers/gpu/drm/omapdrm/displays/Kconfig" +config OMAP2_DSS_DEBUG + bool "Debug support" + default n + help + This enables printing of debug messages. Alternatively, debug messages + can also be enabled by setting CONFIG_DYNAMIC_DEBUG and then setting + appropriate flags in <debugfs>/dynamic_debug/control. + +config OMAP2_DSS_DEBUGFS + bool "Debugfs filesystem support" + depends on DEBUG_FS + default n + help + This enables debugfs for OMAPDSS at <debugfs>/omapdss. This enables + querying about clock configuration and register configuration of dss, + dispc, dsi, hdmi and rfbi. + +config OMAP2_DSS_COLLECT_IRQ_STATS + bool "Collect DSS IRQ statistics" + depends on OMAP2_DSS_DEBUGFS + default n + help + Collect DSS IRQ statistics, printable via debugfs. + + The statistics can be found from + <debugfs>/omapdss/dispc_irq for DISPC interrupts, and + <debugfs>/omapdss/dsi_irq for DSI interrupts. + +config OMAP2_DSS_DPI + bool "DPI support" + default y + help + DPI Interface. This is the Parallel Display Interface. + +config OMAP2_DSS_VENC + bool "VENC support" + default y + help + OMAP Video Encoder support for S-Video and composite TV-out. + +config OMAP2_DSS_HDMI_COMMON + bool + +config OMAP4_DSS_HDMI + bool "HDMI support for OMAP4" + default y + select OMAP2_DSS_HDMI_COMMON + help + HDMI support for OMAP4 based SoCs. + +config OMAP4_DSS_HDMI_CEC + bool "Enable HDMI CEC support for OMAP4" + depends on OMAP4_DSS_HDMI + select CEC_CORE + default y + help + When selected the HDMI transmitter will support the CEC feature. + +config OMAP5_DSS_HDMI + bool "HDMI support for OMAP5" + default n + select OMAP2_DSS_HDMI_COMMON + help + HDMI Interface for OMAP5 and similar cores. This adds the High + Definition Multimedia Interface. See http://www.hdmi.org/ for HDMI + specification. + +config OMAP2_DSS_SDI + bool "SDI support" + default n + help + SDI (Serial Display Interface) support. + + SDI is a high speed one-way display serial bus between the host + processor and a display. + +config OMAP2_DSS_DSI + bool "DSI support" + default n + select DRM_MIPI_DSI + help + MIPI DSI (Display Serial Interface) support. + + DSI is a high speed half-duplex serial interface between the host + processor and a peripheral, such as a display or a framebuffer chip. + + See http://www.mipi.org/ for DSI specifications. + +config OMAP2_DSS_MIN_FCK_PER_PCK + int "Minimum FCK/PCK ratio (for scaling)" + range 0 32 + default 0 + help + This can be used to adjust the minimum FCK/PCK ratio. + + With this you can make sure that DISPC FCK is at least + n x PCK. Video plane scaling requires higher FCK than + normally. + + If this is set to 0, there's no extra constraint on the + DISPC FCK. However, the FCK will at minimum be + 2xPCK (if active matrix) or 3xPCK (if passive matrix). + + Max FCK is 173MHz, so this doesn't work if your PCK + is very high. + +config OMAP2_DSS_SLEEP_AFTER_VENC_RESET + bool "Sleep 20ms after VENC reset" + default y + help + There is a 20ms sleep after VENC reset which seemed to fix the + reset. The reason for the bug is unclear, and it's also unclear + on what platforms this happens. + + This option enables the sleep, and is enabled by default. You can + disable the sleep if it doesn't cause problems on your platform. endif diff --git a/drivers/gpu/drm/omapdrm/Makefile b/drivers/gpu/drm/omapdrm/Makefile index b391be7ecb6c..710b4e0abcf0 100644 --- a/drivers/gpu/drm/omapdrm/Makefile +++ b/drivers/gpu/drm/omapdrm/Makefile @@ -1,18 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0 # # Makefile for the drm device driver. This driver provides support for the # Direct Rendering Infrastructure (DRI) # -obj-y += dss/ -obj-y += displays/ - omapdrm-y := omap_drv.o \ omap_irq.o \ omap_debugfs.o \ omap_crtc.o \ omap_plane.o \ + omap_overlay.o \ omap_encoder.o \ - omap_connector.o \ omap_fb.o \ omap_gem.o \ omap_gem_dmabuf.o \ @@ -21,4 +19,17 @@ omapdrm-y := omap_drv.o \ omapdrm-$(CONFIG_DRM_FBDEV_EMULATION) += omap_fbdev.o -obj-$(CONFIG_DRM_OMAP) += omapdrm.o +omapdrm-y += dss/base.o dss/output.o dss/dss.o dss/dispc.o \ + dss/dispc_coefs.o dss/pll.o dss/video-pll.o +omapdrm-$(CONFIG_OMAP2_DSS_DPI) += dss/dpi.o +omapdrm-$(CONFIG_OMAP2_DSS_VENC) += dss/venc.o +omapdrm-$(CONFIG_OMAP2_DSS_SDI) += dss/sdi.o +omapdrm-$(CONFIG_OMAP2_DSS_DSI) += dss/dsi.o +omapdrm-$(CONFIG_OMAP2_DSS_HDMI_COMMON) += dss/hdmi_common.o dss/hdmi_wp.o \ + dss/hdmi_pll.o dss/hdmi_phy.o +omapdrm-$(CONFIG_OMAP4_DSS_HDMI) += dss/hdmi4.o dss/hdmi4_core.o +omapdrm-$(CONFIG_OMAP4_DSS_HDMI_CEC) += dss/hdmi4_cec.o +omapdrm-$(CONFIG_OMAP5_DSS_HDMI) += dss/hdmi5.o dss/hdmi5_core.o +ccflags-$(CONFIG_OMAP2_DSS_DEBUG) += -DDEBUG + +obj-$(CONFIG_DRM_OMAP) += omapdrm.o diff --git a/drivers/gpu/drm/omapdrm/displays/Kconfig b/drivers/gpu/drm/omapdrm/displays/Kconfig deleted file mode 100644 index c226da145fb3..000000000000 --- a/drivers/gpu/drm/omapdrm/displays/Kconfig +++ /dev/null @@ -1,85 +0,0 @@ -menu "OMAPDRM External Display Device Drivers" - -config DRM_OMAP_ENCODER_OPA362 - tristate "OPA362 external analog amplifier" - help - Driver for OPA362 external analog TV amplifier controlled - through a GPIO. - -config DRM_OMAP_ENCODER_TFP410 - tristate "TFP410 DPI to DVI Encoder" - help - Driver for TFP410 DPI to DVI encoder. - -config DRM_OMAP_ENCODER_TPD12S015 - tristate "TPD12S015 HDMI ESD protection and level shifter" - help - Driver for TPD12S015, which offers HDMI ESD protection and level - shifting. - -config DRM_OMAP_CONNECTOR_DVI - tristate "DVI Connector" - depends on I2C - help - Driver for a generic DVI connector. - -config DRM_OMAP_CONNECTOR_HDMI - tristate "HDMI Connector" - help - Driver for a generic HDMI connector. - -config DRM_OMAP_CONNECTOR_ANALOG_TV - tristate "Analog TV Connector" - help - Driver for a generic analog TV connector. - -config DRM_OMAP_PANEL_DPI - tristate "Generic DPI panel" - help - Driver for generic DPI panels. - -config DRM_OMAP_PANEL_DSI_CM - tristate "Generic DSI Command Mode Panel" - depends on BACKLIGHT_CLASS_DEVICE - help - Driver for generic DSI command mode panels. - -config DRM_OMAP_PANEL_SONY_ACX565AKM - tristate "ACX565AKM Panel" - depends on SPI && BACKLIGHT_CLASS_DEVICE - help - This is the LCD panel used on Nokia N900 - -config DRM_OMAP_PANEL_LGPHILIPS_LB035Q02 - tristate "LG.Philips LB035Q02 LCD Panel" - depends on SPI - help - LCD Panel used on the Gumstix Overo Palo35 - -config DRM_OMAP_PANEL_SHARP_LS037V7DW01 - tristate "Sharp LS037V7DW01 LCD Panel" - depends on BACKLIGHT_CLASS_DEVICE - help - LCD Panel used in TI's SDP3430 and EVM boards - -config DRM_OMAP_PANEL_TPO_TD028TTEC1 - tristate "TPO TD028TTEC1 LCD Panel" - depends on SPI - help - LCD panel used in Openmoko. - -config DRM_OMAP_PANEL_TPO_TD043MTEA1 - tristate "TPO TD043MTEA1 LCD Panel" - depends on SPI - help - LCD Panel used in OMAP3 Pandora - -config DRM_OMAP_PANEL_NEC_NL8048HL11 - tristate "NEC NL8048HL11 Panel" - depends on SPI - depends on BACKLIGHT_CLASS_DEVICE - help - This NEC NL8048HL11 panel is TFT LCD used in the - Zoom2/3/3630 sdp boards. - -endmenu diff --git a/drivers/gpu/drm/omapdrm/displays/Makefile b/drivers/gpu/drm/omapdrm/displays/Makefile deleted file mode 100644 index 46baafb1a83e..000000000000 --- a/drivers/gpu/drm/omapdrm/displays/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -obj-$(CONFIG_DRM_OMAP_ENCODER_OPA362) += encoder-opa362.o -obj-$(CONFIG_DRM_OMAP_ENCODER_TFP410) += encoder-tfp410.o -obj-$(CONFIG_DRM_OMAP_ENCODER_TPD12S015) += encoder-tpd12s015.o -obj-$(CONFIG_DRM_OMAP_CONNECTOR_DVI) += connector-dvi.o -obj-$(CONFIG_DRM_OMAP_CONNECTOR_HDMI) += connector-hdmi.o -obj-$(CONFIG_DRM_OMAP_CONNECTOR_ANALOG_TV) += connector-analog-tv.o -obj-$(CONFIG_DRM_OMAP_PANEL_DPI) += panel-dpi.o -obj-$(CONFIG_DRM_OMAP_PANEL_DSI_CM) += panel-dsi-cm.o -obj-$(CONFIG_DRM_OMAP_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o -obj-$(CONFIG_DRM_OMAP_PANEL_LGPHILIPS_LB035Q02) += panel-lgphilips-lb035q02.o -obj-$(CONFIG_DRM_OMAP_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o -obj-$(CONFIG_DRM_OMAP_PANEL_TPO_TD028TTEC1) += panel-tpo-td028ttec1.o -obj-$(CONFIG_DRM_OMAP_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o -obj-$(CONFIG_DRM_OMAP_PANEL_NEC_NL8048HL11) += panel-nec-nl8048hl11.o diff --git a/drivers/gpu/drm/omapdrm/displays/connector-analog-tv.c b/drivers/gpu/drm/omapdrm/displays/connector-analog-tv.c deleted file mode 100644 index e1fa143a5625..000000000000 --- a/drivers/gpu/drm/omapdrm/displays/connector-analog-tv.c +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Analog TV Connector driver - * - * Copyright (C) 2013 Texas Instruments - * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - */ - -#include <linux/slab.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/of.h> - -#include "../dss/omapdss.h" - -struct panel_drv_data { - struct omap_dss_device dssdev; - struct omap_dss_device *in; - - struct device *dev; - - struct videomode vm; -}; - -static const struct videomode tvc_pal_vm = { - .hactive = 720, - .vactive = 574, - .pixelclock = 13500000, - .hsync_len = 64, - .hfront_porch = 12, - .hback_porch = 68, - .vsync_len = 5, - .vfront_porch = 5, - .vback_porch = 41, - - .flags = DISPLAY_FLAGS_INTERLACED | DISPLAY_FLAGS_HSYNC_LOW | - DISPLAY_FLAGS_VSYNC_LOW, -}; - -static const struct of_device_id tvc_of_match[]; - -#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev) - -static int tvc_connect(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - int r; - - dev_dbg(ddata->dev, "connect\n"); - - if (omapdss_device_is_connected(dssdev)) - return 0; - - r = in->ops.atv->connect(in, dssdev); - if (r) - return r; - - return 0; -} - -static void tvc_disconnect(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - dev_dbg(ddata->dev, "disconnect\n"); - - if (!omapdss_device_is_connected(dssdev)) - return; - - in->ops.atv->disconnect(in, dssdev); -} - -static int tvc_enable(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - int r; - - dev_dbg(ddata->dev, "enable\n"); - - if (!omapdss_device_is_connected(dssdev)) - return -ENODEV; - - if (omapdss_device_is_enabled(dssdev)) - return 0; - - in->ops.atv->set_timings(in, &ddata->vm); - - r = in->ops.atv->enable(in); - if (r) - return r; - - dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; - - return r; -} - -static void tvc_disable(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - dev_dbg(ddata->dev, "disable\n"); - - if (!omapdss_device_is_enabled(dssdev)) - return; - - in->ops.atv->disable(in); - - dssdev->state = OMAP_DSS_DISPLAY_DISABLED; -} - -static void tvc_set_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - ddata->vm = *vm; - dssdev->panel.vm = *vm; - - in->ops.atv->set_timings(in, vm); -} - -static void tvc_get_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - - *vm = ddata->vm; -} - -static int tvc_check_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - return in->ops.atv->check_timings(in, vm); -} - -static u32 tvc_get_wss(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - return in->ops.atv->get_wss(in); -} - -static int tvc_set_wss(struct omap_dss_device *dssdev, u32 wss) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - return in->ops.atv->set_wss(in, wss); -} - -static struct omap_dss_driver tvc_driver = { - .connect = tvc_connect, - .disconnect = tvc_disconnect, - - .enable = tvc_enable, - .disable = tvc_disable, - - .set_timings = tvc_set_timings, - .get_timings = tvc_get_timings, - .check_timings = tvc_check_timings, - - .get_wss = tvc_get_wss, - .set_wss = tvc_set_wss, -}; - -static int tvc_probe_of(struct platform_device *pdev) -{ - struct panel_drv_data *ddata = platform_get_drvdata(pdev); - struct device_node *node = pdev->dev.of_node; - struct omap_dss_device *in; - - in = omapdss_of_find_source_for_first_ep(node); - if (IS_ERR(in)) { - dev_err(&pdev->dev, "failed to find video source\n"); - return PTR_ERR(in); - } - - ddata->in = in; - - return 0; -} - -static int tvc_probe(struct platform_device *pdev) -{ - struct panel_drv_data *ddata; - struct omap_dss_device *dssdev; - int r; - - ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); - if (!ddata) - return -ENOMEM; - - platform_set_drvdata(pdev, ddata); - ddata->dev = &pdev->dev; - - r = tvc_probe_of(pdev); - if (r) - return r; - - ddata->vm = tvc_pal_vm; - - dssdev = &ddata->dssdev; - dssdev->driver = &tvc_driver; - dssdev->dev = &pdev->dev; - dssdev->type = OMAP_DISPLAY_TYPE_VENC; - dssdev->owner = THIS_MODULE; - dssdev->panel.vm = tvc_pal_vm; - - r = omapdss_register_display(dssdev); - if (r) { - dev_err(&pdev->dev, "Failed to register panel\n"); - goto err_reg; - } - - return 0; -err_reg: - omap_dss_put_device(ddata->in); - return r; -} - -static int __exit tvc_remove(struct platform_device *pdev) -{ - struct panel_drv_data *ddata = platform_get_drvdata(pdev); - struct omap_dss_device *dssdev = &ddata->dssdev; - struct omap_dss_device *in = ddata->in; - - omapdss_unregister_display(&ddata->dssdev); - - tvc_disable(dssdev); - tvc_disconnect(dssdev); - - omap_dss_put_device(in); - - return 0; -} - -static const struct of_device_id tvc_of_match[] = { - { .compatible = "omapdss,svideo-connector", }, - { .compatible = "omapdss,composite-video-connector", }, - {}, -}; - -MODULE_DEVICE_TABLE(of, tvc_of_match); - -static struct platform_driver tvc_connector_driver = { - .probe = tvc_probe, - .remove = __exit_p(tvc_remove), - .driver = { - .name = "connector-analog-tv", - .of_match_table = tvc_of_match, - .suppress_bind_attrs = true, - }, -}; - -module_platform_driver(tvc_connector_driver); - -MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); -MODULE_DESCRIPTION("Analog TV Connector driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/omapdrm/displays/connector-dvi.c b/drivers/gpu/drm/omapdrm/displays/connector-dvi.c deleted file mode 100644 index 05fa24a518c8..000000000000 --- a/drivers/gpu/drm/omapdrm/displays/connector-dvi.c +++ /dev/null @@ -1,349 +0,0 @@ -/* - * Generic DVI Connector driver - * - * Copyright (C) 2013 Texas Instruments - * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - */ - -#include <linux/i2c.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/slab.h> - -#include <drm/drm_edid.h> - -#include "../dss/omapdss.h" - -static const struct videomode dvic_default_vm = { - .hactive = 640, - .vactive = 480, - - .pixelclock = 23500000, - - .hfront_porch = 48, - .hsync_len = 32, - .hback_porch = 80, - - .vfront_porch = 3, - .vsync_len = 4, - .vback_porch = 7, - - .flags = DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH | - DISPLAY_FLAGS_SYNC_NEGEDGE | DISPLAY_FLAGS_DE_HIGH | - DISPLAY_FLAGS_PIXDATA_POSEDGE, -}; - -struct panel_drv_data { - struct omap_dss_device dssdev; - struct omap_dss_device *in; - - struct videomode vm; - - struct i2c_adapter *i2c_adapter; -}; - -#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev) - -static int dvic_connect(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - int r; - - if (omapdss_device_is_connected(dssdev)) - return 0; - - r = in->ops.dvi->connect(in, dssdev); - if (r) - return r; - - return 0; -} - -static void dvic_disconnect(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - if (!omapdss_device_is_connected(dssdev)) - return; - - in->ops.dvi->disconnect(in, dssdev); -} - -static int dvic_enable(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - int r; - - if (!omapdss_device_is_connected(dssdev)) - return -ENODEV; - - if (omapdss_device_is_enabled(dssdev)) - return 0; - - in->ops.dvi->set_timings(in, &ddata->vm); - - r = in->ops.dvi->enable(in); - if (r) - return r; - - dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; - - return 0; -} - -static void dvic_disable(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - if (!omapdss_device_is_enabled(dssdev)) - return; - - in->ops.dvi->disable(in); - - dssdev->state = OMAP_DSS_DISPLAY_DISABLED; -} - -static void dvic_set_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - ddata->vm = *vm; - dssdev->panel.vm = *vm; - - in->ops.dvi->set_timings(in, vm); -} - -static void dvic_get_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - - *vm = ddata->vm; -} - -static int dvic_check_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - return in->ops.dvi->check_timings(in, vm); -} - -static int dvic_ddc_read(struct i2c_adapter *adapter, - unsigned char *buf, u16 count, u8 offset) -{ - int r, retries; - - for (retries = 3; retries > 0; retries--) { - struct i2c_msg msgs[] = { - { - .addr = DDC_ADDR, - .flags = 0, - .len = 1, - .buf = &offset, - }, { - .addr = DDC_ADDR, - .flags = I2C_M_RD, - .len = count, - .buf = buf, - } - }; - - r = i2c_transfer(adapter, msgs, 2); - if (r == 2) - return 0; - - if (r != -EAGAIN) - break; - } - - return r < 0 ? r : -EIO; -} - -static int dvic_read_edid(struct omap_dss_device *dssdev, - u8 *edid, int len) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - int r, l, bytes_read; - - if (!ddata->i2c_adapter) - return -ENODEV; - - l = min(EDID_LENGTH, len); - r = dvic_ddc_read(ddata->i2c_adapter, edid, l, 0); - if (r) - return r; - - bytes_read = l; - - /* if there are extensions, read second block */ - if (len > EDID_LENGTH && edid[0x7e] > 0) { - l = min(EDID_LENGTH, len - EDID_LENGTH); - - r = dvic_ddc_read(ddata->i2c_adapter, edid + EDID_LENGTH, - l, EDID_LENGTH); - if (r) - return r; - - bytes_read += l; - } - - return bytes_read; -} - -static bool dvic_detect(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - unsigned char out; - int r; - - if (!ddata->i2c_adapter) - return true; - - r = dvic_ddc_read(ddata->i2c_adapter, &out, 1, 0); - - return r == 0; -} - -static struct omap_dss_driver dvic_driver = { - .connect = dvic_connect, - .disconnect = dvic_disconnect, - - .enable = dvic_enable, - .disable = dvic_disable, - - .set_timings = dvic_set_timings, - .get_timings = dvic_get_timings, - .check_timings = dvic_check_timings, - - .read_edid = dvic_read_edid, - .detect = dvic_detect, -}; - -static int dvic_probe_of(struct platform_device *pdev) -{ - struct panel_drv_data *ddata = platform_get_drvdata(pdev); - struct device_node *node = pdev->dev.of_node; - struct omap_dss_device *in; - struct device_node *adapter_node; - struct i2c_adapter *adapter; - - in = omapdss_of_find_source_for_first_ep(node); - if (IS_ERR(in)) { - dev_err(&pdev->dev, "failed to find video source\n"); - return PTR_ERR(in); - } - - ddata->in = in; - - adapter_node = of_parse_phandle(node, "ddc-i2c-bus", 0); - if (adapter_node) { - adapter = of_get_i2c_adapter_by_node(adapter_node); - of_node_put(adapter_node); - if (adapter == NULL) { - dev_err(&pdev->dev, "failed to parse ddc-i2c-bus\n"); - omap_dss_put_device(ddata->in); - return -EPROBE_DEFER; - } - - ddata->i2c_adapter = adapter; - } - - return 0; -} - -static int dvic_probe(struct platform_device *pdev) -{ - struct panel_drv_data *ddata; - struct omap_dss_device *dssdev; - int r; - - ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); - if (!ddata) - return -ENOMEM; - - platform_set_drvdata(pdev, ddata); - - if (!pdev->dev.of_node) - return -ENODEV; - - r = dvic_probe_of(pdev); - if (r) - return r; - - ddata->vm = dvic_default_vm; - - dssdev = &ddata->dssdev; - dssdev->driver = &dvic_driver; - dssdev->dev = &pdev->dev; - dssdev->type = OMAP_DISPLAY_TYPE_DVI; - dssdev->owner = THIS_MODULE; - dssdev->panel.vm = dvic_default_vm; - - r = omapdss_register_display(dssdev); - if (r) { - dev_err(&pdev->dev, "Failed to register panel\n"); - goto err_reg; - } - - return 0; - -err_reg: - omap_dss_put_device(ddata->in); - - i2c_put_adapter(ddata->i2c_adapter); - - return r; -} - -static int __exit dvic_remove(struct platform_device *pdev) -{ - struct panel_drv_data *ddata = platform_get_drvdata(pdev); - struct omap_dss_device *dssdev = &ddata->dssdev; - struct omap_dss_device *in = ddata->in; - - omapdss_unregister_display(&ddata->dssdev); - - dvic_disable(dssdev); - dvic_disconnect(dssdev); - - omap_dss_put_device(in); - - i2c_put_adapter(ddata->i2c_adapter); - - return 0; -} - -static const struct of_device_id dvic_of_match[] = { - { .compatible = "omapdss,dvi-connector", }, - {}, -}; - -MODULE_DEVICE_TABLE(of, dvic_of_match); - -static struct platform_driver dvi_connector_driver = { - .probe = dvic_probe, - .remove = __exit_p(dvic_remove), - .driver = { - .name = "connector-dvi", - .of_match_table = dvic_of_match, - .suppress_bind_attrs = true, - }, -}; - -module_platform_driver(dvi_connector_driver); - -MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); -MODULE_DESCRIPTION("Generic DVI Connector driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/omapdrm/displays/connector-hdmi.c b/drivers/gpu/drm/omapdrm/displays/connector-hdmi.c deleted file mode 100644 index 79cb69f1acf5..000000000000 --- a/drivers/gpu/drm/omapdrm/displays/connector-hdmi.c +++ /dev/null @@ -1,314 +0,0 @@ -/* - * HDMI Connector driver - * - * Copyright (C) 2013 Texas Instruments - * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - */ - -#include <linux/gpio/consumer.h> -#include <linux/slab.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/of.h> -#include <linux/of_gpio.h> - -#include <drm/drm_edid.h> - -#include "../dss/omapdss.h" - -static const struct videomode hdmic_default_vm = { - .hactive = 640, - .vactive = 480, - .pixelclock = 25175000, - .hsync_len = 96, - .hfront_porch = 16, - .hback_porch = 48, - .vsync_len = 2, - .vfront_porch = 11, - .vback_porch = 31, - - .flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW, -}; - -struct panel_drv_data { - struct omap_dss_device dssdev; - struct omap_dss_device *in; - - struct device *dev; - - struct videomode vm; - - int hpd_gpio; -}; - -#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev) - -static int hdmic_connect(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - int r; - - dev_dbg(ddata->dev, "connect\n"); - - if (omapdss_device_is_connected(dssdev)) - return 0; - - r = in->ops.hdmi->connect(in, dssdev); - if (r) - return r; - - return 0; -} - -static void hdmic_disconnect(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - dev_dbg(ddata->dev, "disconnect\n"); - - if (!omapdss_device_is_connected(dssdev)) - return; - - in->ops.hdmi->disconnect(in, dssdev); -} - -static int hdmic_enable(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - int r; - - dev_dbg(ddata->dev, "enable\n"); - - if (!omapdss_device_is_connected(dssdev)) - return -ENODEV; - - if (omapdss_device_is_enabled(dssdev)) - return 0; - - in->ops.hdmi->set_timings(in, &ddata->vm); - - r = in->ops.hdmi->enable(in); - if (r) - return r; - - dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; - - return r; -} - -static void hdmic_disable(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - dev_dbg(ddata->dev, "disable\n"); - - if (!omapdss_device_is_enabled(dssdev)) - return; - - in->ops.hdmi->disable(in); - - dssdev->state = OMAP_DSS_DISPLAY_DISABLED; -} - -static void hdmic_set_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - ddata->vm = *vm; - dssdev->panel.vm = *vm; - - in->ops.hdmi->set_timings(in, vm); -} - -static void hdmic_get_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - - *vm = ddata->vm; -} - -static int hdmic_check_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - return in->ops.hdmi->check_timings(in, vm); -} - -static int hdmic_read_edid(struct omap_dss_device *dssdev, - u8 *edid, int len) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - return in->ops.hdmi->read_edid(in, edid, len); -} - -static bool hdmic_detect(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - if (gpio_is_valid(ddata->hpd_gpio)) - return gpio_get_value_cansleep(ddata->hpd_gpio); - else - return in->ops.hdmi->detect(in); -} - -static int hdmic_set_hdmi_mode(struct omap_dss_device *dssdev, bool hdmi_mode) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - return in->ops.hdmi->set_hdmi_mode(in, hdmi_mode); -} - -static int hdmic_set_infoframe(struct omap_dss_device *dssdev, - const struct hdmi_avi_infoframe *avi) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - return in->ops.hdmi->set_infoframe(in, avi); -} - -static struct omap_dss_driver hdmic_driver = { - .connect = hdmic_connect, - .disconnect = hdmic_disconnect, - - .enable = hdmic_enable, - .disable = hdmic_disable, - - .set_timings = hdmic_set_timings, - .get_timings = hdmic_get_timings, - .check_timings = hdmic_check_timings, - - .read_edid = hdmic_read_edid, - .detect = hdmic_detect, - .set_hdmi_mode = hdmic_set_hdmi_mode, - .set_hdmi_infoframe = hdmic_set_infoframe, -}; - -static int hdmic_probe_of(struct platform_device *pdev) -{ - struct panel_drv_data *ddata = platform_get_drvdata(pdev); - struct device_node *node = pdev->dev.of_node; - struct omap_dss_device *in; - int gpio; - - /* HPD GPIO */ - gpio = of_get_named_gpio(node, "hpd-gpios", 0); - if (gpio_is_valid(gpio)) - ddata->hpd_gpio = gpio; - else - ddata->hpd_gpio = -ENODEV; - - in = omapdss_of_find_source_for_first_ep(node); - if (IS_ERR(in)) { - dev_err(&pdev->dev, "failed to find video source\n"); - return PTR_ERR(in); - } - - ddata->in = in; - - return 0; -} - -static int hdmic_probe(struct platform_device *pdev) -{ - struct panel_drv_data *ddata; - struct omap_dss_device *dssdev; - int r; - - ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); - if (!ddata) - return -ENOMEM; - - platform_set_drvdata(pdev, ddata); - ddata->dev = &pdev->dev; - - if (!pdev->dev.of_node) - return -ENODEV; - - r = hdmic_probe_of(pdev); - if (r) - return r; - - if (gpio_is_valid(ddata->hpd_gpio)) { - r = devm_gpio_request_one(&pdev->dev, ddata->hpd_gpio, - GPIOF_DIR_IN, "hdmi_hpd"); - if (r) - goto err_reg; - } - - ddata->vm = hdmic_default_vm; - - dssdev = &ddata->dssdev; - dssdev->driver = &hdmic_driver; - dssdev->dev = &pdev->dev; - dssdev->type = OMAP_DISPLAY_TYPE_HDMI; - dssdev->owner = THIS_MODULE; - dssdev->panel.vm = hdmic_default_vm; - - r = omapdss_register_display(dssdev); - if (r) { - dev_err(&pdev->dev, "Failed to register panel\n"); - goto err_reg; - } - - return 0; -err_reg: - omap_dss_put_device(ddata->in); - return r; -} - -static int __exit hdmic_remove(struct platform_device *pdev) -{ - struct panel_drv_data *ddata = platform_get_drvdata(pdev); - struct omap_dss_device *dssdev = &ddata->dssdev; - struct omap_dss_device *in = ddata->in; - - omapdss_unregister_display(&ddata->dssdev); - - hdmic_disable(dssdev); - hdmic_disconnect(dssdev); - - omap_dss_put_device(in); - - return 0; -} - -static const struct of_device_id hdmic_of_match[] = { - { .compatible = "omapdss,hdmi-connector", }, - {}, -}; - -MODULE_DEVICE_TABLE(of, hdmic_of_match); - -static struct platform_driver hdmi_connector_driver = { - .probe = hdmic_probe, - .remove = __exit_p(hdmic_remove), - .driver = { - .name = "connector-hdmi", - .of_match_table = hdmic_of_match, - .suppress_bind_attrs = true, - }, -}; - -module_platform_driver(hdmi_connector_driver); - -MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); -MODULE_DESCRIPTION("HDMI Connector driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/omapdrm/displays/encoder-opa362.c b/drivers/gpu/drm/omapdrm/displays/encoder-opa362.c deleted file mode 100644 index b1f6aa09f699..000000000000 --- a/drivers/gpu/drm/omapdrm/displays/encoder-opa362.c +++ /dev/null @@ -1,267 +0,0 @@ -/* - * OPA362 analog video amplifier with output/power control - * - * Copyright (C) 2014 Golden Delicious Computers - * Author: H. Nikolaus Schaller <hns@goldelico.com> - * - * based on encoder-tfp410 - * - * Copyright (C) 2013 Texas Instruments - * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - */ - -#include <linux/gpio/consumer.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/slab.h> - -#include "../dss/omapdss.h" - -struct panel_drv_data { - struct omap_dss_device dssdev; - struct omap_dss_device *in; - - struct gpio_desc *enable_gpio; - - struct videomode vm; -}; - -#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev) - -static int opa362_connect(struct omap_dss_device *dssdev, - struct omap_dss_device *dst) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - int r; - - dev_dbg(dssdev->dev, "connect\n"); - - if (omapdss_device_is_connected(dssdev)) - return -EBUSY; - - r = in->ops.atv->connect(in, dssdev); - if (r) - return r; - - dst->src = dssdev; - dssdev->dst = dst; - - return 0; -} - -static void opa362_disconnect(struct omap_dss_device *dssdev, - struct omap_dss_device *dst) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - dev_dbg(dssdev->dev, "disconnect\n"); - - WARN_ON(!omapdss_device_is_connected(dssdev)); - if (!omapdss_device_is_connected(dssdev)) - return; - - WARN_ON(dst != dssdev->dst); - if (dst != dssdev->dst) - return; - - dst->src = NULL; - dssdev->dst = NULL; - - in->ops.atv->disconnect(in, &ddata->dssdev); -} - -static int opa362_enable(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - int r; - - dev_dbg(dssdev->dev, "enable\n"); - - if (!omapdss_device_is_connected(dssdev)) - return -ENODEV; - - if (omapdss_device_is_enabled(dssdev)) - return 0; - - in->ops.atv->set_timings(in, &ddata->vm); - - r = in->ops.atv->enable(in); - if (r) - return r; - - if (ddata->enable_gpio) - gpiod_set_value_cansleep(ddata->enable_gpio, 1); - - dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; - - return 0; -} - -static void opa362_disable(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - dev_dbg(dssdev->dev, "disable\n"); - - if (!omapdss_device_is_enabled(dssdev)) - return; - - if (ddata->enable_gpio) - gpiod_set_value_cansleep(ddata->enable_gpio, 0); - - in->ops.atv->disable(in); - - dssdev->state = OMAP_DSS_DISPLAY_DISABLED; -} - -static void opa362_set_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - dev_dbg(dssdev->dev, "set_timings\n"); - - ddata->vm = *vm; - dssdev->panel.vm = *vm; - - in->ops.atv->set_timings(in, vm); -} - -static void opa362_get_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - - dev_dbg(dssdev->dev, "get_timings\n"); - - *vm = ddata->vm; -} - -static int opa362_check_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - dev_dbg(dssdev->dev, "check_timings\n"); - - return in->ops.atv->check_timings(in, vm); -} - -static const struct omapdss_atv_ops opa362_atv_ops = { - .connect = opa362_connect, - .disconnect = opa362_disconnect, - - .enable = opa362_enable, - .disable = opa362_disable, - - .check_timings = opa362_check_timings, - .set_timings = opa362_set_timings, - .get_timings = opa362_get_timings, -}; - -static int opa362_probe(struct platform_device *pdev) -{ - struct device_node *node = pdev->dev.of_node; - struct panel_drv_data *ddata; - struct omap_dss_device *dssdev, *in; - struct gpio_desc *gpio; - int r; - - dev_dbg(&pdev->dev, "probe\n"); - - if (node == NULL) { - dev_err(&pdev->dev, "Unable to find device tree\n"); - return -EINVAL; - } - - ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); - if (!ddata) - return -ENOMEM; - - platform_set_drvdata(pdev, ddata); - - gpio = devm_gpiod_get_optional(&pdev->dev, "enable", GPIOD_OUT_LOW); - if (IS_ERR(gpio)) - return PTR_ERR(gpio); - - ddata->enable_gpio = gpio; - - in = omapdss_of_find_source_for_first_ep(node); - if (IS_ERR(in)) { - dev_err(&pdev->dev, "failed to find video source\n"); - return PTR_ERR(in); - } - - ddata->in = in; - - dssdev = &ddata->dssdev; - dssdev->ops.atv = &opa362_atv_ops; - dssdev->dev = &pdev->dev; - dssdev->type = OMAP_DISPLAY_TYPE_VENC; - dssdev->output_type = OMAP_DISPLAY_TYPE_VENC; - dssdev->owner = THIS_MODULE; - - r = omapdss_register_output(dssdev); - if (r) { - dev_err(&pdev->dev, "Failed to register output\n"); - goto err_reg; - } - - return 0; -err_reg: - omap_dss_put_device(ddata->in); - return r; -} - -static int __exit opa362_remove(struct platform_device *pdev) -{ - struct panel_drv_data *ddata = platform_get_drvdata(pdev); - struct omap_dss_device *dssdev = &ddata->dssdev; - struct omap_dss_device *in = ddata->in; - - omapdss_unregister_output(&ddata->dssdev); - - WARN_ON(omapdss_device_is_enabled(dssdev)); - if (omapdss_device_is_enabled(dssdev)) - opa362_disable(dssdev); - - WARN_ON(omapdss_device_is_connected(dssdev)); - if (omapdss_device_is_connected(dssdev)) - opa362_disconnect(dssdev, dssdev->dst); - - omap_dss_put_device(in); - - return 0; -} - -static const struct of_device_id opa362_of_match[] = { - { .compatible = "omapdss,ti,opa362", }, - {}, -}; -MODULE_DEVICE_TABLE(of, opa362_of_match); - -static struct platform_driver opa362_driver = { - .probe = opa362_probe, - .remove = __exit_p(opa362_remove), - .driver = { - .name = "amplifier-opa362", - .of_match_table = opa362_of_match, - .suppress_bind_attrs = true, - }, -}; - -module_platform_driver(opa362_driver); - -MODULE_AUTHOR("H. Nikolaus Schaller <hns@goldelico.com>"); -MODULE_DESCRIPTION("OPA362 analog video amplifier with output/power control"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/omapdrm/displays/encoder-tfp410.c b/drivers/gpu/drm/omapdrm/displays/encoder-tfp410.c deleted file mode 100644 index 947295f9e30f..000000000000 --- a/drivers/gpu/drm/omapdrm/displays/encoder-tfp410.c +++ /dev/null @@ -1,283 +0,0 @@ -/* - * TFP410 DPI-to-DVI encoder driver - * - * Copyright (C) 2013 Texas Instruments - * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - */ - -#include <linux/gpio/consumer.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/slab.h> -#include <linux/of_gpio.h> - -#include "../dss/omapdss.h" - -struct panel_drv_data { - struct omap_dss_device dssdev; - struct omap_dss_device *in; - - int pd_gpio; - - struct videomode vm; -}; - -#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev) - -static int tfp410_connect(struct omap_dss_device *dssdev, - struct omap_dss_device *dst) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - int r; - - if (omapdss_device_is_connected(dssdev)) - return -EBUSY; - - r = in->ops.dpi->connect(in, dssdev); - if (r) - return r; - - dst->src = dssdev; - dssdev->dst = dst; - - return 0; -} - -static void tfp410_disconnect(struct omap_dss_device *dssdev, - struct omap_dss_device *dst) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - WARN_ON(!omapdss_device_is_connected(dssdev)); - if (!omapdss_device_is_connected(dssdev)) - return; - - WARN_ON(dst != dssdev->dst); - if (dst != dssdev->dst) - return; - - dst->src = NULL; - dssdev->dst = NULL; - - in->ops.dpi->disconnect(in, &ddata->dssdev); -} - -static int tfp410_enable(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - int r; - - if (!omapdss_device_is_connected(dssdev)) - return -ENODEV; - - if (omapdss_device_is_enabled(dssdev)) - return 0; - - in->ops.dpi->set_timings(in, &ddata->vm); - - r = in->ops.dpi->enable(in); - if (r) - return r; - - if (gpio_is_valid(ddata->pd_gpio)) - gpio_set_value_cansleep(ddata->pd_gpio, 1); - - dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; - - return 0; -} - -static void tfp410_disable(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - if (!omapdss_device_is_enabled(dssdev)) - return; - - if (gpio_is_valid(ddata->pd_gpio)) - gpio_set_value_cansleep(ddata->pd_gpio, 0); - - in->ops.dpi->disable(in); - - dssdev->state = OMAP_DSS_DISPLAY_DISABLED; -} - -static void tfp410_fix_timings(struct videomode *vm) -{ - vm->flags |= DISPLAY_FLAGS_DE_HIGH | DISPLAY_FLAGS_PIXDATA_POSEDGE | - DISPLAY_FLAGS_SYNC_POSEDGE; -} - -static void tfp410_set_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - tfp410_fix_timings(vm); - - ddata->vm = *vm; - dssdev->panel.vm = *vm; - - in->ops.dpi->set_timings(in, vm); -} - -static void tfp410_get_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - - *vm = ddata->vm; -} - -static int tfp410_check_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - tfp410_fix_timings(vm); - - return in->ops.dpi->check_timings(in, vm); -} - -static const struct omapdss_dvi_ops tfp410_dvi_ops = { - .connect = tfp410_connect, - .disconnect = tfp410_disconnect, - - .enable = tfp410_enable, - .disable = tfp410_disable, - - .check_timings = tfp410_check_timings, - .set_timings = tfp410_set_timings, - .get_timings = tfp410_get_timings, -}; - -static int tfp410_probe_of(struct platform_device *pdev) -{ - struct panel_drv_data *ddata = platform_get_drvdata(pdev); - struct device_node *node = pdev->dev.of_node; - struct omap_dss_device *in; - int gpio; - - gpio = of_get_named_gpio(node, "powerdown-gpios", 0); - - if (gpio_is_valid(gpio) || gpio == -ENOENT) { - ddata->pd_gpio = gpio; - } else { - dev_err(&pdev->dev, "failed to parse PD gpio\n"); - return gpio; - } - - in = omapdss_of_find_source_for_first_ep(node); - if (IS_ERR(in)) { - dev_err(&pdev->dev, "failed to find video source\n"); - return PTR_ERR(in); - } - - ddata->in = in; - - return 0; -} - -static int tfp410_probe(struct platform_device *pdev) -{ - struct panel_drv_data *ddata; - struct omap_dss_device *dssdev; - int r; - - ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); - if (!ddata) - return -ENOMEM; - - platform_set_drvdata(pdev, ddata); - - if (!pdev->dev.of_node) - return -ENODEV; - - r = tfp410_probe_of(pdev); - if (r) - return r; - - if (gpio_is_valid(ddata->pd_gpio)) { - r = devm_gpio_request_one(&pdev->dev, ddata->pd_gpio, - GPIOF_OUT_INIT_LOW, "tfp410 PD"); - if (r) { - dev_err(&pdev->dev, "Failed to request PD GPIO %d\n", - ddata->pd_gpio); - goto err_gpio; - } - } - - dssdev = &ddata->dssdev; - dssdev->ops.dvi = &tfp410_dvi_ops; - dssdev->dev = &pdev->dev; - dssdev->type = OMAP_DISPLAY_TYPE_DPI; - dssdev->output_type = OMAP_DISPLAY_TYPE_DVI; - dssdev->owner = THIS_MODULE; - dssdev->port_num = 1; - - r = omapdss_register_output(dssdev); - if (r) { - dev_err(&pdev->dev, "Failed to register output\n"); - goto err_reg; - } - - return 0; -err_reg: -err_gpio: - omap_dss_put_device(ddata->in); - return r; -} - -static int __exit tfp410_remove(struct platform_device *pdev) -{ - struct panel_drv_data *ddata = platform_get_drvdata(pdev); - struct omap_dss_device *dssdev = &ddata->dssdev; - struct omap_dss_device *in = ddata->in; - - omapdss_unregister_output(&ddata->dssdev); - - WARN_ON(omapdss_device_is_enabled(dssdev)); - if (omapdss_device_is_enabled(dssdev)) - tfp410_disable(dssdev); - - WARN_ON(omapdss_device_is_connected(dssdev)); - if (omapdss_device_is_connected(dssdev)) - tfp410_disconnect(dssdev, dssdev->dst); - - omap_dss_put_device(in); - - return 0; -} - -static const struct of_device_id tfp410_of_match[] = { - { .compatible = "omapdss,ti,tfp410", }, - {}, -}; - -MODULE_DEVICE_TABLE(of, tfp410_of_match); - -static struct platform_driver tfp410_driver = { - .probe = tfp410_probe, - .remove = __exit_p(tfp410_remove), - .driver = { - .name = "tfp410", - .of_match_table = tfp410_of_match, - .suppress_bind_attrs = true, - }, -}; - -module_platform_driver(tfp410_driver); - -MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); -MODULE_DESCRIPTION("TFP410 DPI to DVI encoder driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/omapdrm/displays/encoder-tpd12s015.c b/drivers/gpu/drm/omapdrm/displays/encoder-tpd12s015.c deleted file mode 100644 index 58276a48112e..000000000000 --- a/drivers/gpu/drm/omapdrm/displays/encoder-tpd12s015.c +++ /dev/null @@ -1,329 +0,0 @@ -/* - * TPD12S015 HDMI ESD protection & level shifter chip driver - * - * Copyright (C) 2013 Texas Instruments - * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - */ - -#include <linux/completion.h> -#include <linux/delay.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/platform_device.h> -#include <linux/gpio/consumer.h> - -#include "../dss/omapdss.h" - -struct panel_drv_data { - struct omap_dss_device dssdev; - struct omap_dss_device *in; - - struct gpio_desc *ct_cp_hpd_gpio; - struct gpio_desc *ls_oe_gpio; - struct gpio_desc *hpd_gpio; - - struct videomode vm; -}; - -#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev) - -static int tpd_connect(struct omap_dss_device *dssdev, - struct omap_dss_device *dst) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - int r; - - r = in->ops.hdmi->connect(in, dssdev); - if (r) - return r; - - dst->src = dssdev; - dssdev->dst = dst; - - gpiod_set_value_cansleep(ddata->ct_cp_hpd_gpio, 1); - /* DC-DC converter needs at max 300us to get to 90% of 5V */ - udelay(300); - - return 0; -} - -static void tpd_disconnect(struct omap_dss_device *dssdev, - struct omap_dss_device *dst) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - WARN_ON(dst != dssdev->dst); - - if (dst != dssdev->dst) - return; - - gpiod_set_value_cansleep(ddata->ct_cp_hpd_gpio, 0); - - dst->src = NULL; - dssdev->dst = NULL; - - in->ops.hdmi->disconnect(in, &ddata->dssdev); -} - -static int tpd_enable(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - int r; - - if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) - return 0; - - in->ops.hdmi->set_timings(in, &ddata->vm); - - r = in->ops.hdmi->enable(in); - if (r) - return r; - - dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; - - return r; -} - -static void tpd_disable(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) - return; - - in->ops.hdmi->disable(in); - - dssdev->state = OMAP_DSS_DISPLAY_DISABLED; -} - -static void tpd_set_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - ddata->vm = *vm; - dssdev->panel.vm = *vm; - - in->ops.hdmi->set_timings(in, vm); -} - -static void tpd_get_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - - *vm = ddata->vm; -} - -static int tpd_check_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - int r; - - r = in->ops.hdmi->check_timings(in, vm); - - return r; -} - -static int tpd_read_edid(struct omap_dss_device *dssdev, - u8 *edid, int len) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - int r; - - if (!gpiod_get_value_cansleep(ddata->hpd_gpio)) - return -ENODEV; - - gpiod_set_value_cansleep(ddata->ls_oe_gpio, 1); - - r = in->ops.hdmi->read_edid(in, edid, len); - - gpiod_set_value_cansleep(ddata->ls_oe_gpio, 0); - - return r; -} - -static bool tpd_detect(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - - return gpiod_get_value_cansleep(ddata->hpd_gpio); -} - -static int tpd_set_infoframe(struct omap_dss_device *dssdev, - const struct hdmi_avi_infoframe *avi) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - return in->ops.hdmi->set_infoframe(in, avi); -} - -static int tpd_set_hdmi_mode(struct omap_dss_device *dssdev, - bool hdmi_mode) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - return in->ops.hdmi->set_hdmi_mode(in, hdmi_mode); -} - -static const struct omapdss_hdmi_ops tpd_hdmi_ops = { - .connect = tpd_connect, - .disconnect = tpd_disconnect, - - .enable = tpd_enable, - .disable = tpd_disable, - - .check_timings = tpd_check_timings, - .set_timings = tpd_set_timings, - .get_timings = tpd_get_timings, - - .read_edid = tpd_read_edid, - .detect = tpd_detect, - .set_infoframe = tpd_set_infoframe, - .set_hdmi_mode = tpd_set_hdmi_mode, -}; - -static int tpd_probe_of(struct platform_device *pdev) -{ - struct panel_drv_data *ddata = platform_get_drvdata(pdev); - struct device_node *node = pdev->dev.of_node; - struct omap_dss_device *in; - - in = omapdss_of_find_source_for_first_ep(node); - if (IS_ERR(in)) { - dev_err(&pdev->dev, "failed to find video source\n"); - return PTR_ERR(in); - } - - ddata->in = in; - - return 0; -} - -static int tpd_probe(struct platform_device *pdev) -{ - struct omap_dss_device *in, *dssdev; - struct panel_drv_data *ddata; - int r; - struct gpio_desc *gpio; - - ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); - if (!ddata) - return -ENOMEM; - - platform_set_drvdata(pdev, ddata); - - if (!pdev->dev.of_node) - return -ENODEV; - - r = tpd_probe_of(pdev); - if (r) - return r; - - gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 0, - GPIOD_OUT_LOW); - if (IS_ERR(gpio)) { - r = PTR_ERR(gpio); - goto err_gpio; - } - - ddata->ct_cp_hpd_gpio = gpio; - - gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 1, - GPIOD_OUT_LOW); - if (IS_ERR(gpio)) { - r = PTR_ERR(gpio); - goto err_gpio; - } - - ddata->ls_oe_gpio = gpio; - - gpio = devm_gpiod_get_index(&pdev->dev, NULL, 2, - GPIOD_IN); - if (IS_ERR(gpio)) { - r = PTR_ERR(gpio); - goto err_gpio; - } - - ddata->hpd_gpio = gpio; - - dssdev = &ddata->dssdev; - dssdev->ops.hdmi = &tpd_hdmi_ops; - dssdev->dev = &pdev->dev; - dssdev->type = OMAP_DISPLAY_TYPE_HDMI; - dssdev->output_type = OMAP_DISPLAY_TYPE_HDMI; - dssdev->owner = THIS_MODULE; - dssdev->port_num = 1; - - in = ddata->in; - - r = omapdss_register_output(dssdev); - if (r) { - dev_err(&pdev->dev, "Failed to register output\n"); - goto err_reg; - } - - return 0; -err_reg: -err_gpio: - omap_dss_put_device(ddata->in); - return r; -} - -static int __exit tpd_remove(struct platform_device *pdev) -{ - struct panel_drv_data *ddata = platform_get_drvdata(pdev); - struct omap_dss_device *dssdev = &ddata->dssdev; - struct omap_dss_device *in = ddata->in; - - omapdss_unregister_output(&ddata->dssdev); - - WARN_ON(omapdss_device_is_enabled(dssdev)); - if (omapdss_device_is_enabled(dssdev)) - tpd_disable(dssdev); - - WARN_ON(omapdss_device_is_connected(dssdev)); - if (omapdss_device_is_connected(dssdev)) - tpd_disconnect(dssdev, dssdev->dst); - - omap_dss_put_device(in); - - return 0; -} - -static const struct of_device_id tpd_of_match[] = { - { .compatible = "omapdss,ti,tpd12s015", }, - {}, -}; - -MODULE_DEVICE_TABLE(of, tpd_of_match); - -static struct platform_driver tpd_driver = { - .probe = tpd_probe, - .remove = __exit_p(tpd_remove), - .driver = { - .name = "tpd12s015", - .of_match_table = tpd_of_match, - .suppress_bind_attrs = true, - }, -}; - -module_platform_driver(tpd_driver); - -MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); -MODULE_DESCRIPTION("TPD12S015 driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/omapdrm/displays/panel-dpi.c b/drivers/gpu/drm/omapdrm/displays/panel-dpi.c deleted file mode 100644 index 6468a765f3d1..000000000000 --- a/drivers/gpu/drm/omapdrm/displays/panel-dpi.c +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Generic MIPI DPI Panel Driver - * - * Copyright (C) 2013 Texas Instruments - * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - */ - -#include <linux/gpio/consumer.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/slab.h> -#include <linux/of.h> -#include <linux/regulator/consumer.h> -#include <linux/backlight.h> - -#include <video/of_display_timing.h> - -#include "../dss/omapdss.h" - -struct panel_drv_data { - struct omap_dss_device dssdev; - struct omap_dss_device *in; - - struct videomode vm; - - struct backlight_device *backlight; - - struct gpio_desc *enable_gpio; - struct regulator *vcc_supply; -}; - -#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev) - -static int panel_dpi_connect(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - int r; - - if (omapdss_device_is_connected(dssdev)) - return 0; - - r = in->ops.dpi->connect(in, dssdev); - if (r) - return r; - - return 0; -} - -static void panel_dpi_disconnect(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - if (!omapdss_device_is_connected(dssdev)) - return; - - in->ops.dpi->disconnect(in, dssdev); -} - -static int panel_dpi_enable(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - int r; - - if (!omapdss_device_is_connected(dssdev)) - return -ENODEV; - - if (omapdss_device_is_enabled(dssdev)) - return 0; - - in->ops.dpi->set_timings(in, &ddata->vm); - - r = in->ops.dpi->enable(in); - if (r) - return r; - - r = regulator_enable(ddata->vcc_supply); - if (r) { - in->ops.dpi->disable(in); - return r; - } - - gpiod_set_value_cansleep(ddata->enable_gpio, 1); - - if (ddata->backlight) { - ddata->backlight->props.power = FB_BLANK_UNBLANK; - backlight_update_status(ddata->backlight); - } - - dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; - - return 0; -} - -static void panel_dpi_disable(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - if (!omapdss_device_is_enabled(dssdev)) - return; - - if (ddata->backlight) { - ddata->backlight->props.power = FB_BLANK_POWERDOWN; - backlight_update_status(ddata->backlight); - } - - gpiod_set_value_cansleep(ddata->enable_gpio, 0); - regulator_disable(ddata->vcc_supply); - - in->ops.dpi->disable(in); - - dssdev->state = OMAP_DSS_DISPLAY_DISABLED; -} - -static void panel_dpi_set_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - ddata->vm = *vm; - dssdev->panel.vm = *vm; - - in->ops.dpi->set_timings(in, vm); -} - -static void panel_dpi_get_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - - *vm = ddata->vm; -} - -static int panel_dpi_check_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - return in->ops.dpi->check_timings(in, vm); -} - -static struct omap_dss_driver panel_dpi_ops = { - .connect = panel_dpi_connect, - .disconnect = panel_dpi_disconnect, - - .enable = panel_dpi_enable, - .disable = panel_dpi_disable, - - .set_timings = panel_dpi_set_timings, - .get_timings = panel_dpi_get_timings, - .check_timings = panel_dpi_check_timings, -}; - -static int panel_dpi_probe_of(struct platform_device *pdev) -{ - struct panel_drv_data *ddata = platform_get_drvdata(pdev); - struct device_node *node = pdev->dev.of_node; - struct device_node *bl_node; - struct omap_dss_device *in; - int r; - struct display_timing timing; - struct gpio_desc *gpio; - - gpio = devm_gpiod_get_optional(&pdev->dev, "enable", GPIOD_OUT_LOW); - if (IS_ERR(gpio)) - return PTR_ERR(gpio); - - ddata->enable_gpio = gpio; - - /* - * Many different panels are supported by this driver and there are - * probably very different needs for their reset pins in regards to - * timing and order relative to the enable gpio. So for now it's just - * ensured that the reset line isn't active. - */ - gpio = devm_gpiod_get_optional(&pdev->dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(gpio)) - return PTR_ERR(gpio); - - ddata->vcc_supply = devm_regulator_get(&pdev->dev, "vcc"); - if (IS_ERR(ddata->vcc_supply)) - return PTR_ERR(ddata->vcc_supply); - - bl_node = of_parse_phandle(node, "backlight", 0); - if (bl_node) { - ddata->backlight = of_find_backlight_by_node(bl_node); - of_node_put(bl_node); - - if (!ddata->backlight) - return -EPROBE_DEFER; - } - - r = of_get_display_timing(node, "panel-timing", &timing); - if (r) { - dev_err(&pdev->dev, "failed to get video timing\n"); - goto error_free_backlight; - } - - videomode_from_timing(&timing, &ddata->vm); - - in = omapdss_of_find_source_for_first_ep(node); - if (IS_ERR(in)) { - dev_err(&pdev->dev, "failed to find video source\n"); - r = PTR_ERR(in); - goto error_free_backlight; - } - - ddata->in = in; - - return 0; - -error_free_backlight: - if (ddata->backlight) - put_device(&ddata->backlight->dev); - - return r; -} - -static int panel_dpi_probe(struct platform_device *pdev) -{ - struct panel_drv_data *ddata; - struct omap_dss_device *dssdev; - int r; - - ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); - if (ddata == NULL) - return -ENOMEM; - - platform_set_drvdata(pdev, ddata); - - r = panel_dpi_probe_of(pdev); - if (r) - return r; - - dssdev = &ddata->dssdev; - dssdev->dev = &pdev->dev; - dssdev->driver = &panel_dpi_ops; - dssdev->type = OMAP_DISPLAY_TYPE_DPI; - dssdev->owner = THIS_MODULE; - dssdev->panel.vm = ddata->vm; - - r = omapdss_register_display(dssdev); - if (r) { - dev_err(&pdev->dev, "Failed to register panel\n"); - goto err_reg; - } - - return 0; - -err_reg: - omap_dss_put_device(ddata->in); - return r; -} - -static int __exit panel_dpi_remove(struct platform_device *pdev) -{ - struct panel_drv_data *ddata = platform_get_drvdata(pdev); - struct omap_dss_device *dssdev = &ddata->dssdev; - struct omap_dss_device *in = ddata->in; - - omapdss_unregister_display(dssdev); - - panel_dpi_disable(dssdev); - panel_dpi_disconnect(dssdev); - - omap_dss_put_device(in); - - if (ddata->backlight) - put_device(&ddata->backlight->dev); - - return 0; -} - -static const struct of_device_id panel_dpi_of_match[] = { - { .compatible = "omapdss,panel-dpi", }, - {}, -}; - -MODULE_DEVICE_TABLE(of, panel_dpi_of_match); - -static struct platform_driver panel_dpi_driver = { - .probe = panel_dpi_probe, - .remove = __exit_p(panel_dpi_remove), - .driver = { - .name = "panel-dpi", - .of_match_table = panel_dpi_of_match, - .suppress_bind_attrs = true, - }, -}; - -module_platform_driver(panel_dpi_driver); - -MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); -MODULE_DESCRIPTION("Generic MIPI DPI Panel Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c b/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c deleted file mode 100644 index 76787a75a4dc..000000000000 --- a/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c +++ /dev/null @@ -1,1336 +0,0 @@ -/* - * Generic DSI Command Mode panel driver - * - * Copyright (C) 2013 Texas Instruments - * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - */ - -/* #define DEBUG */ - -#include <linux/backlight.h> -#include <linux/delay.h> -#include <linux/gpio/consumer.h> -#include <linux/interrupt.h> -#include <linux/jiffies.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/sched/signal.h> -#include <linux/slab.h> -#include <linux/workqueue.h> -#include <linux/of_device.h> -#include <linux/of_gpio.h> - -#include <video/mipi_display.h> - -#include "../dss/omapdss.h" - -/* DSI Virtual channel. Hardcoded for now. */ -#define TCH 0 - -#define DCS_READ_NUM_ERRORS 0x05 -#define DCS_BRIGHTNESS 0x51 -#define DCS_CTRL_DISPLAY 0x53 -#define DCS_GET_ID1 0xda -#define DCS_GET_ID2 0xdb -#define DCS_GET_ID3 0xdc - -struct panel_drv_data { - struct omap_dss_device dssdev; - struct omap_dss_device *in; - - struct videomode vm; - - struct platform_device *pdev; - - struct mutex lock; - - struct backlight_device *bldev; - - unsigned long hw_guard_end; /* next value of jiffies when we can - * issue the next sleep in/out command - */ - unsigned long hw_guard_wait; /* max guard time in jiffies */ - - /* panel HW configuration from DT or platform data */ - int reset_gpio; - int ext_te_gpio; - - bool use_dsi_backlight; - - struct omap_dsi_pin_config pin_config; - - /* runtime variables */ - bool enabled; - - bool te_enabled; - - atomic_t do_update; - int channel; - - struct delayed_work te_timeout_work; - - bool intro_printed; - - struct workqueue_struct *workqueue; - - bool ulps_enabled; - unsigned ulps_timeout; - struct delayed_work ulps_work; -}; - -#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev) - -static irqreturn_t dsicm_te_isr(int irq, void *data); -static void dsicm_te_timeout_work_callback(struct work_struct *work); -static int _dsicm_enable_te(struct panel_drv_data *ddata, bool enable); - -static int dsicm_panel_reset(struct panel_drv_data *ddata); - -static void dsicm_ulps_work(struct work_struct *work); - -static void hw_guard_start(struct panel_drv_data *ddata, int guard_msec) -{ - ddata->hw_guard_wait = msecs_to_jiffies(guard_msec); - ddata->hw_guard_end = jiffies + ddata->hw_guard_wait; -} - -static void hw_guard_wait(struct panel_drv_data *ddata) -{ - unsigned long wait = ddata->hw_guard_end - jiffies; - - if ((long)wait > 0 && wait <= ddata->hw_guard_wait) { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(wait); - } -} - -static int dsicm_dcs_read_1(struct panel_drv_data *ddata, u8 dcs_cmd, u8 *data) -{ - struct omap_dss_device *in = ddata->in; - int r; - u8 buf[1]; - - r = in->ops.dsi->dcs_read(in, ddata->channel, dcs_cmd, buf, 1); - - if (r < 0) - return r; - - *data = buf[0]; - - return 0; -} - -static int dsicm_dcs_write_0(struct panel_drv_data *ddata, u8 dcs_cmd) -{ - struct omap_dss_device *in = ddata->in; - return in->ops.dsi->dcs_write(in, ddata->channel, &dcs_cmd, 1); -} - -static int dsicm_dcs_write_1(struct panel_drv_data *ddata, u8 dcs_cmd, u8 param) -{ - struct omap_dss_device *in = ddata->in; - u8 buf[2] = { dcs_cmd, param }; - - return in->ops.dsi->dcs_write(in, ddata->channel, buf, 2); -} - -static int dsicm_sleep_in(struct panel_drv_data *ddata) - -{ - struct omap_dss_device *in = ddata->in; - u8 cmd; - int r; - - hw_guard_wait(ddata); - - cmd = MIPI_DCS_ENTER_SLEEP_MODE; - r = in->ops.dsi->dcs_write_nosync(in, ddata->channel, &cmd, 1); - if (r) - return r; - - hw_guard_start(ddata, 120); - - usleep_range(5000, 10000); - - return 0; -} - -static int dsicm_sleep_out(struct panel_drv_data *ddata) -{ - int r; - - hw_guard_wait(ddata); - - r = dsicm_dcs_write_0(ddata, MIPI_DCS_EXIT_SLEEP_MODE); - if (r) - return r; - - hw_guard_start(ddata, 120); - - usleep_range(5000, 10000); - - return 0; -} - -static int dsicm_get_id(struct panel_drv_data *ddata, u8 *id1, u8 *id2, u8 *id3) -{ - int r; - - r = dsicm_dcs_read_1(ddata, DCS_GET_ID1, id1); - if (r) - return r; - r = dsicm_dcs_read_1(ddata, DCS_GET_ID2, id2); - if (r) - return r; - r = dsicm_dcs_read_1(ddata, DCS_GET_ID3, id3); - if (r) - return r; - - return 0; -} - -static int dsicm_set_update_window(struct panel_drv_data *ddata, - u16 x, u16 y, u16 w, u16 h) -{ - struct omap_dss_device *in = ddata->in; - int r; - u16 x1 = x; - u16 x2 = x + w - 1; - u16 y1 = y; - u16 y2 = y + h - 1; - - u8 buf[5]; - buf[0] = MIPI_DCS_SET_COLUMN_ADDRESS; - buf[1] = (x1 >> 8) & 0xff; - buf[2] = (x1 >> 0) & 0xff; - buf[3] = (x2 >> 8) & 0xff; - buf[4] = (x2 >> 0) & 0xff; - - r = in->ops.dsi->dcs_write_nosync(in, ddata->channel, buf, sizeof(buf)); - if (r) - return r; - - buf[0] = MIPI_DCS_SET_PAGE_ADDRESS; - buf[1] = (y1 >> 8) & 0xff; - buf[2] = (y1 >> 0) & 0xff; - buf[3] = (y2 >> 8) & 0xff; - buf[4] = (y2 >> 0) & 0xff; - - r = in->ops.dsi->dcs_write_nosync(in, ddata->channel, buf, sizeof(buf)); - if (r) - return r; - - in->ops.dsi->bta_sync(in, ddata->channel); - - return r; -} - -static void dsicm_queue_ulps_work(struct panel_drv_data *ddata) -{ - if (ddata->ulps_timeout > 0) - queue_delayed_work(ddata->workqueue, &ddata->ulps_work, - msecs_to_jiffies(ddata->ulps_timeout)); -} - -static void dsicm_cancel_ulps_work(struct panel_drv_data *ddata) -{ - cancel_delayed_work(&ddata->ulps_work); -} - -static int dsicm_enter_ulps(struct panel_drv_data *ddata) -{ - struct omap_dss_device *in = ddata->in; - int r; - - if (ddata->ulps_enabled) - return 0; - - dsicm_cancel_ulps_work(ddata); - - r = _dsicm_enable_te(ddata, false); - if (r) - goto err; - - if (gpio_is_valid(ddata->ext_te_gpio)) - disable_irq(gpio_to_irq(ddata->ext_te_gpio)); - - in->ops.dsi->disable(in, false, true); - - ddata->ulps_enabled = true; - - return 0; - -err: - dev_err(&ddata->pdev->dev, "enter ULPS failed"); - dsicm_panel_reset(ddata); - - ddata->ulps_enabled = false; - - dsicm_queue_ulps_work(ddata); - - return r; -} - -static int dsicm_exit_ulps(struct panel_drv_data *ddata) -{ - struct omap_dss_device *in = ddata->in; - int r; - - if (!ddata->ulps_enabled) - return 0; - - r = in->ops.dsi->enable(in); - if (r) { - dev_err(&ddata->pdev->dev, "failed to enable DSI\n"); - goto err1; - } - - in->ops.dsi->enable_hs(in, ddata->channel, true); - - r = _dsicm_enable_te(ddata, true); - if (r) { - dev_err(&ddata->pdev->dev, "failed to re-enable TE"); - goto err2; - } - - if (gpio_is_valid(ddata->ext_te_gpio)) - enable_irq(gpio_to_irq(ddata->ext_te_gpio)); - - dsicm_queue_ulps_work(ddata); - - ddata->ulps_enabled = false; - - return 0; - -err2: - dev_err(&ddata->pdev->dev, "failed to exit ULPS"); - - r = dsicm_panel_reset(ddata); - if (!r) { - if (gpio_is_valid(ddata->ext_te_gpio)) - enable_irq(gpio_to_irq(ddata->ext_te_gpio)); - ddata->ulps_enabled = false; - } -err1: - dsicm_queue_ulps_work(ddata); - - return r; -} - -static int dsicm_wake_up(struct panel_drv_data *ddata) -{ - if (ddata->ulps_enabled) - return dsicm_exit_ulps(ddata); - - dsicm_cancel_ulps_work(ddata); - dsicm_queue_ulps_work(ddata); - return 0; -} - -static int dsicm_bl_update_status(struct backlight_device *dev) -{ - struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev); - struct omap_dss_device *in = ddata->in; - int r; - int level; - - if (dev->props.fb_blank == FB_BLANK_UNBLANK && - dev->props.power == FB_BLANK_UNBLANK) - level = dev->props.brightness; - else - level = 0; - - dev_dbg(&ddata->pdev->dev, "update brightness to %d\n", level); - - mutex_lock(&ddata->lock); - - if (ddata->enabled) { - in->ops.dsi->bus_lock(in); - - r = dsicm_wake_up(ddata); - if (!r) - r = dsicm_dcs_write_1(ddata, DCS_BRIGHTNESS, level); - - in->ops.dsi->bus_unlock(in); - } else { - r = 0; - } - - mutex_unlock(&ddata->lock); - - return r; -} - -static int dsicm_bl_get_intensity(struct backlight_device *dev) -{ - if (dev->props.fb_blank == FB_BLANK_UNBLANK && - dev->props.power == FB_BLANK_UNBLANK) - return dev->props.brightness; - - return 0; -} - -static const struct backlight_ops dsicm_bl_ops = { - .get_brightness = dsicm_bl_get_intensity, - .update_status = dsicm_bl_update_status, -}; - -static ssize_t dsicm_num_errors_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct platform_device *pdev = to_platform_device(dev); - struct panel_drv_data *ddata = platform_get_drvdata(pdev); - struct omap_dss_device *in = ddata->in; - u8 errors = 0; - int r; - - mutex_lock(&ddata->lock); - - if (ddata->enabled) { - in->ops.dsi->bus_lock(in); - - r = dsicm_wake_up(ddata); - if (!r) - r = dsicm_dcs_read_1(ddata, DCS_READ_NUM_ERRORS, - &errors); - - in->ops.dsi->bus_unlock(in); - } else { - r = -ENODEV; - } - - mutex_unlock(&ddata->lock); - - if (r) - return r; - - return snprintf(buf, PAGE_SIZE, "%d\n", errors); -} - -static ssize_t dsicm_hw_revision_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct platform_device *pdev = to_platform_device(dev); - struct panel_drv_data *ddata = platform_get_drvdata(pdev); - struct omap_dss_device *in = ddata->in; - u8 id1, id2, id3; - int r; - - mutex_lock(&ddata->lock); - - if (ddata->enabled) { - in->ops.dsi->bus_lock(in); - - r = dsicm_wake_up(ddata); - if (!r) - r = dsicm_get_id(ddata, &id1, &id2, &id3); - - in->ops.dsi->bus_unlock(in); - } else { - r = -ENODEV; - } - - mutex_unlock(&ddata->lock); - - if (r) - return r; - - return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x\n", id1, id2, id3); -} - -static ssize_t dsicm_store_ulps(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct platform_device *pdev = to_platform_device(dev); - struct panel_drv_data *ddata = platform_get_drvdata(pdev); - struct omap_dss_device *in = ddata->in; - unsigned long t; - int r; - - r = kstrtoul(buf, 0, &t); - if (r) - return r; - - mutex_lock(&ddata->lock); - - if (ddata->enabled) { - in->ops.dsi->bus_lock(in); - - if (t) - r = dsicm_enter_ulps(ddata); - else - r = dsicm_wake_up(ddata); - - in->ops.dsi->bus_unlock(in); - } - - mutex_unlock(&ddata->lock); - - if (r) - return r; - - return count; -} - -static ssize_t dsicm_show_ulps(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct platform_device *pdev = to_platform_device(dev); - struct panel_drv_data *ddata = platform_get_drvdata(pdev); - unsigned t; - - mutex_lock(&ddata->lock); - t = ddata->ulps_enabled; - mutex_unlock(&ddata->lock); - - return snprintf(buf, PAGE_SIZE, "%u\n", t); -} - -static ssize_t dsicm_store_ulps_timeout(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct platform_device *pdev = to_platform_device(dev); - struct panel_drv_data *ddata = platform_get_drvdata(pdev); - struct omap_dss_device *in = ddata->in; - unsigned long t; - int r; - - r = kstrtoul(buf, 0, &t); - if (r) - return r; - - mutex_lock(&ddata->lock); - ddata->ulps_timeout = t; - - if (ddata->enabled) { - /* dsicm_wake_up will restart the timer */ - in->ops.dsi->bus_lock(in); - r = dsicm_wake_up(ddata); - in->ops.dsi->bus_unlock(in); - } - - mutex_unlock(&ddata->lock); - - if (r) - return r; - - return count; -} - -static ssize_t dsicm_show_ulps_timeout(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct platform_device *pdev = to_platform_device(dev); - struct panel_drv_data *ddata = platform_get_drvdata(pdev); - unsigned t; - - mutex_lock(&ddata->lock); - t = ddata->ulps_timeout; - mutex_unlock(&ddata->lock); - - return snprintf(buf, PAGE_SIZE, "%u\n", t); -} - -static DEVICE_ATTR(num_dsi_errors, S_IRUGO, dsicm_num_errors_show, NULL); -static DEVICE_ATTR(hw_revision, S_IRUGO, dsicm_hw_revision_show, NULL); -static DEVICE_ATTR(ulps, S_IRUGO | S_IWUSR, - dsicm_show_ulps, dsicm_store_ulps); -static DEVICE_ATTR(ulps_timeout, S_IRUGO | S_IWUSR, - dsicm_show_ulps_timeout, dsicm_store_ulps_timeout); - -static struct attribute *dsicm_attrs[] = { - &dev_attr_num_dsi_errors.attr, - &dev_attr_hw_revision.attr, - &dev_attr_ulps.attr, - &dev_attr_ulps_timeout.attr, - NULL, -}; - -static struct attribute_group dsicm_attr_group = { - .attrs = dsicm_attrs, -}; - -static void dsicm_hw_reset(struct panel_drv_data *ddata) -{ - if (!gpio_is_valid(ddata->reset_gpio)) - return; - - gpio_set_value(ddata->reset_gpio, 1); - udelay(10); - /* reset the panel */ - gpio_set_value(ddata->reset_gpio, 0); - /* assert reset */ - udelay(10); - gpio_set_value(ddata->reset_gpio, 1); - /* wait after releasing reset */ - usleep_range(5000, 10000); -} - -static int dsicm_power_on(struct panel_drv_data *ddata) -{ - struct omap_dss_device *in = ddata->in; - u8 id1, id2, id3; - int r; - struct omap_dss_dsi_config dsi_config = { - .mode = OMAP_DSS_DSI_CMD_MODE, - .pixel_format = OMAP_DSS_DSI_FMT_RGB888, - .vm = &ddata->vm, - .hs_clk_min = 150000000, - .hs_clk_max = 300000000, - .lp_clk_min = 7000000, - .lp_clk_max = 10000000, - }; - - if (ddata->pin_config.num_pins > 0) { - r = in->ops.dsi->configure_pins(in, &ddata->pin_config); - if (r) { - dev_err(&ddata->pdev->dev, - "failed to configure DSI pins\n"); - goto err0; - } - } - - r = in->ops.dsi->set_config(in, &dsi_config); - if (r) { - dev_err(&ddata->pdev->dev, "failed to configure DSI\n"); - goto err0; - } - - r = in->ops.dsi->enable(in); - if (r) { - dev_err(&ddata->pdev->dev, "failed to enable DSI\n"); - goto err0; - } - - dsicm_hw_reset(ddata); - - in->ops.dsi->enable_hs(in, ddata->channel, false); - - r = dsicm_sleep_out(ddata); - if (r) - goto err; - - r = dsicm_get_id(ddata, &id1, &id2, &id3); - if (r) - goto err; - - r = dsicm_dcs_write_1(ddata, DCS_BRIGHTNESS, 0xff); - if (r) - goto err; - - r = dsicm_dcs_write_1(ddata, DCS_CTRL_DISPLAY, - (1<<2) | (1<<5)); /* BL | BCTRL */ - if (r) - goto err; - - r = dsicm_dcs_write_1(ddata, MIPI_DCS_SET_PIXEL_FORMAT, - MIPI_DCS_PIXEL_FMT_24BIT); - if (r) - goto err; - - r = dsicm_dcs_write_0(ddata, MIPI_DCS_SET_DISPLAY_ON); - if (r) - goto err; - - r = _dsicm_enable_te(ddata, ddata->te_enabled); - if (r) - goto err; - - r = in->ops.dsi->enable_video_output(in, ddata->channel); - if (r) - goto err; - - ddata->enabled = 1; - - if (!ddata->intro_printed) { - dev_info(&ddata->pdev->dev, "panel revision %02x.%02x.%02x\n", - id1, id2, id3); - ddata->intro_printed = true; - } - - in->ops.dsi->enable_hs(in, ddata->channel, true); - - return 0; -err: - dev_err(&ddata->pdev->dev, "error while enabling panel, issuing HW reset\n"); - - dsicm_hw_reset(ddata); - - in->ops.dsi->disable(in, true, false); -err0: - return r; -} - -static void dsicm_power_off(struct panel_drv_data *ddata) -{ - struct omap_dss_device *in = ddata->in; - int r; - - in->ops.dsi->disable_video_output(in, ddata->channel); - - r = dsicm_dcs_write_0(ddata, MIPI_DCS_SET_DISPLAY_OFF); - if (!r) - r = dsicm_sleep_in(ddata); - - if (r) { - dev_err(&ddata->pdev->dev, - "error disabling panel, issuing HW reset\n"); - dsicm_hw_reset(ddata); - } - - in->ops.dsi->disable(in, true, false); - - ddata->enabled = 0; -} - -static int dsicm_panel_reset(struct panel_drv_data *ddata) -{ - dev_err(&ddata->pdev->dev, "performing LCD reset\n"); - - dsicm_power_off(ddata); - dsicm_hw_reset(ddata); - return dsicm_power_on(ddata); -} - -static int dsicm_connect(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - struct device *dev = &ddata->pdev->dev; - int r; - - if (omapdss_device_is_connected(dssdev)) - return 0; - - r = in->ops.dsi->connect(in, dssdev); - if (r) { - dev_err(dev, "Failed to connect to video source\n"); - return r; - } - - r = in->ops.dsi->request_vc(ddata->in, &ddata->channel); - if (r) { - dev_err(dev, "failed to get virtual channel\n"); - goto err_req_vc; - } - - r = in->ops.dsi->set_vc_id(ddata->in, ddata->channel, TCH); - if (r) { - dev_err(dev, "failed to set VC_ID\n"); - goto err_vc_id; - } - - return 0; - -err_vc_id: - in->ops.dsi->release_vc(ddata->in, ddata->channel); -err_req_vc: - in->ops.dsi->disconnect(in, dssdev); - return r; -} - -static void dsicm_disconnect(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - if (!omapdss_device_is_connected(dssdev)) - return; - - in->ops.dsi->release_vc(in, ddata->channel); - in->ops.dsi->disconnect(in, dssdev); -} - -static int dsicm_enable(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - int r; - - dev_dbg(&ddata->pdev->dev, "enable\n"); - - mutex_lock(&ddata->lock); - - if (!omapdss_device_is_connected(dssdev)) { - r = -ENODEV; - goto err; - } - - if (omapdss_device_is_enabled(dssdev)) { - r = 0; - goto err; - } - - in->ops.dsi->bus_lock(in); - - r = dsicm_power_on(ddata); - - in->ops.dsi->bus_unlock(in); - - if (r) - goto err; - - dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; - - mutex_unlock(&ddata->lock); - - return 0; -err: - dev_dbg(&ddata->pdev->dev, "enable failed\n"); - mutex_unlock(&ddata->lock); - return r; -} - -static void dsicm_disable(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - int r; - - dev_dbg(&ddata->pdev->dev, "disable\n"); - - mutex_lock(&ddata->lock); - - dsicm_cancel_ulps_work(ddata); - - in->ops.dsi->bus_lock(in); - - if (omapdss_device_is_enabled(dssdev)) { - r = dsicm_wake_up(ddata); - if (!r) - dsicm_power_off(ddata); - } - - in->ops.dsi->bus_unlock(in); - - dssdev->state = OMAP_DSS_DISPLAY_DISABLED; - - mutex_unlock(&ddata->lock); -} - -static void dsicm_framedone_cb(int err, void *data) -{ - struct panel_drv_data *ddata = data; - struct omap_dss_device *in = ddata->in; - - dev_dbg(&ddata->pdev->dev, "framedone, err %d\n", err); - in->ops.dsi->bus_unlock(ddata->in); -} - -static irqreturn_t dsicm_te_isr(int irq, void *data) -{ - struct panel_drv_data *ddata = data; - struct omap_dss_device *in = ddata->in; - int old; - int r; - - old = atomic_cmpxchg(&ddata->do_update, 1, 0); - - if (old) { - cancel_delayed_work(&ddata->te_timeout_work); - - r = in->ops.dsi->update(in, ddata->channel, dsicm_framedone_cb, - ddata); - if (r) - goto err; - } - - return IRQ_HANDLED; -err: - dev_err(&ddata->pdev->dev, "start update failed\n"); - in->ops.dsi->bus_unlock(in); - return IRQ_HANDLED; -} - -static void dsicm_te_timeout_work_callback(struct work_struct *work) -{ - struct panel_drv_data *ddata = container_of(work, struct panel_drv_data, - te_timeout_work.work); - struct omap_dss_device *in = ddata->in; - - dev_err(&ddata->pdev->dev, "TE not received for 250ms!\n"); - - atomic_set(&ddata->do_update, 0); - in->ops.dsi->bus_unlock(in); -} - -static int dsicm_update(struct omap_dss_device *dssdev, - u16 x, u16 y, u16 w, u16 h) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - int r; - - dev_dbg(&ddata->pdev->dev, "update %d, %d, %d x %d\n", x, y, w, h); - - mutex_lock(&ddata->lock); - in->ops.dsi->bus_lock(in); - - r = dsicm_wake_up(ddata); - if (r) - goto err; - - if (!ddata->enabled) { - r = 0; - goto err; - } - - /* XXX no need to send this every frame, but dsi break if not done */ - r = dsicm_set_update_window(ddata, 0, 0, - dssdev->panel.vm.hactive, - dssdev->panel.vm.vactive); - if (r) - goto err; - - if (ddata->te_enabled && gpio_is_valid(ddata->ext_te_gpio)) { - schedule_delayed_work(&ddata->te_timeout_work, - msecs_to_jiffies(250)); - atomic_set(&ddata->do_update, 1); - } else { - r = in->ops.dsi->update(in, ddata->channel, dsicm_framedone_cb, - ddata); - if (r) - goto err; - } - - /* note: no bus_unlock here. unlock is in framedone_cb */ - mutex_unlock(&ddata->lock); - return 0; -err: - in->ops.dsi->bus_unlock(in); - mutex_unlock(&ddata->lock); - return r; -} - -static int dsicm_sync(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - dev_dbg(&ddata->pdev->dev, "sync\n"); - - mutex_lock(&ddata->lock); - in->ops.dsi->bus_lock(in); - in->ops.dsi->bus_unlock(in); - mutex_unlock(&ddata->lock); - - dev_dbg(&ddata->pdev->dev, "sync done\n"); - - return 0; -} - -static int _dsicm_enable_te(struct panel_drv_data *ddata, bool enable) -{ - struct omap_dss_device *in = ddata->in; - int r; - - if (enable) - r = dsicm_dcs_write_1(ddata, MIPI_DCS_SET_TEAR_ON, 0); - else - r = dsicm_dcs_write_0(ddata, MIPI_DCS_SET_TEAR_OFF); - - if (!gpio_is_valid(ddata->ext_te_gpio)) - in->ops.dsi->enable_te(in, enable); - - /* possible panel bug */ - msleep(100); - - return r; -} - -static int dsicm_enable_te(struct omap_dss_device *dssdev, bool enable) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - int r; - - mutex_lock(&ddata->lock); - - if (ddata->te_enabled == enable) - goto end; - - in->ops.dsi->bus_lock(in); - - if (ddata->enabled) { - r = dsicm_wake_up(ddata); - if (r) - goto err; - - r = _dsicm_enable_te(ddata, enable); - if (r) - goto err; - } - - ddata->te_enabled = enable; - - in->ops.dsi->bus_unlock(in); -end: - mutex_unlock(&ddata->lock); - - return 0; -err: - in->ops.dsi->bus_unlock(in); - mutex_unlock(&ddata->lock); - - return r; -} - -static int dsicm_get_te(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - int r; - - mutex_lock(&ddata->lock); - r = ddata->te_enabled; - mutex_unlock(&ddata->lock); - - return r; -} - -static int dsicm_memory_read(struct omap_dss_device *dssdev, - void *buf, size_t size, - u16 x, u16 y, u16 w, u16 h) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - int r; - int first = 1; - int plen; - unsigned buf_used = 0; - - if (size < w * h * 3) - return -ENOMEM; - - mutex_lock(&ddata->lock); - - if (!ddata->enabled) { - r = -ENODEV; - goto err1; - } - - size = min((u32)w * h * 3, - dssdev->panel.vm.hactive * dssdev->panel.vm.vactive * 3); - - in->ops.dsi->bus_lock(in); - - r = dsicm_wake_up(ddata); - if (r) - goto err2; - - /* plen 1 or 2 goes into short packet. until checksum error is fixed, - * use short packets. plen 32 works, but bigger packets seem to cause - * an error. */ - if (size % 2) - plen = 1; - else - plen = 2; - - dsicm_set_update_window(ddata, x, y, w, h); - - r = in->ops.dsi->set_max_rx_packet_size(in, ddata->channel, plen); - if (r) - goto err2; - - while (buf_used < size) { - u8 dcs_cmd = first ? 0x2e : 0x3e; - first = 0; - - r = in->ops.dsi->dcs_read(in, ddata->channel, dcs_cmd, - buf + buf_used, size - buf_used); - - if (r < 0) { - dev_err(dssdev->dev, "read error\n"); - goto err3; - } - - buf_used += r; - - if (r < plen) { - dev_err(&ddata->pdev->dev, "short read\n"); - break; - } - - if (signal_pending(current)) { - dev_err(&ddata->pdev->dev, "signal pending, " - "aborting memory read\n"); - r = -ERESTARTSYS; - goto err3; - } - } - - r = buf_used; - -err3: - in->ops.dsi->set_max_rx_packet_size(in, ddata->channel, 1); -err2: - in->ops.dsi->bus_unlock(in); -err1: - mutex_unlock(&ddata->lock); - return r; -} - -static void dsicm_ulps_work(struct work_struct *work) -{ - struct panel_drv_data *ddata = container_of(work, struct panel_drv_data, - ulps_work.work); - struct omap_dss_device *dssdev = &ddata->dssdev; - struct omap_dss_device *in = ddata->in; - - mutex_lock(&ddata->lock); - - if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE || !ddata->enabled) { - mutex_unlock(&ddata->lock); - return; - } - - in->ops.dsi->bus_lock(in); - - dsicm_enter_ulps(ddata); - - in->ops.dsi->bus_unlock(in); - mutex_unlock(&ddata->lock); -} - -static struct omap_dss_driver dsicm_ops = { - .connect = dsicm_connect, - .disconnect = dsicm_disconnect, - - .enable = dsicm_enable, - .disable = dsicm_disable, - - .update = dsicm_update, - .sync = dsicm_sync, - - .enable_te = dsicm_enable_te, - .get_te = dsicm_get_te, - - .memory_read = dsicm_memory_read, -}; - -static int dsicm_probe_of(struct platform_device *pdev) -{ - struct device_node *node = pdev->dev.of_node; - struct panel_drv_data *ddata = platform_get_drvdata(pdev); - struct omap_dss_device *in; - int gpio; - - gpio = of_get_named_gpio(node, "reset-gpios", 0); - if (!gpio_is_valid(gpio)) { - dev_err(&pdev->dev, "failed to parse reset gpio\n"); - return gpio; - } - ddata->reset_gpio = gpio; - - gpio = of_get_named_gpio(node, "te-gpios", 0); - if (gpio_is_valid(gpio) || gpio == -ENOENT) { - ddata->ext_te_gpio = gpio; - } else { - dev_err(&pdev->dev, "failed to parse TE gpio\n"); - return gpio; - } - - in = omapdss_of_find_source_for_first_ep(node); - if (IS_ERR(in)) { - dev_err(&pdev->dev, "failed to find video source\n"); - return PTR_ERR(in); - } - - ddata->in = in; - - /* TODO: ulps, backlight */ - - return 0; -} - -static int dsicm_probe(struct platform_device *pdev) -{ - struct backlight_properties props; - struct panel_drv_data *ddata; - struct backlight_device *bldev = NULL; - struct device *dev = &pdev->dev; - struct omap_dss_device *dssdev; - int r; - - dev_dbg(dev, "probe\n"); - - ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); - if (!ddata) - return -ENOMEM; - - platform_set_drvdata(pdev, ddata); - ddata->pdev = pdev; - - if (!pdev->dev.of_node) - return -ENODEV; - - r = dsicm_probe_of(pdev); - if (r) - return r; - - ddata->vm.hactive = 864; - ddata->vm.vactive = 480; - ddata->vm.pixelclock = 864 * 480 * 60; - - dssdev = &ddata->dssdev; - dssdev->dev = dev; - dssdev->driver = &dsicm_ops; - dssdev->panel.vm = ddata->vm; - dssdev->type = OMAP_DISPLAY_TYPE_DSI; - dssdev->owner = THIS_MODULE; - - dssdev->panel.dsi_pix_fmt = OMAP_DSS_DSI_FMT_RGB888; - dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE | - OMAP_DSS_DISPLAY_CAP_TEAR_ELIM; - - r = omapdss_register_display(dssdev); - if (r) { - dev_err(dev, "Failed to register panel\n"); - goto err_reg; - } - - mutex_init(&ddata->lock); - - atomic_set(&ddata->do_update, 0); - - if (gpio_is_valid(ddata->reset_gpio)) { - r = devm_gpio_request_one(dev, ddata->reset_gpio, - GPIOF_OUT_INIT_LOW, "taal rst"); - if (r) { - dev_err(dev, "failed to request reset gpio\n"); - return r; - } - } - - if (gpio_is_valid(ddata->ext_te_gpio)) { - r = devm_gpio_request_one(dev, ddata->ext_te_gpio, - GPIOF_IN, "taal irq"); - if (r) { - dev_err(dev, "GPIO request failed\n"); - return r; - } - - r = devm_request_irq(dev, gpio_to_irq(ddata->ext_te_gpio), - dsicm_te_isr, - IRQF_TRIGGER_RISING, - "taal vsync", ddata); - - if (r) { - dev_err(dev, "IRQ request failed\n"); - return r; - } - - INIT_DEFERRABLE_WORK(&ddata->te_timeout_work, - dsicm_te_timeout_work_callback); - - dev_dbg(dev, "Using GPIO TE\n"); - } - - ddata->workqueue = create_singlethread_workqueue("dsicm_wq"); - if (ddata->workqueue == NULL) { - dev_err(dev, "can't create workqueue\n"); - return -ENOMEM; - } - INIT_DELAYED_WORK(&ddata->ulps_work, dsicm_ulps_work); - - dsicm_hw_reset(ddata); - - if (ddata->use_dsi_backlight) { - memset(&props, 0, sizeof(props)); - props.max_brightness = 255; - - props.type = BACKLIGHT_RAW; - bldev = backlight_device_register(dev_name(dev), - dev, ddata, &dsicm_bl_ops, &props); - if (IS_ERR(bldev)) { - r = PTR_ERR(bldev); - goto err_bl; - } - - ddata->bldev = bldev; - - bldev->props.fb_blank = FB_BLANK_UNBLANK; - bldev->props.power = FB_BLANK_UNBLANK; - bldev->props.brightness = 255; - - dsicm_bl_update_status(bldev); - } - - r = sysfs_create_group(&dev->kobj, &dsicm_attr_group); - if (r) { - dev_err(dev, "failed to create sysfs files\n"); - goto err_sysfs_create; - } - - return 0; - -err_sysfs_create: - backlight_device_unregister(bldev); -err_bl: - destroy_workqueue(ddata->workqueue); -err_reg: - return r; -} - -static int __exit dsicm_remove(struct platform_device *pdev) -{ - struct panel_drv_data *ddata = platform_get_drvdata(pdev); - struct omap_dss_device *dssdev = &ddata->dssdev; - struct backlight_device *bldev; - - dev_dbg(&pdev->dev, "remove\n"); - - omapdss_unregister_display(dssdev); - - dsicm_disable(dssdev); - dsicm_disconnect(dssdev); - - sysfs_remove_group(&pdev->dev.kobj, &dsicm_attr_group); - - bldev = ddata->bldev; - if (bldev != NULL) { - bldev->props.power = FB_BLANK_POWERDOWN; - dsicm_bl_update_status(bldev); - backlight_device_unregister(bldev); - } - - omap_dss_put_device(ddata->in); - - dsicm_cancel_ulps_work(ddata); - destroy_workqueue(ddata->workqueue); - - /* reset, to be sure that the panel is in a valid state */ - dsicm_hw_reset(ddata); - - return 0; -} - -static const struct of_device_id dsicm_of_match[] = { - { .compatible = "omapdss,panel-dsi-cm", }, - {}, -}; - -MODULE_DEVICE_TABLE(of, dsicm_of_match); - -static struct platform_driver dsicm_driver = { - .probe = dsicm_probe, - .remove = __exit_p(dsicm_remove), - .driver = { - .name = "panel-dsi-cm", - .of_match_table = dsicm_of_match, - .suppress_bind_attrs = true, - }, -}; - -module_platform_driver(dsicm_driver); - -MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); -MODULE_DESCRIPTION("Generic DSI Command Mode Panel Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/omapdrm/displays/panel-lgphilips-lb035q02.c b/drivers/gpu/drm/omapdrm/displays/panel-lgphilips-lb035q02.c deleted file mode 100644 index c90474afaebd..000000000000 --- a/drivers/gpu/drm/omapdrm/displays/panel-lgphilips-lb035q02.c +++ /dev/null @@ -1,338 +0,0 @@ -/* - * LG.Philips LB035Q02 LCD Panel driver - * - * Copyright (C) 2013 Texas Instruments - * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> - * Based on a driver by: Steve Sakoman <steve@sakoman.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - */ - -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/spi/spi.h> -#include <linux/mutex.h> -#include <linux/gpio.h> -#include <linux/gpio/consumer.h> - -#include "../dss/omapdss.h" - -static struct videomode lb035q02_vm = { - .hactive = 320, - .vactive = 240, - - .pixelclock = 6500000, - - .hsync_len = 2, - .hfront_porch = 20, - .hback_porch = 68, - - .vsync_len = 2, - .vfront_porch = 4, - .vback_porch = 18, - - .flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW | - DISPLAY_FLAGS_DE_HIGH | DISPLAY_FLAGS_SYNC_NEGEDGE | - DISPLAY_FLAGS_PIXDATA_POSEDGE, - /* - * Note: According to the panel documentation: - * DE is active LOW - * DATA needs to be driven on the FALLING edge - */ -}; - -struct panel_drv_data { - struct omap_dss_device dssdev; - struct omap_dss_device *in; - - struct spi_device *spi; - - struct videomode vm; - - struct gpio_desc *enable_gpio; -}; - -#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev) - -static int lb035q02_write_reg(struct spi_device *spi, u8 reg, u16 val) -{ - struct spi_message msg; - struct spi_transfer index_xfer = { - .len = 3, - .cs_change = 1, - }; - struct spi_transfer value_xfer = { - .len = 3, - }; - u8 buffer[16]; - - spi_message_init(&msg); - - /* register index */ - buffer[0] = 0x70; - buffer[1] = 0x00; - buffer[2] = reg & 0x7f; - index_xfer.tx_buf = buffer; - spi_message_add_tail(&index_xfer, &msg); - - /* register value */ - buffer[4] = 0x72; - buffer[5] = val >> 8; - buffer[6] = val; - value_xfer.tx_buf = buffer + 4; - spi_message_add_tail(&value_xfer, &msg); - - return spi_sync(spi, &msg); -} - -static void init_lb035q02_panel(struct spi_device *spi) -{ - /* Init sequence from page 28 of the lb035q02 spec */ - lb035q02_write_reg(spi, 0x01, 0x6300); - lb035q02_write_reg(spi, 0x02, 0x0200); - lb035q02_write_reg(spi, 0x03, 0x0177); - lb035q02_write_reg(spi, 0x04, 0x04c7); - lb035q02_write_reg(spi, 0x05, 0xffc0); - lb035q02_write_reg(spi, 0x06, 0xe806); - lb035q02_write_reg(spi, 0x0a, 0x4008); - lb035q02_write_reg(spi, 0x0b, 0x0000); - lb035q02_write_reg(spi, 0x0d, 0x0030); - lb035q02_write_reg(spi, 0x0e, 0x2800); - lb035q02_write_reg(spi, 0x0f, 0x0000); - lb035q02_write_reg(spi, 0x16, 0x9f80); - lb035q02_write_reg(spi, 0x17, 0x0a0f); - lb035q02_write_reg(spi, 0x1e, 0x00c1); - lb035q02_write_reg(spi, 0x30, 0x0300); - lb035q02_write_reg(spi, 0x31, 0x0007); - lb035q02_write_reg(spi, 0x32, 0x0000); - lb035q02_write_reg(spi, 0x33, 0x0000); - lb035q02_write_reg(spi, 0x34, 0x0707); - lb035q02_write_reg(spi, 0x35, 0x0004); - lb035q02_write_reg(spi, 0x36, 0x0302); - lb035q02_write_reg(spi, 0x37, 0x0202); - lb035q02_write_reg(spi, 0x3a, 0x0a0d); - lb035q02_write_reg(spi, 0x3b, 0x0806); -} - -static int lb035q02_connect(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - int r; - - if (omapdss_device_is_connected(dssdev)) - return 0; - - r = in->ops.dpi->connect(in, dssdev); - if (r) - return r; - - init_lb035q02_panel(ddata->spi); - - return 0; -} - -static void lb035q02_disconnect(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - if (!omapdss_device_is_connected(dssdev)) - return; - - in->ops.dpi->disconnect(in, dssdev); -} - -static int lb035q02_enable(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - int r; - - if (!omapdss_device_is_connected(dssdev)) - return -ENODEV; - - if (omapdss_device_is_enabled(dssdev)) - return 0; - - in->ops.dpi->set_timings(in, &ddata->vm); - - r = in->ops.dpi->enable(in); - if (r) - return r; - - if (ddata->enable_gpio) - gpiod_set_value_cansleep(ddata->enable_gpio, 1); - - dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; - - return 0; -} - -static void lb035q02_disable(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - if (!omapdss_device_is_enabled(dssdev)) - return; - - if (ddata->enable_gpio) - gpiod_set_value_cansleep(ddata->enable_gpio, 0); - - in->ops.dpi->disable(in); - - dssdev->state = OMAP_DSS_DISPLAY_DISABLED; -} - -static void lb035q02_set_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - ddata->vm = *vm; - dssdev->panel.vm = *vm; - - in->ops.dpi->set_timings(in, vm); -} - -static void lb035q02_get_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - - *vm = ddata->vm; -} - -static int lb035q02_check_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - return in->ops.dpi->check_timings(in, vm); -} - -static struct omap_dss_driver lb035q02_ops = { - .connect = lb035q02_connect, - .disconnect = lb035q02_disconnect, - - .enable = lb035q02_enable, - .disable = lb035q02_disable, - - .set_timings = lb035q02_set_timings, - .get_timings = lb035q02_get_timings, - .check_timings = lb035q02_check_timings, -}; - -static int lb035q02_probe_of(struct spi_device *spi) -{ - struct device_node *node = spi->dev.of_node; - struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); - struct omap_dss_device *in; - struct gpio_desc *gpio; - - gpio = devm_gpiod_get(&spi->dev, "enable", GPIOD_OUT_LOW); - if (IS_ERR(gpio)) { - dev_err(&spi->dev, "failed to parse enable gpio\n"); - return PTR_ERR(gpio); - } - - ddata->enable_gpio = gpio; - - in = omapdss_of_find_source_for_first_ep(node); - if (IS_ERR(in)) { - dev_err(&spi->dev, "failed to find video source\n"); - return PTR_ERR(in); - } - - ddata->in = in; - - return 0; -} - -static int lb035q02_panel_spi_probe(struct spi_device *spi) -{ - struct panel_drv_data *ddata; - struct omap_dss_device *dssdev; - int r; - - ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL); - if (ddata == NULL) - return -ENOMEM; - - dev_set_drvdata(&spi->dev, ddata); - - ddata->spi = spi; - - if (!spi->dev.of_node) - return -ENODEV; - - r = lb035q02_probe_of(spi); - if (r) - return r; - - ddata->vm = lb035q02_vm; - - dssdev = &ddata->dssdev; - dssdev->dev = &spi->dev; - dssdev->driver = &lb035q02_ops; - dssdev->type = OMAP_DISPLAY_TYPE_DPI; - dssdev->owner = THIS_MODULE; - dssdev->panel.vm = ddata->vm; - - r = omapdss_register_display(dssdev); - if (r) { - dev_err(&spi->dev, "Failed to register panel\n"); - goto err_reg; - } - - return 0; - -err_reg: - omap_dss_put_device(ddata->in); - return r; -} - -static int lb035q02_panel_spi_remove(struct spi_device *spi) -{ - struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); - struct omap_dss_device *dssdev = &ddata->dssdev; - struct omap_dss_device *in = ddata->in; - - omapdss_unregister_display(dssdev); - - lb035q02_disable(dssdev); - lb035q02_disconnect(dssdev); - - omap_dss_put_device(in); - - return 0; -} - -static const struct of_device_id lb035q02_of_match[] = { - { .compatible = "omapdss,lgphilips,lb035q02", }, - {}, -}; - -MODULE_DEVICE_TABLE(of, lb035q02_of_match); - -static struct spi_driver lb035q02_spi_driver = { - .probe = lb035q02_panel_spi_probe, - .remove = lb035q02_panel_spi_remove, - .driver = { - .name = "panel_lgphilips_lb035q02", - .of_match_table = lb035q02_of_match, - .suppress_bind_attrs = true, - }, -}; - -module_spi_driver(lb035q02_spi_driver); - -MODULE_ALIAS("spi:lgphilips,lb035q02"); -MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); -MODULE_DESCRIPTION("LG.Philips LB035Q02 LCD Panel driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/omapdrm/displays/panel-nec-nl8048hl11.c b/drivers/gpu/drm/omapdrm/displays/panel-nec-nl8048hl11.c deleted file mode 100644 index df8132d3b9c6..000000000000 --- a/drivers/gpu/drm/omapdrm/displays/panel-nec-nl8048hl11.c +++ /dev/null @@ -1,394 +0,0 @@ -/* - * NEC NL8048HL11 Panel driver - * - * Copyright (C) 2010 Texas Instruments Inc. - * Author: Erik Gilling <konkers@android.com> - * Converted to new DSS device model: Tomi Valkeinen <tomi.valkeinen@ti.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/spi/spi.h> -#include <linux/gpio/consumer.h> -#include <linux/of_gpio.h> - -#include "../dss/omapdss.h" - -struct panel_drv_data { - struct omap_dss_device dssdev; - struct omap_dss_device *in; - - struct videomode vm; - - int res_gpio; - int qvga_gpio; - - struct spi_device *spi; -}; - -#define LCD_XRES 800 -#define LCD_YRES 480 -/* - * NEC PIX Clock Ratings - * MIN:21.8MHz TYP:23.8MHz MAX:25.7MHz - */ -#define LCD_PIXEL_CLOCK 23800000 - -static const struct { - unsigned char addr; - unsigned char dat; -} nec_8048_init_seq[] = { - { 3, 0x01 }, { 0, 0x00 }, { 1, 0x01 }, { 4, 0x00 }, { 5, 0x14 }, - { 6, 0x24 }, { 16, 0xD7 }, { 17, 0x00 }, { 18, 0x00 }, { 19, 0x55 }, - { 20, 0x01 }, { 21, 0x70 }, { 22, 0x1E }, { 23, 0x25 }, { 24, 0x25 }, - { 25, 0x02 }, { 26, 0x02 }, { 27, 0xA0 }, { 32, 0x2F }, { 33, 0x0F }, - { 34, 0x0F }, { 35, 0x0F }, { 36, 0x0F }, { 37, 0x0F }, { 38, 0x0F }, - { 39, 0x00 }, { 40, 0x02 }, { 41, 0x02 }, { 42, 0x02 }, { 43, 0x0F }, - { 44, 0x0F }, { 45, 0x0F }, { 46, 0x0F }, { 47, 0x0F }, { 48, 0x0F }, - { 49, 0x0F }, { 50, 0x00 }, { 51, 0x02 }, { 52, 0x02 }, { 53, 0x02 }, - { 80, 0x0C }, { 83, 0x42 }, { 84, 0x42 }, { 85, 0x41 }, { 86, 0x14 }, - { 89, 0x88 }, { 90, 0x01 }, { 91, 0x00 }, { 92, 0x02 }, { 93, 0x0C }, - { 94, 0x1C }, { 95, 0x27 }, { 98, 0x49 }, { 99, 0x27 }, { 102, 0x76 }, - { 103, 0x27 }, { 112, 0x01 }, { 113, 0x0E }, { 114, 0x02 }, - { 115, 0x0C }, { 118, 0x0C }, { 121, 0x30 }, { 130, 0x00 }, - { 131, 0x00 }, { 132, 0xFC }, { 134, 0x00 }, { 136, 0x00 }, - { 138, 0x00 }, { 139, 0x00 }, { 140, 0x00 }, { 141, 0xFC }, - { 143, 0x00 }, { 145, 0x00 }, { 147, 0x00 }, { 148, 0x00 }, - { 149, 0x00 }, { 150, 0xFC }, { 152, 0x00 }, { 154, 0x00 }, - { 156, 0x00 }, { 157, 0x00 }, { 2, 0x00 }, -}; - -static const struct videomode nec_8048_panel_vm = { - .hactive = LCD_XRES, - .vactive = LCD_YRES, - .pixelclock = LCD_PIXEL_CLOCK, - .hfront_porch = 6, - .hsync_len = 1, - .hback_porch = 4, - .vfront_porch = 3, - .vsync_len = 1, - .vback_porch = 4, - - .flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW | - DISPLAY_FLAGS_DE_HIGH | DISPLAY_FLAGS_SYNC_POSEDGE | - DISPLAY_FLAGS_PIXDATA_POSEDGE, -}; - -#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev) - -static int nec_8048_spi_send(struct spi_device *spi, unsigned char reg_addr, - unsigned char reg_data) -{ - int ret = 0; - unsigned int cmd = 0, data = 0; - - cmd = 0x0000 | reg_addr; /* register address write */ - data = 0x0100 | reg_data; /* register data write */ - data = (cmd << 16) | data; - - ret = spi_write(spi, (unsigned char *)&data, 4); - if (ret) - pr_err("error in spi_write %x\n", data); - - return ret; -} - -static int init_nec_8048_wvga_lcd(struct spi_device *spi) -{ - unsigned int i; - /* Initialization Sequence */ - /* nec_8048_spi_send(spi, REG, VAL) */ - for (i = 0; i < (ARRAY_SIZE(nec_8048_init_seq) - 1); i++) - nec_8048_spi_send(spi, nec_8048_init_seq[i].addr, - nec_8048_init_seq[i].dat); - udelay(20); - nec_8048_spi_send(spi, nec_8048_init_seq[i].addr, - nec_8048_init_seq[i].dat); - return 0; -} - -static int nec_8048_connect(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - int r; - - if (omapdss_device_is_connected(dssdev)) - return 0; - - r = in->ops.dpi->connect(in, dssdev); - if (r) - return r; - - return 0; -} - -static void nec_8048_disconnect(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - if (!omapdss_device_is_connected(dssdev)) - return; - - in->ops.dpi->disconnect(in, dssdev); -} - -static int nec_8048_enable(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - int r; - - if (!omapdss_device_is_connected(dssdev)) - return -ENODEV; - - if (omapdss_device_is_enabled(dssdev)) - return 0; - - in->ops.dpi->set_timings(in, &ddata->vm); - - r = in->ops.dpi->enable(in); - if (r) - return r; - - if (gpio_is_valid(ddata->res_gpio)) - gpio_set_value_cansleep(ddata->res_gpio, 1); - - dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; - - return 0; -} - -static void nec_8048_disable(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - if (!omapdss_device_is_enabled(dssdev)) - return; - - if (gpio_is_valid(ddata->res_gpio)) - gpio_set_value_cansleep(ddata->res_gpio, 0); - - in->ops.dpi->disable(in); - - dssdev->state = OMAP_DSS_DISPLAY_DISABLED; -} - -static void nec_8048_set_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - ddata->vm = *vm; - dssdev->panel.vm = *vm; - - in->ops.dpi->set_timings(in, vm); -} - -static void nec_8048_get_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - - *vm = ddata->vm; -} - -static int nec_8048_check_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - return in->ops.dpi->check_timings(in, vm); -} - -static struct omap_dss_driver nec_8048_ops = { - .connect = nec_8048_connect, - .disconnect = nec_8048_disconnect, - - .enable = nec_8048_enable, - .disable = nec_8048_disable, - - .set_timings = nec_8048_set_timings, - .get_timings = nec_8048_get_timings, - .check_timings = nec_8048_check_timings, -}; - -static int nec_8048_probe_of(struct spi_device *spi) -{ - struct device_node *node = spi->dev.of_node; - struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); - struct omap_dss_device *in; - int gpio; - - gpio = of_get_named_gpio(node, "reset-gpios", 0); - if (!gpio_is_valid(gpio)) { - dev_err(&spi->dev, "failed to parse enable gpio\n"); - return gpio; - } - ddata->res_gpio = gpio; - - /* XXX the panel spec doesn't mention any QVGA pin?? */ - ddata->qvga_gpio = -ENOENT; - - in = omapdss_of_find_source_for_first_ep(node); - if (IS_ERR(in)) { - dev_err(&spi->dev, "failed to find video source\n"); - return PTR_ERR(in); - } - - ddata->in = in; - - return 0; -} - -static int nec_8048_probe(struct spi_device *spi) -{ - struct panel_drv_data *ddata; - struct omap_dss_device *dssdev; - int r; - - dev_dbg(&spi->dev, "%s\n", __func__); - - spi->mode = SPI_MODE_0; - spi->bits_per_word = 32; - - r = spi_setup(spi); - if (r < 0) { - dev_err(&spi->dev, "spi_setup failed: %d\n", r); - return r; - } - - init_nec_8048_wvga_lcd(spi); - - ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL); - if (ddata == NULL) - return -ENOMEM; - - dev_set_drvdata(&spi->dev, ddata); - - ddata->spi = spi; - - if (!spi->dev.of_node) - return -ENODEV; - - r = nec_8048_probe_of(spi); - if (r) - return r; - - if (gpio_is_valid(ddata->qvga_gpio)) { - r = devm_gpio_request_one(&spi->dev, ddata->qvga_gpio, - GPIOF_OUT_INIT_HIGH, "lcd QVGA"); - if (r) - goto err_gpio; - } - - if (gpio_is_valid(ddata->res_gpio)) { - r = devm_gpio_request_one(&spi->dev, ddata->res_gpio, - GPIOF_OUT_INIT_LOW, "lcd RES"); - if (r) - goto err_gpio; - } - - ddata->vm = nec_8048_panel_vm; - - dssdev = &ddata->dssdev; - dssdev->dev = &spi->dev; - dssdev->driver = &nec_8048_ops; - dssdev->type = OMAP_DISPLAY_TYPE_DPI; - dssdev->owner = THIS_MODULE; - dssdev->panel.vm = ddata->vm; - - r = omapdss_register_display(dssdev); - if (r) { - dev_err(&spi->dev, "Failed to register panel\n"); - goto err_reg; - } - - return 0; - -err_reg: -err_gpio: - omap_dss_put_device(ddata->in); - return r; -} - -static int nec_8048_remove(struct spi_device *spi) -{ - struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); - struct omap_dss_device *dssdev = &ddata->dssdev; - struct omap_dss_device *in = ddata->in; - - dev_dbg(&ddata->spi->dev, "%s\n", __func__); - - omapdss_unregister_display(dssdev); - - nec_8048_disable(dssdev); - nec_8048_disconnect(dssdev); - - omap_dss_put_device(in); - - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int nec_8048_suspend(struct device *dev) -{ - struct spi_device *spi = to_spi_device(dev); - - nec_8048_spi_send(spi, 2, 0x01); - mdelay(40); - - return 0; -} - -static int nec_8048_resume(struct device *dev) -{ - struct spi_device *spi = to_spi_device(dev); - - /* reinitialize the panel */ - spi_setup(spi); - nec_8048_spi_send(spi, 2, 0x00); - init_nec_8048_wvga_lcd(spi); - - return 0; -} -static SIMPLE_DEV_PM_OPS(nec_8048_pm_ops, nec_8048_suspend, - nec_8048_resume); -#define NEC_8048_PM_OPS (&nec_8048_pm_ops) -#else -#define NEC_8048_PM_OPS NULL -#endif - -static const struct of_device_id nec_8048_of_match[] = { - { .compatible = "omapdss,nec,nl8048hl11", }, - {}, -}; - -MODULE_DEVICE_TABLE(of, nec_8048_of_match); - -static struct spi_driver nec_8048_driver = { - .driver = { - .name = "panel-nec-nl8048hl11", - .pm = NEC_8048_PM_OPS, - .of_match_table = nec_8048_of_match, - .suppress_bind_attrs = true, - }, - .probe = nec_8048_probe, - .remove = nec_8048_remove, -}; - -module_spi_driver(nec_8048_driver); - -MODULE_ALIAS("spi:nec,nl8048hl11"); -MODULE_AUTHOR("Erik Gilling <konkers@android.com>"); -MODULE_DESCRIPTION("NEC-NL8048HL11 Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/omapdrm/displays/panel-sharp-ls037v7dw01.c b/drivers/gpu/drm/omapdrm/displays/panel-sharp-ls037v7dw01.c deleted file mode 100644 index 98d170aecaba..000000000000 --- a/drivers/gpu/drm/omapdrm/displays/panel-sharp-ls037v7dw01.c +++ /dev/null @@ -1,337 +0,0 @@ -/* - * LCD panel driver for Sharp LS037V7DW01 - * - * Copyright (C) 2013 Texas Instruments - * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - */ - -#include <linux/delay.h> -#include <linux/gpio/consumer.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/platform_device.h> -#include <linux/slab.h> -#include <linux/regulator/consumer.h> - -#include "../dss/omapdss.h" - -struct panel_drv_data { - struct omap_dss_device dssdev; - struct omap_dss_device *in; - struct regulator *vcc; - - struct videomode vm; - - struct gpio_desc *resb_gpio; /* low = reset active min 20 us */ - struct gpio_desc *ini_gpio; /* high = power on */ - struct gpio_desc *mo_gpio; /* low = 480x640, high = 240x320 */ - struct gpio_desc *lr_gpio; /* high = conventional horizontal scanning */ - struct gpio_desc *ud_gpio; /* high = conventional vertical scanning */ -}; - -static const struct videomode sharp_ls_vm = { - .hactive = 480, - .vactive = 640, - - .pixelclock = 19200000, - - .hsync_len = 2, - .hfront_porch = 1, - .hback_porch = 28, - - .vsync_len = 1, - .vfront_porch = 1, - .vback_porch = 1, - - .flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW | - DISPLAY_FLAGS_DE_HIGH | DISPLAY_FLAGS_SYNC_NEGEDGE | - DISPLAY_FLAGS_PIXDATA_POSEDGE, - /* - * Note: According to the panel documentation: - * DATA needs to be driven on the FALLING edge - */ -}; - -#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev) - -static int sharp_ls_connect(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - int r; - - if (omapdss_device_is_connected(dssdev)) - return 0; - - r = in->ops.dpi->connect(in, dssdev); - if (r) - return r; - - return 0; -} - -static void sharp_ls_disconnect(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - if (!omapdss_device_is_connected(dssdev)) - return; - - in->ops.dpi->disconnect(in, dssdev); -} - -static int sharp_ls_enable(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - int r; - - if (!omapdss_device_is_connected(dssdev)) - return -ENODEV; - - if (omapdss_device_is_enabled(dssdev)) - return 0; - - in->ops.dpi->set_timings(in, &ddata->vm); - - if (ddata->vcc) { - r = regulator_enable(ddata->vcc); - if (r != 0) - return r; - } - - r = in->ops.dpi->enable(in); - if (r) { - regulator_disable(ddata->vcc); - return r; - } - - /* wait couple of vsyncs until enabling the LCD */ - msleep(50); - - if (ddata->resb_gpio) - gpiod_set_value_cansleep(ddata->resb_gpio, 1); - - if (ddata->ini_gpio) - gpiod_set_value_cansleep(ddata->ini_gpio, 1); - - dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; - - return 0; -} - -static void sharp_ls_disable(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - if (!omapdss_device_is_enabled(dssdev)) - return; - - if (ddata->ini_gpio) - gpiod_set_value_cansleep(ddata->ini_gpio, 0); - - if (ddata->resb_gpio) - gpiod_set_value_cansleep(ddata->resb_gpio, 0); - - /* wait at least 5 vsyncs after disabling the LCD */ - - msleep(100); - - in->ops.dpi->disable(in); - - if (ddata->vcc) - regulator_disable(ddata->vcc); - - dssdev->state = OMAP_DSS_DISPLAY_DISABLED; -} - -static void sharp_ls_set_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - ddata->vm = *vm; - dssdev->panel.vm = *vm; - - in->ops.dpi->set_timings(in, vm); -} - -static void sharp_ls_get_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - - *vm = ddata->vm; -} - -static int sharp_ls_check_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - return in->ops.dpi->check_timings(in, vm); -} - -static struct omap_dss_driver sharp_ls_ops = { - .connect = sharp_ls_connect, - .disconnect = sharp_ls_disconnect, - - .enable = sharp_ls_enable, - .disable = sharp_ls_disable, - - .set_timings = sharp_ls_set_timings, - .get_timings = sharp_ls_get_timings, - .check_timings = sharp_ls_check_timings, -}; - -static int sharp_ls_get_gpio_of(struct device *dev, int index, int val, - const char *desc, struct gpio_desc **gpiod) -{ - struct gpio_desc *gd; - - *gpiod = NULL; - - gd = devm_gpiod_get_index(dev, desc, index, GPIOD_OUT_LOW); - if (IS_ERR(gd)) - return PTR_ERR(gd); - - *gpiod = gd; - return 0; -} - -static int sharp_ls_probe_of(struct platform_device *pdev) -{ - struct panel_drv_data *ddata = platform_get_drvdata(pdev); - struct device_node *node = pdev->dev.of_node; - struct omap_dss_device *in; - int r; - - ddata->vcc = devm_regulator_get(&pdev->dev, "envdd"); - if (IS_ERR(ddata->vcc)) { - dev_err(&pdev->dev, "failed to get regulator\n"); - return PTR_ERR(ddata->vcc); - } - - /* lcd INI */ - r = sharp_ls_get_gpio_of(&pdev->dev, 0, 0, "enable", &ddata->ini_gpio); - if (r) - return r; - - /* lcd RESB */ - r = sharp_ls_get_gpio_of(&pdev->dev, 0, 0, "reset", &ddata->resb_gpio); - if (r) - return r; - - /* lcd MO */ - r = sharp_ls_get_gpio_of(&pdev->dev, 0, 0, "mode", &ddata->mo_gpio); - if (r) - return r; - - /* lcd LR */ - r = sharp_ls_get_gpio_of(&pdev->dev, 1, 1, "mode", &ddata->lr_gpio); - if (r) - return r; - - /* lcd UD */ - r = sharp_ls_get_gpio_of(&pdev->dev, 2, 1, "mode", &ddata->ud_gpio); - if (r) - return r; - - in = omapdss_of_find_source_for_first_ep(node); - if (IS_ERR(in)) { - dev_err(&pdev->dev, "failed to find video source\n"); - return PTR_ERR(in); - } - - ddata->in = in; - - return 0; -} - -static int sharp_ls_probe(struct platform_device *pdev) -{ - struct panel_drv_data *ddata; - struct omap_dss_device *dssdev; - int r; - - ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); - if (ddata == NULL) - return -ENOMEM; - - platform_set_drvdata(pdev, ddata); - - if (!pdev->dev.of_node) - return -ENODEV; - - r = sharp_ls_probe_of(pdev); - if (r) - return r; - - ddata->vm = sharp_ls_vm; - - dssdev = &ddata->dssdev; - dssdev->dev = &pdev->dev; - dssdev->driver = &sharp_ls_ops; - dssdev->type = OMAP_DISPLAY_TYPE_DPI; - dssdev->owner = THIS_MODULE; - dssdev->panel.vm = ddata->vm; - - r = omapdss_register_display(dssdev); - if (r) { - dev_err(&pdev->dev, "Failed to register panel\n"); - goto err_reg; - } - - return 0; - -err_reg: - omap_dss_put_device(ddata->in); - return r; -} - -static int __exit sharp_ls_remove(struct platform_device *pdev) -{ - struct panel_drv_data *ddata = platform_get_drvdata(pdev); - struct omap_dss_device *dssdev = &ddata->dssdev; - struct omap_dss_device *in = ddata->in; - - omapdss_unregister_display(dssdev); - - sharp_ls_disable(dssdev); - sharp_ls_disconnect(dssdev); - - omap_dss_put_device(in); - - return 0; -} - -static const struct of_device_id sharp_ls_of_match[] = { - { .compatible = "omapdss,sharp,ls037v7dw01", }, - {}, -}; - -MODULE_DEVICE_TABLE(of, sharp_ls_of_match); - -static struct platform_driver sharp_ls_driver = { - .probe = sharp_ls_probe, - .remove = __exit_p(sharp_ls_remove), - .driver = { - .name = "panel-sharp-ls037v7dw01", - .of_match_table = sharp_ls_of_match, - .suppress_bind_attrs = true, - }, -}; - -module_platform_driver(sharp_ls_driver); - -MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); -MODULE_DESCRIPTION("Sharp LS037V7DW01 Panel Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/omapdrm/displays/panel-sony-acx565akm.c b/drivers/gpu/drm/omapdrm/displays/panel-sony-acx565akm.c deleted file mode 100644 index 346aefdb015f..000000000000 --- a/drivers/gpu/drm/omapdrm/displays/panel-sony-acx565akm.c +++ /dev/null @@ -1,872 +0,0 @@ -/* - * Sony ACX565AKM LCD Panel driver - * - * Copyright (C) 2010 Nokia Corporation - * - * Original Driver Author: Imre Deak <imre.deak@nokia.com> - * Based on panel-generic.c by Tomi Valkeinen <tomi.valkeinen@nokia.com> - * Adapted to new DSS2 framework: Roger Quadros <roger.quadros@nokia.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/delay.h> -#include <linux/spi/spi.h> -#include <linux/jiffies.h> -#include <linux/sched.h> -#include <linux/backlight.h> -#include <linux/gpio/consumer.h> -#include <linux/of.h> -#include <linux/of_gpio.h> - -#include "../dss/omapdss.h" - -#define MIPID_CMD_READ_DISP_ID 0x04 -#define MIPID_CMD_READ_RED 0x06 -#define MIPID_CMD_READ_GREEN 0x07 -#define MIPID_CMD_READ_BLUE 0x08 -#define MIPID_CMD_READ_DISP_STATUS 0x09 -#define MIPID_CMD_RDDSDR 0x0F -#define MIPID_CMD_SLEEP_IN 0x10 -#define MIPID_CMD_SLEEP_OUT 0x11 -#define MIPID_CMD_DISP_OFF 0x28 -#define MIPID_CMD_DISP_ON 0x29 -#define MIPID_CMD_WRITE_DISP_BRIGHTNESS 0x51 -#define MIPID_CMD_READ_DISP_BRIGHTNESS 0x52 -#define MIPID_CMD_WRITE_CTRL_DISP 0x53 - -#define CTRL_DISP_BRIGHTNESS_CTRL_ON (1 << 5) -#define CTRL_DISP_AMBIENT_LIGHT_CTRL_ON (1 << 4) -#define CTRL_DISP_BACKLIGHT_ON (1 << 2) -#define CTRL_DISP_AUTO_BRIGHTNESS_ON (1 << 1) - -#define MIPID_CMD_READ_CTRL_DISP 0x54 -#define MIPID_CMD_WRITE_CABC 0x55 -#define MIPID_CMD_READ_CABC 0x56 - -#define MIPID_VER_LPH8923 3 -#define MIPID_VER_LS041Y3 4 -#define MIPID_VER_L4F00311 8 -#define MIPID_VER_ACX565AKM 9 - -struct panel_drv_data { - struct omap_dss_device dssdev; - struct omap_dss_device *in; - - int reset_gpio; - - struct videomode vm; - - char *name; - int enabled; - int model; - int revision; - u8 display_id[3]; - unsigned has_bc:1; - unsigned has_cabc:1; - unsigned cabc_mode; - unsigned long hw_guard_end; /* next value of jiffies - when we can issue the - next sleep in/out command */ - unsigned long hw_guard_wait; /* max guard time in jiffies */ - - struct spi_device *spi; - struct mutex mutex; - - struct backlight_device *bl_dev; -}; - -static const struct videomode acx565akm_panel_vm = { - .hactive = 800, - .vactive = 480, - .pixelclock = 24000000, - .hfront_porch = 28, - .hsync_len = 4, - .hback_porch = 24, - .vfront_porch = 3, - .vsync_len = 3, - .vback_porch = 4, - - .flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW | - DISPLAY_FLAGS_DE_HIGH | DISPLAY_FLAGS_SYNC_NEGEDGE | - DISPLAY_FLAGS_PIXDATA_POSEDGE, -}; - -#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev) - -static void acx565akm_transfer(struct panel_drv_data *ddata, int cmd, - const u8 *wbuf, int wlen, u8 *rbuf, int rlen) -{ - struct spi_message m; - struct spi_transfer *x, xfer[5]; - int r; - - BUG_ON(ddata->spi == NULL); - - spi_message_init(&m); - - memset(xfer, 0, sizeof(xfer)); - x = &xfer[0]; - - cmd &= 0xff; - x->tx_buf = &cmd; - x->bits_per_word = 9; - x->len = 2; - - if (rlen > 1 && wlen == 0) { - /* - * Between the command and the response data there is a - * dummy clock cycle. Add an extra bit after the command - * word to account for this. - */ - x->bits_per_word = 10; - cmd <<= 1; - } - spi_message_add_tail(x, &m); - - if (wlen) { - x++; - x->tx_buf = wbuf; - x->len = wlen; - x->bits_per_word = 9; - spi_message_add_tail(x, &m); - } - - if (rlen) { - x++; - x->rx_buf = rbuf; - x->len = rlen; - spi_message_add_tail(x, &m); - } - - r = spi_sync(ddata->spi, &m); - if (r < 0) - dev_dbg(&ddata->spi->dev, "spi_sync %d\n", r); -} - -static inline void acx565akm_cmd(struct panel_drv_data *ddata, int cmd) -{ - acx565akm_transfer(ddata, cmd, NULL, 0, NULL, 0); -} - -static inline void acx565akm_write(struct panel_drv_data *ddata, - int reg, const u8 *buf, int len) -{ - acx565akm_transfer(ddata, reg, buf, len, NULL, 0); -} - -static inline void acx565akm_read(struct panel_drv_data *ddata, - int reg, u8 *buf, int len) -{ - acx565akm_transfer(ddata, reg, NULL, 0, buf, len); -} - -static void hw_guard_start(struct panel_drv_data *ddata, int guard_msec) -{ - ddata->hw_guard_wait = msecs_to_jiffies(guard_msec); - ddata->hw_guard_end = jiffies + ddata->hw_guard_wait; -} - -static void hw_guard_wait(struct panel_drv_data *ddata) -{ - unsigned long wait = ddata->hw_guard_end - jiffies; - - if ((long)wait > 0 && wait <= ddata->hw_guard_wait) { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(wait); - } -} - -static void set_sleep_mode(struct panel_drv_data *ddata, int on) -{ - int cmd; - - if (on) - cmd = MIPID_CMD_SLEEP_IN; - else - cmd = MIPID_CMD_SLEEP_OUT; - /* - * We have to keep 120msec between sleep in/out commands. - * (8.2.15, 8.2.16). - */ - hw_guard_wait(ddata); - acx565akm_cmd(ddata, cmd); - hw_guard_start(ddata, 120); -} - -static void set_display_state(struct panel_drv_data *ddata, int enabled) -{ - int cmd = enabled ? MIPID_CMD_DISP_ON : MIPID_CMD_DISP_OFF; - - acx565akm_cmd(ddata, cmd); -} - -static int panel_enabled(struct panel_drv_data *ddata) -{ - u32 disp_status; - int enabled; - - acx565akm_read(ddata, MIPID_CMD_READ_DISP_STATUS, - (u8 *)&disp_status, 4); - disp_status = __be32_to_cpu(disp_status); - enabled = (disp_status & (1 << 17)) && (disp_status & (1 << 10)); - dev_dbg(&ddata->spi->dev, - "LCD panel %senabled by bootloader (status 0x%04x)\n", - enabled ? "" : "not ", disp_status); - return enabled; -} - -static int panel_detect(struct panel_drv_data *ddata) -{ - acx565akm_read(ddata, MIPID_CMD_READ_DISP_ID, ddata->display_id, 3); - dev_dbg(&ddata->spi->dev, "MIPI display ID: %02x%02x%02x\n", - ddata->display_id[0], - ddata->display_id[1], - ddata->display_id[2]); - - switch (ddata->display_id[0]) { - case 0x10: - ddata->model = MIPID_VER_ACX565AKM; - ddata->name = "acx565akm"; - ddata->has_bc = 1; - ddata->has_cabc = 1; - break; - case 0x29: - ddata->model = MIPID_VER_L4F00311; - ddata->name = "l4f00311"; - break; - case 0x45: - ddata->model = MIPID_VER_LPH8923; - ddata->name = "lph8923"; - break; - case 0x83: - ddata->model = MIPID_VER_LS041Y3; - ddata->name = "ls041y3"; - break; - default: - ddata->name = "unknown"; - dev_err(&ddata->spi->dev, "invalid display ID\n"); - return -ENODEV; - } - - ddata->revision = ddata->display_id[1]; - - dev_info(&ddata->spi->dev, "omapfb: %s rev %02x LCD detected\n", - ddata->name, ddata->revision); - - return 0; -} - -/*----------------------Backlight Control-------------------------*/ - -static void enable_backlight_ctrl(struct panel_drv_data *ddata, int enable) -{ - u16 ctrl; - - acx565akm_read(ddata, MIPID_CMD_READ_CTRL_DISP, (u8 *)&ctrl, 1); - if (enable) { - ctrl |= CTRL_DISP_BRIGHTNESS_CTRL_ON | - CTRL_DISP_BACKLIGHT_ON; - } else { - ctrl &= ~(CTRL_DISP_BRIGHTNESS_CTRL_ON | - CTRL_DISP_BACKLIGHT_ON); - } - - ctrl |= 1 << 8; - acx565akm_write(ddata, MIPID_CMD_WRITE_CTRL_DISP, (u8 *)&ctrl, 2); -} - -static void set_cabc_mode(struct panel_drv_data *ddata, unsigned mode) -{ - u16 cabc_ctrl; - - ddata->cabc_mode = mode; - if (!ddata->enabled) - return; - cabc_ctrl = 0; - acx565akm_read(ddata, MIPID_CMD_READ_CABC, (u8 *)&cabc_ctrl, 1); - cabc_ctrl &= ~3; - cabc_ctrl |= (1 << 8) | (mode & 3); - acx565akm_write(ddata, MIPID_CMD_WRITE_CABC, (u8 *)&cabc_ctrl, 2); -} - -static unsigned get_cabc_mode(struct panel_drv_data *ddata) -{ - return ddata->cabc_mode; -} - -static unsigned get_hw_cabc_mode(struct panel_drv_data *ddata) -{ - u8 cabc_ctrl; - - acx565akm_read(ddata, MIPID_CMD_READ_CABC, &cabc_ctrl, 1); - return cabc_ctrl & 3; -} - -static void acx565akm_set_brightness(struct panel_drv_data *ddata, int level) -{ - int bv; - - bv = level | (1 << 8); - acx565akm_write(ddata, MIPID_CMD_WRITE_DISP_BRIGHTNESS, (u8 *)&bv, 2); - - if (level) - enable_backlight_ctrl(ddata, 1); - else - enable_backlight_ctrl(ddata, 0); -} - -static int acx565akm_get_actual_brightness(struct panel_drv_data *ddata) -{ - u8 bv; - - acx565akm_read(ddata, MIPID_CMD_READ_DISP_BRIGHTNESS, &bv, 1); - - return bv; -} - - -static int acx565akm_bl_update_status(struct backlight_device *dev) -{ - struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev); - int level; - - dev_dbg(&ddata->spi->dev, "%s\n", __func__); - - if (dev->props.fb_blank == FB_BLANK_UNBLANK && - dev->props.power == FB_BLANK_UNBLANK) - level = dev->props.brightness; - else - level = 0; - - if (ddata->has_bc) - acx565akm_set_brightness(ddata, level); - else - return -ENODEV; - - return 0; -} - -static int acx565akm_bl_get_intensity(struct backlight_device *dev) -{ - struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev); - - dev_dbg(&dev->dev, "%s\n", __func__); - - if (!ddata->has_bc) - return -ENODEV; - - if (dev->props.fb_blank == FB_BLANK_UNBLANK && - dev->props.power == FB_BLANK_UNBLANK) { - if (ddata->has_bc) - return acx565akm_get_actual_brightness(ddata); - else - return dev->props.brightness; - } - - return 0; -} - -static int acx565akm_bl_update_status_locked(struct backlight_device *dev) -{ - struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev); - int r; - - mutex_lock(&ddata->mutex); - r = acx565akm_bl_update_status(dev); - mutex_unlock(&ddata->mutex); - - return r; -} - -static int acx565akm_bl_get_intensity_locked(struct backlight_device *dev) -{ - struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev); - int r; - - mutex_lock(&ddata->mutex); - r = acx565akm_bl_get_intensity(dev); - mutex_unlock(&ddata->mutex); - - return r; -} - -static const struct backlight_ops acx565akm_bl_ops = { - .get_brightness = acx565akm_bl_get_intensity_locked, - .update_status = acx565akm_bl_update_status_locked, -}; - -/*--------------------Auto Brightness control via Sysfs---------------------*/ - -static const char * const cabc_modes[] = { - "off", /* always used when CABC is not supported */ - "ui", - "still-image", - "moving-image", -}; - -static ssize_t show_cabc_mode(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct panel_drv_data *ddata = dev_get_drvdata(dev); - const char *mode_str; - int mode; - int len; - - if (!ddata->has_cabc) - mode = 0; - else - mode = get_cabc_mode(ddata); - mode_str = "unknown"; - if (mode >= 0 && mode < ARRAY_SIZE(cabc_modes)) - mode_str = cabc_modes[mode]; - len = snprintf(buf, PAGE_SIZE, "%s\n", mode_str); - - return len < PAGE_SIZE - 1 ? len : PAGE_SIZE - 1; -} - -static ssize_t store_cabc_mode(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct panel_drv_data *ddata = dev_get_drvdata(dev); - int i; - - for (i = 0; i < ARRAY_SIZE(cabc_modes); i++) { - const char *mode_str = cabc_modes[i]; - int cmp_len = strlen(mode_str); - - if (count > 0 && buf[count - 1] == '\n') - count--; - if (count != cmp_len) - continue; - - if (strncmp(buf, mode_str, cmp_len) == 0) - break; - } - - if (i == ARRAY_SIZE(cabc_modes)) - return -EINVAL; - - if (!ddata->has_cabc && i != 0) - return -EINVAL; - - mutex_lock(&ddata->mutex); - set_cabc_mode(ddata, i); - mutex_unlock(&ddata->mutex); - - return count; -} - -static ssize_t show_cabc_available_modes(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct panel_drv_data *ddata = dev_get_drvdata(dev); - int len; - int i; - - if (!ddata->has_cabc) - return snprintf(buf, PAGE_SIZE, "%s\n", cabc_modes[0]); - - for (i = 0, len = 0; - len < PAGE_SIZE && i < ARRAY_SIZE(cabc_modes); i++) - len += snprintf(&buf[len], PAGE_SIZE - len, "%s%s%s", - i ? " " : "", cabc_modes[i], - i == ARRAY_SIZE(cabc_modes) - 1 ? "\n" : ""); - - return len < PAGE_SIZE ? len : PAGE_SIZE - 1; -} - -static DEVICE_ATTR(cabc_mode, S_IRUGO | S_IWUSR, - show_cabc_mode, store_cabc_mode); -static DEVICE_ATTR(cabc_available_modes, S_IRUGO, - show_cabc_available_modes, NULL); - -static struct attribute *bldev_attrs[] = { - &dev_attr_cabc_mode.attr, - &dev_attr_cabc_available_modes.attr, - NULL, -}; - -static struct attribute_group bldev_attr_group = { - .attrs = bldev_attrs, -}; - -static int acx565akm_connect(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - int r; - - if (omapdss_device_is_connected(dssdev)) - return 0; - - r = in->ops.sdi->connect(in, dssdev); - if (r) - return r; - - return 0; -} - -static void acx565akm_disconnect(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - if (!omapdss_device_is_connected(dssdev)) - return; - - in->ops.sdi->disconnect(in, dssdev); -} - -static int acx565akm_panel_power_on(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - int r; - - dev_dbg(&ddata->spi->dev, "%s\n", __func__); - - in->ops.sdi->set_timings(in, &ddata->vm); - - r = in->ops.sdi->enable(in); - if (r) { - pr_err("%s sdi enable failed\n", __func__); - return r; - } - - /*FIXME tweak me */ - msleep(50); - - if (gpio_is_valid(ddata->reset_gpio)) - gpio_set_value(ddata->reset_gpio, 1); - - if (ddata->enabled) { - dev_dbg(&ddata->spi->dev, "panel already enabled\n"); - return 0; - } - - /* - * We have to meet all the following delay requirements: - * 1. tRW: reset pulse width 10usec (7.12.1) - * 2. tRT: reset cancel time 5msec (7.12.1) - * 3. Providing PCLK,HS,VS signals for 2 frames = ~50msec worst - * case (7.6.2) - * 4. 120msec before the sleep out command (7.12.1) - */ - msleep(120); - - set_sleep_mode(ddata, 0); - ddata->enabled = 1; - - /* 5msec between sleep out and the next command. (8.2.16) */ - usleep_range(5000, 10000); - set_display_state(ddata, 1); - set_cabc_mode(ddata, ddata->cabc_mode); - - return acx565akm_bl_update_status(ddata->bl_dev); -} - -static void acx565akm_panel_power_off(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - dev_dbg(dssdev->dev, "%s\n", __func__); - - if (!ddata->enabled) - return; - - set_display_state(ddata, 0); - set_sleep_mode(ddata, 1); - ddata->enabled = 0; - /* - * We have to provide PCLK,HS,VS signals for 2 frames (worst case - * ~50msec) after sending the sleep in command and asserting the - * reset signal. We probably could assert the reset w/o the delay - * but we still delay to avoid possible artifacts. (7.6.1) - */ - msleep(50); - - if (gpio_is_valid(ddata->reset_gpio)) - gpio_set_value(ddata->reset_gpio, 0); - - /* FIXME need to tweak this delay */ - msleep(100); - - in->ops.sdi->disable(in); -} - -static int acx565akm_enable(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - int r; - - dev_dbg(dssdev->dev, "%s\n", __func__); - - if (!omapdss_device_is_connected(dssdev)) - return -ENODEV; - - if (omapdss_device_is_enabled(dssdev)) - return 0; - - mutex_lock(&ddata->mutex); - r = acx565akm_panel_power_on(dssdev); - mutex_unlock(&ddata->mutex); - if (r) - return r; - - dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; - - return 0; -} - -static void acx565akm_disable(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - - dev_dbg(dssdev->dev, "%s\n", __func__); - - if (!omapdss_device_is_enabled(dssdev)) - return; - - mutex_lock(&ddata->mutex); - acx565akm_panel_power_off(dssdev); - mutex_unlock(&ddata->mutex); - - dssdev->state = OMAP_DSS_DISPLAY_DISABLED; -} - -static void acx565akm_set_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - ddata->vm = *vm; - dssdev->panel.vm = *vm; - - in->ops.sdi->set_timings(in, vm); -} - -static void acx565akm_get_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - - *vm = ddata->vm; -} - -static int acx565akm_check_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - return in->ops.sdi->check_timings(in, vm); -} - -static struct omap_dss_driver acx565akm_ops = { - .connect = acx565akm_connect, - .disconnect = acx565akm_disconnect, - - .enable = acx565akm_enable, - .disable = acx565akm_disable, - - .set_timings = acx565akm_set_timings, - .get_timings = acx565akm_get_timings, - .check_timings = acx565akm_check_timings, -}; - -static int acx565akm_probe_of(struct spi_device *spi) -{ - struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); - struct device_node *np = spi->dev.of_node; - - ddata->reset_gpio = of_get_named_gpio(np, "reset-gpios", 0); - - ddata->in = omapdss_of_find_source_for_first_ep(np); - if (IS_ERR(ddata->in)) { - dev_err(&spi->dev, "failed to find video source\n"); - return PTR_ERR(ddata->in); - } - - return 0; -} - -static int acx565akm_probe(struct spi_device *spi) -{ - struct panel_drv_data *ddata; - struct omap_dss_device *dssdev; - struct backlight_device *bldev; - int max_brightness, brightness; - struct backlight_properties props; - int r; - - dev_dbg(&spi->dev, "%s\n", __func__); - - spi->mode = SPI_MODE_3; - - ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL); - if (ddata == NULL) - return -ENOMEM; - - dev_set_drvdata(&spi->dev, ddata); - - ddata->spi = spi; - - mutex_init(&ddata->mutex); - - r = acx565akm_probe_of(spi); - if (r) - return r; - - if (gpio_is_valid(ddata->reset_gpio)) { - r = devm_gpio_request_one(&spi->dev, ddata->reset_gpio, - GPIOF_OUT_INIT_LOW, "lcd reset"); - if (r) - goto err_gpio; - } - - if (gpio_is_valid(ddata->reset_gpio)) - gpio_set_value(ddata->reset_gpio, 1); - - /* - * After reset we have to wait 5 msec before the first - * command can be sent. - */ - usleep_range(5000, 10000); - - ddata->enabled = panel_enabled(ddata); - - r = panel_detect(ddata); - - if (!ddata->enabled && gpio_is_valid(ddata->reset_gpio)) - gpio_set_value(ddata->reset_gpio, 0); - - if (r) { - dev_err(&spi->dev, "%s panel detect error\n", __func__); - goto err_detect; - } - - memset(&props, 0, sizeof(props)); - props.fb_blank = FB_BLANK_UNBLANK; - props.power = FB_BLANK_UNBLANK; - props.type = BACKLIGHT_RAW; - - bldev = backlight_device_register("acx565akm", &ddata->spi->dev, - ddata, &acx565akm_bl_ops, &props); - if (IS_ERR(bldev)) { - r = PTR_ERR(bldev); - goto err_reg_bl; - } - ddata->bl_dev = bldev; - if (ddata->has_cabc) { - r = sysfs_create_group(&bldev->dev.kobj, &bldev_attr_group); - if (r) { - dev_err(&bldev->dev, - "%s failed to create sysfs files\n", __func__); - goto err_sysfs; - } - ddata->cabc_mode = get_hw_cabc_mode(ddata); - } - - max_brightness = 255; - - if (ddata->has_bc) - brightness = acx565akm_get_actual_brightness(ddata); - else - brightness = 0; - - bldev->props.max_brightness = max_brightness; - bldev->props.brightness = brightness; - - acx565akm_bl_update_status(bldev); - - - ddata->vm = acx565akm_panel_vm; - - dssdev = &ddata->dssdev; - dssdev->dev = &spi->dev; - dssdev->driver = &acx565akm_ops; - dssdev->type = OMAP_DISPLAY_TYPE_SDI; - dssdev->owner = THIS_MODULE; - dssdev->panel.vm = ddata->vm; - - r = omapdss_register_display(dssdev); - if (r) { - dev_err(&spi->dev, "Failed to register panel\n"); - goto err_reg; - } - - return 0; - -err_reg: - sysfs_remove_group(&bldev->dev.kobj, &bldev_attr_group); -err_sysfs: - backlight_device_unregister(bldev); -err_reg_bl: -err_detect: -err_gpio: - omap_dss_put_device(ddata->in); - return r; -} - -static int acx565akm_remove(struct spi_device *spi) -{ - struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); - struct omap_dss_device *dssdev = &ddata->dssdev; - struct omap_dss_device *in = ddata->in; - - dev_dbg(&ddata->spi->dev, "%s\n", __func__); - - sysfs_remove_group(&ddata->bl_dev->dev.kobj, &bldev_attr_group); - backlight_device_unregister(ddata->bl_dev); - - omapdss_unregister_display(dssdev); - - acx565akm_disable(dssdev); - acx565akm_disconnect(dssdev); - - omap_dss_put_device(in); - - return 0; -} - -static const struct of_device_id acx565akm_of_match[] = { - { .compatible = "omapdss,sony,acx565akm", }, - {}, -}; -MODULE_DEVICE_TABLE(of, acx565akm_of_match); - -static struct spi_driver acx565akm_driver = { - .driver = { - .name = "acx565akm", - .of_match_table = acx565akm_of_match, - .suppress_bind_attrs = true, - }, - .probe = acx565akm_probe, - .remove = acx565akm_remove, -}; - -module_spi_driver(acx565akm_driver); - -MODULE_ALIAS("spi:sony,acx565akm"); -MODULE_AUTHOR("Nokia Corporation"); -MODULE_DESCRIPTION("acx565akm LCD Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/omapdrm/displays/panel-tpo-td028ttec1.c b/drivers/gpu/drm/omapdrm/displays/panel-tpo-td028ttec1.c deleted file mode 100644 index cbf4c67c4933..000000000000 --- a/drivers/gpu/drm/omapdrm/displays/panel-tpo-td028ttec1.c +++ /dev/null @@ -1,477 +0,0 @@ -/* - * Toppoly TD028TTEC1 panel support - * - * Copyright (C) 2008 Nokia Corporation - * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> - * - * Neo 1973 code (jbt6k74.c): - * Copyright (C) 2006-2007 by OpenMoko, Inc. - * Author: Harald Welte <laforge@openmoko.org> - * - * Ported and adapted from Neo 1973 U-Boot by: - * H. Nikolaus Schaller <hns@goldelico.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/spi/spi.h> -#include <linux/gpio.h> - -#include "../dss/omapdss.h" - -struct panel_drv_data { - struct omap_dss_device dssdev; - struct omap_dss_device *in; - - struct videomode vm; - - struct spi_device *spi_dev; -}; - -static struct videomode td028ttec1_panel_vm = { - .hactive = 480, - .vactive = 640, - .pixelclock = 22153000, - .hfront_porch = 24, - .hsync_len = 8, - .hback_porch = 8, - .vfront_porch = 4, - .vsync_len = 2, - .vback_porch = 2, - - .flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW | - DISPLAY_FLAGS_DE_HIGH | DISPLAY_FLAGS_SYNC_POSEDGE | - DISPLAY_FLAGS_PIXDATA_NEGEDGE, - /* - * Note: According to the panel documentation: - * SYNC needs to be driven on the FALLING edge - */ -}; - -#define JBT_COMMAND 0x000 -#define JBT_DATA 0x100 - -static int jbt_ret_write_0(struct panel_drv_data *ddata, u8 reg) -{ - int rc; - u16 tx_buf = JBT_COMMAND | reg; - - rc = spi_write(ddata->spi_dev, (u8 *)&tx_buf, - 1*sizeof(u16)); - if (rc != 0) - dev_err(&ddata->spi_dev->dev, - "jbt_ret_write_0 spi_write ret %d\n", rc); - - return rc; -} - -static int jbt_reg_write_1(struct panel_drv_data *ddata, u8 reg, u8 data) -{ - int rc; - u16 tx_buf[2]; - - tx_buf[0] = JBT_COMMAND | reg; - tx_buf[1] = JBT_DATA | data; - rc = spi_write(ddata->spi_dev, (u8 *)tx_buf, - 2*sizeof(u16)); - if (rc != 0) - dev_err(&ddata->spi_dev->dev, - "jbt_reg_write_1 spi_write ret %d\n", rc); - - return rc; -} - -static int jbt_reg_write_2(struct panel_drv_data *ddata, u8 reg, u16 data) -{ - int rc; - u16 tx_buf[3]; - - tx_buf[0] = JBT_COMMAND | reg; - tx_buf[1] = JBT_DATA | (data >> 8); - tx_buf[2] = JBT_DATA | (data & 0xff); - - rc = spi_write(ddata->spi_dev, (u8 *)tx_buf, - 3*sizeof(u16)); - - if (rc != 0) - dev_err(&ddata->spi_dev->dev, - "jbt_reg_write_2 spi_write ret %d\n", rc); - - return rc; -} - -enum jbt_register { - JBT_REG_SLEEP_IN = 0x10, - JBT_REG_SLEEP_OUT = 0x11, - - JBT_REG_DISPLAY_OFF = 0x28, - JBT_REG_DISPLAY_ON = 0x29, - - JBT_REG_RGB_FORMAT = 0x3a, - JBT_REG_QUAD_RATE = 0x3b, - - JBT_REG_POWER_ON_OFF = 0xb0, - JBT_REG_BOOSTER_OP = 0xb1, - JBT_REG_BOOSTER_MODE = 0xb2, - JBT_REG_BOOSTER_FREQ = 0xb3, - JBT_REG_OPAMP_SYSCLK = 0xb4, - JBT_REG_VSC_VOLTAGE = 0xb5, - JBT_REG_VCOM_VOLTAGE = 0xb6, - JBT_REG_EXT_DISPL = 0xb7, - JBT_REG_OUTPUT_CONTROL = 0xb8, - JBT_REG_DCCLK_DCEV = 0xb9, - JBT_REG_DISPLAY_MODE1 = 0xba, - JBT_REG_DISPLAY_MODE2 = 0xbb, - JBT_REG_DISPLAY_MODE = 0xbc, - JBT_REG_ASW_SLEW = 0xbd, - JBT_REG_DUMMY_DISPLAY = 0xbe, - JBT_REG_DRIVE_SYSTEM = 0xbf, - - JBT_REG_SLEEP_OUT_FR_A = 0xc0, - JBT_REG_SLEEP_OUT_FR_B = 0xc1, - JBT_REG_SLEEP_OUT_FR_C = 0xc2, - JBT_REG_SLEEP_IN_LCCNT_D = 0xc3, - JBT_REG_SLEEP_IN_LCCNT_E = 0xc4, - JBT_REG_SLEEP_IN_LCCNT_F = 0xc5, - JBT_REG_SLEEP_IN_LCCNT_G = 0xc6, - - JBT_REG_GAMMA1_FINE_1 = 0xc7, - JBT_REG_GAMMA1_FINE_2 = 0xc8, - JBT_REG_GAMMA1_INCLINATION = 0xc9, - JBT_REG_GAMMA1_BLUE_OFFSET = 0xca, - - JBT_REG_BLANK_CONTROL = 0xcf, - JBT_REG_BLANK_TH_TV = 0xd0, - JBT_REG_CKV_ON_OFF = 0xd1, - JBT_REG_CKV_1_2 = 0xd2, - JBT_REG_OEV_TIMING = 0xd3, - JBT_REG_ASW_TIMING_1 = 0xd4, - JBT_REG_ASW_TIMING_2 = 0xd5, - - JBT_REG_HCLOCK_VGA = 0xec, - JBT_REG_HCLOCK_QVGA = 0xed, -}; - -#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev) - -static int td028ttec1_panel_connect(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - int r; - - if (omapdss_device_is_connected(dssdev)) - return 0; - - r = in->ops.dpi->connect(in, dssdev); - if (r) - return r; - - return 0; -} - -static void td028ttec1_panel_disconnect(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - if (!omapdss_device_is_connected(dssdev)) - return; - - in->ops.dpi->disconnect(in, dssdev); -} - -static int td028ttec1_panel_enable(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - int r; - - if (!omapdss_device_is_connected(dssdev)) - return -ENODEV; - - if (omapdss_device_is_enabled(dssdev)) - return 0; - - in->ops.dpi->set_timings(in, &ddata->vm); - - r = in->ops.dpi->enable(in); - if (r) - return r; - - dev_dbg(dssdev->dev, "td028ttec1_panel_enable() - state %d\n", - dssdev->state); - - /* three times command zero */ - r |= jbt_ret_write_0(ddata, 0x00); - usleep_range(1000, 2000); - r |= jbt_ret_write_0(ddata, 0x00); - usleep_range(1000, 2000); - r |= jbt_ret_write_0(ddata, 0x00); - usleep_range(1000, 2000); - - if (r) { - dev_warn(dssdev->dev, "transfer error\n"); - goto transfer_err; - } - - /* deep standby out */ - r |= jbt_reg_write_1(ddata, JBT_REG_POWER_ON_OFF, 0x17); - - /* RGB I/F on, RAM write off, QVGA through, SIGCON enable */ - r |= jbt_reg_write_1(ddata, JBT_REG_DISPLAY_MODE, 0x80); - - /* Quad mode off */ - r |= jbt_reg_write_1(ddata, JBT_REG_QUAD_RATE, 0x00); - - /* AVDD on, XVDD on */ - r |= jbt_reg_write_1(ddata, JBT_REG_POWER_ON_OFF, 0x16); - - /* Output control */ - r |= jbt_reg_write_2(ddata, JBT_REG_OUTPUT_CONTROL, 0xfff9); - - /* Sleep mode off */ - r |= jbt_ret_write_0(ddata, JBT_REG_SLEEP_OUT); - - /* at this point we have like 50% grey */ - - /* initialize register set */ - r |= jbt_reg_write_1(ddata, JBT_REG_DISPLAY_MODE1, 0x01); - r |= jbt_reg_write_1(ddata, JBT_REG_DISPLAY_MODE2, 0x00); - r |= jbt_reg_write_1(ddata, JBT_REG_RGB_FORMAT, 0x60); - r |= jbt_reg_write_1(ddata, JBT_REG_DRIVE_SYSTEM, 0x10); - r |= jbt_reg_write_1(ddata, JBT_REG_BOOSTER_OP, 0x56); - r |= jbt_reg_write_1(ddata, JBT_REG_BOOSTER_MODE, 0x33); - r |= jbt_reg_write_1(ddata, JBT_REG_BOOSTER_FREQ, 0x11); - r |= jbt_reg_write_1(ddata, JBT_REG_BOOSTER_FREQ, 0x11); - r |= jbt_reg_write_1(ddata, JBT_REG_OPAMP_SYSCLK, 0x02); - r |= jbt_reg_write_1(ddata, JBT_REG_VSC_VOLTAGE, 0x2b); - r |= jbt_reg_write_1(ddata, JBT_REG_VCOM_VOLTAGE, 0x40); - r |= jbt_reg_write_1(ddata, JBT_REG_EXT_DISPL, 0x03); - r |= jbt_reg_write_1(ddata, JBT_REG_DCCLK_DCEV, 0x04); - /* - * default of 0x02 in JBT_REG_ASW_SLEW responsible for 72Hz requirement - * to avoid red / blue flicker - */ - r |= jbt_reg_write_1(ddata, JBT_REG_ASW_SLEW, 0x04); - r |= jbt_reg_write_1(ddata, JBT_REG_DUMMY_DISPLAY, 0x00); - - r |= jbt_reg_write_1(ddata, JBT_REG_SLEEP_OUT_FR_A, 0x11); - r |= jbt_reg_write_1(ddata, JBT_REG_SLEEP_OUT_FR_B, 0x11); - r |= jbt_reg_write_1(ddata, JBT_REG_SLEEP_OUT_FR_C, 0x11); - r |= jbt_reg_write_2(ddata, JBT_REG_SLEEP_IN_LCCNT_D, 0x2040); - r |= jbt_reg_write_2(ddata, JBT_REG_SLEEP_IN_LCCNT_E, 0x60c0); - r |= jbt_reg_write_2(ddata, JBT_REG_SLEEP_IN_LCCNT_F, 0x1020); - r |= jbt_reg_write_2(ddata, JBT_REG_SLEEP_IN_LCCNT_G, 0x60c0); - - r |= jbt_reg_write_2(ddata, JBT_REG_GAMMA1_FINE_1, 0x5533); - r |= jbt_reg_write_1(ddata, JBT_REG_GAMMA1_FINE_2, 0x00); - r |= jbt_reg_write_1(ddata, JBT_REG_GAMMA1_INCLINATION, 0x00); - r |= jbt_reg_write_1(ddata, JBT_REG_GAMMA1_BLUE_OFFSET, 0x00); - - r |= jbt_reg_write_2(ddata, JBT_REG_HCLOCK_VGA, 0x1f0); - r |= jbt_reg_write_1(ddata, JBT_REG_BLANK_CONTROL, 0x02); - r |= jbt_reg_write_2(ddata, JBT_REG_BLANK_TH_TV, 0x0804); - - r |= jbt_reg_write_1(ddata, JBT_REG_CKV_ON_OFF, 0x01); - r |= jbt_reg_write_2(ddata, JBT_REG_CKV_1_2, 0x0000); - - r |= jbt_reg_write_2(ddata, JBT_REG_OEV_TIMING, 0x0d0e); - r |= jbt_reg_write_2(ddata, JBT_REG_ASW_TIMING_1, 0x11a4); - r |= jbt_reg_write_1(ddata, JBT_REG_ASW_TIMING_2, 0x0e); - - r |= jbt_ret_write_0(ddata, JBT_REG_DISPLAY_ON); - - dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; - -transfer_err: - - return r ? -EIO : 0; -} - -static void td028ttec1_panel_disable(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - if (!omapdss_device_is_enabled(dssdev)) - return; - - dev_dbg(dssdev->dev, "td028ttec1_panel_disable()\n"); - - jbt_ret_write_0(ddata, JBT_REG_DISPLAY_OFF); - jbt_reg_write_2(ddata, JBT_REG_OUTPUT_CONTROL, 0x8002); - jbt_ret_write_0(ddata, JBT_REG_SLEEP_IN); - jbt_reg_write_1(ddata, JBT_REG_POWER_ON_OFF, 0x00); - - in->ops.dpi->disable(in); - - dssdev->state = OMAP_DSS_DISPLAY_DISABLED; -} - -static void td028ttec1_panel_set_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - ddata->vm = *vm; - dssdev->panel.vm = *vm; - - in->ops.dpi->set_timings(in, vm); -} - -static void td028ttec1_panel_get_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - - *vm = ddata->vm; -} - -static int td028ttec1_panel_check_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - return in->ops.dpi->check_timings(in, vm); -} - -static struct omap_dss_driver td028ttec1_ops = { - .connect = td028ttec1_panel_connect, - .disconnect = td028ttec1_panel_disconnect, - - .enable = td028ttec1_panel_enable, - .disable = td028ttec1_panel_disable, - - .set_timings = td028ttec1_panel_set_timings, - .get_timings = td028ttec1_panel_get_timings, - .check_timings = td028ttec1_panel_check_timings, -}; - -static int td028ttec1_probe_of(struct spi_device *spi) -{ - struct device_node *node = spi->dev.of_node; - struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); - struct omap_dss_device *in; - - in = omapdss_of_find_source_for_first_ep(node); - if (IS_ERR(in)) { - dev_err(&spi->dev, "failed to find video source\n"); - return PTR_ERR(in); - } - - ddata->in = in; - - return 0; -} - -static int td028ttec1_panel_probe(struct spi_device *spi) -{ - struct panel_drv_data *ddata; - struct omap_dss_device *dssdev; - int r; - - dev_dbg(&spi->dev, "%s\n", __func__); - - spi->bits_per_word = 9; - spi->mode = SPI_MODE_3; - - r = spi_setup(spi); - if (r < 0) { - dev_err(&spi->dev, "spi_setup failed: %d\n", r); - return r; - } - - ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL); - if (ddata == NULL) - return -ENOMEM; - - dev_set_drvdata(&spi->dev, ddata); - - ddata->spi_dev = spi; - - if (!spi->dev.of_node) - return -ENODEV; - - r = td028ttec1_probe_of(spi); - if (r) - return r; - - ddata->vm = td028ttec1_panel_vm; - - dssdev = &ddata->dssdev; - dssdev->dev = &spi->dev; - dssdev->driver = &td028ttec1_ops; - dssdev->type = OMAP_DISPLAY_TYPE_DPI; - dssdev->owner = THIS_MODULE; - dssdev->panel.vm = ddata->vm; - - r = omapdss_register_display(dssdev); - if (r) { - dev_err(&spi->dev, "Failed to register panel\n"); - goto err_reg; - } - - return 0; - -err_reg: - omap_dss_put_device(ddata->in); - return r; -} - -static int td028ttec1_panel_remove(struct spi_device *spi) -{ - struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); - struct omap_dss_device *dssdev = &ddata->dssdev; - struct omap_dss_device *in = ddata->in; - - dev_dbg(&ddata->spi_dev->dev, "%s\n", __func__); - - omapdss_unregister_display(dssdev); - - td028ttec1_panel_disable(dssdev); - td028ttec1_panel_disconnect(dssdev); - - omap_dss_put_device(in); - - return 0; -} - -static const struct of_device_id td028ttec1_of_match[] = { - { .compatible = "omapdss,toppoly,td028ttec1", }, - {}, -}; - -MODULE_DEVICE_TABLE(of, td028ttec1_of_match); - -static struct spi_driver td028ttec1_spi_driver = { - .probe = td028ttec1_panel_probe, - .remove = td028ttec1_panel_remove, - - .driver = { - .name = "panel-tpo-td028ttec1", - .of_match_table = td028ttec1_of_match, - .suppress_bind_attrs = true, - }, -}; - -module_spi_driver(td028ttec1_spi_driver); - -MODULE_ALIAS("spi:toppoly,td028ttec1"); -MODULE_AUTHOR("H. Nikolaus Schaller <hns@goldelico.com>"); -MODULE_DESCRIPTION("Toppoly TD028TTEC1 panel driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/omapdrm/displays/panel-tpo-td043mtea1.c b/drivers/gpu/drm/omapdrm/displays/panel-tpo-td043mtea1.c deleted file mode 100644 index 20c6d8fe215a..000000000000 --- a/drivers/gpu/drm/omapdrm/displays/panel-tpo-td043mtea1.c +++ /dev/null @@ -1,649 +0,0 @@ -/* - * TPO TD043MTEA1 Panel driver - * - * Author: Gražvydas Ignotas <notasas@gmail.com> - * Converted to new DSS device model: Tomi Valkeinen <tomi.valkeinen@ti.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/spi/spi.h> -#include <linux/regulator/consumer.h> -#include <linux/gpio/consumer.h> -#include <linux/err.h> -#include <linux/slab.h> -#include <linux/of_gpio.h> - -#include "../dss/omapdss.h" - -#define TPO_R02_MODE(x) ((x) & 7) -#define TPO_R02_MODE_800x480 7 -#define TPO_R02_NCLK_RISING BIT(3) -#define TPO_R02_HSYNC_HIGH BIT(4) -#define TPO_R02_VSYNC_HIGH BIT(5) - -#define TPO_R03_NSTANDBY BIT(0) -#define TPO_R03_EN_CP_CLK BIT(1) -#define TPO_R03_EN_VGL_PUMP BIT(2) -#define TPO_R03_EN_PWM BIT(3) -#define TPO_R03_DRIVING_CAP_100 BIT(4) -#define TPO_R03_EN_PRE_CHARGE BIT(6) -#define TPO_R03_SOFTWARE_CTL BIT(7) - -#define TPO_R04_NFLIP_H BIT(0) -#define TPO_R04_NFLIP_V BIT(1) -#define TPO_R04_CP_CLK_FREQ_1H BIT(2) -#define TPO_R04_VGL_FREQ_1H BIT(4) - -#define TPO_R03_VAL_NORMAL (TPO_R03_NSTANDBY | TPO_R03_EN_CP_CLK | \ - TPO_R03_EN_VGL_PUMP | TPO_R03_EN_PWM | \ - TPO_R03_DRIVING_CAP_100 | TPO_R03_EN_PRE_CHARGE | \ - TPO_R03_SOFTWARE_CTL) - -#define TPO_R03_VAL_STANDBY (TPO_R03_DRIVING_CAP_100 | \ - TPO_R03_EN_PRE_CHARGE | TPO_R03_SOFTWARE_CTL) - -static const u16 tpo_td043_def_gamma[12] = { - 105, 315, 381, 431, 490, 537, 579, 686, 780, 837, 880, 1023 -}; - -struct panel_drv_data { - struct omap_dss_device dssdev; - struct omap_dss_device *in; - - struct videomode vm; - - struct spi_device *spi; - struct regulator *vcc_reg; - int nreset_gpio; - u16 gamma[12]; - u32 mode; - u32 hmirror:1; - u32 vmirror:1; - u32 powered_on:1; - u32 spi_suspended:1; - u32 power_on_resume:1; -}; - -static const struct videomode tpo_td043_vm = { - .hactive = 800, - .vactive = 480, - - .pixelclock = 36000000, - - .hsync_len = 1, - .hfront_porch = 68, - .hback_porch = 214, - - .vsync_len = 1, - .vfront_porch = 39, - .vback_porch = 34, - - .flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW | - DISPLAY_FLAGS_DE_HIGH | DISPLAY_FLAGS_SYNC_POSEDGE | - DISPLAY_FLAGS_PIXDATA_NEGEDGE, - /* - * Note: According to the panel documentation: - * SYNC needs to be driven on the FALLING edge - */ -}; - -#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev) - -static int tpo_td043_write(struct spi_device *spi, u8 addr, u8 data) -{ - struct spi_message m; - struct spi_transfer xfer; - u16 w; - int r; - - spi_message_init(&m); - - memset(&xfer, 0, sizeof(xfer)); - - w = ((u16)addr << 10) | (1 << 8) | data; - xfer.tx_buf = &w; - xfer.bits_per_word = 16; - xfer.len = 2; - spi_message_add_tail(&xfer, &m); - - r = spi_sync(spi, &m); - if (r < 0) - dev_warn(&spi->dev, "failed to write to LCD reg (%d)\n", r); - return r; -} - -static void tpo_td043_write_gamma(struct spi_device *spi, u16 gamma[12]) -{ - u8 i, val; - - /* gamma bits [9:8] */ - for (val = i = 0; i < 4; i++) - val |= (gamma[i] & 0x300) >> ((i + 1) * 2); - tpo_td043_write(spi, 0x11, val); - - for (val = i = 0; i < 4; i++) - val |= (gamma[i+4] & 0x300) >> ((i + 1) * 2); - tpo_td043_write(spi, 0x12, val); - - for (val = i = 0; i < 4; i++) - val |= (gamma[i+8] & 0x300) >> ((i + 1) * 2); - tpo_td043_write(spi, 0x13, val); - - /* gamma bits [7:0] */ - for (val = i = 0; i < 12; i++) - tpo_td043_write(spi, 0x14 + i, gamma[i] & 0xff); -} - -static int tpo_td043_write_mirror(struct spi_device *spi, bool h, bool v) -{ - u8 reg4 = TPO_R04_NFLIP_H | TPO_R04_NFLIP_V | - TPO_R04_CP_CLK_FREQ_1H | TPO_R04_VGL_FREQ_1H; - if (h) - reg4 &= ~TPO_R04_NFLIP_H; - if (v) - reg4 &= ~TPO_R04_NFLIP_V; - - return tpo_td043_write(spi, 4, reg4); -} - -static int tpo_td043_set_hmirror(struct omap_dss_device *dssdev, bool enable) -{ - struct panel_drv_data *ddata = dev_get_drvdata(dssdev->dev); - - ddata->hmirror = enable; - return tpo_td043_write_mirror(ddata->spi, ddata->hmirror, - ddata->vmirror); -} - -static bool tpo_td043_get_hmirror(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = dev_get_drvdata(dssdev->dev); - - return ddata->hmirror; -} - -static ssize_t tpo_td043_vmirror_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct panel_drv_data *ddata = dev_get_drvdata(dev); - - return snprintf(buf, PAGE_SIZE, "%d\n", ddata->vmirror); -} - -static ssize_t tpo_td043_vmirror_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct panel_drv_data *ddata = dev_get_drvdata(dev); - int val; - int ret; - - ret = kstrtoint(buf, 0, &val); - if (ret < 0) - return ret; - - val = !!val; - - ret = tpo_td043_write_mirror(ddata->spi, ddata->hmirror, val); - if (ret < 0) - return ret; - - ddata->vmirror = val; - - return count; -} - -static ssize_t tpo_td043_mode_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct panel_drv_data *ddata = dev_get_drvdata(dev); - - return snprintf(buf, PAGE_SIZE, "%d\n", ddata->mode); -} - -static ssize_t tpo_td043_mode_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct panel_drv_data *ddata = dev_get_drvdata(dev); - long val; - int ret; - - ret = kstrtol(buf, 0, &val); - if (ret != 0 || val & ~7) - return -EINVAL; - - ddata->mode = val; - - val |= TPO_R02_NCLK_RISING; - tpo_td043_write(ddata->spi, 2, val); - - return count; -} - -static ssize_t tpo_td043_gamma_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct panel_drv_data *ddata = dev_get_drvdata(dev); - ssize_t len = 0; - int ret; - int i; - - for (i = 0; i < ARRAY_SIZE(ddata->gamma); i++) { - ret = snprintf(buf + len, PAGE_SIZE - len, "%u ", - ddata->gamma[i]); - if (ret < 0) - return ret; - len += ret; - } - buf[len - 1] = '\n'; - - return len; -} - -static ssize_t tpo_td043_gamma_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct panel_drv_data *ddata = dev_get_drvdata(dev); - unsigned int g[12]; - int ret; - int i; - - ret = sscanf(buf, "%u %u %u %u %u %u %u %u %u %u %u %u", - &g[0], &g[1], &g[2], &g[3], &g[4], &g[5], - &g[6], &g[7], &g[8], &g[9], &g[10], &g[11]); - - if (ret != 12) - return -EINVAL; - - for (i = 0; i < 12; i++) - ddata->gamma[i] = g[i]; - - tpo_td043_write_gamma(ddata->spi, ddata->gamma); - - return count; -} - -static DEVICE_ATTR(vmirror, S_IRUGO | S_IWUSR, - tpo_td043_vmirror_show, tpo_td043_vmirror_store); -static DEVICE_ATTR(mode, S_IRUGO | S_IWUSR, - tpo_td043_mode_show, tpo_td043_mode_store); -static DEVICE_ATTR(gamma, S_IRUGO | S_IWUSR, - tpo_td043_gamma_show, tpo_td043_gamma_store); - -static struct attribute *tpo_td043_attrs[] = { - &dev_attr_vmirror.attr, - &dev_attr_mode.attr, - &dev_attr_gamma.attr, - NULL, -}; - -static struct attribute_group tpo_td043_attr_group = { - .attrs = tpo_td043_attrs, -}; - -static int tpo_td043_power_on(struct panel_drv_data *ddata) -{ - int r; - - if (ddata->powered_on) - return 0; - - r = regulator_enable(ddata->vcc_reg); - if (r != 0) - return r; - - /* wait for panel to stabilize */ - msleep(160); - - if (gpio_is_valid(ddata->nreset_gpio)) - gpio_set_value(ddata->nreset_gpio, 1); - - tpo_td043_write(ddata->spi, 2, - TPO_R02_MODE(ddata->mode) | TPO_R02_NCLK_RISING); - tpo_td043_write(ddata->spi, 3, TPO_R03_VAL_NORMAL); - tpo_td043_write(ddata->spi, 0x20, 0xf0); - tpo_td043_write(ddata->spi, 0x21, 0xf0); - tpo_td043_write_mirror(ddata->spi, ddata->hmirror, - ddata->vmirror); - tpo_td043_write_gamma(ddata->spi, ddata->gamma); - - ddata->powered_on = 1; - return 0; -} - -static void tpo_td043_power_off(struct panel_drv_data *ddata) -{ - if (!ddata->powered_on) - return; - - tpo_td043_write(ddata->spi, 3, - TPO_R03_VAL_STANDBY | TPO_R03_EN_PWM); - - if (gpio_is_valid(ddata->nreset_gpio)) - gpio_set_value(ddata->nreset_gpio, 0); - - /* wait for at least 2 vsyncs before cutting off power */ - msleep(50); - - tpo_td043_write(ddata->spi, 3, TPO_R03_VAL_STANDBY); - - regulator_disable(ddata->vcc_reg); - - ddata->powered_on = 0; -} - -static int tpo_td043_connect(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - int r; - - if (omapdss_device_is_connected(dssdev)) - return 0; - - r = in->ops.dpi->connect(in, dssdev); - if (r) - return r; - - return 0; -} - -static void tpo_td043_disconnect(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - if (!omapdss_device_is_connected(dssdev)) - return; - - in->ops.dpi->disconnect(in, dssdev); -} - -static int tpo_td043_enable(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - int r; - - if (!omapdss_device_is_connected(dssdev)) - return -ENODEV; - - if (omapdss_device_is_enabled(dssdev)) - return 0; - - in->ops.dpi->set_timings(in, &ddata->vm); - - r = in->ops.dpi->enable(in); - if (r) - return r; - - /* - * If we are resuming from system suspend, SPI clocks might not be - * enabled yet, so we'll program the LCD from SPI PM resume callback. - */ - if (!ddata->spi_suspended) { - r = tpo_td043_power_on(ddata); - if (r) { - in->ops.dpi->disable(in); - return r; - } - } - - dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; - - return 0; -} - -static void tpo_td043_disable(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - if (!omapdss_device_is_enabled(dssdev)) - return; - - in->ops.dpi->disable(in); - - if (!ddata->spi_suspended) - tpo_td043_power_off(ddata); - - dssdev->state = OMAP_DSS_DISPLAY_DISABLED; -} - -static void tpo_td043_set_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - ddata->vm = *vm; - dssdev->panel.vm = *vm; - - in->ops.dpi->set_timings(in, vm); -} - -static void tpo_td043_get_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - - *vm = ddata->vm; -} - -static int tpo_td043_check_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - return in->ops.dpi->check_timings(in, vm); -} - -static struct omap_dss_driver tpo_td043_ops = { - .connect = tpo_td043_connect, - .disconnect = tpo_td043_disconnect, - - .enable = tpo_td043_enable, - .disable = tpo_td043_disable, - - .set_timings = tpo_td043_set_timings, - .get_timings = tpo_td043_get_timings, - .check_timings = tpo_td043_check_timings, - - .set_mirror = tpo_td043_set_hmirror, - .get_mirror = tpo_td043_get_hmirror, -}; - -static int tpo_td043_probe_of(struct spi_device *spi) -{ - struct device_node *node = spi->dev.of_node; - struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); - struct omap_dss_device *in; - int gpio; - - gpio = of_get_named_gpio(node, "reset-gpios", 0); - if (!gpio_is_valid(gpio)) { - dev_err(&spi->dev, "failed to parse enable gpio\n"); - return gpio; - } - ddata->nreset_gpio = gpio; - - in = omapdss_of_find_source_for_first_ep(node); - if (IS_ERR(in)) { - dev_err(&spi->dev, "failed to find video source\n"); - return PTR_ERR(in); - } - - ddata->in = in; - - return 0; -} - -static int tpo_td043_probe(struct spi_device *spi) -{ - struct panel_drv_data *ddata; - struct omap_dss_device *dssdev; - int r; - - dev_dbg(&spi->dev, "%s\n", __func__); - - spi->bits_per_word = 16; - spi->mode = SPI_MODE_0; - - r = spi_setup(spi); - if (r < 0) { - dev_err(&spi->dev, "spi_setup failed: %d\n", r); - return r; - } - - ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL); - if (ddata == NULL) - return -ENOMEM; - - dev_set_drvdata(&spi->dev, ddata); - - ddata->spi = spi; - - if (!spi->dev.of_node) - return -ENODEV; - - r = tpo_td043_probe_of(spi); - if (r) - return r; - - ddata->mode = TPO_R02_MODE_800x480; - memcpy(ddata->gamma, tpo_td043_def_gamma, sizeof(ddata->gamma)); - - ddata->vcc_reg = devm_regulator_get(&spi->dev, "vcc"); - if (IS_ERR(ddata->vcc_reg)) { - dev_err(&spi->dev, "failed to get LCD VCC regulator\n"); - r = PTR_ERR(ddata->vcc_reg); - goto err_regulator; - } - - if (gpio_is_valid(ddata->nreset_gpio)) { - r = devm_gpio_request_one(&spi->dev, - ddata->nreset_gpio, GPIOF_OUT_INIT_LOW, - "lcd reset"); - if (r < 0) { - dev_err(&spi->dev, "couldn't request reset GPIO\n"); - goto err_gpio_req; - } - } - - r = sysfs_create_group(&spi->dev.kobj, &tpo_td043_attr_group); - if (r) { - dev_err(&spi->dev, "failed to create sysfs files\n"); - goto err_sysfs; - } - - ddata->vm = tpo_td043_vm; - - dssdev = &ddata->dssdev; - dssdev->dev = &spi->dev; - dssdev->driver = &tpo_td043_ops; - dssdev->type = OMAP_DISPLAY_TYPE_DPI; - dssdev->owner = THIS_MODULE; - dssdev->panel.vm = ddata->vm; - - r = omapdss_register_display(dssdev); - if (r) { - dev_err(&spi->dev, "Failed to register panel\n"); - goto err_reg; - } - - return 0; - -err_reg: - sysfs_remove_group(&spi->dev.kobj, &tpo_td043_attr_group); -err_sysfs: -err_gpio_req: -err_regulator: - omap_dss_put_device(ddata->in); - return r; -} - -static int tpo_td043_remove(struct spi_device *spi) -{ - struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); - struct omap_dss_device *dssdev = &ddata->dssdev; - struct omap_dss_device *in = ddata->in; - - dev_dbg(&ddata->spi->dev, "%s\n", __func__); - - omapdss_unregister_display(dssdev); - - tpo_td043_disable(dssdev); - tpo_td043_disconnect(dssdev); - - omap_dss_put_device(in); - - sysfs_remove_group(&spi->dev.kobj, &tpo_td043_attr_group); - - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int tpo_td043_spi_suspend(struct device *dev) -{ - struct panel_drv_data *ddata = dev_get_drvdata(dev); - - dev_dbg(dev, "tpo_td043_spi_suspend, tpo %p\n", ddata); - - ddata->power_on_resume = ddata->powered_on; - tpo_td043_power_off(ddata); - ddata->spi_suspended = 1; - - return 0; -} - -static int tpo_td043_spi_resume(struct device *dev) -{ - struct panel_drv_data *ddata = dev_get_drvdata(dev); - int ret; - - dev_dbg(dev, "tpo_td043_spi_resume\n"); - - if (ddata->power_on_resume) { - ret = tpo_td043_power_on(ddata); - if (ret) - return ret; - } - ddata->spi_suspended = 0; - - return 0; -} -#endif - -static SIMPLE_DEV_PM_OPS(tpo_td043_spi_pm, - tpo_td043_spi_suspend, tpo_td043_spi_resume); - -static const struct of_device_id tpo_td043_of_match[] = { - { .compatible = "omapdss,tpo,td043mtea1", }, - {}, -}; - -MODULE_DEVICE_TABLE(of, tpo_td043_of_match); - -static struct spi_driver tpo_td043_spi_driver = { - .driver = { - .name = "panel-tpo-td043mtea1", - .pm = &tpo_td043_spi_pm, - .of_match_table = tpo_td043_of_match, - .suppress_bind_attrs = true, - }, - .probe = tpo_td043_probe, - .remove = tpo_td043_remove, -}; - -module_spi_driver(tpo_td043_spi_driver); - -MODULE_ALIAS("spi:tpo,td043mtea1"); -MODULE_AUTHOR("Gražvydas Ignotas <notasas@gmail.com>"); -MODULE_DESCRIPTION("TPO TD043MTEA1 LCD Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/omapdrm/dss/Kconfig b/drivers/gpu/drm/omapdrm/dss/Kconfig deleted file mode 100644 index 8b87d5cf45fc..000000000000 --- a/drivers/gpu/drm/omapdrm/dss/Kconfig +++ /dev/null @@ -1,126 +0,0 @@ -config OMAP2_DSS_INIT - bool - -config OMAP_DSS_BASE - tristate - -menuconfig OMAP2_DSS - tristate "OMAP2+ Display Subsystem support" - select OMAP_DSS_BASE - select VIDEOMODE_HELPERS - select OMAP2_DSS_INIT - select HDMI - help - OMAP2+ Display Subsystem support. - -if OMAP2_DSS - -config OMAP2_DSS_DEBUG - bool "Debug support" - default n - help - This enables printing of debug messages. Alternatively, debug messages - can also be enabled by setting CONFIG_DYNAMIC_DEBUG and then setting - appropriate flags in <debugfs>/dynamic_debug/control. - -config OMAP2_DSS_DEBUGFS - bool "Debugfs filesystem support" - depends on DEBUG_FS - default n - help - This enables debugfs for OMAPDSS at <debugfs>/omapdss. This enables - querying about clock configuration and register configuration of dss, - dispc, dsi, hdmi and rfbi. - -config OMAP2_DSS_COLLECT_IRQ_STATS - bool "Collect DSS IRQ statistics" - depends on OMAP2_DSS_DEBUGFS - default n - help - Collect DSS IRQ statistics, printable via debugfs. - - The statistics can be found from - <debugfs>/omapdss/dispc_irq for DISPC interrupts, and - <debugfs>/omapdss/dsi_irq for DSI interrupts. - -config OMAP2_DSS_DPI - bool "DPI support" - default y - help - DPI Interface. This is the Parallel Display Interface. - -config OMAP2_DSS_VENC - bool "VENC support" - default y - help - OMAP Video Encoder support for S-Video and composite TV-out. - -config OMAP2_DSS_HDMI_COMMON - bool - -config OMAP4_DSS_HDMI - bool "HDMI support for OMAP4" - default y - select OMAP2_DSS_HDMI_COMMON - help - HDMI support for OMAP4 based SoCs. - -config OMAP5_DSS_HDMI - bool "HDMI support for OMAP5" - default n - select OMAP2_DSS_HDMI_COMMON - help - HDMI Interface for OMAP5 and similar cores. This adds the High - Definition Multimedia Interface. See http://www.hdmi.org/ for HDMI - specification. - -config OMAP2_DSS_SDI - bool "SDI support" - default n - help - SDI (Serial Display Interface) support. - - SDI is a high speed one-way display serial bus between the host - processor and a display. - -config OMAP2_DSS_DSI - bool "DSI support" - default n - help - MIPI DSI (Display Serial Interface) support. - - DSI is a high speed half-duplex serial interface between the host - processor and a peripheral, such as a display or a framebuffer chip. - - See http://www.mipi.org/ for DSI specifications. - -config OMAP2_DSS_MIN_FCK_PER_PCK - int "Minimum FCK/PCK ratio (for scaling)" - range 0 32 - default 0 - help - This can be used to adjust the minimum FCK/PCK ratio. - - With this you can make sure that DISPC FCK is at least - n x PCK. Video plane scaling requires higher FCK than - normally. - - If this is set to 0, there's no extra constraint on the - DISPC FCK. However, the FCK will at minimum be - 2xPCK (if active matrix) or 3xPCK (if passive matrix). - - Max FCK is 173MHz, so this doesn't work if your PCK - is very high. - -config OMAP2_DSS_SLEEP_AFTER_VENC_RESET - bool "Sleep 20ms after VENC reset" - default y - help - There is a 20ms sleep after VENC reset which seemed to fix the - reset. The reason for the bug is unclear, and it's also unclear - on what platforms this happens. - - This option enables the sleep, and is enabled by default. You can - disable the sleep if it doesn't cause problems on your platform. - -endif diff --git a/drivers/gpu/drm/omapdrm/dss/Makefile b/drivers/gpu/drm/omapdrm/dss/Makefile deleted file mode 100644 index 688195e448c5..000000000000 --- a/drivers/gpu/drm/omapdrm/dss/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -obj-$(CONFIG_OMAP2_DSS_INIT) += omapdss-boot-init.o - -obj-$(CONFIG_OMAP_DSS_BASE) += omapdss-base.o -omapdss-base-y := base.o display.o dss-of.o output.o - -obj-$(CONFIG_OMAP2_DSS) += omapdss.o -# Core DSS files -omapdss-y := core.o dss.o dss_features.o dispc.o dispc_coefs.o \ - pll.o video-pll.o -omapdss-$(CONFIG_OMAP2_DSS_DPI) += dpi.o -omapdss-$(CONFIG_OMAP2_DSS_VENC) += venc.o -omapdss-$(CONFIG_OMAP2_DSS_SDI) += sdi.o -omapdss-$(CONFIG_OMAP2_DSS_DSI) += dsi.o -omapdss-$(CONFIG_OMAP2_DSS_HDMI_COMMON) += hdmi_common.o hdmi_wp.o hdmi_pll.o \ - hdmi_phy.o -omapdss-$(CONFIG_OMAP4_DSS_HDMI) += hdmi4.o hdmi4_core.o -omapdss-$(CONFIG_OMAP5_DSS_HDMI) += hdmi5.o hdmi5_core.o -ccflags-$(CONFIG_OMAP2_DSS_DEBUG) += -DDEBUG diff --git a/drivers/gpu/drm/omapdrm/dss/base.c b/drivers/gpu/drm/omapdrm/dss/base.c index 13e91faaf7a6..a4ac113e1690 100644 --- a/drivers/gpu/drm/omapdrm/dss/base.c +++ b/drivers/gpu/drm/omapdrm/dss/base.c @@ -1,44 +1,189 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP Display Subsystem Base + * + * Copyright (C) 2015-2017 Texas Instruments Incorporated - https://www.ti.com/ + */ + #include <linux/kernel.h> +#include <linux/list.h> #include <linux/module.h> +#include <linux/mutex.h> #include <linux/of.h> #include <linux/of_graph.h> -#include <linux/list.h> +#include <linux/platform_device.h> + +#include "dss.h" #include "omapdss.h" -static bool dss_initialized; -static const struct dispc_ops *ops; +struct dispc_device *dispc_get_dispc(struct dss_device *dss) +{ + return dss->dispc; +} -static struct list_head omapdss_comp_list; +/* ----------------------------------------------------------------------------- + * OMAP DSS Devices Handling + */ -struct omapdss_comp_node { - struct list_head list; - struct device_node *node; - bool dss_core_component; -}; +static LIST_HEAD(omapdss_devices_list); +static DEFINE_MUTEX(omapdss_devices_lock); + +void omapdss_device_register(struct omap_dss_device *dssdev) +{ + mutex_lock(&omapdss_devices_lock); + list_add_tail(&dssdev->list, &omapdss_devices_list); + mutex_unlock(&omapdss_devices_lock); +} + +void omapdss_device_unregister(struct omap_dss_device *dssdev) +{ + mutex_lock(&omapdss_devices_lock); + list_del(&dssdev->list); + mutex_unlock(&omapdss_devices_lock); +} + +static bool omapdss_device_is_registered(struct device_node *node) +{ + struct omap_dss_device *dssdev; + bool found = false; + + mutex_lock(&omapdss_devices_lock); + + list_for_each_entry(dssdev, &omapdss_devices_list, list) { + if (dssdev->dev->of_node == node) { + found = true; + break; + } + } + + mutex_unlock(&omapdss_devices_lock); + return found; +} + +struct omap_dss_device *omapdss_device_get(struct omap_dss_device *dssdev) +{ + if (get_device(dssdev->dev) == NULL) + return NULL; + + return dssdev; +} + +void omapdss_device_put(struct omap_dss_device *dssdev) +{ + put_device(dssdev->dev); +} + +struct omap_dss_device *omapdss_find_device_by_node(struct device_node *node) +{ + struct omap_dss_device *dssdev; + + list_for_each_entry(dssdev, &omapdss_devices_list, list) { + if (dssdev->dev->of_node == node) + return omapdss_device_get(dssdev); + } + + return NULL; +} -void omapdss_set_is_initialized(bool set) +/* + * Search for the next output device starting at @from. Release the reference to + * the @from device, and acquire a reference to the returned device if found. + */ +struct omap_dss_device *omapdss_device_next_output(struct omap_dss_device *from) { - dss_initialized = set; + struct omap_dss_device *dssdev; + struct list_head *list; + + mutex_lock(&omapdss_devices_lock); + + if (list_empty(&omapdss_devices_list)) { + dssdev = NULL; + goto done; + } + + /* + * Start from the from entry if given or from omapdss_devices_list + * otherwise. + */ + list = from ? &from->list : &omapdss_devices_list; + + list_for_each_entry(dssdev, list, list) { + /* + * Stop if we reach the omapdss_devices_list, that's the end of + * the list. + */ + if (&dssdev->list == &omapdss_devices_list) { + dssdev = NULL; + goto done; + } + + if (dssdev->id && dssdev->bridge) + goto done; + } + + dssdev = NULL; + +done: + if (from) + omapdss_device_put(from); + if (dssdev) + omapdss_device_get(dssdev); + + mutex_unlock(&omapdss_devices_lock); + return dssdev; } -EXPORT_SYMBOL(omapdss_set_is_initialized); -bool omapdss_is_initialized(void) +static bool omapdss_device_is_connected(struct omap_dss_device *dssdev) { - return dss_initialized; + return dssdev->dss; } -EXPORT_SYMBOL(omapdss_is_initialized); -void dispc_set_ops(const struct dispc_ops *o) +int omapdss_device_connect(struct dss_device *dss, + struct omap_dss_device *dst) { - ops = o; + dev_dbg(&dss->pdev->dev, "connect(%s)\n", + dst ? dev_name(dst->dev) : "NULL"); + + if (!dst) + return -EINVAL; + + if (omapdss_device_is_connected(dst)) + return -EBUSY; + + dst->dss = dss; + + return 0; } -EXPORT_SYMBOL(dispc_set_ops); -const struct dispc_ops *dispc_get_ops(void) +void omapdss_device_disconnect(struct dss_device *dss, + struct omap_dss_device *dst) { - return ops; + dev_dbg(&dss->pdev->dev, "disconnect(%s)\n", + dst ? dev_name(dst->dev) : "NULL"); + + if (WARN_ON(!dst)) + return; + + if (!dst->id && !omapdss_device_is_connected(dst)) { + WARN_ON(1); + return; + } + + dst->dss = NULL; } -EXPORT_SYMBOL(dispc_get_ops); + +/* ----------------------------------------------------------------------------- + * Components Handling + */ + +static struct list_head omapdss_comp_list; + +struct omapdss_comp_node { + struct list_head list; + struct device_node *node; + bool dss_core_component; + const char *compat; +}; static bool omapdss_list_contains(const struct device_node *node) { @@ -55,13 +200,20 @@ static bool omapdss_list_contains(const struct device_node *node) static void omapdss_walk_device(struct device *dev, struct device_node *node, bool dss_core) { + struct omapdss_comp_node *comp; struct device_node *n; - struct omapdss_comp_node *comp = devm_kzalloc(dev, sizeof(*comp), - GFP_KERNEL); + const char *compat; + int ret; + + ret = of_property_read_string(node, "compatible", &compat); + if (ret < 0) + return; + comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL); if (comp) { comp->node = node; comp->dss_core_component = dss_core; + comp->compat = compat; list_add(&comp->list, &omapdss_comp_list); } @@ -77,8 +229,7 @@ static void omapdss_walk_device(struct device *dev, struct device_node *node, of_node_put(n); - n = NULL; - while ((n = of_graph_get_next_endpoint(node, n)) != NULL) { + for_each_endpoint_of_node(node, n) { struct device_node *pn = of_graph_get_remote_port_parent(n); if (!pn) @@ -101,22 +252,17 @@ void omapdss_gather_components(struct device *dev) omapdss_walk_device(dev, dev->of_node, true); - for_each_available_child_of_node(dev->of_node, child) { - if (!of_find_property(child, "compatible", NULL)) - continue; - + for_each_available_child_of_node(dev->of_node, child) omapdss_walk_device(dev, child, true); - } } -EXPORT_SYMBOL(omapdss_gather_components); static bool omapdss_component_is_loaded(struct omapdss_comp_node *comp) { if (comp->dss_core_component) return true; - if (omapdss_component_is_display(comp->node)) + if (!strstarts(comp->compat, "omapdss,")) return true; - if (omapdss_component_is_output(comp->node)) + if (omapdss_device_is_registered(comp->node)) return true; return false; @@ -133,8 +279,3 @@ bool omapdss_stack_is_ready(void) return true; } -EXPORT_SYMBOL(omapdss_stack_is_ready); - -MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); -MODULE_DESCRIPTION("OMAP Display Subsystem Base"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/omapdrm/dss/core.c b/drivers/gpu/drm/omapdrm/dss/core.c deleted file mode 100644 index bdce4bfdf6e0..000000000000 --- a/drivers/gpu/drm/omapdrm/dss/core.c +++ /dev/null @@ -1,283 +0,0 @@ -/* - * linux/drivers/video/omap2/dss/core.c - * - * Copyright (C) 2009 Nokia Corporation - * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> - * - * Some code and ideas taken from drivers/video/omap/ driver - * by Imre Deak. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#define DSS_SUBSYS_NAME "CORE" - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/clk.h> -#include <linux/err.h> -#include <linux/platform_device.h> -#include <linux/seq_file.h> -#include <linux/debugfs.h> -#include <linux/io.h> -#include <linux/device.h> -#include <linux/regulator/consumer.h> -#include <linux/suspend.h> -#include <linux/slab.h> - -#include "omapdss.h" -#include "dss.h" -#include "dss_features.h" - -static struct { - struct platform_device *pdev; -} core; - -enum omapdss_version omapdss_get_version(void) -{ - struct omap_dss_board_info *pdata = core.pdev->dev.platform_data; - return pdata->version; -} -EXPORT_SYMBOL(omapdss_get_version); - -int dss_dsi_enable_pads(int dsi_id, unsigned lane_mask) -{ - struct omap_dss_board_info *board_data = core.pdev->dev.platform_data; - - if (!board_data->dsi_enable_pads) - return -ENOENT; - - return board_data->dsi_enable_pads(dsi_id, lane_mask); -} - -void dss_dsi_disable_pads(int dsi_id, unsigned lane_mask) -{ - struct omap_dss_board_info *board_data = core.pdev->dev.platform_data; - - if (!board_data->dsi_disable_pads) - return; - - return board_data->dsi_disable_pads(dsi_id, lane_mask); -} - -int dss_set_min_bus_tput(struct device *dev, unsigned long tput) -{ - struct omap_dss_board_info *pdata = core.pdev->dev.platform_data; - - if (pdata->set_min_bus_tput) - return pdata->set_min_bus_tput(dev, tput); - else - return 0; -} - -#if defined(CONFIG_OMAP2_DSS_DEBUGFS) -static int dss_debug_show(struct seq_file *s, void *unused) -{ - void (*func)(struct seq_file *) = s->private; - func(s); - return 0; -} - -static int dss_debug_open(struct inode *inode, struct file *file) -{ - return single_open(file, dss_debug_show, inode->i_private); -} - -static const struct file_operations dss_debug_fops = { - .open = dss_debug_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static struct dentry *dss_debugfs_dir; - -static int dss_initialize_debugfs(void) -{ - dss_debugfs_dir = debugfs_create_dir("omapdss", NULL); - if (IS_ERR(dss_debugfs_dir)) { - int err = PTR_ERR(dss_debugfs_dir); - dss_debugfs_dir = NULL; - return err; - } - - debugfs_create_file("clk", S_IRUGO, dss_debugfs_dir, - &dss_debug_dump_clocks, &dss_debug_fops); - - return 0; -} - -static void dss_uninitialize_debugfs(void) -{ - if (dss_debugfs_dir) - debugfs_remove_recursive(dss_debugfs_dir); -} - -int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *)) -{ - struct dentry *d; - - d = debugfs_create_file(name, S_IRUGO, dss_debugfs_dir, - write, &dss_debug_fops); - - return PTR_ERR_OR_ZERO(d); -} -#else /* CONFIG_OMAP2_DSS_DEBUGFS */ -static inline int dss_initialize_debugfs(void) -{ - return 0; -} -static inline void dss_uninitialize_debugfs(void) -{ -} -int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *)) -{ - return 0; -} -#endif /* CONFIG_OMAP2_DSS_DEBUGFS */ - -/* PLATFORM DEVICE */ - -static void dss_disable_all_devices(void) -{ - struct omap_dss_device *dssdev = NULL; - - for_each_dss_dev(dssdev) { - if (!dssdev->driver) - continue; - - if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) - dssdev->driver->disable(dssdev); - } -} - -static int __init omap_dss_probe(struct platform_device *pdev) -{ - int r; - - core.pdev = pdev; - - dss_features_init(omapdss_get_version()); - - r = dss_initialize_debugfs(); - if (r) - goto err_debugfs; - - return 0; - -err_debugfs: - - return r; -} - -static int omap_dss_remove(struct platform_device *pdev) -{ - dss_uninitialize_debugfs(); - - return 0; -} - -static void omap_dss_shutdown(struct platform_device *pdev) -{ - DSSDBG("shutdown\n"); - dss_disable_all_devices(); -} - -static struct platform_driver omap_dss_driver = { - .remove = omap_dss_remove, - .shutdown = omap_dss_shutdown, - .driver = { - .name = "omapdss", - }, -}; - -/* INIT */ -static int (*dss_output_drv_reg_funcs[])(void) __initdata = { - dss_init_platform_driver, - dispc_init_platform_driver, -#ifdef CONFIG_OMAP2_DSS_DSI - dsi_init_platform_driver, -#endif -#ifdef CONFIG_OMAP2_DSS_VENC - venc_init_platform_driver, -#endif -#ifdef CONFIG_OMAP4_DSS_HDMI - hdmi4_init_platform_driver, -#endif -#ifdef CONFIG_OMAP5_DSS_HDMI - hdmi5_init_platform_driver, -#endif -}; - -static void (*dss_output_drv_unreg_funcs[])(void) = { -#ifdef CONFIG_OMAP5_DSS_HDMI - hdmi5_uninit_platform_driver, -#endif -#ifdef CONFIG_OMAP4_DSS_HDMI - hdmi4_uninit_platform_driver, -#endif -#ifdef CONFIG_OMAP2_DSS_VENC - venc_uninit_platform_driver, -#endif -#ifdef CONFIG_OMAP2_DSS_DSI - dsi_uninit_platform_driver, -#endif - dispc_uninit_platform_driver, - dss_uninit_platform_driver, -}; - -static int __init omap_dss_init(void) -{ - int r; - int i; - - r = platform_driver_probe(&omap_dss_driver, omap_dss_probe); - if (r) - return r; - - for (i = 0; i < ARRAY_SIZE(dss_output_drv_reg_funcs); ++i) { - r = dss_output_drv_reg_funcs[i](); - if (r) - goto err_reg; - } - - return 0; - -err_reg: - for (i = ARRAY_SIZE(dss_output_drv_reg_funcs) - i; - i < ARRAY_SIZE(dss_output_drv_reg_funcs); - ++i) - dss_output_drv_unreg_funcs[i](); - - platform_driver_unregister(&omap_dss_driver); - - return r; -} - -static void __exit omap_dss_exit(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(dss_output_drv_unreg_funcs); ++i) - dss_output_drv_unreg_funcs[i](); - - platform_driver_unregister(&omap_dss_driver); -} - -module_init(omap_dss_init); -module_exit(omap_dss_exit); - -MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>"); -MODULE_DESCRIPTION("OMAP2/3 Display Subsystem"); -MODULE_LICENSE("GPL v2"); - diff --git a/drivers/gpu/drm/omapdrm/dss/dispc.c b/drivers/gpu/drm/omapdrm/dss/dispc.c index fd7504b37e3b..cf055815077c 100644 --- a/drivers/gpu/drm/omapdrm/dss/dispc.c +++ b/drivers/gpu/drm/omapdrm/dss/dispc.c @@ -1,23 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* - * linux/drivers/video/omap2/dss/dispc.c - * * Copyright (C) 2009 Nokia Corporation - * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> * * Some code and ideas taken from drivers/video/omap/ driver * by Imre Deak. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. */ #define DSS_SUBSYS_NAME "DISPC" @@ -35,19 +22,22 @@ #include <linux/hardirq.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/property.h> #include <linux/sizes.h> #include <linux/mfd/syscon.h> #include <linux/regmap.h> #include <linux/of.h> #include <linux/component.h> +#include <linux/sys_soc.h> #include <drm/drm_fourcc.h> #include <drm/drm_blend.h> #include "omapdss.h" #include "dss.h" -#include "dss_features.h" #include "dispc.h" +struct dispc_device; + /* DISPC */ #define DISPC_SZ_REGS SZ_4K @@ -57,11 +47,39 @@ enum omap_burst_size { BURST_SIZE_X8 = 2, }; -#define REG_GET(idx, start, end) \ - FLD_GET(dispc_read_reg(idx), start, end) - -#define REG_FLD_MOD(idx, val, start, end) \ - dispc_write_reg(idx, FLD_MOD(dispc_read_reg(idx), val, start, end)) +#define REG_GET(dispc, idx, start, end) \ + FLD_GET(dispc_read_reg(dispc, idx), start, end) + +#define REG_FLD_MOD(dispc, idx, val, start, end) \ + dispc_write_reg(dispc, idx, \ + FLD_MOD(dispc_read_reg(dispc, idx), val, start, end)) + +/* DISPC has feature id */ +enum dispc_feature_id { + FEAT_LCDENABLEPOL, + FEAT_LCDENABLESIGNAL, + FEAT_PCKFREEENABLE, + FEAT_FUNCGATED, + FEAT_MGR_LCD2, + FEAT_MGR_LCD3, + FEAT_LINEBUFFERSPLIT, + FEAT_ROWREPEATENABLE, + FEAT_RESIZECONF, + /* Independent core clk divider */ + FEAT_CORE_CLK_DIV, + FEAT_HANDLE_UV_SEPARATE, + FEAT_ATTR2, + FEAT_CPR, + FEAT_PRELOAD, + FEAT_FIR_COEF_V, + FEAT_ALPHA_FIXED_ZORDER, + FEAT_ALPHA_FREE_ZORDER, + FEAT_FIFO_MERGE, + /* An unknown HW bug causing the normal FIFO thresholds not to work */ + FEAT_OMAP3_DSI_FIFO_BUG, + FEAT_BURST_2D, + FEAT_MFLAG, +}; struct dispc_features { u8 sw_start; @@ -74,9 +92,15 @@ struct dispc_features { u8 mgr_height_start; u16 mgr_width_max; u16 mgr_height_max; + u16 ovl_width_max; + u16 ovl_height_max; unsigned long max_lcd_pclk; unsigned long max_tv_pclk; - int (*calc_scaling) (unsigned long pclk, unsigned long lclk, + unsigned int max_downscale; + unsigned int max_line_width; + unsigned int min_pcd; + int (*calc_scaling)(struct dispc_device *dispc, + unsigned long pclk, unsigned long lclk, const struct videomode *vm, u16 width, u16 height, u16 out_width, u16 out_height, u32 fourcc, bool *five_taps, @@ -86,6 +110,17 @@ struct dispc_features { u16 width, u16 height, u16 out_width, u16 out_height, bool mem_to_mem); u8 num_fifos; + const enum dispc_feature_id *features; + unsigned int num_features; + const struct dss_reg_field *reg_fields; + const unsigned int num_reg_fields; + const enum omap_overlay_caps *overlay_caps; + const u32 **supported_color_modes; + const u32 *supported_scaler_color_modes; + unsigned int num_mgrs; + unsigned int num_ovls; + unsigned int buffer_size_unit; + unsigned int burst_size_unit; /* swap GFX & WB fifos */ bool gfx_fifo_workaround:1; @@ -123,9 +158,12 @@ struct dispc_features { #define DISPC_MAX_NR_FIFOS 5 #define DISPC_MAX_CHANNEL_GAMMA 4 -static struct { +struct dispc_device { struct platform_device *pdev; void __iomem *base; + struct dss_device *dss; + + struct dss_debugfs_entry *debugfs; int irq; irq_handler_t user_handler; @@ -149,10 +187,7 @@ static struct { struct regmap *syscon_pol; u32 syscon_pol_offset; - - /* DISPC_CONTROL & DISPC_CONFIG lock*/ - spinlock_t control_lock; -} dispc; +}; enum omap_color_component { /* used for all color formats for OMAP3 and earlier @@ -180,6 +215,17 @@ enum mgr_reg_fields { DISPC_MGR_FLD_NUM, }; +/* DISPC register field id */ +enum dispc_feat_reg_field { + FEAT_REG_FIRHINC, + FEAT_REG_FIRVINC, + FEAT_REG_FIFOHIGHTHRESHOLD, + FEAT_REG_FIFOLOWTHRESHOLD, + FEAT_REG_FIFOSIZE, + FEAT_REG_HORIZONTALACCU, + FEAT_REG_VERTICALACCU, +}; + struct dispc_reg_field { u16 reg; u8 high; @@ -295,281 +341,304 @@ static const struct { }, }; -struct color_conv_coef { - int ry, rcr, rcb, gy, gcr, gcb, by, bcr, bcb; - int full_range; -}; +static unsigned long dispc_fclk_rate(struct dispc_device *dispc); +static unsigned long dispc_core_clk_rate(struct dispc_device *dispc); +static unsigned long dispc_mgr_lclk_rate(struct dispc_device *dispc, + enum omap_channel channel); +static unsigned long dispc_mgr_pclk_rate(struct dispc_device *dispc, + enum omap_channel channel); -static unsigned long dispc_fclk_rate(void); -static unsigned long dispc_core_clk_rate(void); -static unsigned long dispc_mgr_lclk_rate(enum omap_channel channel); -static unsigned long dispc_mgr_pclk_rate(enum omap_channel channel); +static unsigned long dispc_plane_pclk_rate(struct dispc_device *dispc, + enum omap_plane_id plane); +static unsigned long dispc_plane_lclk_rate(struct dispc_device *dispc, + enum omap_plane_id plane); -static unsigned long dispc_plane_pclk_rate(enum omap_plane_id plane); -static unsigned long dispc_plane_lclk_rate(enum omap_plane_id plane); +static inline void dispc_write_reg(struct dispc_device *dispc, u16 idx, u32 val) +{ + __raw_writel(val, dispc->base + idx); +} -static void dispc_clear_irqstatus(u32 mask); -static bool dispc_mgr_is_enabled(enum omap_channel channel); -static void dispc_clear_irqstatus(u32 mask); +static inline u32 dispc_read_reg(struct dispc_device *dispc, u16 idx) +{ + return __raw_readl(dispc->base + idx); +} -static inline void dispc_write_reg(const u16 idx, u32 val) +static u32 mgr_fld_read(struct dispc_device *dispc, enum omap_channel channel, + enum mgr_reg_fields regfld) { - __raw_writel(val, dispc.base + idx); + const struct dispc_reg_field *rfld = &mgr_desc[channel].reg_desc[regfld]; + + return REG_GET(dispc, rfld->reg, rfld->high, rfld->low); } -static inline u32 dispc_read_reg(const u16 idx) +static void mgr_fld_write(struct dispc_device *dispc, enum omap_channel channel, + enum mgr_reg_fields regfld, int val) { - return __raw_readl(dispc.base + idx); + const struct dispc_reg_field *rfld = &mgr_desc[channel].reg_desc[regfld]; + + REG_FLD_MOD(dispc, rfld->reg, val, rfld->high, rfld->low); } -static u32 mgr_fld_read(enum omap_channel channel, enum mgr_reg_fields regfld) +int dispc_get_num_ovls(struct dispc_device *dispc) { - const struct dispc_reg_field rfld = mgr_desc[channel].reg_desc[regfld]; - return REG_GET(rfld.reg, rfld.high, rfld.low); + return dispc->feat->num_ovls; } -static void mgr_fld_write(enum omap_channel channel, - enum mgr_reg_fields regfld, int val) { - const struct dispc_reg_field rfld = mgr_desc[channel].reg_desc[regfld]; - const bool need_lock = rfld.reg == DISPC_CONTROL || rfld.reg == DISPC_CONFIG; - unsigned long flags; +int dispc_get_num_mgrs(struct dispc_device *dispc) +{ + return dispc->feat->num_mgrs; +} + +static void dispc_get_reg_field(struct dispc_device *dispc, + enum dispc_feat_reg_field id, + u8 *start, u8 *end) +{ + BUG_ON(id >= dispc->feat->num_reg_fields); + + *start = dispc->feat->reg_fields[id].start; + *end = dispc->feat->reg_fields[id].end; +} - if (need_lock) - spin_lock_irqsave(&dispc.control_lock, flags); +static bool dispc_has_feature(struct dispc_device *dispc, + enum dispc_feature_id id) +{ + unsigned int i; - REG_FLD_MOD(rfld.reg, val, rfld.high, rfld.low); + for (i = 0; i < dispc->feat->num_features; i++) { + if (dispc->feat->features[i] == id) + return true; + } - if (need_lock) - spin_unlock_irqrestore(&dispc.control_lock, flags); + return false; } -#define SR(reg) \ - dispc.ctx[DISPC_##reg / sizeof(u32)] = dispc_read_reg(DISPC_##reg) -#define RR(reg) \ - dispc_write_reg(DISPC_##reg, dispc.ctx[DISPC_##reg / sizeof(u32)]) +#define SR(dispc, reg) \ + dispc->ctx[DISPC_##reg / sizeof(u32)] = dispc_read_reg(dispc, DISPC_##reg) +#define RR(dispc, reg) \ + dispc_write_reg(dispc, DISPC_##reg, dispc->ctx[DISPC_##reg / sizeof(u32)]) -static void dispc_save_context(void) +static void dispc_save_context(struct dispc_device *dispc) { int i, j; DSSDBG("dispc_save_context\n"); - SR(IRQENABLE); - SR(CONTROL); - SR(CONFIG); - SR(LINE_NUMBER); - if (dss_has_feature(FEAT_ALPHA_FIXED_ZORDER) || - dss_has_feature(FEAT_ALPHA_FREE_ZORDER)) - SR(GLOBAL_ALPHA); - if (dss_has_feature(FEAT_MGR_LCD2)) { - SR(CONTROL2); - SR(CONFIG2); + SR(dispc, IRQENABLE); + SR(dispc, CONTROL); + SR(dispc, CONFIG); + SR(dispc, LINE_NUMBER); + if (dispc_has_feature(dispc, FEAT_ALPHA_FIXED_ZORDER) || + dispc_has_feature(dispc, FEAT_ALPHA_FREE_ZORDER)) + SR(dispc, GLOBAL_ALPHA); + if (dispc_has_feature(dispc, FEAT_MGR_LCD2)) { + SR(dispc, CONTROL2); + SR(dispc, CONFIG2); } - if (dss_has_feature(FEAT_MGR_LCD3)) { - SR(CONTROL3); - SR(CONFIG3); + if (dispc_has_feature(dispc, FEAT_MGR_LCD3)) { + SR(dispc, CONTROL3); + SR(dispc, CONFIG3); } - for (i = 0; i < dss_feat_get_num_mgrs(); i++) { - SR(DEFAULT_COLOR(i)); - SR(TRANS_COLOR(i)); - SR(SIZE_MGR(i)); + for (i = 0; i < dispc_get_num_mgrs(dispc); i++) { + SR(dispc, DEFAULT_COLOR(i)); + SR(dispc, TRANS_COLOR(i)); + SR(dispc, SIZE_MGR(i)); if (i == OMAP_DSS_CHANNEL_DIGIT) continue; - SR(TIMING_H(i)); - SR(TIMING_V(i)); - SR(POL_FREQ(i)); - SR(DIVISORo(i)); - - SR(DATA_CYCLE1(i)); - SR(DATA_CYCLE2(i)); - SR(DATA_CYCLE3(i)); - - if (dss_has_feature(FEAT_CPR)) { - SR(CPR_COEF_R(i)); - SR(CPR_COEF_G(i)); - SR(CPR_COEF_B(i)); + SR(dispc, TIMING_H(i)); + SR(dispc, TIMING_V(i)); + SR(dispc, POL_FREQ(i)); + SR(dispc, DIVISORo(i)); + + SR(dispc, DATA_CYCLE1(i)); + SR(dispc, DATA_CYCLE2(i)); + SR(dispc, DATA_CYCLE3(i)); + + if (dispc_has_feature(dispc, FEAT_CPR)) { + SR(dispc, CPR_COEF_R(i)); + SR(dispc, CPR_COEF_G(i)); + SR(dispc, CPR_COEF_B(i)); } } - for (i = 0; i < dss_feat_get_num_ovls(); i++) { - SR(OVL_BA0(i)); - SR(OVL_BA1(i)); - SR(OVL_POSITION(i)); - SR(OVL_SIZE(i)); - SR(OVL_ATTRIBUTES(i)); - SR(OVL_FIFO_THRESHOLD(i)); - SR(OVL_ROW_INC(i)); - SR(OVL_PIXEL_INC(i)); - if (dss_has_feature(FEAT_PRELOAD)) - SR(OVL_PRELOAD(i)); + for (i = 0; i < dispc_get_num_ovls(dispc); i++) { + SR(dispc, OVL_BA0(i)); + SR(dispc, OVL_BA1(i)); + SR(dispc, OVL_POSITION(i)); + SR(dispc, OVL_SIZE(i)); + SR(dispc, OVL_ATTRIBUTES(i)); + SR(dispc, OVL_FIFO_THRESHOLD(i)); + SR(dispc, OVL_ROW_INC(i)); + SR(dispc, OVL_PIXEL_INC(i)); + if (dispc_has_feature(dispc, FEAT_PRELOAD)) + SR(dispc, OVL_PRELOAD(i)); if (i == OMAP_DSS_GFX) { - SR(OVL_WINDOW_SKIP(i)); - SR(OVL_TABLE_BA(i)); + SR(dispc, OVL_WINDOW_SKIP(i)); + SR(dispc, OVL_TABLE_BA(i)); continue; } - SR(OVL_FIR(i)); - SR(OVL_PICTURE_SIZE(i)); - SR(OVL_ACCU0(i)); - SR(OVL_ACCU1(i)); + SR(dispc, OVL_FIR(i)); + SR(dispc, OVL_PICTURE_SIZE(i)); + SR(dispc, OVL_ACCU0(i)); + SR(dispc, OVL_ACCU1(i)); for (j = 0; j < 8; j++) - SR(OVL_FIR_COEF_H(i, j)); + SR(dispc, OVL_FIR_COEF_H(i, j)); for (j = 0; j < 8; j++) - SR(OVL_FIR_COEF_HV(i, j)); + SR(dispc, OVL_FIR_COEF_HV(i, j)); for (j = 0; j < 5; j++) - SR(OVL_CONV_COEF(i, j)); + SR(dispc, OVL_CONV_COEF(i, j)); - if (dss_has_feature(FEAT_FIR_COEF_V)) { + if (dispc_has_feature(dispc, FEAT_FIR_COEF_V)) { for (j = 0; j < 8; j++) - SR(OVL_FIR_COEF_V(i, j)); + SR(dispc, OVL_FIR_COEF_V(i, j)); } - if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) { - SR(OVL_BA0_UV(i)); - SR(OVL_BA1_UV(i)); - SR(OVL_FIR2(i)); - SR(OVL_ACCU2_0(i)); - SR(OVL_ACCU2_1(i)); + if (dispc_has_feature(dispc, FEAT_HANDLE_UV_SEPARATE)) { + SR(dispc, OVL_BA0_UV(i)); + SR(dispc, OVL_BA1_UV(i)); + SR(dispc, OVL_FIR2(i)); + SR(dispc, OVL_ACCU2_0(i)); + SR(dispc, OVL_ACCU2_1(i)); for (j = 0; j < 8; j++) - SR(OVL_FIR_COEF_H2(i, j)); + SR(dispc, OVL_FIR_COEF_H2(i, j)); for (j = 0; j < 8; j++) - SR(OVL_FIR_COEF_HV2(i, j)); + SR(dispc, OVL_FIR_COEF_HV2(i, j)); for (j = 0; j < 8; j++) - SR(OVL_FIR_COEF_V2(i, j)); + SR(dispc, OVL_FIR_COEF_V2(i, j)); } - if (dss_has_feature(FEAT_ATTR2)) - SR(OVL_ATTRIBUTES2(i)); + if (dispc_has_feature(dispc, FEAT_ATTR2)) + SR(dispc, OVL_ATTRIBUTES2(i)); } - if (dss_has_feature(FEAT_CORE_CLK_DIV)) - SR(DIVISOR); + if (dispc_has_feature(dispc, FEAT_CORE_CLK_DIV)) + SR(dispc, DIVISOR); - dispc.ctx_valid = true; + dispc->ctx_valid = true; DSSDBG("context saved\n"); } -static void dispc_restore_context(void) +static noinline_for_stack void dispc_restore_context(struct dispc_device *dispc) { int i, j; DSSDBG("dispc_restore_context\n"); - if (!dispc.ctx_valid) + if (!dispc->ctx_valid) return; - /*RR(IRQENABLE);*/ - /*RR(CONTROL);*/ - RR(CONFIG); - RR(LINE_NUMBER); - if (dss_has_feature(FEAT_ALPHA_FIXED_ZORDER) || - dss_has_feature(FEAT_ALPHA_FREE_ZORDER)) - RR(GLOBAL_ALPHA); - if (dss_has_feature(FEAT_MGR_LCD2)) - RR(CONFIG2); - if (dss_has_feature(FEAT_MGR_LCD3)) - RR(CONFIG3); - - for (i = 0; i < dss_feat_get_num_mgrs(); i++) { - RR(DEFAULT_COLOR(i)); - RR(TRANS_COLOR(i)); - RR(SIZE_MGR(i)); + /*RR(dispc, IRQENABLE);*/ + /*RR(dispc, CONTROL);*/ + RR(dispc, CONFIG); + RR(dispc, LINE_NUMBER); + if (dispc_has_feature(dispc, FEAT_ALPHA_FIXED_ZORDER) || + dispc_has_feature(dispc, FEAT_ALPHA_FREE_ZORDER)) + RR(dispc, GLOBAL_ALPHA); + if (dispc_has_feature(dispc, FEAT_MGR_LCD2)) + RR(dispc, CONFIG2); + if (dispc_has_feature(dispc, FEAT_MGR_LCD3)) + RR(dispc, CONFIG3); + + for (i = 0; i < dispc_get_num_mgrs(dispc); i++) { + RR(dispc, DEFAULT_COLOR(i)); + RR(dispc, TRANS_COLOR(i)); + RR(dispc, SIZE_MGR(i)); if (i == OMAP_DSS_CHANNEL_DIGIT) continue; - RR(TIMING_H(i)); - RR(TIMING_V(i)); - RR(POL_FREQ(i)); - RR(DIVISORo(i)); - - RR(DATA_CYCLE1(i)); - RR(DATA_CYCLE2(i)); - RR(DATA_CYCLE3(i)); - - if (dss_has_feature(FEAT_CPR)) { - RR(CPR_COEF_R(i)); - RR(CPR_COEF_G(i)); - RR(CPR_COEF_B(i)); + RR(dispc, TIMING_H(i)); + RR(dispc, TIMING_V(i)); + RR(dispc, POL_FREQ(i)); + RR(dispc, DIVISORo(i)); + + RR(dispc, DATA_CYCLE1(i)); + RR(dispc, DATA_CYCLE2(i)); + RR(dispc, DATA_CYCLE3(i)); + + if (dispc_has_feature(dispc, FEAT_CPR)) { + RR(dispc, CPR_COEF_R(i)); + RR(dispc, CPR_COEF_G(i)); + RR(dispc, CPR_COEF_B(i)); } } - for (i = 0; i < dss_feat_get_num_ovls(); i++) { - RR(OVL_BA0(i)); - RR(OVL_BA1(i)); - RR(OVL_POSITION(i)); - RR(OVL_SIZE(i)); - RR(OVL_ATTRIBUTES(i)); - RR(OVL_FIFO_THRESHOLD(i)); - RR(OVL_ROW_INC(i)); - RR(OVL_PIXEL_INC(i)); - if (dss_has_feature(FEAT_PRELOAD)) - RR(OVL_PRELOAD(i)); + for (i = 0; i < dispc_get_num_ovls(dispc); i++) { + RR(dispc, OVL_BA0(i)); + RR(dispc, OVL_BA1(i)); + RR(dispc, OVL_POSITION(i)); + RR(dispc, OVL_SIZE(i)); + RR(dispc, OVL_ATTRIBUTES(i)); + RR(dispc, OVL_FIFO_THRESHOLD(i)); + RR(dispc, OVL_ROW_INC(i)); + RR(dispc, OVL_PIXEL_INC(i)); + if (dispc_has_feature(dispc, FEAT_PRELOAD)) + RR(dispc, OVL_PRELOAD(i)); if (i == OMAP_DSS_GFX) { - RR(OVL_WINDOW_SKIP(i)); - RR(OVL_TABLE_BA(i)); + RR(dispc, OVL_WINDOW_SKIP(i)); + RR(dispc, OVL_TABLE_BA(i)); continue; } - RR(OVL_FIR(i)); - RR(OVL_PICTURE_SIZE(i)); - RR(OVL_ACCU0(i)); - RR(OVL_ACCU1(i)); + RR(dispc, OVL_FIR(i)); + RR(dispc, OVL_PICTURE_SIZE(i)); + RR(dispc, OVL_ACCU0(i)); + RR(dispc, OVL_ACCU1(i)); for (j = 0; j < 8; j++) - RR(OVL_FIR_COEF_H(i, j)); + RR(dispc, OVL_FIR_COEF_H(i, j)); for (j = 0; j < 8; j++) - RR(OVL_FIR_COEF_HV(i, j)); + RR(dispc, OVL_FIR_COEF_HV(i, j)); for (j = 0; j < 5; j++) - RR(OVL_CONV_COEF(i, j)); + RR(dispc, OVL_CONV_COEF(i, j)); - if (dss_has_feature(FEAT_FIR_COEF_V)) { + if (dispc_has_feature(dispc, FEAT_FIR_COEF_V)) { for (j = 0; j < 8; j++) - RR(OVL_FIR_COEF_V(i, j)); + RR(dispc, OVL_FIR_COEF_V(i, j)); } - if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) { - RR(OVL_BA0_UV(i)); - RR(OVL_BA1_UV(i)); - RR(OVL_FIR2(i)); - RR(OVL_ACCU2_0(i)); - RR(OVL_ACCU2_1(i)); + if (dispc_has_feature(dispc, FEAT_HANDLE_UV_SEPARATE)) { + RR(dispc, OVL_BA0_UV(i)); + RR(dispc, OVL_BA1_UV(i)); + RR(dispc, OVL_FIR2(i)); + RR(dispc, OVL_ACCU2_0(i)); + RR(dispc, OVL_ACCU2_1(i)); for (j = 0; j < 8; j++) - RR(OVL_FIR_COEF_H2(i, j)); + RR(dispc, OVL_FIR_COEF_H2(i, j)); for (j = 0; j < 8; j++) - RR(OVL_FIR_COEF_HV2(i, j)); + RR(dispc, OVL_FIR_COEF_HV2(i, j)); for (j = 0; j < 8; j++) - RR(OVL_FIR_COEF_V2(i, j)); + RR(dispc, OVL_FIR_COEF_V2(i, j)); } - if (dss_has_feature(FEAT_ATTR2)) - RR(OVL_ATTRIBUTES2(i)); + if (dispc_has_feature(dispc, FEAT_ATTR2)) + RR(dispc, OVL_ATTRIBUTES2(i)); } - if (dss_has_feature(FEAT_CORE_CLK_DIV)) - RR(DIVISOR); + if (dispc_has_feature(dispc, FEAT_CORE_CLK_DIV)) + RR(dispc, DIVISOR); /* enable last, because LCD & DIGIT enable are here */ - RR(CONTROL); - if (dss_has_feature(FEAT_MGR_LCD2)) - RR(CONTROL2); - if (dss_has_feature(FEAT_MGR_LCD3)) - RR(CONTROL3); + RR(dispc, CONTROL); + if (dispc_has_feature(dispc, FEAT_MGR_LCD2)) + RR(dispc, CONTROL2); + if (dispc_has_feature(dispc, FEAT_MGR_LCD3)) + RR(dispc, CONTROL3); /* clear spurious SYNC_LOST_DIGIT interrupts */ - dispc_clear_irqstatus(DISPC_IRQ_SYNC_LOST_DIGIT); + dispc_clear_irqstatus(dispc, DISPC_IRQ_SYNC_LOST_DIGIT); /* * enable last so IRQs won't trigger before * the context is fully restored */ - RR(IRQENABLE); + RR(dispc, IRQENABLE); DSSDBG("context restored\n"); } @@ -577,146 +646,133 @@ static void dispc_restore_context(void) #undef SR #undef RR -int dispc_runtime_get(void) +int dispc_runtime_get(struct dispc_device *dispc) { int r; DSSDBG("dispc_runtime_get\n"); - r = pm_runtime_get_sync(&dispc.pdev->dev); - WARN_ON(r < 0); - return r < 0 ? r : 0; + r = pm_runtime_get_sync(&dispc->pdev->dev); + if (WARN_ON(r < 0)) { + pm_runtime_put_noidle(&dispc->pdev->dev); + return r; + } + return 0; } -void dispc_runtime_put(void) +void dispc_runtime_put(struct dispc_device *dispc) { int r; DSSDBG("dispc_runtime_put\n"); - r = pm_runtime_put_sync(&dispc.pdev->dev); + r = pm_runtime_put_sync(&dispc->pdev->dev); WARN_ON(r < 0 && r != -ENOSYS); } -static u32 dispc_mgr_get_vsync_irq(enum omap_channel channel) +u32 dispc_mgr_get_vsync_irq(struct dispc_device *dispc, + enum omap_channel channel) { return mgr_desc[channel].vsync_irq; } -static u32 dispc_mgr_get_framedone_irq(enum omap_channel channel) +u32 dispc_mgr_get_framedone_irq(struct dispc_device *dispc, + enum omap_channel channel) { - if (channel == OMAP_DSS_CHANNEL_DIGIT && dispc.feat->no_framedone_tv) + if (channel == OMAP_DSS_CHANNEL_DIGIT && dispc->feat->no_framedone_tv) return 0; return mgr_desc[channel].framedone_irq; } -static u32 dispc_mgr_get_sync_lost_irq(enum omap_channel channel) +u32 dispc_mgr_get_sync_lost_irq(struct dispc_device *dispc, + enum omap_channel channel) { return mgr_desc[channel].sync_lost_irq; } -u32 dispc_wb_get_framedone_irq(void) -{ - return DISPC_IRQ_FRAMEDONEWB; -} - -static void dispc_mgr_enable(enum omap_channel channel, bool enable) +void dispc_mgr_enable(struct dispc_device *dispc, + enum omap_channel channel, bool enable) { - mgr_fld_write(channel, DISPC_MGR_FLD_ENABLE, enable); + mgr_fld_write(dispc, channel, DISPC_MGR_FLD_ENABLE, enable); /* flush posted write */ - mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE); + mgr_fld_read(dispc, channel, DISPC_MGR_FLD_ENABLE); } -static bool dispc_mgr_is_enabled(enum omap_channel channel) +static bool dispc_mgr_is_enabled(struct dispc_device *dispc, + enum omap_channel channel) { - return !!mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE); + return !!mgr_fld_read(dispc, channel, DISPC_MGR_FLD_ENABLE); } -static bool dispc_mgr_go_busy(enum omap_channel channel) +bool dispc_mgr_go_busy(struct dispc_device *dispc, + enum omap_channel channel) { - return mgr_fld_read(channel, DISPC_MGR_FLD_GO) == 1; + return mgr_fld_read(dispc, channel, DISPC_MGR_FLD_GO) == 1; } -static void dispc_mgr_go(enum omap_channel channel) +void dispc_mgr_go(struct dispc_device *dispc, enum omap_channel channel) { - WARN_ON(!dispc_mgr_is_enabled(channel)); - WARN_ON(dispc_mgr_go_busy(channel)); + WARN_ON(!dispc_mgr_is_enabled(dispc, channel)); + WARN_ON(dispc_mgr_go_busy(dispc, channel)); DSSDBG("GO %s\n", mgr_desc[channel].name); - mgr_fld_write(channel, DISPC_MGR_FLD_GO, 1); -} - -bool dispc_wb_go_busy(void) -{ - return REG_GET(DISPC_CONTROL2, 6, 6) == 1; -} - -void dispc_wb_go(void) -{ - enum omap_plane_id plane = OMAP_DSS_WB; - bool enable, go; - - enable = REG_GET(DISPC_OVL_ATTRIBUTES(plane), 0, 0) == 1; - - if (!enable) - return; - - go = REG_GET(DISPC_CONTROL2, 6, 6) == 1; - if (go) { - DSSERR("GO bit not down for WB\n"); - return; - } - - REG_FLD_MOD(DISPC_CONTROL2, 1, 6, 6); + mgr_fld_write(dispc, channel, DISPC_MGR_FLD_GO, 1); } -static void dispc_ovl_write_firh_reg(enum omap_plane_id plane, int reg, +static void dispc_ovl_write_firh_reg(struct dispc_device *dispc, + enum omap_plane_id plane, int reg, u32 value) { - dispc_write_reg(DISPC_OVL_FIR_COEF_H(plane, reg), value); + dispc_write_reg(dispc, DISPC_OVL_FIR_COEF_H(plane, reg), value); } -static void dispc_ovl_write_firhv_reg(enum omap_plane_id plane, int reg, +static void dispc_ovl_write_firhv_reg(struct dispc_device *dispc, + enum omap_plane_id plane, int reg, u32 value) { - dispc_write_reg(DISPC_OVL_FIR_COEF_HV(plane, reg), value); + dispc_write_reg(dispc, DISPC_OVL_FIR_COEF_HV(plane, reg), value); } -static void dispc_ovl_write_firv_reg(enum omap_plane_id plane, int reg, +static void dispc_ovl_write_firv_reg(struct dispc_device *dispc, + enum omap_plane_id plane, int reg, u32 value) { - dispc_write_reg(DISPC_OVL_FIR_COEF_V(plane, reg), value); + dispc_write_reg(dispc, DISPC_OVL_FIR_COEF_V(plane, reg), value); } -static void dispc_ovl_write_firh2_reg(enum omap_plane_id plane, int reg, +static void dispc_ovl_write_firh2_reg(struct dispc_device *dispc, + enum omap_plane_id plane, int reg, u32 value) { BUG_ON(plane == OMAP_DSS_GFX); - dispc_write_reg(DISPC_OVL_FIR_COEF_H2(plane, reg), value); + dispc_write_reg(dispc, DISPC_OVL_FIR_COEF_H2(plane, reg), value); } -static void dispc_ovl_write_firhv2_reg(enum omap_plane_id plane, int reg, - u32 value) +static void dispc_ovl_write_firhv2_reg(struct dispc_device *dispc, + enum omap_plane_id plane, int reg, + u32 value) { BUG_ON(plane == OMAP_DSS_GFX); - dispc_write_reg(DISPC_OVL_FIR_COEF_HV2(plane, reg), value); + dispc_write_reg(dispc, DISPC_OVL_FIR_COEF_HV2(plane, reg), value); } -static void dispc_ovl_write_firv2_reg(enum omap_plane_id plane, int reg, +static void dispc_ovl_write_firv2_reg(struct dispc_device *dispc, + enum omap_plane_id plane, int reg, u32 value) { BUG_ON(plane == OMAP_DSS_GFX); - dispc_write_reg(DISPC_OVL_FIR_COEF_V2(plane, reg), value); + dispc_write_reg(dispc, DISPC_OVL_FIR_COEF_V2(plane, reg), value); } -static void dispc_ovl_set_scale_coef(enum omap_plane_id plane, int fir_hinc, - int fir_vinc, int five_taps, - enum omap_color_component color_comp) +static void dispc_ovl_set_scale_coef(struct dispc_device *dispc, + enum omap_plane_id 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; @@ -724,6 +780,12 @@ static void dispc_ovl_set_scale_coef(enum omap_plane_id plane, int fir_hinc, h_coef = dispc_ovl_get_scale_coef(fir_hinc, true); v_coef = dispc_ovl_get_scale_coef(fir_vinc, five_taps); + if (!h_coef || !v_coef) { + dev_err(&dispc->pdev->dev, "%s: failed to find scale coefs\n", + __func__); + return; + } + for (i = 0; i < 8; i++) { u32 h, hv; @@ -737,11 +799,11 @@ static void dispc_ovl_set_scale_coef(enum omap_plane_id plane, int fir_hinc, | 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); - dispc_ovl_write_firhv_reg(plane, i, hv); + dispc_ovl_write_firh_reg(dispc, plane, i, h); + dispc_ovl_write_firhv_reg(dispc, plane, i, hv); } else { - dispc_ovl_write_firh2_reg(plane, i, h); - dispc_ovl_write_firhv2_reg(plane, i, hv); + dispc_ovl_write_firh2_reg(dispc, plane, i, h); + dispc_ovl_write_firhv2_reg(dispc, plane, i, hv); } } @@ -752,72 +814,120 @@ static void dispc_ovl_set_scale_coef(enum omap_plane_id plane, int fir_hinc, 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); + dispc_ovl_write_firv_reg(dispc, plane, i, v); else - dispc_ovl_write_firv2_reg(plane, i, v); + dispc_ovl_write_firv2_reg(dispc, plane, i, v); } } } +struct csc_coef_yuv2rgb { + int ry, rcb, rcr, gy, gcb, gcr, by, bcb, bcr; + bool full_range; +}; -static void dispc_ovl_write_color_conv_coef(enum omap_plane_id plane, - const struct color_conv_coef *ct) +static void dispc_ovl_write_color_conv_coef(struct dispc_device *dispc, + enum omap_plane_id plane, + const struct csc_coef_yuv2rgb *ct) { #define CVAL(x, y) (FLD_VAL(x, 26, 16) | FLD_VAL(y, 10, 0)) - dispc_write_reg(DISPC_OVL_CONV_COEF(plane, 0), CVAL(ct->rcr, ct->ry)); - dispc_write_reg(DISPC_OVL_CONV_COEF(plane, 1), CVAL(ct->gy, ct->rcb)); - dispc_write_reg(DISPC_OVL_CONV_COEF(plane, 2), CVAL(ct->gcb, ct->gcr)); - dispc_write_reg(DISPC_OVL_CONV_COEF(plane, 3), CVAL(ct->bcr, ct->by)); - dispc_write_reg(DISPC_OVL_CONV_COEF(plane, 4), CVAL(0, ct->bcb)); + dispc_write_reg(dispc, DISPC_OVL_CONV_COEF(plane, 0), CVAL(ct->rcr, ct->ry)); + dispc_write_reg(dispc, DISPC_OVL_CONV_COEF(plane, 1), CVAL(ct->gy, ct->rcb)); + dispc_write_reg(dispc, DISPC_OVL_CONV_COEF(plane, 2), CVAL(ct->gcb, ct->gcr)); + dispc_write_reg(dispc, DISPC_OVL_CONV_COEF(plane, 3), CVAL(ct->bcr, ct->by)); + dispc_write_reg(dispc, DISPC_OVL_CONV_COEF(plane, 4), CVAL(0, ct->bcb)); - REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), ct->full_range, 11, 11); + REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES(plane), ct->full_range, 11, 11); #undef CVAL } -static void dispc_setup_color_conv_coef(void) +/* YUV -> RGB, ITU-R BT.601, full range */ +static const struct csc_coef_yuv2rgb coefs_yuv2rgb_bt601_full = { + 256, 0, 358, /* ry, rcb, rcr |1.000 0.000 1.402|*/ + 256, -88, -182, /* gy, gcb, gcr |1.000 -0.344 -0.714|*/ + 256, 452, 0, /* by, bcb, bcr |1.000 1.772 0.000|*/ + true, /* full range */ +}; + +/* YUV -> RGB, ITU-R BT.601, limited range */ +static const struct csc_coef_yuv2rgb coefs_yuv2rgb_bt601_lim = { + 298, 0, 409, /* ry, rcb, rcr |1.164 0.000 1.596|*/ + 298, -100, -208, /* gy, gcb, gcr |1.164 -0.392 -0.813|*/ + 298, 516, 0, /* by, bcb, bcr |1.164 2.017 0.000|*/ + false, /* limited range */ +}; + +/* YUV -> RGB, ITU-R BT.709, full range */ +static const struct csc_coef_yuv2rgb coefs_yuv2rgb_bt709_full = { + 256, 0, 402, /* ry, rcb, rcr |1.000 0.000 1.570|*/ + 256, -48, -120, /* gy, gcb, gcr |1.000 -0.187 -0.467|*/ + 256, 475, 0, /* by, bcb, bcr |1.000 1.856 0.000|*/ + true, /* full range */ +}; + +/* YUV -> RGB, ITU-R BT.709, limited range */ +static const struct csc_coef_yuv2rgb coefs_yuv2rgb_bt709_lim = { + 298, 0, 459, /* ry, rcb, rcr |1.164 0.000 1.793|*/ + 298, -55, -136, /* gy, gcb, gcr |1.164 -0.213 -0.533|*/ + 298, 541, 0, /* by, bcb, bcr |1.164 2.112 0.000|*/ + false, /* limited range */ +}; + +static void dispc_ovl_set_csc(struct dispc_device *dispc, + enum omap_plane_id plane, + enum drm_color_encoding color_encoding, + enum drm_color_range color_range) { - int i; - int num_ovl = dss_feat_get_num_ovls(); - const struct color_conv_coef ctbl_bt601_5_ovl = { - /* YUV -> RGB */ - 298, 409, 0, 298, -208, -100, 298, 0, 517, 0, - }; - const struct color_conv_coef ctbl_bt601_5_wb = { - /* RGB -> YUV */ - 66, 129, 25, 112, -94, -18, -38, -74, 112, 0, - }; + const struct csc_coef_yuv2rgb *csc; - for (i = 1; i < num_ovl; i++) - dispc_ovl_write_color_conv_coef(i, &ctbl_bt601_5_ovl); + switch (color_encoding) { + default: + case DRM_COLOR_YCBCR_BT601: + if (color_range == DRM_COLOR_YCBCR_FULL_RANGE) + csc = &coefs_yuv2rgb_bt601_full; + else + csc = &coefs_yuv2rgb_bt601_lim; + break; + case DRM_COLOR_YCBCR_BT709: + if (color_range == DRM_COLOR_YCBCR_FULL_RANGE) + csc = &coefs_yuv2rgb_bt709_full; + else + csc = &coefs_yuv2rgb_bt709_lim; + break; + } - if (dispc.feat->has_writeback) - dispc_ovl_write_color_conv_coef(OMAP_DSS_WB, &ctbl_bt601_5_wb); + dispc_ovl_write_color_conv_coef(dispc, plane, csc); } -static void dispc_ovl_set_ba0(enum omap_plane_id plane, u32 paddr) +static void dispc_ovl_set_ba0(struct dispc_device *dispc, + enum omap_plane_id plane, u32 paddr) { - dispc_write_reg(DISPC_OVL_BA0(plane), paddr); + dispc_write_reg(dispc, DISPC_OVL_BA0(plane), paddr); } -static void dispc_ovl_set_ba1(enum omap_plane_id plane, u32 paddr) +static void dispc_ovl_set_ba1(struct dispc_device *dispc, + enum omap_plane_id plane, u32 paddr) { - dispc_write_reg(DISPC_OVL_BA1(plane), paddr); + dispc_write_reg(dispc, DISPC_OVL_BA1(plane), paddr); } -static void dispc_ovl_set_ba0_uv(enum omap_plane_id plane, u32 paddr) +static void dispc_ovl_set_ba0_uv(struct dispc_device *dispc, + enum omap_plane_id plane, u32 paddr) { - dispc_write_reg(DISPC_OVL_BA0_UV(plane), paddr); + dispc_write_reg(dispc, DISPC_OVL_BA0_UV(plane), paddr); } -static void dispc_ovl_set_ba1_uv(enum omap_plane_id plane, u32 paddr) +static void dispc_ovl_set_ba1_uv(struct dispc_device *dispc, + enum omap_plane_id plane, u32 paddr) { - dispc_write_reg(DISPC_OVL_BA1_UV(plane), paddr); + dispc_write_reg(dispc, DISPC_OVL_BA1_UV(plane), paddr); } -static void dispc_ovl_set_pos(enum omap_plane_id plane, - enum omap_overlay_caps caps, int x, int y) +static void dispc_ovl_set_pos(struct dispc_device *dispc, + enum omap_plane_id plane, + enum omap_overlay_caps caps, int x, int y) { u32 val; @@ -826,22 +936,24 @@ static void dispc_ovl_set_pos(enum omap_plane_id plane, val = FLD_VAL(y, 26, 16) | FLD_VAL(x, 10, 0); - dispc_write_reg(DISPC_OVL_POSITION(plane), val); + dispc_write_reg(dispc, DISPC_OVL_POSITION(plane), val); } -static void dispc_ovl_set_input_size(enum omap_plane_id plane, int width, - int height) +static void dispc_ovl_set_input_size(struct dispc_device *dispc, + enum omap_plane_id plane, int width, + int height) { u32 val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); if (plane == OMAP_DSS_GFX || plane == OMAP_DSS_WB) - dispc_write_reg(DISPC_OVL_SIZE(plane), val); + dispc_write_reg(dispc, DISPC_OVL_SIZE(plane), val); else - dispc_write_reg(DISPC_OVL_PICTURE_SIZE(plane), val); + dispc_write_reg(dispc, DISPC_OVL_PICTURE_SIZE(plane), val); } -static void dispc_ovl_set_output_size(enum omap_plane_id plane, int width, - int height) +static void dispc_ovl_set_output_size(struct dispc_device *dispc, + enum omap_plane_id plane, int width, + int height) { u32 val; @@ -850,64 +962,72 @@ static void dispc_ovl_set_output_size(enum omap_plane_id plane, int width, val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); if (plane == OMAP_DSS_WB) - dispc_write_reg(DISPC_OVL_PICTURE_SIZE(plane), val); + dispc_write_reg(dispc, DISPC_OVL_PICTURE_SIZE(plane), val); else - dispc_write_reg(DISPC_OVL_SIZE(plane), val); + dispc_write_reg(dispc, DISPC_OVL_SIZE(plane), val); } -static void dispc_ovl_set_zorder(enum omap_plane_id plane, - enum omap_overlay_caps caps, u8 zorder) +static void dispc_ovl_set_zorder(struct dispc_device *dispc, + enum omap_plane_id plane, + enum omap_overlay_caps caps, u8 zorder) { if ((caps & OMAP_DSS_OVL_CAP_ZORDER) == 0) return; - REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), zorder, 27, 26); + REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES(plane), zorder, 27, 26); } -static void dispc_ovl_enable_zorder_planes(void) +static void dispc_ovl_enable_zorder_planes(struct dispc_device *dispc) { int i; - if (!dss_has_feature(FEAT_ALPHA_FREE_ZORDER)) + if (!dispc_has_feature(dispc, FEAT_ALPHA_FREE_ZORDER)) return; - for (i = 0; i < dss_feat_get_num_ovls(); i++) - REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(i), 1, 25, 25); + for (i = 0; i < dispc_get_num_ovls(dispc); i++) + REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES(i), 1, 25, 25); } -static void dispc_ovl_set_pre_mult_alpha(enum omap_plane_id plane, - enum omap_overlay_caps caps, bool enable) +static void dispc_ovl_set_pre_mult_alpha(struct dispc_device *dispc, + enum omap_plane_id plane, + enum omap_overlay_caps caps, + bool enable) { if ((caps & OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA) == 0) return; - REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable ? 1 : 0, 28, 28); + REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES(plane), enable ? 1 : 0, 28, 28); } -static void dispc_ovl_setup_global_alpha(enum omap_plane_id plane, - enum omap_overlay_caps caps, u8 global_alpha) +static void dispc_ovl_setup_global_alpha(struct dispc_device *dispc, + enum omap_plane_id plane, + enum omap_overlay_caps caps, + u8 global_alpha) { - static const unsigned shifts[] = { 0, 8, 16, 24, }; + static const unsigned int shifts[] = { 0, 8, 16, 24, }; int shift; if ((caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) == 0) return; shift = shifts[plane]; - REG_FLD_MOD(DISPC_GLOBAL_ALPHA, global_alpha, shift + 7, shift); + REG_FLD_MOD(dispc, DISPC_GLOBAL_ALPHA, global_alpha, shift + 7, shift); } -static void dispc_ovl_set_pix_inc(enum omap_plane_id plane, s32 inc) +static void dispc_ovl_set_pix_inc(struct dispc_device *dispc, + enum omap_plane_id plane, s32 inc) { - dispc_write_reg(DISPC_OVL_PIXEL_INC(plane), inc); + dispc_write_reg(dispc, DISPC_OVL_PIXEL_INC(plane), inc); } -static void dispc_ovl_set_row_inc(enum omap_plane_id plane, s32 inc) +static void dispc_ovl_set_row_inc(struct dispc_device *dispc, + enum omap_plane_id plane, s32 inc) { - dispc_write_reg(DISPC_OVL_ROW_INC(plane), inc); + dispc_write_reg(dispc, DISPC_OVL_ROW_INC(plane), inc); } -static void dispc_ovl_set_color_mode(enum omap_plane_id plane, u32 fourcc) +static void dispc_ovl_set_color_mode(struct dispc_device *dispc, + enum omap_plane_id plane, u32 fourcc) { u32 m = 0; if (plane != OMAP_DSS_GFX) { @@ -976,34 +1096,24 @@ static void dispc_ovl_set_color_mode(enum omap_plane_id plane, u32 fourcc) } } - REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), m, 4, 1); -} - -static bool format_is_yuv(u32 fourcc) -{ - switch (fourcc) { - case DRM_FORMAT_YUYV: - case DRM_FORMAT_UYVY: - case DRM_FORMAT_NV12: - return true; - default: - return false; - } + REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES(plane), m, 4, 1); } -static void dispc_ovl_configure_burst_type(enum omap_plane_id plane, - enum omap_dss_rotation_type rotation_type) +static void dispc_ovl_configure_burst_type(struct dispc_device *dispc, + enum omap_plane_id plane, + enum omap_dss_rotation_type rotation) { - if (dss_has_feature(FEAT_BURST_2D) == 0) + if (dispc_has_feature(dispc, FEAT_BURST_2D) == 0) return; - if (rotation_type == OMAP_DSS_ROT_TILER) - REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), 1, 29, 29); + if (rotation == OMAP_DSS_ROT_TILER) + REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES(plane), 1, 29, 29); else - REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), 0, 29, 29); + REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES(plane), 0, 29, 29); } -static void dispc_ovl_set_channel_out(enum omap_plane_id plane, +static void dispc_ovl_set_channel_out(struct dispc_device *dispc, + enum omap_plane_id plane, enum omap_channel channel) { int shift; @@ -1024,8 +1134,8 @@ static void dispc_ovl_set_channel_out(enum omap_plane_id plane, return; } - val = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane)); - if (dss_has_feature(FEAT_MGR_LCD2)) { + val = dispc_read_reg(dispc, DISPC_OVL_ATTRIBUTES(plane)); + if (dispc_has_feature(dispc, FEAT_MGR_LCD2)) { switch (channel) { case OMAP_DSS_CHANNEL_LCD: chan = 0; @@ -1040,7 +1150,7 @@ static void dispc_ovl_set_channel_out(enum omap_plane_id plane, chan2 = 1; break; case OMAP_DSS_CHANNEL_LCD3: - if (dss_has_feature(FEAT_MGR_LCD3)) { + if (dispc_has_feature(dispc, FEAT_MGR_LCD3)) { chan = 0; chan2 = 2; } else { @@ -1062,10 +1172,11 @@ static void dispc_ovl_set_channel_out(enum omap_plane_id plane, } else { val = FLD_MOD(val, channel, shift, shift); } - dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), val); + dispc_write_reg(dispc, DISPC_OVL_ATTRIBUTES(plane), val); } -static enum omap_channel dispc_ovl_get_channel_out(enum omap_plane_id plane) +static enum omap_channel dispc_ovl_get_channel_out(struct dispc_device *dispc, + enum omap_plane_id plane) { int shift; u32 val; @@ -1084,12 +1195,12 @@ static enum omap_channel dispc_ovl_get_channel_out(enum omap_plane_id plane) return 0; } - val = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane)); + val = dispc_read_reg(dispc, DISPC_OVL_ATTRIBUTES(plane)); if (FLD_GET(val, shift, shift) == 1) return OMAP_DSS_CHANNEL_DIGIT; - if (!dss_has_feature(FEAT_MGR_LCD2)) + if (!dispc_has_feature(dispc, FEAT_MGR_LCD2)) return OMAP_DSS_CHANNEL_LCD; switch (FLD_GET(val, 31, 30)) { @@ -1105,62 +1216,71 @@ static enum omap_channel dispc_ovl_get_channel_out(enum omap_plane_id plane) } } -void dispc_wb_set_channel_in(enum dss_writeback_channel channel) +static void dispc_ovl_set_burst_size(struct dispc_device *dispc, + enum omap_plane_id plane, + enum omap_burst_size burst_size) { - enum omap_plane_id plane = OMAP_DSS_WB; - - REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), channel, 18, 16); -} - -static void dispc_ovl_set_burst_size(enum omap_plane_id plane, - enum omap_burst_size burst_size) -{ - static const unsigned shifts[] = { 6, 14, 14, 14, 14, }; + static const unsigned int shifts[] = { 6, 14, 14, 14, 14, }; int shift; shift = shifts[plane]; - REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), burst_size, shift + 1, shift); + REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES(plane), burst_size, + shift + 1, shift); } -static void dispc_configure_burst_sizes(void) +static void dispc_configure_burst_sizes(struct dispc_device *dispc) { int i; const int burst_size = BURST_SIZE_X8; /* Configure burst size always to maximum size */ - for (i = 0; i < dss_feat_get_num_ovls(); ++i) - dispc_ovl_set_burst_size(i, burst_size); - if (dispc.feat->has_writeback) - dispc_ovl_set_burst_size(OMAP_DSS_WB, burst_size); + for (i = 0; i < dispc_get_num_ovls(dispc); ++i) + dispc_ovl_set_burst_size(dispc, i, burst_size); + if (dispc->feat->has_writeback) + dispc_ovl_set_burst_size(dispc, OMAP_DSS_WB, burst_size); } -static u32 dispc_ovl_get_burst_size(enum omap_plane_id plane) +static u32 dispc_ovl_get_burst_size(struct dispc_device *dispc, + enum omap_plane_id plane) { - unsigned unit = dss_feat_get_burst_size_unit(); /* burst multiplier is always x8 (see dispc_configure_burst_sizes()) */ - return unit * 8; + return dispc->feat->burst_size_unit * 8; } -static const u32 *dispc_ovl_get_color_modes(enum omap_plane_id plane) +bool dispc_ovl_color_mode_supported(struct dispc_device *dispc, + enum omap_plane_id plane, u32 fourcc) { - return dss_feat_get_supported_color_modes(plane); + const u32 *modes; + unsigned int i; + + modes = dispc->feat->supported_color_modes[plane]; + + for (i = 0; modes[i]; ++i) { + if (modes[i] == fourcc) + return true; + } + + return false; } -static int dispc_get_num_ovls(void) +const u32 *dispc_ovl_get_color_modes(struct dispc_device *dispc, + enum omap_plane_id plane) { - return dss_feat_get_num_ovls(); + return dispc->feat->supported_color_modes[plane]; } -static void dispc_mgr_enable_cpr(enum omap_channel channel, bool enable) +static void dispc_mgr_enable_cpr(struct dispc_device *dispc, + enum omap_channel channel, bool enable) { if (channel == OMAP_DSS_CHANNEL_DIGIT) return; - mgr_fld_write(channel, DISPC_MGR_FLD_CPR, enable); + mgr_fld_write(dispc, channel, DISPC_MGR_FLD_CPR, enable); } -static void dispc_mgr_set_cpr_coef(enum omap_channel channel, - const struct omap_dss_cpr_coefs *coefs) +static void dispc_mgr_set_cpr_coef(struct dispc_device *dispc, + enum omap_channel channel, + const struct omap_dss_cpr_coefs *coefs) { u32 coef_r, coef_g, coef_b; @@ -1174,48 +1294,50 @@ static void dispc_mgr_set_cpr_coef(enum omap_channel channel, coef_b = FLD_VAL(coefs->br, 31, 22) | FLD_VAL(coefs->bg, 20, 11) | FLD_VAL(coefs->bb, 9, 0); - dispc_write_reg(DISPC_CPR_COEF_R(channel), coef_r); - dispc_write_reg(DISPC_CPR_COEF_G(channel), coef_g); - dispc_write_reg(DISPC_CPR_COEF_B(channel), coef_b); + dispc_write_reg(dispc, DISPC_CPR_COEF_R(channel), coef_r); + dispc_write_reg(dispc, DISPC_CPR_COEF_G(channel), coef_g); + dispc_write_reg(dispc, DISPC_CPR_COEF_B(channel), coef_b); } -static void dispc_ovl_set_vid_color_conv(enum omap_plane_id plane, - bool enable) +static void dispc_ovl_set_vid_color_conv(struct dispc_device *dispc, + enum omap_plane_id plane, bool enable) { u32 val; BUG_ON(plane == OMAP_DSS_GFX); - val = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane)); + val = dispc_read_reg(dispc, DISPC_OVL_ATTRIBUTES(plane)); val = FLD_MOD(val, enable, 9, 9); - dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), val); + dispc_write_reg(dispc, DISPC_OVL_ATTRIBUTES(plane), val); } -static void dispc_ovl_enable_replication(enum omap_plane_id plane, - enum omap_overlay_caps caps, bool enable) +static void dispc_ovl_enable_replication(struct dispc_device *dispc, + enum omap_plane_id plane, + enum omap_overlay_caps caps, + bool enable) { - static const unsigned shifts[] = { 5, 10, 10, 10 }; + static const unsigned int shifts[] = { 5, 10, 10, 10 }; int shift; if ((caps & OMAP_DSS_OVL_CAP_REPLICATION) == 0) return; shift = shifts[plane]; - REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable, shift, shift); + REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES(plane), enable, shift, shift); } -static void dispc_mgr_set_size(enum omap_channel channel, u16 width, - u16 height) +static void dispc_mgr_set_size(struct dispc_device *dispc, + enum omap_channel channel, u16 width, u16 height) { u32 val; - val = FLD_VAL(height - 1, dispc.feat->mgr_height_start, 16) | - FLD_VAL(width - 1, dispc.feat->mgr_width_start, 0); + val = FLD_VAL(height - 1, dispc->feat->mgr_height_start, 16) | + FLD_VAL(width - 1, dispc->feat->mgr_width_start, 0); - dispc_write_reg(DISPC_SIZE_MGR(channel), val); + dispc_write_reg(dispc, DISPC_SIZE_MGR(channel), val); } -static void dispc_init_fifos(void) +static void dispc_init_fifos(struct dispc_device *dispc) { u32 size; int fifo; @@ -1223,20 +1345,21 @@ static void dispc_init_fifos(void) u32 unit; int i; - unit = dss_feat_get_buffer_size_unit(); + unit = dispc->feat->buffer_size_unit; - dss_feat_get_reg_field(FEAT_REG_FIFOSIZE, &start, &end); + dispc_get_reg_field(dispc, FEAT_REG_FIFOSIZE, &start, &end); - for (fifo = 0; fifo < dispc.feat->num_fifos; ++fifo) { - size = REG_GET(DISPC_OVL_FIFO_SIZE_STATUS(fifo), start, end); + for (fifo = 0; fifo < dispc->feat->num_fifos; ++fifo) { + size = REG_GET(dispc, DISPC_OVL_FIFO_SIZE_STATUS(fifo), + start, end); size *= unit; - dispc.fifo_size[fifo] = size; + dispc->fifo_size[fifo] = size; /* * By default fifos are mapped directly to overlays, fifo 0 to * ovl 0, fifo 1 to ovl 1, etc. */ - dispc.fifo_assignment[fifo] = fifo; + dispc->fifo_assignment[fifo] = fifo; } /* @@ -1246,68 +1369,71 @@ static void dispc_init_fifos(void) * giving GFX plane a larger fifo. WB but should work fine with a * smaller fifo. */ - if (dispc.feat->gfx_fifo_workaround) { + if (dispc->feat->gfx_fifo_workaround) { u32 v; - v = dispc_read_reg(DISPC_GLOBAL_BUFFER); + v = dispc_read_reg(dispc, DISPC_GLOBAL_BUFFER); v = FLD_MOD(v, 4, 2, 0); /* GFX BUF top to WB */ v = FLD_MOD(v, 4, 5, 3); /* GFX BUF bottom to WB */ v = FLD_MOD(v, 0, 26, 24); /* WB BUF top to GFX */ v = FLD_MOD(v, 0, 29, 27); /* WB BUF bottom to GFX */ - dispc_write_reg(DISPC_GLOBAL_BUFFER, v); + dispc_write_reg(dispc, DISPC_GLOBAL_BUFFER, v); - dispc.fifo_assignment[OMAP_DSS_GFX] = OMAP_DSS_WB; - dispc.fifo_assignment[OMAP_DSS_WB] = OMAP_DSS_GFX; + dispc->fifo_assignment[OMAP_DSS_GFX] = OMAP_DSS_WB; + dispc->fifo_assignment[OMAP_DSS_WB] = OMAP_DSS_GFX; } /* * Setup default fifo thresholds. */ - for (i = 0; i < dss_feat_get_num_ovls(); ++i) { + for (i = 0; i < dispc_get_num_ovls(dispc); ++i) { u32 low, high; const bool use_fifomerge = false; const bool manual_update = false; - dispc_ovl_compute_fifo_thresholds(i, &low, &high, - use_fifomerge, manual_update); + dispc_ovl_compute_fifo_thresholds(dispc, i, &low, &high, + use_fifomerge, manual_update); - dispc_ovl_set_fifo_threshold(i, low, high); + dispc_ovl_set_fifo_threshold(dispc, i, low, high); } - if (dispc.feat->has_writeback) { + if (dispc->feat->has_writeback) { u32 low, high; const bool use_fifomerge = false; const bool manual_update = false; - dispc_ovl_compute_fifo_thresholds(OMAP_DSS_WB, &low, &high, - use_fifomerge, manual_update); + dispc_ovl_compute_fifo_thresholds(dispc, OMAP_DSS_WB, + &low, &high, use_fifomerge, + manual_update); - dispc_ovl_set_fifo_threshold(OMAP_DSS_WB, low, high); + dispc_ovl_set_fifo_threshold(dispc, OMAP_DSS_WB, low, high); } } -static u32 dispc_ovl_get_fifo_size(enum omap_plane_id plane) +static u32 dispc_ovl_get_fifo_size(struct dispc_device *dispc, + enum omap_plane_id plane) { int fifo; u32 size = 0; - for (fifo = 0; fifo < dispc.feat->num_fifos; ++fifo) { - if (dispc.fifo_assignment[fifo] == plane) - size += dispc.fifo_size[fifo]; + for (fifo = 0; fifo < dispc->feat->num_fifos; ++fifo) { + if (dispc->fifo_assignment[fifo] == plane) + size += dispc->fifo_size[fifo]; } return size; } -void dispc_ovl_set_fifo_threshold(enum omap_plane_id plane, u32 low, - u32 high) +void dispc_ovl_set_fifo_threshold(struct dispc_device *dispc, + enum omap_plane_id plane, + u32 low, u32 high) { u8 hi_start, hi_end, lo_start, lo_end; u32 unit; - unit = dss_feat_get_buffer_size_unit(); + unit = dispc->feat->buffer_size_unit; WARN_ON(low % unit != 0); WARN_ON(high % unit != 0); @@ -1315,18 +1441,20 @@ void dispc_ovl_set_fifo_threshold(enum omap_plane_id plane, u32 low, low /= unit; high /= unit; - dss_feat_get_reg_field(FEAT_REG_FIFOHIGHTHRESHOLD, &hi_start, &hi_end); - dss_feat_get_reg_field(FEAT_REG_FIFOLOWTHRESHOLD, &lo_start, &lo_end); + dispc_get_reg_field(dispc, FEAT_REG_FIFOHIGHTHRESHOLD, + &hi_start, &hi_end); + dispc_get_reg_field(dispc, FEAT_REG_FIFOLOWTHRESHOLD, + &lo_start, &lo_end); DSSDBG("fifo(%d) threshold (bytes), old %u/%u, new %u/%u\n", plane, - REG_GET(DISPC_OVL_FIFO_THRESHOLD(plane), + REG_GET(dispc, DISPC_OVL_FIFO_THRESHOLD(plane), lo_start, lo_end) * unit, - REG_GET(DISPC_OVL_FIFO_THRESHOLD(plane), + REG_GET(dispc, DISPC_OVL_FIFO_THRESHOLD(plane), hi_start, hi_end) * unit, low * unit, high * unit); - dispc_write_reg(DISPC_OVL_FIFO_THRESHOLD(plane), + dispc_write_reg(dispc, DISPC_OVL_FIFO_THRESHOLD(plane), FLD_VAL(high, hi_start, hi_end) | FLD_VAL(low, lo_start, lo_end)); @@ -1335,42 +1463,32 @@ void dispc_ovl_set_fifo_threshold(enum omap_plane_id plane, u32 low, * large for the preload field, set the threshold to the maximum value * that can be held by the preload register */ - if (dss_has_feature(FEAT_PRELOAD) && dispc.feat->set_max_preload && - plane != OMAP_DSS_WB) - dispc_write_reg(DISPC_OVL_PRELOAD(plane), min(high, 0xfffu)); -} - -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); + if (dispc_has_feature(dispc, FEAT_PRELOAD) && + dispc->feat->set_max_preload && plane != OMAP_DSS_WB) + dispc_write_reg(dispc, DISPC_OVL_PRELOAD(plane), + min(high, 0xfffu)); } -void dispc_ovl_compute_fifo_thresholds(enum omap_plane_id plane, - u32 *fifo_low, u32 *fifo_high, bool use_fifomerge, - bool manual_update) +void dispc_ovl_compute_fifo_thresholds(struct dispc_device *dispc, + enum omap_plane_id plane, + u32 *fifo_low, u32 *fifo_high, + bool use_fifomerge, bool manual_update) { /* * 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; + unsigned int buf_unit = dispc->feat->buffer_size_unit; + unsigned int 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); + burst_size = dispc_ovl_get_burst_size(dispc, plane); + ovl_fifo_size = dispc_ovl_get_fifo_size(dispc, plane); if (use_fifomerge) { total_fifo_size = 0; - for (i = 0; i < dss_feat_get_num_ovls(); ++i) - total_fifo_size += dispc_ovl_get_fifo_size(i); + for (i = 0; i < dispc_get_num_ovls(dispc); ++i) + total_fifo_size += dispc_ovl_get_fifo_size(dispc, i); } else { total_fifo_size = ovl_fifo_size; } @@ -1381,7 +1499,7 @@ void dispc_ovl_compute_fifo_thresholds(enum omap_plane_id plane, * combined fifo size */ - if (manual_update && dss_has_feature(FEAT_OMAP3_DSI_FIFO_BUG)) { + if (manual_update && dispc_has_feature(dispc, FEAT_OMAP3_DSI_FIFO_BUG)) { *fifo_low = ovl_fifo_size - burst_size * 2; *fifo_high = total_fifo_size - burst_size; } else if (plane == OMAP_DSS_WB) { @@ -1398,7 +1516,8 @@ void dispc_ovl_compute_fifo_thresholds(enum omap_plane_id plane, } } -static void dispc_ovl_set_mflag(enum omap_plane_id plane, bool enable) +static void dispc_ovl_set_mflag(struct dispc_device *dispc, + enum omap_plane_id plane, bool enable) { int bit; @@ -1407,17 +1526,18 @@ static void dispc_ovl_set_mflag(enum omap_plane_id plane, bool enable) else bit = 23; - REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable, bit, bit); + REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES(plane), enable, bit, bit); } -static void dispc_ovl_set_mflag_threshold(enum omap_plane_id plane, - int low, int high) +static void dispc_ovl_set_mflag_threshold(struct dispc_device *dispc, + enum omap_plane_id plane, + int low, int high) { - dispc_write_reg(DISPC_OVL_MFLAG_THRESHOLD(plane), + dispc_write_reg(dispc, DISPC_OVL_MFLAG_THRESHOLD(plane), FLD_VAL(high, 31, 16) | FLD_VAL(low, 15, 0)); } -static void dispc_init_mflag(void) +static void dispc_init_mflag(struct dispc_device *dispc) { int i; @@ -1431,16 +1551,16 @@ static void dispc_init_mflag(void) * * As a work-around, set force MFLAG to always on. */ - dispc_write_reg(DISPC_GLOBAL_MFLAG_ATTRIBUTE, + dispc_write_reg(dispc, DISPC_GLOBAL_MFLAG_ATTRIBUTE, (1 << 0) | /* MFLAG_CTRL = force always on */ (0 << 2)); /* MFLAG_START = disable */ - for (i = 0; i < dss_feat_get_num_ovls(); ++i) { - u32 size = dispc_ovl_get_fifo_size(i); - u32 unit = dss_feat_get_buffer_size_unit(); + for (i = 0; i < dispc_get_num_ovls(dispc); ++i) { + u32 size = dispc_ovl_get_fifo_size(dispc, i); + u32 unit = dispc->feat->buffer_size_unit; u32 low, high; - dispc_ovl_set_mflag(i, true); + dispc_ovl_set_mflag(dispc, i, true); /* * Simulation team suggests below thesholds: @@ -1451,15 +1571,15 @@ static void dispc_init_mflag(void) low = size * 4 / 8 / unit; high = size * 5 / 8 / unit; - dispc_ovl_set_mflag_threshold(i, low, high); + dispc_ovl_set_mflag_threshold(dispc, i, low, high); } - if (dispc.feat->has_writeback) { - u32 size = dispc_ovl_get_fifo_size(OMAP_DSS_WB); - u32 unit = dss_feat_get_buffer_size_unit(); + if (dispc->feat->has_writeback) { + u32 size = dispc_ovl_get_fifo_size(dispc, OMAP_DSS_WB); + u32 unit = dispc->feat->buffer_size_unit; u32 low, high; - dispc_ovl_set_mflag(OMAP_DSS_WB, true); + dispc_ovl_set_mflag(dispc, OMAP_DSS_WB, true); /* * Simulation team suggests below thesholds: @@ -1470,100 +1590,112 @@ static void dispc_init_mflag(void) low = size * 4 / 8 / unit; high = size * 5 / 8 / unit; - dispc_ovl_set_mflag_threshold(OMAP_DSS_WB, low, high); + dispc_ovl_set_mflag_threshold(dispc, OMAP_DSS_WB, low, high); } } -static void dispc_ovl_set_fir(enum omap_plane_id plane, - int hinc, int vinc, - enum omap_color_component color_comp) +static void dispc_ovl_set_fir(struct dispc_device *dispc, + enum omap_plane_id plane, + int hinc, int vinc, + enum omap_color_component color_comp) { u32 val; if (color_comp == DISPC_COLOR_COMPONENT_RGB_Y) { u8 hinc_start, hinc_end, vinc_start, vinc_end; - dss_feat_get_reg_field(FEAT_REG_FIRHINC, - &hinc_start, &hinc_end); - dss_feat_get_reg_field(FEAT_REG_FIRVINC, - &vinc_start, &vinc_end); + dispc_get_reg_field(dispc, FEAT_REG_FIRHINC, + &hinc_start, &hinc_end); + dispc_get_reg_field(dispc, FEAT_REG_FIRVINC, + &vinc_start, &vinc_end); val = FLD_VAL(vinc, vinc_start, vinc_end) | FLD_VAL(hinc, hinc_start, hinc_end); - dispc_write_reg(DISPC_OVL_FIR(plane), val); + dispc_write_reg(dispc, DISPC_OVL_FIR(plane), val); } else { val = FLD_VAL(vinc, 28, 16) | FLD_VAL(hinc, 12, 0); - dispc_write_reg(DISPC_OVL_FIR2(plane), val); + dispc_write_reg(dispc, DISPC_OVL_FIR2(plane), val); } } -static void dispc_ovl_set_vid_accu0(enum omap_plane_id plane, int haccu, +static void dispc_ovl_set_vid_accu0(struct dispc_device *dispc, + enum omap_plane_id plane, int haccu, int vaccu) { u32 val; u8 hor_start, hor_end, vert_start, vert_end; - dss_feat_get_reg_field(FEAT_REG_HORIZONTALACCU, &hor_start, &hor_end); - dss_feat_get_reg_field(FEAT_REG_VERTICALACCU, &vert_start, &vert_end); + dispc_get_reg_field(dispc, FEAT_REG_HORIZONTALACCU, + &hor_start, &hor_end); + dispc_get_reg_field(dispc, FEAT_REG_VERTICALACCU, + &vert_start, &vert_end); val = FLD_VAL(vaccu, vert_start, vert_end) | FLD_VAL(haccu, hor_start, hor_end); - dispc_write_reg(DISPC_OVL_ACCU0(plane), val); + dispc_write_reg(dispc, DISPC_OVL_ACCU0(plane), val); } -static void dispc_ovl_set_vid_accu1(enum omap_plane_id plane, int haccu, +static void dispc_ovl_set_vid_accu1(struct dispc_device *dispc, + enum omap_plane_id plane, int haccu, int vaccu) { u32 val; u8 hor_start, hor_end, vert_start, vert_end; - dss_feat_get_reg_field(FEAT_REG_HORIZONTALACCU, &hor_start, &hor_end); - dss_feat_get_reg_field(FEAT_REG_VERTICALACCU, &vert_start, &vert_end); + dispc_get_reg_field(dispc, FEAT_REG_HORIZONTALACCU, + &hor_start, &hor_end); + dispc_get_reg_field(dispc, FEAT_REG_VERTICALACCU, + &vert_start, &vert_end); val = FLD_VAL(vaccu, vert_start, vert_end) | FLD_VAL(haccu, hor_start, hor_end); - dispc_write_reg(DISPC_OVL_ACCU1(plane), val); + dispc_write_reg(dispc, DISPC_OVL_ACCU1(plane), val); } -static void dispc_ovl_set_vid_accu2_0(enum omap_plane_id plane, int haccu, - int vaccu) +static void dispc_ovl_set_vid_accu2_0(struct dispc_device *dispc, + enum omap_plane_id plane, int haccu, + int vaccu) { u32 val; val = FLD_VAL(vaccu, 26, 16) | FLD_VAL(haccu, 10, 0); - dispc_write_reg(DISPC_OVL_ACCU2_0(plane), val); + dispc_write_reg(dispc, DISPC_OVL_ACCU2_0(plane), val); } -static void dispc_ovl_set_vid_accu2_1(enum omap_plane_id plane, int haccu, - int vaccu) +static void dispc_ovl_set_vid_accu2_1(struct dispc_device *dispc, + enum omap_plane_id plane, int haccu, + int vaccu) { u32 val; val = FLD_VAL(vaccu, 26, 16) | FLD_VAL(haccu, 10, 0); - dispc_write_reg(DISPC_OVL_ACCU2_1(plane), val); + dispc_write_reg(dispc, DISPC_OVL_ACCU2_1(plane), val); } -static void dispc_ovl_set_scale_param(enum omap_plane_id plane, - u16 orig_width, u16 orig_height, - u16 out_width, u16 out_height, - bool five_taps, u8 rotation, - enum omap_color_component color_comp) +static void dispc_ovl_set_scale_param(struct dispc_device *dispc, + enum omap_plane_id plane, + u16 orig_width, u16 orig_height, + u16 out_width, u16 out_height, + bool five_taps, u8 rotation, + enum omap_color_component color_comp) { int fir_hinc, fir_vinc; 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); + dispc_ovl_set_scale_coef(dispc, plane, fir_hinc, fir_vinc, five_taps, + color_comp); + dispc_ovl_set_fir(dispc, plane, fir_hinc, fir_vinc, color_comp); } -static void dispc_ovl_set_accu_uv(enum omap_plane_id plane, - u16 orig_width, u16 orig_height, u16 out_width, u16 out_height, - bool ilace, u32 fourcc, u8 rotation) +static void dispc_ovl_set_accu_uv(struct dispc_device *dispc, + enum omap_plane_id plane, + u16 orig_width, u16 orig_height, + u16 out_width, u16 out_height, + bool ilace, u32 fourcc, u8 rotation) { int h_accu2_0, h_accu2_1; int v_accu2_0, v_accu2_1; @@ -1644,25 +1776,26 @@ static void dispc_ovl_set_accu_uv(enum omap_plane_id plane, v_accu2_0 = (accu_val->v0_m * chroma_vinc / accu_val->v0_n) % 1024; v_accu2_1 = (accu_val->v1_m * chroma_vinc / accu_val->v1_n) % 1024; - dispc_ovl_set_vid_accu2_0(plane, h_accu2_0, v_accu2_0); - dispc_ovl_set_vid_accu2_1(plane, h_accu2_1, v_accu2_1); + dispc_ovl_set_vid_accu2_0(dispc, plane, h_accu2_0, v_accu2_0); + dispc_ovl_set_vid_accu2_1(dispc, plane, h_accu2_1, v_accu2_1); } -static void dispc_ovl_set_scaling_common(enum omap_plane_id plane, - u16 orig_width, u16 orig_height, - u16 out_width, u16 out_height, - bool ilace, bool five_taps, - bool fieldmode, u32 fourcc, - u8 rotation) +static void dispc_ovl_set_scaling_common(struct dispc_device *dispc, + enum omap_plane_id plane, + u16 orig_width, u16 orig_height, + u16 out_width, u16 out_height, + bool ilace, bool five_taps, + bool fieldmode, u32 fourcc, + u8 rotation) { int accu0 = 0; int accu1 = 0; u32 l; - dispc_ovl_set_scale_param(plane, orig_width, orig_height, - out_width, out_height, five_taps, - rotation, DISPC_COLOR_COMPONENT_RGB_Y); - l = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane)); + dispc_ovl_set_scale_param(dispc, plane, orig_width, orig_height, + out_width, out_height, five_taps, + rotation, DISPC_COLOR_COMPONENT_RGB_Y); + l = dispc_read_reg(dispc, DISPC_OVL_ATTRIBUTES(plane)); /* RESIZEENABLE and VERTICALTAPS */ l &= ~((0x3 << 5) | (0x1 << 21)); @@ -1671,19 +1804,19 @@ static void dispc_ovl_set_scaling_common(enum omap_plane_id plane, l |= five_taps ? (1 << 21) : 0; /* VRESIZECONF and HRESIZECONF */ - if (dss_has_feature(FEAT_RESIZECONF)) { + if (dispc_has_feature(dispc, FEAT_RESIZECONF)) { l &= ~(0x3 << 7); l |= (orig_width <= out_width) ? 0 : (1 << 7); l |= (orig_height <= out_height) ? 0 : (1 << 8); } /* LINEBUFFERSPLIT */ - if (dss_has_feature(FEAT_LINEBUFFERSPLIT)) { + if (dispc_has_feature(dispc, FEAT_LINEBUFFERSPLIT)) { l &= ~(0x1 << 22); l |= five_taps ? (1 << 22) : 0; } - dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), l); + dispc_write_reg(dispc, DISPC_OVL_ATTRIBUTES(plane), l); /* * field 0 = even field = bottom field @@ -1698,33 +1831,38 @@ static void dispc_ovl_set_scaling_common(enum omap_plane_id plane, } } - dispc_ovl_set_vid_accu0(plane, 0, accu0); - dispc_ovl_set_vid_accu1(plane, 0, accu1); + dispc_ovl_set_vid_accu0(dispc, plane, 0, accu0); + dispc_ovl_set_vid_accu1(dispc, plane, 0, accu1); } -static void dispc_ovl_set_scaling_uv(enum omap_plane_id plane, - u16 orig_width, u16 orig_height, - u16 out_width, u16 out_height, - bool ilace, bool five_taps, - bool fieldmode, u32 fourcc, - u8 rotation) +static void dispc_ovl_set_scaling_uv(struct dispc_device *dispc, + enum omap_plane_id plane, + u16 orig_width, u16 orig_height, + u16 out_width, u16 out_height, + bool ilace, bool five_taps, + bool fieldmode, u32 fourcc, + u8 rotation) { int scale_x = out_width != orig_width; int scale_y = out_height != orig_height; bool chroma_upscale = plane != OMAP_DSS_WB; + const struct drm_format_info *info; + + info = drm_format_info(fourcc); - if (!dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) + if (!dispc_has_feature(dispc, FEAT_HANDLE_UV_SEPARATE)) return; - if (!format_is_yuv(fourcc)) { + if (!info->is_yuv) { /* reset chroma resampling for RGB formats */ if (plane != OMAP_DSS_WB) - REG_FLD_MOD(DISPC_OVL_ATTRIBUTES2(plane), 0, 8, 8); + REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES2(plane), + 0, 8, 8); return; } - dispc_ovl_set_accu_uv(plane, orig_width, orig_height, out_width, - out_height, ilace, fourcc, rotation); + dispc_ovl_set_accu_uv(dispc, plane, orig_width, orig_height, out_width, + out_height, ilace, fourcc, rotation); switch (fourcc) { case DRM_FORMAT_NV12: @@ -1766,46 +1904,43 @@ static void dispc_ovl_set_scaling_uv(enum omap_plane_id plane, if (out_height != orig_height) scale_y = true; - dispc_ovl_set_scale_param(plane, orig_width, orig_height, - out_width, out_height, five_taps, - rotation, DISPC_COLOR_COMPONENT_UV); + dispc_ovl_set_scale_param(dispc, plane, orig_width, orig_height, + out_width, out_height, five_taps, + rotation, DISPC_COLOR_COMPONENT_UV); if (plane != OMAP_DSS_WB) - REG_FLD_MOD(DISPC_OVL_ATTRIBUTES2(plane), + REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES2(plane), (scale_x || scale_y) ? 1 : 0, 8, 8); /* set H scaling */ - REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), scale_x ? 1 : 0, 5, 5); + REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES(plane), scale_x ? 1 : 0, 5, 5); /* set V scaling */ - REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), scale_y ? 1 : 0, 6, 6); + REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES(plane), scale_y ? 1 : 0, 6, 6); } -static void dispc_ovl_set_scaling(enum omap_plane_id plane, - u16 orig_width, u16 orig_height, - u16 out_width, u16 out_height, - bool ilace, bool five_taps, - bool fieldmode, u32 fourcc, - u8 rotation) +static void dispc_ovl_set_scaling(struct dispc_device *dispc, + enum omap_plane_id plane, + u16 orig_width, u16 orig_height, + u16 out_width, u16 out_height, + bool ilace, bool five_taps, + bool fieldmode, u32 fourcc, + u8 rotation) { BUG_ON(plane == OMAP_DSS_GFX); - dispc_ovl_set_scaling_common(plane, - orig_width, orig_height, - out_width, out_height, - ilace, five_taps, - fieldmode, fourcc, - rotation); + dispc_ovl_set_scaling_common(dispc, plane, orig_width, orig_height, + out_width, out_height, ilace, five_taps, + fieldmode, fourcc, rotation); - dispc_ovl_set_scaling_uv(plane, - orig_width, orig_height, - out_width, out_height, - ilace, five_taps, - fieldmode, fourcc, - rotation); + dispc_ovl_set_scaling_uv(dispc, plane, orig_width, orig_height, + out_width, out_height, ilace, five_taps, + fieldmode, fourcc, rotation); } -static void dispc_ovl_set_rotation_attrs(enum omap_plane_id plane, u8 rotation, - enum omap_dss_rotation_type rotation_type, u32 fourcc) +static void dispc_ovl_set_rotation_attrs(struct dispc_device *dispc, + enum omap_plane_id plane, u8 rotation, + enum omap_dss_rotation_type rotation_type, + u32 fourcc) { bool row_repeat = false; int vidrot = 0; @@ -1859,19 +1994,20 @@ static void dispc_ovl_set_rotation_attrs(enum omap_plane_id plane, u8 rotation, if (fourcc == DRM_FORMAT_NV12 && rotation_type != OMAP_DSS_ROT_TILER) vidrot = 1; - REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), vidrot, 13, 12); - if (dss_has_feature(FEAT_ROWREPEATENABLE)) - REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), + REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES(plane), vidrot, 13, 12); + if (dispc_has_feature(dispc, FEAT_ROWREPEATENABLE)) + REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES(plane), row_repeat ? 1 : 0, 18, 18); - if (dss_feat_color_mode_supported(plane, DRM_FORMAT_NV12)) { + if (dispc_ovl_color_mode_supported(dispc, plane, DRM_FORMAT_NV12)) { bool doublestride = fourcc == DRM_FORMAT_NV12 && rotation_type == OMAP_DSS_ROT_TILER && !drm_rotation_90_or_270(rotation); /* DOUBLESTRIDE */ - REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), doublestride, 22, 22); + REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES(plane), + doublestride, 22, 22); } } @@ -1911,14 +2047,13 @@ static s32 pixinc(int pixels, u8 ps) return 1 + (pixels - 1) * ps; else if (pixels < 0) return 1 - (-pixels + 1) * ps; - else - BUG(); - return 0; + + BUG(); } static void calc_offset(u16 screen_width, u16 width, - u32 fourcc, bool fieldmode, - unsigned int field_offset, unsigned *offset0, unsigned *offset1, + u32 fourcc, bool fieldmode, unsigned int field_offset, + unsigned int *offset0, unsigned int *offset1, s32 *row_inc, s32 *pix_inc, int x_predecim, int y_predecim, enum omap_dss_rotation_type rotation_type, u8 rotation) { @@ -2108,28 +2243,31 @@ static unsigned long calc_core_clk_44xx(unsigned long pclk, u16 width, return pclk; } -static int dispc_ovl_calc_scaling_24xx(unsigned long pclk, unsigned long lclk, - const struct videomode *vm, - u16 width, u16 height, u16 out_width, u16 out_height, - u32 fourcc, bool *five_taps, - int *x_predecim, int *y_predecim, int *decim_x, int *decim_y, - u16 pos_x, unsigned long *core_clk, bool mem_to_mem) +static int dispc_ovl_calc_scaling_24xx(struct dispc_device *dispc, + unsigned long pclk, unsigned long lclk, + const struct videomode *vm, + u16 width, u16 height, + u16 out_width, u16 out_height, + u32 fourcc, bool *five_taps, + int *x_predecim, int *y_predecim, + int *decim_x, int *decim_y, + u16 pos_x, unsigned long *core_clk, + bool mem_to_mem) { int error; u16 in_width, in_height; int min_factor = min(*decim_x, *decim_y); - const int maxsinglelinewidth = - dss_feat_get_param_max(FEAT_PARAM_LINEWIDTH); + const int maxsinglelinewidth = dispc->feat->max_line_width; *five_taps = false; do { in_height = height / *decim_y; in_width = width / *decim_x; - *core_clk = dispc.feat->calc_core_clk(pclk, in_width, + *core_clk = dispc->feat->calc_core_clk(pclk, in_width, in_height, out_width, out_height, mem_to_mem); error = (in_width > maxsinglelinewidth || !*core_clk || - *core_clk > dispc_core_clk_rate()); + *core_clk > dispc_core_clk_rate(dispc)); if (error) { if (*decim_x == *decim_y) { *decim_x = min_factor; @@ -2148,23 +2286,26 @@ static int dispc_ovl_calc_scaling_24xx(unsigned long pclk, unsigned long lclk, } if (in_width > maxsinglelinewidth) { - DSSERR("Cannot scale max input width exceeded"); + DSSERR("Cannot scale max input width exceeded\n"); return -EINVAL; } return 0; } -static int dispc_ovl_calc_scaling_34xx(unsigned long pclk, unsigned long lclk, - const struct videomode *vm, - u16 width, u16 height, u16 out_width, u16 out_height, - u32 fourcc, bool *five_taps, - int *x_predecim, int *y_predecim, int *decim_x, int *decim_y, - u16 pos_x, unsigned long *core_clk, bool mem_to_mem) +static int dispc_ovl_calc_scaling_34xx(struct dispc_device *dispc, + unsigned long pclk, unsigned long lclk, + const struct videomode *vm, + u16 width, u16 height, + u16 out_width, u16 out_height, + u32 fourcc, bool *five_taps, + int *x_predecim, int *y_predecim, + int *decim_x, int *decim_y, + u16 pos_x, unsigned long *core_clk, + bool mem_to_mem) { int error; u16 in_width, in_height; - const int maxsinglelinewidth = - dss_feat_get_param_max(FEAT_PARAM_LINEWIDTH); + const int maxsinglelinewidth = dispc->feat->max_line_width; do { in_height = height / *decim_y; @@ -2181,7 +2322,7 @@ again: in_width, in_height, out_width, out_height, fourcc); else - *core_clk = dispc.feat->calc_core_clk(pclk, in_width, + *core_clk = dispc->feat->calc_core_clk(pclk, in_width, in_height, out_width, out_height, mem_to_mem); @@ -2195,7 +2336,7 @@ again: error = (error || in_width > maxsinglelinewidth * 2 || (in_width > maxsinglelinewidth && *five_taps) || - !*core_clk || *core_clk > dispc_core_clk_rate()); + !*core_clk || *core_clk > dispc_core_clk_rate(dispc)); if (!error) { /* verify that we're inside the limits of scaler */ @@ -2227,42 +2368,45 @@ again: } if (in_width > (maxsinglelinewidth * 2)) { - DSSERR("Cannot setup scaling"); - DSSERR("width exceeds maximum width possible"); + DSSERR("Cannot setup scaling\n"); + DSSERR("width exceeds maximum width possible\n"); return -EINVAL; } if (in_width > maxsinglelinewidth && *five_taps) { - DSSERR("cannot setup scaling with five taps"); + DSSERR("cannot setup scaling with five taps\n"); return -EINVAL; } return 0; } -static int dispc_ovl_calc_scaling_44xx(unsigned long pclk, unsigned long lclk, - const struct videomode *vm, - u16 width, u16 height, u16 out_width, u16 out_height, - u32 fourcc, bool *five_taps, - int *x_predecim, int *y_predecim, int *decim_x, int *decim_y, - u16 pos_x, unsigned long *core_clk, bool mem_to_mem) +static int dispc_ovl_calc_scaling_44xx(struct dispc_device *dispc, + unsigned long pclk, unsigned long lclk, + const struct videomode *vm, + u16 width, u16 height, + u16 out_width, u16 out_height, + u32 fourcc, bool *five_taps, + int *x_predecim, int *y_predecim, + int *decim_x, int *decim_y, + u16 pos_x, unsigned long *core_clk, + bool mem_to_mem) { u16 in_width, in_width_max; int decim_x_min = *decim_x; u16 in_height = height / *decim_y; - const int maxsinglelinewidth = - dss_feat_get_param_max(FEAT_PARAM_LINEWIDTH); - const int maxdownscale = dss_feat_get_param_max(FEAT_PARAM_DOWNSCALE); + const int maxsinglelinewidth = dispc->feat->max_line_width; + const int maxdownscale = dispc->feat->max_downscale; if (mem_to_mem) { in_width_max = out_width * maxdownscale; } else { - in_width_max = dispc_core_clk_rate() / - DIV_ROUND_UP(pclk, out_width); + in_width_max = dispc_core_clk_rate(dispc) + / DIV_ROUND_UP(pclk, out_width); } *decim_x = DIV_ROUND_UP(width, in_width_max); - *decim_x = *decim_x > decim_x_min ? *decim_x : decim_x_min; + *decim_x = max(*decim_x, decim_x_min); if (*decim_x > *x_predecim) return -EINVAL; @@ -2272,7 +2416,7 @@ static int dispc_ovl_calc_scaling_44xx(unsigned long pclk, unsigned long lclk, in_width > maxsinglelinewidth && ++*decim_x); if (in_width > maxsinglelinewidth) { - DSSERR("Cannot scale width exceeds max line width"); + DSSERR("Cannot scale width exceeds max line width\n"); return -EINVAL; } @@ -2290,28 +2434,38 @@ static int dispc_ovl_calc_scaling_44xx(unsigned long pclk, unsigned long lclk, * bandwidth. Despite what theory says this appears to * be true also for 16-bit color formats. */ - DSSERR("Not enough bandwidth, too much downscaling (x-decimation factor %d > 4)", *decim_x); + DSSERR("Not enough bandwidth, too much downscaling (x-decimation factor %d > 4)\n", *decim_x); return -EINVAL; } - *core_clk = dispc.feat->calc_core_clk(pclk, in_width, in_height, + *core_clk = dispc->feat->calc_core_clk(pclk, in_width, in_height, out_width, out_height, mem_to_mem); return 0; } +enum omap_overlay_caps dispc_ovl_get_caps(struct dispc_device *dispc, enum omap_plane_id plane) +{ + return dispc->feat->overlay_caps[plane]; +} + #define DIV_FRAC(dividend, divisor) \ ((dividend) * 100 / (divisor) - ((dividend) / (divisor) * 100)) -static int dispc_ovl_calc_scaling(unsigned long pclk, unsigned long lclk, - enum omap_overlay_caps caps, - const struct videomode *vm, - u16 width, u16 height, u16 out_width, u16 out_height, - u32 fourcc, bool *five_taps, - int *x_predecim, int *y_predecim, u16 pos_x, - enum omap_dss_rotation_type rotation_type, bool mem_to_mem) -{ - const int maxdownscale = dss_feat_get_param_max(FEAT_PARAM_DOWNSCALE); +static int dispc_ovl_calc_scaling(struct dispc_device *dispc, + enum omap_plane_id plane, + unsigned long pclk, unsigned long lclk, + enum omap_overlay_caps caps, + const struct videomode *vm, + u16 width, u16 height, + u16 out_width, u16 out_height, + u32 fourcc, bool *five_taps, + int *x_predecim, int *y_predecim, u16 pos_x, + enum omap_dss_rotation_type rotation_type, + bool mem_to_mem) +{ + int maxhdownscale = dispc->feat->max_downscale; + int maxvdownscale = dispc->feat->max_downscale; const int max_decim_limit = 16; unsigned long core_clk = 0; int decim_x, decim_y, ret; @@ -2319,6 +2473,33 @@ static int dispc_ovl_calc_scaling(unsigned long pclk, unsigned long lclk, if (width == out_width && height == out_height) return 0; + if (dispc->feat->supported_scaler_color_modes) { + const u32 *modes = dispc->feat->supported_scaler_color_modes; + unsigned int i; + + for (i = 0; modes[i]; ++i) { + if (modes[i] == fourcc) + break; + } + + if (modes[i] == 0) + return -EINVAL; + } + + if (plane == OMAP_DSS_WB) { + switch (fourcc) { + case DRM_FORMAT_NV12: + maxhdownscale = maxvdownscale = 2; + break; + case DRM_FORMAT_YUYV: + case DRM_FORMAT_UYVY: + maxhdownscale = 2; + maxvdownscale = 4; + break; + default: + break; + } + } if (!mem_to_mem && (pclk == 0 || vm->pixelclock == 0)) { DSSERR("cannot calculate scaling settings: pclk is zero\n"); return -EINVAL; @@ -2332,12 +2513,12 @@ static int dispc_ovl_calc_scaling(unsigned long pclk, unsigned long lclk, } else { *x_predecim = max_decim_limit; *y_predecim = (rotation_type == OMAP_DSS_ROT_TILER && - dss_has_feature(FEAT_BURST_2D)) ? + dispc_has_feature(dispc, FEAT_BURST_2D)) ? 2 : max_decim_limit; } - decim_x = DIV_ROUND_UP(DIV_ROUND_UP(width, out_width), maxdownscale); - decim_y = DIV_ROUND_UP(DIV_ROUND_UP(height, out_height), maxdownscale); + decim_x = DIV_ROUND_UP(DIV_ROUND_UP(width, out_width), maxhdownscale); + decim_y = DIV_ROUND_UP(DIV_ROUND_UP(height, out_height), maxvdownscale); if (decim_x > *x_predecim || out_width > width * 8) return -EINVAL; @@ -2345,10 +2526,11 @@ static int dispc_ovl_calc_scaling(unsigned long pclk, unsigned long lclk, if (decim_y > *y_predecim || out_height > height * 8) return -EINVAL; - ret = dispc.feat->calc_scaling(pclk, lclk, vm, width, height, - out_width, out_height, fourcc, five_taps, - x_predecim, y_predecim, &decim_x, &decim_y, pos_x, &core_clk, - mem_to_mem); + ret = dispc->feat->calc_scaling(dispc, pclk, lclk, vm, width, height, + out_width, out_height, fourcc, + five_taps, x_predecim, y_predecim, + &decim_x, &decim_y, pos_x, &core_clk, + mem_to_mem); if (ret) return ret; @@ -2364,13 +2546,13 @@ static int dispc_ovl_calc_scaling(unsigned long pclk, unsigned long lclk, out_height / (height / decim_y), DIV_FRAC(out_height, height / decim_y), *five_taps ? 5 : 3, - core_clk, dispc_core_clk_rate()); + core_clk, dispc_core_clk_rate(dispc)); - if (!core_clk || core_clk > dispc_core_clk_rate()) { + if (!core_clk || core_clk > dispc_core_clk_rate(dispc)) { DSSERR("failed to set up scaling, " "required core clk rate = %lu Hz, " "current core clk rate = %lu Hz\n", - core_clk, dispc_core_clk_rate()); + core_clk, dispc_core_clk_rate(dispc)); return -EINVAL; } @@ -2379,34 +2561,53 @@ static int dispc_ovl_calc_scaling(unsigned long pclk, unsigned long lclk, return 0; } -static int dispc_ovl_setup_common(enum omap_plane_id plane, - enum omap_overlay_caps caps, u32 paddr, u32 p_uv_addr, - u16 screen_width, int pos_x, int pos_y, u16 width, u16 height, - u16 out_width, u16 out_height, u32 fourcc, - u8 rotation, u8 zorder, u8 pre_mult_alpha, - u8 global_alpha, enum omap_dss_rotation_type rotation_type, - bool replication, const struct videomode *vm, - bool mem_to_mem) +void dispc_ovl_get_max_size(struct dispc_device *dispc, u16 *width, u16 *height) +{ + *width = dispc->feat->ovl_width_max; + *height = dispc->feat->ovl_height_max; +} + +static int dispc_ovl_setup_common(struct dispc_device *dispc, + enum omap_plane_id plane, + enum omap_overlay_caps caps, + u32 paddr, u32 p_uv_addr, + u16 screen_width, int pos_x, int pos_y, + u16 width, u16 height, + u16 out_width, u16 out_height, + u32 fourcc, u8 rotation, u8 zorder, + u8 pre_mult_alpha, u8 global_alpha, + enum omap_dss_rotation_type rotation_type, + bool replication, const struct videomode *vm, + bool mem_to_mem, + enum drm_color_encoding color_encoding, + enum drm_color_range color_range) { bool five_taps = true; bool fieldmode = false; int r, cconv = 0; - unsigned offset0, offset1; + unsigned int offset0, offset1; s32 row_inc; s32 pix_inc; - u16 frame_width, frame_height; + u16 frame_width; unsigned int field_offset = 0; u16 in_height = height; u16 in_width = width; int x_predecim = 1, y_predecim = 1; bool ilace = !!(vm->flags & DISPLAY_FLAGS_INTERLACED); - unsigned long pclk = dispc_plane_pclk_rate(plane); - unsigned long lclk = dispc_plane_lclk_rate(plane); + unsigned long pclk = dispc_plane_pclk_rate(dispc, plane); + unsigned long lclk = dispc_plane_lclk_rate(dispc, plane); + const struct drm_format_info *info; + + info = drm_format_info(fourcc); + + /* when setting up WB, dispc_plane_pclk_rate() returns 0 */ + if (plane == OMAP_DSS_WB) + pclk = vm->pixelclock; if (paddr == 0 && rotation_type != OMAP_DSS_ROT_TILER) return -EINVAL; - if (format_is_yuv(fourcc) && (in_width & 1)) { + if (info->is_yuv && (in_width & 1)) { DSSERR("input width %d is not even for YUV format\n", in_width); return -EINVAL; } @@ -2414,27 +2615,28 @@ static int dispc_ovl_setup_common(enum omap_plane_id plane, out_width = out_width == 0 ? width : out_width; out_height = out_height == 0 ? height : out_height; - if (ilace && height == out_height) - fieldmode = true; + if (plane != OMAP_DSS_WB) { + if (ilace && height == out_height) + fieldmode = true; - if (ilace) { - if (fieldmode) - in_height /= 2; - pos_y /= 2; - out_height /= 2; + if (ilace) { + if (fieldmode) + in_height /= 2; + pos_y /= 2; + out_height /= 2; - DSSDBG("adjusting for ilace: height %d, pos_y %d, " - "out_height %d\n", in_height, pos_y, - out_height); + DSSDBG("adjusting for ilace: height %d, pos_y %d, out_height %d\n", + in_height, pos_y, out_height); + } } - if (!dss_feat_color_mode_supported(plane, fourcc)) + if (!dispc_ovl_color_mode_supported(dispc, plane, fourcc)) return -EINVAL; - r = dispc_ovl_calc_scaling(pclk, lclk, caps, vm, in_width, - in_height, out_width, out_height, fourcc, - &five_taps, &x_predecim, &y_predecim, pos_x, - rotation_type, mem_to_mem); + r = dispc_ovl_calc_scaling(dispc, plane, pclk, lclk, caps, vm, in_width, + in_height, out_width, out_height, fourcc, + &five_taps, &x_predecim, &y_predecim, pos_x, + rotation_type, mem_to_mem); if (r) return r; @@ -2445,7 +2647,7 @@ static int dispc_ovl_setup_common(enum omap_plane_id plane, DSSDBG("predecimation %d x %x, new input size %d x %d\n", x_predecim, y_predecim, in_width, in_height); - if (format_is_yuv(fourcc) && (in_width & 1)) { + if (info->is_yuv && (in_width & 1)) { DSSDBG("predecimated input width is not even for YUV format\n"); DSSDBG("adjusting input width %d -> %d\n", in_width, in_width & ~1); @@ -2453,7 +2655,7 @@ static int dispc_ovl_setup_common(enum omap_plane_id plane, in_width &= ~1; } - if (format_is_yuv(fourcc)) + if (info->is_yuv) cconv = 1; if (ilace && !fieldmode) { @@ -2479,13 +2681,10 @@ static int dispc_ovl_setup_common(enum omap_plane_id plane, row_inc = 0; pix_inc = 0; - if (plane == OMAP_DSS_WB) { + if (plane == OMAP_DSS_WB) frame_width = out_width; - frame_height = out_height; - } else { + else frame_width = in_width; - frame_height = height; - } calc_offset(screen_width, frame_width, fourcc, fieldmode, field_offset, @@ -2496,60 +2695,65 @@ static int dispc_ovl_setup_common(enum omap_plane_id plane, DSSDBG("offset0 %u, offset1 %u, row_inc %d, pix_inc %d\n", offset0, offset1, row_inc, pix_inc); - dispc_ovl_set_color_mode(plane, fourcc); + dispc_ovl_set_color_mode(dispc, plane, fourcc); - dispc_ovl_configure_burst_type(plane, rotation_type); + dispc_ovl_configure_burst_type(dispc, plane, rotation_type); - if (dispc.feat->reverse_ilace_field_order) + if (dispc->feat->reverse_ilace_field_order) swap(offset0, offset1); - dispc_ovl_set_ba0(plane, paddr + offset0); - dispc_ovl_set_ba1(plane, paddr + offset1); + dispc_ovl_set_ba0(dispc, plane, paddr + offset0); + dispc_ovl_set_ba1(dispc, plane, paddr + offset1); if (fourcc == DRM_FORMAT_NV12) { - dispc_ovl_set_ba0_uv(plane, p_uv_addr + offset0); - dispc_ovl_set_ba1_uv(plane, p_uv_addr + offset1); + dispc_ovl_set_ba0_uv(dispc, plane, p_uv_addr + offset0); + dispc_ovl_set_ba1_uv(dispc, plane, p_uv_addr + offset1); } - if (dispc.feat->last_pixel_inc_missing) + if (dispc->feat->last_pixel_inc_missing) row_inc += pix_inc - 1; - dispc_ovl_set_row_inc(plane, row_inc); - dispc_ovl_set_pix_inc(plane, pix_inc); + dispc_ovl_set_row_inc(dispc, plane, row_inc); + dispc_ovl_set_pix_inc(dispc, plane, pix_inc); DSSDBG("%d,%d %dx%d -> %dx%d\n", pos_x, pos_y, in_width, in_height, out_width, out_height); - dispc_ovl_set_pos(plane, caps, pos_x, pos_y); + dispc_ovl_set_pos(dispc, plane, caps, pos_x, pos_y); - dispc_ovl_set_input_size(plane, in_width, in_height); + dispc_ovl_set_input_size(dispc, plane, in_width, in_height); if (caps & OMAP_DSS_OVL_CAP_SCALE) { - dispc_ovl_set_scaling(plane, in_width, in_height, out_width, - out_height, ilace, five_taps, fieldmode, - fourcc, rotation); - dispc_ovl_set_output_size(plane, out_width, out_height); - dispc_ovl_set_vid_color_conv(plane, cconv); + dispc_ovl_set_scaling(dispc, plane, in_width, in_height, + out_width, out_height, ilace, five_taps, + fieldmode, fourcc, rotation); + dispc_ovl_set_output_size(dispc, plane, out_width, out_height); + dispc_ovl_set_vid_color_conv(dispc, plane, cconv); + + if (plane != OMAP_DSS_WB) + dispc_ovl_set_csc(dispc, plane, color_encoding, color_range); } - dispc_ovl_set_rotation_attrs(plane, rotation, rotation_type, fourcc); + dispc_ovl_set_rotation_attrs(dispc, plane, rotation, rotation_type, + fourcc); - dispc_ovl_set_zorder(plane, caps, zorder); - dispc_ovl_set_pre_mult_alpha(plane, caps, pre_mult_alpha); - dispc_ovl_setup_global_alpha(plane, caps, global_alpha); + dispc_ovl_set_zorder(dispc, plane, caps, zorder); + dispc_ovl_set_pre_mult_alpha(dispc, plane, caps, pre_mult_alpha); + dispc_ovl_setup_global_alpha(dispc, plane, caps, global_alpha); - dispc_ovl_enable_replication(plane, caps, replication); + dispc_ovl_enable_replication(dispc, plane, caps, replication); return 0; } -static int dispc_ovl_setup(enum omap_plane_id plane, - const struct omap_overlay_info *oi, - const struct videomode *vm, bool mem_to_mem, - enum omap_channel channel) +int dispc_ovl_setup(struct dispc_device *dispc, + enum omap_plane_id plane, + const struct omap_overlay_info *oi, + const struct videomode *vm, bool mem_to_mem, + enum omap_channel channel) { int r; - enum omap_overlay_caps caps = dss_feat_get_overlay_caps(plane); + enum omap_overlay_caps caps = dispc->feat->overlay_caps[plane]; const bool replication = true; DSSDBG("dispc_ovl_setup %d, pa %pad, pa_uv %pad, sw %d, %d,%d, %dx%d ->" @@ -2558,190 +2762,128 @@ static int dispc_ovl_setup(enum omap_plane_id plane, oi->pos_y, oi->width, oi->height, oi->out_width, oi->out_height, oi->fourcc, oi->rotation, channel, replication); - dispc_ovl_set_channel_out(plane, channel); + dispc_ovl_set_channel_out(dispc, plane, channel); - r = dispc_ovl_setup_common(plane, caps, oi->paddr, oi->p_uv_addr, + r = dispc_ovl_setup_common(dispc, plane, caps, 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->fourcc, oi->rotation, oi->zorder, oi->pre_mult_alpha, oi->global_alpha, - oi->rotation_type, replication, vm, mem_to_mem); - - return r; -} - -int dispc_wb_setup(const struct omap_dss_writeback_info *wi, - bool mem_to_mem, const struct videomode *vm) -{ - int r; - u32 l; - enum omap_plane_id plane = OMAP_DSS_WB; - const int pos_x = 0, pos_y = 0; - const u8 zorder = 0, global_alpha = 0; - const bool replication = true; - bool truncation; - int in_width = vm->hactive; - int in_height = vm->vactive; - enum omap_overlay_caps caps = - OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA; - - DSSDBG("dispc_wb_setup, pa %x, pa_uv %x, %d,%d -> %dx%d, cmode %x, " - "rot %d\n", wi->paddr, wi->p_uv_addr, in_width, - in_height, wi->width, wi->height, wi->fourcc, wi->rotation); - - r = dispc_ovl_setup_common(plane, caps, wi->paddr, wi->p_uv_addr, - wi->buf_width, pos_x, pos_y, in_width, in_height, wi->width, - wi->height, wi->fourcc, wi->rotation, zorder, - wi->pre_mult_alpha, global_alpha, wi->rotation_type, - replication, vm, mem_to_mem); - - switch (wi->fourcc) { - case DRM_FORMAT_RGB565: - case DRM_FORMAT_RGB888: - case DRM_FORMAT_ARGB4444: - case DRM_FORMAT_RGBA4444: - case DRM_FORMAT_RGBX4444: - case DRM_FORMAT_ARGB1555: - case DRM_FORMAT_XRGB1555: - case DRM_FORMAT_XRGB4444: - truncation = true; - break; - default: - truncation = false; - break; - } - - /* setup extra DISPC_WB_ATTRIBUTES */ - l = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane)); - l = FLD_MOD(l, truncation, 10, 10); /* TRUNCATIONENABLE */ - l = FLD_MOD(l, mem_to_mem, 19, 19); /* WRITEBACKMODE */ - if (mem_to_mem) - l = FLD_MOD(l, 1, 26, 24); /* CAPTUREMODE */ - else - l = FLD_MOD(l, 0, 26, 24); /* CAPTUREMODE */ - dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), l); - - if (mem_to_mem) { - /* WBDELAYCOUNT */ - REG_FLD_MOD(DISPC_OVL_ATTRIBUTES2(plane), 0, 7, 0); - } else { - int wbdelay; - - wbdelay = min(vm->vfront_porch + - vm->vsync_len + vm->vback_porch, (u32)255); - - /* WBDELAYCOUNT */ - REG_FLD_MOD(DISPC_OVL_ATTRIBUTES2(plane), wbdelay, 7, 0); - } + oi->rotation_type, replication, vm, mem_to_mem, + oi->color_encoding, oi->color_range); return r; } -static int dispc_ovl_enable(enum omap_plane_id plane, bool enable) +int dispc_ovl_enable(struct dispc_device *dispc, + enum omap_plane_id plane, bool enable) { DSSDBG("dispc_enable_plane %d, %d\n", plane, enable); - REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable ? 1 : 0, 0, 0); + REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES(plane), enable ? 1 : 0, 0, 0); return 0; } -static enum omap_dss_output_id dispc_mgr_get_supported_outputs(enum omap_channel channel) -{ - return dss_feat_get_supported_outputs(channel); -} - -static void dispc_lcd_enable_signal_polarity(bool act_high) +static void dispc_lcd_enable_signal_polarity(struct dispc_device *dispc, + bool act_high) { - if (!dss_has_feature(FEAT_LCDENABLEPOL)) + if (!dispc_has_feature(dispc, FEAT_LCDENABLEPOL)) return; - REG_FLD_MOD(DISPC_CONTROL, act_high ? 1 : 0, 29, 29); + REG_FLD_MOD(dispc, DISPC_CONTROL, act_high ? 1 : 0, 29, 29); } -void dispc_lcd_enable_signal(bool enable) +void dispc_lcd_enable_signal(struct dispc_device *dispc, bool enable) { - if (!dss_has_feature(FEAT_LCDENABLESIGNAL)) + if (!dispc_has_feature(dispc, FEAT_LCDENABLESIGNAL)) return; - REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 28, 28); + REG_FLD_MOD(dispc, DISPC_CONTROL, enable ? 1 : 0, 28, 28); } -void dispc_pck_free_enable(bool enable) +void dispc_pck_free_enable(struct dispc_device *dispc, bool enable) { - if (!dss_has_feature(FEAT_PCKFREEENABLE)) + if (!dispc_has_feature(dispc, FEAT_PCKFREEENABLE)) return; - REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 27, 27); + REG_FLD_MOD(dispc, DISPC_CONTROL, enable ? 1 : 0, 27, 27); } -static int dispc_get_num_mgrs(void) +static void dispc_mgr_enable_fifohandcheck(struct dispc_device *dispc, + enum omap_channel channel, + bool enable) { - return dss_feat_get_num_mgrs(); -} - -static void dispc_mgr_enable_fifohandcheck(enum omap_channel channel, bool enable) -{ - mgr_fld_write(channel, DISPC_MGR_FLD_FIFOHANDCHECK, enable); + mgr_fld_write(dispc, channel, DISPC_MGR_FLD_FIFOHANDCHECK, enable); } -static void dispc_mgr_set_lcd_type_tft(enum omap_channel channel) +static void dispc_mgr_set_lcd_type_tft(struct dispc_device *dispc, + enum omap_channel channel) { - mgr_fld_write(channel, DISPC_MGR_FLD_STNTFT, 1); + mgr_fld_write(dispc, channel, DISPC_MGR_FLD_STNTFT, 1); } -static void dispc_set_loadmode(enum omap_dss_load_mode mode) +static void dispc_set_loadmode(struct dispc_device *dispc, + enum omap_dss_load_mode mode) { - REG_FLD_MOD(DISPC_CONFIG, mode, 2, 1); + REG_FLD_MOD(dispc, DISPC_CONFIG, mode, 2, 1); } -static void dispc_mgr_set_default_color(enum omap_channel channel, u32 color) +static void dispc_mgr_set_default_color(struct dispc_device *dispc, + enum omap_channel channel, u32 color) { - dispc_write_reg(DISPC_DEFAULT_COLOR(channel), color); + dispc_write_reg(dispc, DISPC_DEFAULT_COLOR(channel), color); } -static void dispc_mgr_set_trans_key(enum omap_channel ch, - enum omap_dss_trans_key_type type, - u32 trans_key) +static void dispc_mgr_set_trans_key(struct dispc_device *dispc, + enum omap_channel ch, + enum omap_dss_trans_key_type type, + u32 trans_key) { - mgr_fld_write(ch, DISPC_MGR_FLD_TCKSELECTION, type); + mgr_fld_write(dispc, ch, DISPC_MGR_FLD_TCKSELECTION, type); - dispc_write_reg(DISPC_TRANS_COLOR(ch), trans_key); + dispc_write_reg(dispc, DISPC_TRANS_COLOR(ch), trans_key); } -static void dispc_mgr_enable_trans_key(enum omap_channel ch, bool enable) +static void dispc_mgr_enable_trans_key(struct dispc_device *dispc, + enum omap_channel ch, bool enable) { - mgr_fld_write(ch, DISPC_MGR_FLD_TCKENABLE, enable); + mgr_fld_write(dispc, ch, DISPC_MGR_FLD_TCKENABLE, enable); } -static void dispc_mgr_enable_alpha_fixed_zorder(enum omap_channel ch, - bool enable) +static void dispc_mgr_enable_alpha_fixed_zorder(struct dispc_device *dispc, + enum omap_channel ch, + bool enable) { - if (!dss_has_feature(FEAT_ALPHA_FIXED_ZORDER)) + if (!dispc_has_feature(dispc, FEAT_ALPHA_FIXED_ZORDER)) return; if (ch == OMAP_DSS_CHANNEL_LCD) - REG_FLD_MOD(DISPC_CONFIG, enable, 18, 18); + REG_FLD_MOD(dispc, DISPC_CONFIG, enable, 18, 18); else if (ch == OMAP_DSS_CHANNEL_DIGIT) - REG_FLD_MOD(DISPC_CONFIG, enable, 19, 19); + REG_FLD_MOD(dispc, DISPC_CONFIG, enable, 19, 19); } -static void dispc_mgr_setup(enum omap_channel channel, - const struct omap_overlay_manager_info *info) +void dispc_mgr_setup(struct dispc_device *dispc, + enum omap_channel channel, + const struct omap_overlay_manager_info *info) { - 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, + dispc_mgr_set_default_color(dispc, channel, info->default_color); + dispc_mgr_set_trans_key(dispc, channel, info->trans_key_type, + info->trans_key); + dispc_mgr_enable_trans_key(dispc, channel, info->trans_enabled); + dispc_mgr_enable_alpha_fixed_zorder(dispc, 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); + if (dispc_has_feature(dispc, FEAT_CPR)) { + dispc_mgr_enable_cpr(dispc, channel, info->cpr_enable); + dispc_mgr_set_cpr_coef(dispc, channel, &info->cpr_coefs); } } -static void dispc_mgr_set_tft_data_lines(enum omap_channel channel, u8 data_lines) +static void dispc_mgr_set_tft_data_lines(struct dispc_device *dispc, + enum omap_channel channel, + u8 data_lines) { int code; @@ -2763,10 +2905,11 @@ static void dispc_mgr_set_tft_data_lines(enum omap_channel channel, u8 data_line return; } - mgr_fld_write(channel, DISPC_MGR_FLD_TFTDATALINES, code); + mgr_fld_write(dispc, channel, DISPC_MGR_FLD_TFTDATALINES, code); } -static void dispc_mgr_set_io_pad_mode(enum dss_io_pad_mode mode) +static void dispc_mgr_set_io_pad_mode(struct dispc_device *dispc, + enum dss_io_pad_mode mode) { u32 l; int gpout0, gpout1; @@ -2789,128 +2932,115 @@ static void dispc_mgr_set_io_pad_mode(enum dss_io_pad_mode mode) return; } - l = dispc_read_reg(DISPC_CONTROL); + l = dispc_read_reg(dispc, DISPC_CONTROL); l = FLD_MOD(l, gpout0, 15, 15); l = FLD_MOD(l, gpout1, 16, 16); - dispc_write_reg(DISPC_CONTROL, l); + dispc_write_reg(dispc, DISPC_CONTROL, l); } -static void dispc_mgr_enable_stallmode(enum omap_channel channel, bool enable) +static void dispc_mgr_enable_stallmode(struct dispc_device *dispc, + enum omap_channel channel, bool enable) { - mgr_fld_write(channel, DISPC_MGR_FLD_STALLMODE, enable); + mgr_fld_write(dispc, channel, DISPC_MGR_FLD_STALLMODE, enable); } -static void dispc_mgr_set_lcd_config(enum omap_channel channel, - const struct dss_lcd_mgr_config *config) +void dispc_mgr_set_lcd_config(struct dispc_device *dispc, + enum omap_channel channel, + const struct dss_lcd_mgr_config *config) { - dispc_mgr_set_io_pad_mode(config->io_pad_mode); + dispc_mgr_set_io_pad_mode(dispc, config->io_pad_mode); - dispc_mgr_enable_stallmode(channel, config->stallmode); - dispc_mgr_enable_fifohandcheck(channel, config->fifohandcheck); + dispc_mgr_enable_stallmode(dispc, channel, config->stallmode); + dispc_mgr_enable_fifohandcheck(dispc, channel, config->fifohandcheck); - dispc_mgr_set_clock_div(channel, &config->clock_info); + dispc_mgr_set_clock_div(dispc, channel, &config->clock_info); - dispc_mgr_set_tft_data_lines(channel, config->video_port_width); + dispc_mgr_set_tft_data_lines(dispc, channel, config->video_port_width); - dispc_lcd_enable_signal_polarity(config->lcden_sig_polarity); + dispc_lcd_enable_signal_polarity(dispc, config->lcden_sig_polarity); - dispc_mgr_set_lcd_type_tft(channel); + dispc_mgr_set_lcd_type_tft(dispc, channel); } -static bool _dispc_mgr_size_ok(u16 width, u16 height) +static bool _dispc_mgr_size_ok(struct dispc_device *dispc, + u16 width, u16 height) { - return width <= dispc.feat->mgr_width_max && - height <= dispc.feat->mgr_height_max; + return width <= dispc->feat->mgr_width_max && + height <= dispc->feat->mgr_height_max; } -static bool _dispc_lcd_timings_ok(int hsync_len, int hfp, int hbp, - int vsw, int vfp, int vbp) +static bool _dispc_lcd_timings_ok(struct dispc_device *dispc, + int hsync_len, int hfp, int hbp, + int vsw, int vfp, int vbp) { - if (hsync_len < 1 || hsync_len > dispc.feat->sw_max || - hfp < 1 || hfp > dispc.feat->hp_max || - hbp < 1 || hbp > dispc.feat->hp_max || - vsw < 1 || vsw > dispc.feat->sw_max || - vfp < 0 || vfp > dispc.feat->vp_max || - vbp < 0 || vbp > dispc.feat->vp_max) + if (hsync_len < 1 || hsync_len > dispc->feat->sw_max || + hfp < 1 || hfp > dispc->feat->hp_max || + hbp < 1 || hbp > dispc->feat->hp_max || + vsw < 1 || vsw > dispc->feat->sw_max || + vfp < 0 || vfp > dispc->feat->vp_max || + vbp < 0 || vbp > dispc->feat->vp_max) return false; return true; } -static bool _dispc_mgr_pclk_ok(enum omap_channel channel, - unsigned long pclk) +static bool _dispc_mgr_pclk_ok(struct dispc_device *dispc, + enum omap_channel channel, + unsigned long pclk) { if (dss_mgr_is_lcd(channel)) - return pclk <= dispc.feat->max_lcd_pclk; + return pclk <= dispc->feat->max_lcd_pclk; else - return pclk <= dispc.feat->max_tv_pclk; + return pclk <= dispc->feat->max_tv_pclk; } -bool dispc_mgr_timings_ok(enum omap_channel channel, const struct videomode *vm) +int dispc_mgr_check_timings(struct dispc_device *dispc, + enum omap_channel channel, + const struct videomode *vm) { - if (!_dispc_mgr_size_ok(vm->hactive, vm->vactive)) - return false; + if (!_dispc_mgr_size_ok(dispc, vm->hactive, vm->vactive)) + return MODE_BAD; - if (!_dispc_mgr_pclk_ok(channel, vm->pixelclock)) - return false; + if (!_dispc_mgr_pclk_ok(dispc, channel, vm->pixelclock)) + return MODE_BAD; if (dss_mgr_is_lcd(channel)) { /* TODO: OMAP4+ supports interlace for LCD outputs */ if (vm->flags & DISPLAY_FLAGS_INTERLACED) - return false; + return MODE_BAD; - if (!_dispc_lcd_timings_ok(vm->hsync_len, + if (!_dispc_lcd_timings_ok(dispc, vm->hsync_len, vm->hfront_porch, vm->hback_porch, vm->vsync_len, vm->vfront_porch, vm->vback_porch)) - return false; + return MODE_BAD; } - return true; + return MODE_OK; } -static void _dispc_mgr_set_lcd_timings(enum omap_channel channel, +static void _dispc_mgr_set_lcd_timings(struct dispc_device *dispc, + enum omap_channel channel, const struct videomode *vm) { u32 timing_h, timing_v, l; bool onoff, rf, ipc, vs, hs, de; - timing_h = FLD_VAL(vm->hsync_len - 1, dispc.feat->sw_start, 0) | - FLD_VAL(vm->hfront_porch - 1, dispc.feat->fp_start, 8) | - FLD_VAL(vm->hback_porch - 1, dispc.feat->bp_start, 20); - timing_v = FLD_VAL(vm->vsync_len - 1, dispc.feat->sw_start, 0) | - FLD_VAL(vm->vfront_porch, dispc.feat->fp_start, 8) | - FLD_VAL(vm->vback_porch, dispc.feat->bp_start, 20); + timing_h = FLD_VAL(vm->hsync_len - 1, dispc->feat->sw_start, 0) | + FLD_VAL(vm->hfront_porch - 1, dispc->feat->fp_start, 8) | + FLD_VAL(vm->hback_porch - 1, dispc->feat->bp_start, 20); + timing_v = FLD_VAL(vm->vsync_len - 1, dispc->feat->sw_start, 0) | + FLD_VAL(vm->vfront_porch, dispc->feat->fp_start, 8) | + FLD_VAL(vm->vback_porch, dispc->feat->bp_start, 20); - dispc_write_reg(DISPC_TIMING_H(channel), timing_h); - dispc_write_reg(DISPC_TIMING_V(channel), timing_v); + dispc_write_reg(dispc, DISPC_TIMING_H(channel), timing_h); + dispc_write_reg(dispc, DISPC_TIMING_V(channel), timing_v); - if (vm->flags & DISPLAY_FLAGS_VSYNC_HIGH) - vs = false; - else - vs = true; - - if (vm->flags & DISPLAY_FLAGS_HSYNC_HIGH) - hs = false; - else - hs = true; - - if (vm->flags & DISPLAY_FLAGS_DE_HIGH) - de = false; - else - de = true; - - if (vm->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE) - ipc = false; - else - ipc = true; - - /* always use the 'rf' setting */ - onoff = true; - - if (vm->flags & DISPLAY_FLAGS_SYNC_POSEDGE) - rf = true; - else - rf = false; + vs = !!(vm->flags & DISPLAY_FLAGS_VSYNC_LOW); + hs = !!(vm->flags & DISPLAY_FLAGS_HSYNC_LOW); + de = !!(vm->flags & DISPLAY_FLAGS_DE_LOW); + ipc = !!(vm->flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE); + onoff = true; /* always use the 'rf' setting */ + rf = !!(vm->flags & DISPLAY_FLAGS_SYNC_POSEDGE); l = FLD_VAL(onoff, 17, 17) | FLD_VAL(rf, 16, 16) | @@ -2920,12 +3050,12 @@ static void _dispc_mgr_set_lcd_timings(enum omap_channel channel, FLD_VAL(vs, 12, 12); /* always set ALIGN bit when available */ - if (dispc.feat->supports_sync_align) + if (dispc->feat->supports_sync_align) l |= (1 << 18); - dispc_write_reg(DISPC_POL_FREQ(channel), l); + dispc_write_reg(dispc, DISPC_POL_FREQ(channel), l); - if (dispc.syscon_pol) { + if (dispc->syscon_pol) { const int shifts[] = { [OMAP_DSS_CHANNEL_LCD] = 0, [OMAP_DSS_CHANNEL_LCD2] = 1, @@ -2940,8 +3070,8 @@ static void _dispc_mgr_set_lcd_timings(enum omap_channel channel, mask <<= 16 + shifts[channel]; val <<= 16 + shifts[channel]; - regmap_update_bits(dispc.syscon_pol, dispc.syscon_pol_offset, - mask, val); + regmap_update_bits(dispc->syscon_pol, dispc->syscon_pol_offset, + mask, val); } } @@ -2956,22 +3086,23 @@ static int vm_flag_to_int(enum display_flags flags, enum display_flags high, } /* change name to mode? */ -static void dispc_mgr_set_timings(enum omap_channel channel, - const struct videomode *vm) +void dispc_mgr_set_timings(struct dispc_device *dispc, + enum omap_channel channel, + const struct videomode *vm) { - unsigned xtot, ytot; + unsigned int xtot, ytot; unsigned long ht, vt; struct videomode t = *vm; DSSDBG("channel %d xres %u yres %u\n", channel, t.hactive, t.vactive); - if (!dispc_mgr_timings_ok(channel, &t)) { + if (dispc_mgr_check_timings(dispc, channel, &t)) { BUG(); return; } if (dss_mgr_is_lcd(channel)) { - _dispc_mgr_set_lcd_timings(channel, &t); + _dispc_mgr_set_lcd_timings(dispc, channel, &t); xtot = t.hactive + t.hfront_porch + t.hsync_len + t.hback_porch; ytot = t.vactive + t.vfront_porch + t.vsync_len + t.vback_porch; @@ -2995,52 +3126,54 @@ static void dispc_mgr_set_timings(enum omap_channel channel, if (t.flags & DISPLAY_FLAGS_INTERLACED) t.vactive /= 2; - if (dispc.feat->supports_double_pixel) - REG_FLD_MOD(DISPC_CONTROL, + if (dispc->feat->supports_double_pixel) + REG_FLD_MOD(dispc, DISPC_CONTROL, !!(t.flags & DISPLAY_FLAGS_DOUBLECLK), 19, 17); } - dispc_mgr_set_size(channel, t.hactive, t.vactive); + dispc_mgr_set_size(dispc, channel, t.hactive, t.vactive); } -static void dispc_mgr_set_lcd_divisor(enum omap_channel channel, u16 lck_div, - u16 pck_div) +static void dispc_mgr_set_lcd_divisor(struct dispc_device *dispc, + enum omap_channel channel, u16 lck_div, + u16 pck_div) { BUG_ON(lck_div < 1); BUG_ON(pck_div < 1); - dispc_write_reg(DISPC_DIVISORo(channel), + dispc_write_reg(dispc, DISPC_DIVISORo(channel), FLD_VAL(lck_div, 23, 16) | FLD_VAL(pck_div, 7, 0)); - if (!dss_has_feature(FEAT_CORE_CLK_DIV) && + if (!dispc_has_feature(dispc, FEAT_CORE_CLK_DIV) && channel == OMAP_DSS_CHANNEL_LCD) - dispc.core_clk_rate = dispc_fclk_rate() / lck_div; + dispc->core_clk_rate = dispc_fclk_rate(dispc) / lck_div; } -static void dispc_mgr_get_lcd_divisor(enum omap_channel channel, int *lck_div, - int *pck_div) +static void dispc_mgr_get_lcd_divisor(struct dispc_device *dispc, + enum omap_channel channel, int *lck_div, + int *pck_div) { u32 l; - l = dispc_read_reg(DISPC_DIVISORo(channel)); + l = dispc_read_reg(dispc, DISPC_DIVISORo(channel)); *lck_div = FLD_GET(l, 23, 16); *pck_div = FLD_GET(l, 7, 0); } -static unsigned long dispc_fclk_rate(void) +static unsigned long dispc_fclk_rate(struct dispc_device *dispc) { unsigned long r; enum dss_clk_source src; - src = dss_get_dispc_clk_source(); + src = dss_get_dispc_clk_source(dispc->dss); if (src == DSS_CLK_SRC_FCK) { - r = dss_get_dispc_clk_rate(); + r = dss_get_dispc_clk_rate(dispc->dss); } else { struct dss_pll *pll; - unsigned clkout_idx; + unsigned int clkout_idx; - pll = dss_pll_find_by_src(src); + pll = dss_pll_find_by_src(dispc->dss, src); clkout_idx = dss_pll_get_clkout_idx_for_src(src); r = pll->cinfo.clkout[clkout_idx]; @@ -3049,7 +3182,8 @@ static unsigned long dispc_fclk_rate(void) return r; } -static unsigned long dispc_mgr_lclk_rate(enum omap_channel channel) +static unsigned long dispc_mgr_lclk_rate(struct dispc_device *dispc, + enum omap_channel channel) { int lcd; unsigned long r; @@ -3057,28 +3191,29 @@ static unsigned long dispc_mgr_lclk_rate(enum omap_channel channel) /* for TV, LCLK rate is the FCLK rate */ if (!dss_mgr_is_lcd(channel)) - return dispc_fclk_rate(); + return dispc_fclk_rate(dispc); - src = dss_get_lcd_clk_source(channel); + src = dss_get_lcd_clk_source(dispc->dss, channel); if (src == DSS_CLK_SRC_FCK) { - r = dss_get_dispc_clk_rate(); + r = dss_get_dispc_clk_rate(dispc->dss); } else { struct dss_pll *pll; - unsigned clkout_idx; + unsigned int clkout_idx; - pll = dss_pll_find_by_src(src); + pll = dss_pll_find_by_src(dispc->dss, src); clkout_idx = dss_pll_get_clkout_idx_for_src(src); r = pll->cinfo.clkout[clkout_idx]; } - lcd = REG_GET(DISPC_DIVISORo(channel), 23, 16); + lcd = REG_GET(dispc, DISPC_DIVISORo(channel), 23, 16); return r / lcd; } -static unsigned long dispc_mgr_pclk_rate(enum omap_channel channel) +static unsigned long dispc_mgr_pclk_rate(struct dispc_device *dispc, + enum omap_channel channel) { unsigned long r; @@ -3086,109 +3221,115 @@ static unsigned long dispc_mgr_pclk_rate(enum omap_channel channel) int pcd; u32 l; - l = dispc_read_reg(DISPC_DIVISORo(channel)); + l = dispc_read_reg(dispc, DISPC_DIVISORo(channel)); pcd = FLD_GET(l, 7, 0); - r = dispc_mgr_lclk_rate(channel); + r = dispc_mgr_lclk_rate(dispc, channel); return r / pcd; } else { - return dispc.tv_pclk_rate; + return dispc->tv_pclk_rate; } } -void dispc_set_tv_pclk(unsigned long pclk) +void dispc_set_tv_pclk(struct dispc_device *dispc, unsigned long pclk) { - dispc.tv_pclk_rate = pclk; + dispc->tv_pclk_rate = pclk; } -static unsigned long dispc_core_clk_rate(void) +static unsigned long dispc_core_clk_rate(struct dispc_device *dispc) { - return dispc.core_clk_rate; + return dispc->core_clk_rate; } -static unsigned long dispc_plane_pclk_rate(enum omap_plane_id plane) +static unsigned long dispc_plane_pclk_rate(struct dispc_device *dispc, + enum omap_plane_id plane) { enum omap_channel channel; if (plane == OMAP_DSS_WB) return 0; - channel = dispc_ovl_get_channel_out(plane); + channel = dispc_ovl_get_channel_out(dispc, plane); - return dispc_mgr_pclk_rate(channel); + return dispc_mgr_pclk_rate(dispc, channel); } -static unsigned long dispc_plane_lclk_rate(enum omap_plane_id plane) +static unsigned long dispc_plane_lclk_rate(struct dispc_device *dispc, + enum omap_plane_id plane) { enum omap_channel channel; if (plane == OMAP_DSS_WB) return 0; - channel = dispc_ovl_get_channel_out(plane); + channel = dispc_ovl_get_channel_out(dispc, plane); - return dispc_mgr_lclk_rate(channel); + return dispc_mgr_lclk_rate(dispc, channel); } -static void dispc_dump_clocks_channel(struct seq_file *s, enum omap_channel channel) +static void dispc_dump_clocks_channel(struct dispc_device *dispc, + struct seq_file *s, + enum omap_channel channel) { int lcd, pcd; enum dss_clk_source lcd_clk_src; seq_printf(s, "- %s -\n", mgr_desc[channel].name); - lcd_clk_src = dss_get_lcd_clk_source(channel); + lcd_clk_src = dss_get_lcd_clk_source(dispc->dss, channel); seq_printf(s, "%s clk source = %s\n", mgr_desc[channel].name, dss_get_clk_source_name(lcd_clk_src)); - dispc_mgr_get_lcd_divisor(channel, &lcd, &pcd); + dispc_mgr_get_lcd_divisor(dispc, channel, &lcd, &pcd); seq_printf(s, "lck\t\t%-16lulck div\t%u\n", - dispc_mgr_lclk_rate(channel), lcd); + dispc_mgr_lclk_rate(dispc, channel), lcd); seq_printf(s, "pck\t\t%-16lupck div\t%u\n", - dispc_mgr_pclk_rate(channel), pcd); + dispc_mgr_pclk_rate(dispc, channel), pcd); } -void dispc_dump_clocks(struct seq_file *s) +void dispc_dump_clocks(struct dispc_device *dispc, struct seq_file *s) { + enum dss_clk_source dispc_clk_src; int lcd; u32 l; - enum dss_clk_source dispc_clk_src = dss_get_dispc_clk_source(); - if (dispc_runtime_get()) + if (dispc_runtime_get(dispc)) return; seq_printf(s, "- DISPC -\n"); + dispc_clk_src = dss_get_dispc_clk_source(dispc->dss); seq_printf(s, "dispc fclk source = %s\n", dss_get_clk_source_name(dispc_clk_src)); - seq_printf(s, "fck\t\t%-16lu\n", dispc_fclk_rate()); + seq_printf(s, "fck\t\t%-16lu\n", dispc_fclk_rate(dispc)); - if (dss_has_feature(FEAT_CORE_CLK_DIV)) { + if (dispc_has_feature(dispc, FEAT_CORE_CLK_DIV)) { seq_printf(s, "- DISPC-CORE-CLK -\n"); - l = dispc_read_reg(DISPC_DIVISOR); + l = dispc_read_reg(dispc, DISPC_DIVISOR); lcd = FLD_GET(l, 23, 16); seq_printf(s, "lck\t\t%-16lulck div\t%u\n", - (dispc_fclk_rate()/lcd), lcd); + (dispc_fclk_rate(dispc)/lcd), lcd); } - dispc_dump_clocks_channel(s, OMAP_DSS_CHANNEL_LCD); + dispc_dump_clocks_channel(dispc, s, OMAP_DSS_CHANNEL_LCD); - if (dss_has_feature(FEAT_MGR_LCD2)) - dispc_dump_clocks_channel(s, OMAP_DSS_CHANNEL_LCD2); - if (dss_has_feature(FEAT_MGR_LCD3)) - dispc_dump_clocks_channel(s, OMAP_DSS_CHANNEL_LCD3); + if (dispc_has_feature(dispc, FEAT_MGR_LCD2)) + dispc_dump_clocks_channel(dispc, s, OMAP_DSS_CHANNEL_LCD2); + if (dispc_has_feature(dispc, FEAT_MGR_LCD3)) + dispc_dump_clocks_channel(dispc, s, OMAP_DSS_CHANNEL_LCD3); - dispc_runtime_put(); + dispc_runtime_put(dispc); } -static void dispc_dump_regs(struct seq_file *s) +static int dispc_dump_regs(struct seq_file *s, void *p) { + struct dispc_device *dispc = s->private; int i, j; const char *mgr_names[] = { [OMAP_DSS_CHANNEL_LCD] = "LCD", @@ -3205,186 +3346,190 @@ static void dispc_dump_regs(struct seq_file *s) }; const char **p_names; -#define DUMPREG(r) seq_printf(s, "%-50s %08x\n", #r, dispc_read_reg(r)) +#define DUMPREG(dispc, r) \ + seq_printf(s, "%-50s %08x\n", #r, dispc_read_reg(dispc, r)) - if (dispc_runtime_get()) - return; + if (dispc_runtime_get(dispc)) + return 0; /* DISPC common registers */ - DUMPREG(DISPC_REVISION); - DUMPREG(DISPC_SYSCONFIG); - DUMPREG(DISPC_SYSSTATUS); - DUMPREG(DISPC_IRQSTATUS); - DUMPREG(DISPC_IRQENABLE); - DUMPREG(DISPC_CONTROL); - DUMPREG(DISPC_CONFIG); - DUMPREG(DISPC_CAPABLE); - DUMPREG(DISPC_LINE_STATUS); - DUMPREG(DISPC_LINE_NUMBER); - if (dss_has_feature(FEAT_ALPHA_FIXED_ZORDER) || - dss_has_feature(FEAT_ALPHA_FREE_ZORDER)) - DUMPREG(DISPC_GLOBAL_ALPHA); - if (dss_has_feature(FEAT_MGR_LCD2)) { - DUMPREG(DISPC_CONTROL2); - DUMPREG(DISPC_CONFIG2); + DUMPREG(dispc, DISPC_REVISION); + DUMPREG(dispc, DISPC_SYSCONFIG); + DUMPREG(dispc, DISPC_SYSSTATUS); + DUMPREG(dispc, DISPC_IRQSTATUS); + DUMPREG(dispc, DISPC_IRQENABLE); + DUMPREG(dispc, DISPC_CONTROL); + DUMPREG(dispc, DISPC_CONFIG); + DUMPREG(dispc, DISPC_CAPABLE); + DUMPREG(dispc, DISPC_LINE_STATUS); + DUMPREG(dispc, DISPC_LINE_NUMBER); + if (dispc_has_feature(dispc, FEAT_ALPHA_FIXED_ZORDER) || + dispc_has_feature(dispc, FEAT_ALPHA_FREE_ZORDER)) + DUMPREG(dispc, DISPC_GLOBAL_ALPHA); + if (dispc_has_feature(dispc, FEAT_MGR_LCD2)) { + DUMPREG(dispc, DISPC_CONTROL2); + DUMPREG(dispc, DISPC_CONFIG2); } - if (dss_has_feature(FEAT_MGR_LCD3)) { - DUMPREG(DISPC_CONTROL3); - DUMPREG(DISPC_CONFIG3); + if (dispc_has_feature(dispc, FEAT_MGR_LCD3)) { + DUMPREG(dispc, DISPC_CONTROL3); + DUMPREG(dispc, DISPC_CONFIG3); } - if (dss_has_feature(FEAT_MFLAG)) - DUMPREG(DISPC_GLOBAL_MFLAG_ATTRIBUTE); + if (dispc_has_feature(dispc, FEAT_MFLAG)) + DUMPREG(dispc, DISPC_GLOBAL_MFLAG_ATTRIBUTE); #undef DUMPREG #define DISPC_REG(i, name) name(i) -#define DUMPREG(i, r) seq_printf(s, "%s(%s)%*s %08x\n", #r, p_names[i], \ +#define DUMPREG(dispc, i, r) seq_printf(s, "%s(%s)%*s %08x\n", #r, p_names[i], \ (int)(48 - strlen(#r) - strlen(p_names[i])), " ", \ - dispc_read_reg(DISPC_REG(i, r))) + dispc_read_reg(dispc, DISPC_REG(i, r))) p_names = mgr_names; /* DISPC channel specific registers */ - for (i = 0; i < dss_feat_get_num_mgrs(); i++) { - DUMPREG(i, DISPC_DEFAULT_COLOR); - DUMPREG(i, DISPC_TRANS_COLOR); - DUMPREG(i, DISPC_SIZE_MGR); + for (i = 0; i < dispc_get_num_mgrs(dispc); i++) { + DUMPREG(dispc, i, DISPC_DEFAULT_COLOR); + DUMPREG(dispc, i, DISPC_TRANS_COLOR); + DUMPREG(dispc, i, DISPC_SIZE_MGR); if (i == OMAP_DSS_CHANNEL_DIGIT) continue; - DUMPREG(i, DISPC_TIMING_H); - DUMPREG(i, DISPC_TIMING_V); - DUMPREG(i, DISPC_POL_FREQ); - DUMPREG(i, DISPC_DIVISORo); + DUMPREG(dispc, i, DISPC_TIMING_H); + DUMPREG(dispc, i, DISPC_TIMING_V); + DUMPREG(dispc, i, DISPC_POL_FREQ); + DUMPREG(dispc, i, DISPC_DIVISORo); - DUMPREG(i, DISPC_DATA_CYCLE1); - DUMPREG(i, DISPC_DATA_CYCLE2); - DUMPREG(i, DISPC_DATA_CYCLE3); + DUMPREG(dispc, i, DISPC_DATA_CYCLE1); + DUMPREG(dispc, i, DISPC_DATA_CYCLE2); + DUMPREG(dispc, i, DISPC_DATA_CYCLE3); - if (dss_has_feature(FEAT_CPR)) { - DUMPREG(i, DISPC_CPR_COEF_R); - DUMPREG(i, DISPC_CPR_COEF_G); - DUMPREG(i, DISPC_CPR_COEF_B); + if (dispc_has_feature(dispc, FEAT_CPR)) { + DUMPREG(dispc, i, DISPC_CPR_COEF_R); + DUMPREG(dispc, i, DISPC_CPR_COEF_G); + DUMPREG(dispc, i, DISPC_CPR_COEF_B); } } p_names = ovl_names; - for (i = 0; i < dss_feat_get_num_ovls(); i++) { - DUMPREG(i, DISPC_OVL_BA0); - DUMPREG(i, DISPC_OVL_BA1); - DUMPREG(i, DISPC_OVL_POSITION); - DUMPREG(i, DISPC_OVL_SIZE); - DUMPREG(i, DISPC_OVL_ATTRIBUTES); - DUMPREG(i, DISPC_OVL_FIFO_THRESHOLD); - DUMPREG(i, DISPC_OVL_FIFO_SIZE_STATUS); - DUMPREG(i, DISPC_OVL_ROW_INC); - DUMPREG(i, DISPC_OVL_PIXEL_INC); - - if (dss_has_feature(FEAT_PRELOAD)) - DUMPREG(i, DISPC_OVL_PRELOAD); - if (dss_has_feature(FEAT_MFLAG)) - DUMPREG(i, DISPC_OVL_MFLAG_THRESHOLD); + for (i = 0; i < dispc_get_num_ovls(dispc); i++) { + DUMPREG(dispc, i, DISPC_OVL_BA0); + DUMPREG(dispc, i, DISPC_OVL_BA1); + DUMPREG(dispc, i, DISPC_OVL_POSITION); + DUMPREG(dispc, i, DISPC_OVL_SIZE); + DUMPREG(dispc, i, DISPC_OVL_ATTRIBUTES); + DUMPREG(dispc, i, DISPC_OVL_FIFO_THRESHOLD); + DUMPREG(dispc, i, DISPC_OVL_FIFO_SIZE_STATUS); + DUMPREG(dispc, i, DISPC_OVL_ROW_INC); + DUMPREG(dispc, i, DISPC_OVL_PIXEL_INC); + + if (dispc_has_feature(dispc, FEAT_PRELOAD)) + DUMPREG(dispc, i, DISPC_OVL_PRELOAD); + if (dispc_has_feature(dispc, FEAT_MFLAG)) + DUMPREG(dispc, i, DISPC_OVL_MFLAG_THRESHOLD); if (i == OMAP_DSS_GFX) { - DUMPREG(i, DISPC_OVL_WINDOW_SKIP); - DUMPREG(i, DISPC_OVL_TABLE_BA); + DUMPREG(dispc, i, DISPC_OVL_WINDOW_SKIP); + DUMPREG(dispc, i, DISPC_OVL_TABLE_BA); continue; } - DUMPREG(i, DISPC_OVL_FIR); - DUMPREG(i, DISPC_OVL_PICTURE_SIZE); - DUMPREG(i, DISPC_OVL_ACCU0); - DUMPREG(i, DISPC_OVL_ACCU1); - if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) { - DUMPREG(i, DISPC_OVL_BA0_UV); - DUMPREG(i, DISPC_OVL_BA1_UV); - DUMPREG(i, DISPC_OVL_FIR2); - DUMPREG(i, DISPC_OVL_ACCU2_0); - DUMPREG(i, DISPC_OVL_ACCU2_1); + DUMPREG(dispc, i, DISPC_OVL_FIR); + DUMPREG(dispc, i, DISPC_OVL_PICTURE_SIZE); + DUMPREG(dispc, i, DISPC_OVL_ACCU0); + DUMPREG(dispc, i, DISPC_OVL_ACCU1); + if (dispc_has_feature(dispc, FEAT_HANDLE_UV_SEPARATE)) { + DUMPREG(dispc, i, DISPC_OVL_BA0_UV); + DUMPREG(dispc, i, DISPC_OVL_BA1_UV); + DUMPREG(dispc, i, DISPC_OVL_FIR2); + DUMPREG(dispc, i, DISPC_OVL_ACCU2_0); + DUMPREG(dispc, i, DISPC_OVL_ACCU2_1); } - if (dss_has_feature(FEAT_ATTR2)) - DUMPREG(i, DISPC_OVL_ATTRIBUTES2); + if (dispc_has_feature(dispc, FEAT_ATTR2)) + DUMPREG(dispc, i, DISPC_OVL_ATTRIBUTES2); } - if (dispc.feat->has_writeback) { + if (dispc->feat->has_writeback) { i = OMAP_DSS_WB; - DUMPREG(i, DISPC_OVL_BA0); - DUMPREG(i, DISPC_OVL_BA1); - DUMPREG(i, DISPC_OVL_SIZE); - DUMPREG(i, DISPC_OVL_ATTRIBUTES); - DUMPREG(i, DISPC_OVL_FIFO_THRESHOLD); - DUMPREG(i, DISPC_OVL_FIFO_SIZE_STATUS); - DUMPREG(i, DISPC_OVL_ROW_INC); - DUMPREG(i, DISPC_OVL_PIXEL_INC); - - if (dss_has_feature(FEAT_MFLAG)) - DUMPREG(i, DISPC_OVL_MFLAG_THRESHOLD); - - DUMPREG(i, DISPC_OVL_FIR); - DUMPREG(i, DISPC_OVL_PICTURE_SIZE); - DUMPREG(i, DISPC_OVL_ACCU0); - DUMPREG(i, DISPC_OVL_ACCU1); - if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) { - DUMPREG(i, DISPC_OVL_BA0_UV); - DUMPREG(i, DISPC_OVL_BA1_UV); - DUMPREG(i, DISPC_OVL_FIR2); - DUMPREG(i, DISPC_OVL_ACCU2_0); - DUMPREG(i, DISPC_OVL_ACCU2_1); + DUMPREG(dispc, i, DISPC_OVL_BA0); + DUMPREG(dispc, i, DISPC_OVL_BA1); + DUMPREG(dispc, i, DISPC_OVL_SIZE); + DUMPREG(dispc, i, DISPC_OVL_ATTRIBUTES); + DUMPREG(dispc, i, DISPC_OVL_FIFO_THRESHOLD); + DUMPREG(dispc, i, DISPC_OVL_FIFO_SIZE_STATUS); + DUMPREG(dispc, i, DISPC_OVL_ROW_INC); + DUMPREG(dispc, i, DISPC_OVL_PIXEL_INC); + + if (dispc_has_feature(dispc, FEAT_MFLAG)) + DUMPREG(dispc, i, DISPC_OVL_MFLAG_THRESHOLD); + + DUMPREG(dispc, i, DISPC_OVL_FIR); + DUMPREG(dispc, i, DISPC_OVL_PICTURE_SIZE); + DUMPREG(dispc, i, DISPC_OVL_ACCU0); + DUMPREG(dispc, i, DISPC_OVL_ACCU1); + if (dispc_has_feature(dispc, FEAT_HANDLE_UV_SEPARATE)) { + DUMPREG(dispc, i, DISPC_OVL_BA0_UV); + DUMPREG(dispc, i, DISPC_OVL_BA1_UV); + DUMPREG(dispc, i, DISPC_OVL_FIR2); + DUMPREG(dispc, i, DISPC_OVL_ACCU2_0); + DUMPREG(dispc, i, DISPC_OVL_ACCU2_1); } - if (dss_has_feature(FEAT_ATTR2)) - DUMPREG(i, DISPC_OVL_ATTRIBUTES2); + if (dispc_has_feature(dispc, FEAT_ATTR2)) + DUMPREG(dispc, i, DISPC_OVL_ATTRIBUTES2); } #undef DISPC_REG #undef DUMPREG #define DISPC_REG(plane, name, i) name(plane, i) -#define DUMPREG(plane, name, i) \ +#define DUMPREG(dispc, plane, name, i) \ seq_printf(s, "%s_%d(%s)%*s %08x\n", #name, i, p_names[plane], \ (int)(46 - strlen(#name) - strlen(p_names[plane])), " ", \ - dispc_read_reg(DISPC_REG(plane, name, i))) + dispc_read_reg(dispc, DISPC_REG(plane, name, i))) /* Video pipeline coefficient registers */ /* start from OMAP_DSS_VIDEO1 */ - for (i = 1; i < dss_feat_get_num_ovls(); i++) { + for (i = 1; i < dispc_get_num_ovls(dispc); i++) { for (j = 0; j < 8; j++) - DUMPREG(i, DISPC_OVL_FIR_COEF_H, j); + DUMPREG(dispc, i, DISPC_OVL_FIR_COEF_H, j); for (j = 0; j < 8; j++) - DUMPREG(i, DISPC_OVL_FIR_COEF_HV, j); + DUMPREG(dispc, i, DISPC_OVL_FIR_COEF_HV, j); for (j = 0; j < 5; j++) - DUMPREG(i, DISPC_OVL_CONV_COEF, j); + DUMPREG(dispc, i, DISPC_OVL_CONV_COEF, j); - if (dss_has_feature(FEAT_FIR_COEF_V)) { + if (dispc_has_feature(dispc, FEAT_FIR_COEF_V)) { for (j = 0; j < 8; j++) - DUMPREG(i, DISPC_OVL_FIR_COEF_V, j); + DUMPREG(dispc, i, DISPC_OVL_FIR_COEF_V, j); } - if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) { + if (dispc_has_feature(dispc, FEAT_HANDLE_UV_SEPARATE)) { for (j = 0; j < 8; j++) - DUMPREG(i, DISPC_OVL_FIR_COEF_H2, j); + DUMPREG(dispc, i, DISPC_OVL_FIR_COEF_H2, j); for (j = 0; j < 8; j++) - DUMPREG(i, DISPC_OVL_FIR_COEF_HV2, j); + DUMPREG(dispc, i, DISPC_OVL_FIR_COEF_HV2, j); for (j = 0; j < 8; j++) - DUMPREG(i, DISPC_OVL_FIR_COEF_V2, j); + DUMPREG(dispc, i, DISPC_OVL_FIR_COEF_V2, j); } } - dispc_runtime_put(); + dispc_runtime_put(dispc); #undef DISPC_REG #undef DUMPREG + + return 0; } /* calculate clock rates using dividers in cinfo */ -int dispc_calc_clock_rates(unsigned long dispc_fclk_rate, - struct dispc_clock_info *cinfo) +int dispc_calc_clock_rates(struct dispc_device *dispc, + unsigned long dispc_fclk_rate, + struct dispc_clock_info *cinfo) { if (cinfo->lck_div > 255 || cinfo->lck_div == 0) return -EINVAL; @@ -3397,16 +3542,16 @@ int dispc_calc_clock_rates(unsigned long dispc_fclk_rate, return 0; } -bool dispc_div_calc(unsigned long dispc, - unsigned long pck_min, unsigned long pck_max, - dispc_div_calc_func func, void *data) +bool dispc_div_calc(struct dispc_device *dispc, unsigned long dispc_freq, + unsigned long pck_min, unsigned long pck_max, + dispc_div_calc_func func, void *data) { int lckd, lckd_start, lckd_stop; int pckd, pckd_start, pckd_stop; unsigned long pck, lck; unsigned long lck_max; unsigned long pckd_hw_min, pckd_hw_max; - unsigned min_fck_per_pck; + unsigned int min_fck_per_pck; unsigned long fck; #ifdef CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK @@ -3415,19 +3560,19 @@ bool dispc_div_calc(unsigned long dispc, min_fck_per_pck = 0; #endif - pckd_hw_min = dss_feat_get_param_min(FEAT_PARAM_DSS_PCD); - pckd_hw_max = dss_feat_get_param_max(FEAT_PARAM_DSS_PCD); + pckd_hw_min = dispc->feat->min_pcd; + pckd_hw_max = 255; - lck_max = dss_feat_get_param_max(FEAT_PARAM_DSS_FCK); + lck_max = dss_get_max_fck_rate(dispc->dss); pck_min = pck_min ? pck_min : 1; pck_max = pck_max ? pck_max : ULONG_MAX; - lckd_start = max(DIV_ROUND_UP(dispc, lck_max), 1ul); - lckd_stop = min(dispc / pck_min, 255ul); + lckd_start = max(DIV_ROUND_UP(dispc_freq, lck_max), 1ul); + lckd_stop = min(dispc_freq / pck_min, 255ul); for (lckd = lckd_start; lckd <= lckd_stop; ++lckd) { - lck = dispc / lckd; + lck = dispc_freq / lckd; pckd_start = max(DIV_ROUND_UP(lck, pck_max), pckd_hw_min); pckd_stop = min(lck / pck_min, pckd_hw_max); @@ -3441,8 +3586,8 @@ bool dispc_div_calc(unsigned long dispc, * also. Thus we need to use the calculated lck. For * OMAP4+ the DISPC fclk is a separate clock. */ - if (dss_has_feature(FEAT_CORE_CLK_DIV)) - fck = dispc_core_clk_rate(); + if (dispc_has_feature(dispc, FEAT_CORE_CLK_DIV)) + fck = dispc_core_clk_rate(dispc); else fck = lck; @@ -3457,78 +3602,67 @@ bool dispc_div_calc(unsigned long dispc, return false; } -void dispc_mgr_set_clock_div(enum omap_channel channel, - const struct dispc_clock_info *cinfo) +void dispc_mgr_set_clock_div(struct dispc_device *dispc, + enum omap_channel channel, + const struct dispc_clock_info *cinfo) { DSSDBG("lck = %lu (%u)\n", cinfo->lck, cinfo->lck_div); DSSDBG("pck = %lu (%u)\n", cinfo->pck, cinfo->pck_div); - dispc_mgr_set_lcd_divisor(channel, cinfo->lck_div, cinfo->pck_div); + dispc_mgr_set_lcd_divisor(dispc, channel, cinfo->lck_div, + cinfo->pck_div); } -int dispc_mgr_get_clock_div(enum omap_channel channel, - struct dispc_clock_info *cinfo) +u32 dispc_read_irqstatus(struct dispc_device *dispc) { - unsigned long fck; - - fck = dispc_fclk_rate(); - - cinfo->lck_div = REG_GET(DISPC_DIVISORo(channel), 23, 16); - cinfo->pck_div = REG_GET(DISPC_DIVISORo(channel), 7, 0); - - cinfo->lck = fck / cinfo->lck_div; - cinfo->pck = cinfo->lck / cinfo->pck_div; - - return 0; + return dispc_read_reg(dispc, DISPC_IRQSTATUS); } -static u32 dispc_read_irqstatus(void) +void dispc_clear_irqstatus(struct dispc_device *dispc, u32 mask) { - return dispc_read_reg(DISPC_IRQSTATUS); + dispc_write_reg(dispc, DISPC_IRQSTATUS, mask); } -static void dispc_clear_irqstatus(u32 mask) +void dispc_write_irqenable(struct dispc_device *dispc, u32 mask) { - dispc_write_reg(DISPC_IRQSTATUS, mask); -} - -static void dispc_write_irqenable(u32 mask) -{ - u32 old_mask = dispc_read_reg(DISPC_IRQENABLE); + u32 old_mask = dispc_read_reg(dispc, DISPC_IRQENABLE); /* clear the irqstatus for newly enabled irqs */ - dispc_clear_irqstatus((mask ^ old_mask) & mask); + dispc_clear_irqstatus(dispc, (mask ^ old_mask) & mask); - dispc_write_reg(DISPC_IRQENABLE, mask); + dispc_write_reg(dispc, DISPC_IRQENABLE, mask); /* flush posted write */ - dispc_read_reg(DISPC_IRQENABLE); + dispc_read_reg(dispc, DISPC_IRQENABLE); } -void dispc_enable_sidle(void) +void dispc_enable_sidle(struct dispc_device *dispc) { - REG_FLD_MOD(DISPC_SYSCONFIG, 2, 4, 3); /* SIDLEMODE: smart idle */ + /* SIDLEMODE: smart idle */ + REG_FLD_MOD(dispc, DISPC_SYSCONFIG, 2, 4, 3); } -void dispc_disable_sidle(void) +void dispc_disable_sidle(struct dispc_device *dispc) { - REG_FLD_MOD(DISPC_SYSCONFIG, 1, 4, 3); /* SIDLEMODE: no idle */ + REG_FLD_MOD(dispc, DISPC_SYSCONFIG, 1, 4, 3); /* SIDLEMODE: no idle */ } -static u32 dispc_mgr_gamma_size(enum omap_channel channel) +u32 dispc_mgr_gamma_size(struct dispc_device *dispc, + enum omap_channel channel) { const struct dispc_gamma_desc *gdesc = &mgr_desc[channel].gamma; - if (!dispc.feat->has_gamma_table) + if (!dispc->feat->has_gamma_table) return 0; return gdesc->len; } -static void dispc_mgr_write_gamma_table(enum omap_channel channel) +static void dispc_mgr_write_gamma_table(struct dispc_device *dispc, + enum omap_channel channel) { const struct dispc_gamma_desc *gdesc = &mgr_desc[channel].gamma; - u32 *table = dispc.gamma_table[channel]; + u32 *table = dispc->gamma_table[channel]; unsigned int i; DSSDBG("%s: channel %d\n", __func__, channel); @@ -3541,26 +3675,26 @@ static void dispc_mgr_write_gamma_table(enum omap_channel channel) else if (i == 0) v |= 1 << 31; - dispc_write_reg(gdesc->reg, v); + dispc_write_reg(dispc, gdesc->reg, v); } } -static void dispc_restore_gamma_tables(void) +static void dispc_restore_gamma_tables(struct dispc_device *dispc) { DSSDBG("%s()\n", __func__); - if (!dispc.feat->has_gamma_table) + if (!dispc->feat->has_gamma_table) return; - dispc_mgr_write_gamma_table(OMAP_DSS_CHANNEL_LCD); + dispc_mgr_write_gamma_table(dispc, OMAP_DSS_CHANNEL_LCD); - dispc_mgr_write_gamma_table(OMAP_DSS_CHANNEL_DIGIT); + dispc_mgr_write_gamma_table(dispc, OMAP_DSS_CHANNEL_DIGIT); - if (dss_has_feature(FEAT_MGR_LCD2)) - dispc_mgr_write_gamma_table(OMAP_DSS_CHANNEL_LCD2); + if (dispc_has_feature(dispc, FEAT_MGR_LCD2)) + dispc_mgr_write_gamma_table(dispc, OMAP_DSS_CHANNEL_LCD2); - if (dss_has_feature(FEAT_MGR_LCD3)) - dispc_mgr_write_gamma_table(OMAP_DSS_CHANNEL_LCD3); + if (dispc_has_feature(dispc, FEAT_MGR_LCD3)) + dispc_mgr_write_gamma_table(dispc, OMAP_DSS_CHANNEL_LCD3); } static const struct drm_color_lut dispc_mgr_gamma_default_lut[] = { @@ -3568,18 +3702,19 @@ static const struct drm_color_lut dispc_mgr_gamma_default_lut[] = { { .red = U16_MAX, .green = U16_MAX, .blue = U16_MAX, }, }; -static void dispc_mgr_set_gamma(enum omap_channel channel, - const struct drm_color_lut *lut, - unsigned int length) +void dispc_mgr_set_gamma(struct dispc_device *dispc, + enum omap_channel channel, + const struct drm_color_lut *lut, + unsigned int length) { const struct dispc_gamma_desc *gdesc = &mgr_desc[channel].gamma; - u32 *table = dispc.gamma_table[channel]; + u32 *table = dispc->gamma_table[channel]; uint i; DSSDBG("%s: channel %d, lut len %u, hw len %u\n", __func__, channel, length, gdesc->len); - if (!dispc.feat->has_gamma_table) + if (!dispc->feat->has_gamma_table) return; if (lut == NULL || length < 2) { @@ -3611,84 +3746,351 @@ static void dispc_mgr_set_gamma(enum omap_channel channel, } } - if (dispc.is_enabled) - dispc_mgr_write_gamma_table(channel); + if (dispc->is_enabled) + dispc_mgr_write_gamma_table(dispc, channel); } -static int dispc_init_gamma_tables(void) +static int dispc_init_gamma_tables(struct dispc_device *dispc) { int channel; - if (!dispc.feat->has_gamma_table) + if (!dispc->feat->has_gamma_table) return 0; - for (channel = 0; channel < ARRAY_SIZE(dispc.gamma_table); channel++) { + for (channel = 0; channel < ARRAY_SIZE(dispc->gamma_table); channel++) { const struct dispc_gamma_desc *gdesc = &mgr_desc[channel].gamma; u32 *gt; if (channel == OMAP_DSS_CHANNEL_LCD2 && - !dss_has_feature(FEAT_MGR_LCD2)) + !dispc_has_feature(dispc, FEAT_MGR_LCD2)) continue; if (channel == OMAP_DSS_CHANNEL_LCD3 && - !dss_has_feature(FEAT_MGR_LCD3)) + !dispc_has_feature(dispc, FEAT_MGR_LCD3)) continue; - gt = devm_kmalloc_array(&dispc.pdev->dev, gdesc->len, - sizeof(u32), GFP_KERNEL); + gt = devm_kmalloc_array(&dispc->pdev->dev, gdesc->len, + sizeof(u32), GFP_KERNEL); if (!gt) return -ENOMEM; - dispc.gamma_table[channel] = gt; + dispc->gamma_table[channel] = gt; - dispc_mgr_set_gamma(channel, NULL, 0); + dispc_mgr_set_gamma(dispc, channel, NULL, 0); } return 0; } -static void _omap_dispc_initial_config(void) +static void _omap_dispc_initial_config(struct dispc_device *dispc) { u32 l; /* Exclusively enable DISPC_CORE_CLK and set divider to 1 */ - if (dss_has_feature(FEAT_CORE_CLK_DIV)) { - l = dispc_read_reg(DISPC_DIVISOR); + if (dispc_has_feature(dispc, FEAT_CORE_CLK_DIV)) { + l = dispc_read_reg(dispc, DISPC_DIVISOR); /* Use DISPC_DIVISOR.LCD, instead of DISPC_DIVISOR1.LCD */ l = FLD_MOD(l, 1, 0, 0); l = FLD_MOD(l, 1, 23, 16); - dispc_write_reg(DISPC_DIVISOR, l); + dispc_write_reg(dispc, DISPC_DIVISOR, l); - dispc.core_clk_rate = dispc_fclk_rate(); + dispc->core_clk_rate = dispc_fclk_rate(dispc); } /* Use gamma table mode, instead of palette mode */ - if (dispc.feat->has_gamma_table) - REG_FLD_MOD(DISPC_CONFIG, 1, 3, 3); + if (dispc->feat->has_gamma_table) + REG_FLD_MOD(dispc, DISPC_CONFIG, 1, 3, 3); /* For older DSS versions (FEAT_FUNCGATED) this enables * func-clock auto-gating. For newer versions - * (dispc.feat->has_gamma_table) this enables tv-out gamma tables. + * (dispc->feat->has_gamma_table) this enables tv-out gamma tables. */ - if (dss_has_feature(FEAT_FUNCGATED) || dispc.feat->has_gamma_table) - REG_FLD_MOD(DISPC_CONFIG, 1, 9, 9); - - dispc_setup_color_conv_coef(); + if (dispc_has_feature(dispc, FEAT_FUNCGATED) || + dispc->feat->has_gamma_table) + REG_FLD_MOD(dispc, DISPC_CONFIG, 1, 9, 9); - dispc_set_loadmode(OMAP_DSS_LOAD_FRAME_ONLY); + dispc_set_loadmode(dispc, OMAP_DSS_LOAD_FRAME_ONLY); - dispc_init_fifos(); + dispc_init_fifos(dispc); - dispc_configure_burst_sizes(); + dispc_configure_burst_sizes(dispc); - dispc_ovl_enable_zorder_planes(); + dispc_ovl_enable_zorder_planes(dispc); - if (dispc.feat->mstandby_workaround) - REG_FLD_MOD(DISPC_MSTANDBY_CTRL, 1, 0, 0); + if (dispc->feat->mstandby_workaround) + REG_FLD_MOD(dispc, DISPC_MSTANDBY_CTRL, 1, 0, 0); - if (dss_has_feature(FEAT_MFLAG)) - dispc_init_mflag(); + if (dispc_has_feature(dispc, FEAT_MFLAG)) + dispc_init_mflag(dispc); } +static const enum dispc_feature_id omap2_dispc_features_list[] = { + FEAT_LCDENABLEPOL, + FEAT_LCDENABLESIGNAL, + FEAT_PCKFREEENABLE, + FEAT_FUNCGATED, + FEAT_ROWREPEATENABLE, + FEAT_RESIZECONF, +}; + +static const enum dispc_feature_id omap3_dispc_features_list[] = { + FEAT_LCDENABLEPOL, + FEAT_LCDENABLESIGNAL, + FEAT_PCKFREEENABLE, + FEAT_FUNCGATED, + FEAT_LINEBUFFERSPLIT, + FEAT_ROWREPEATENABLE, + FEAT_RESIZECONF, + FEAT_CPR, + FEAT_PRELOAD, + FEAT_FIR_COEF_V, + FEAT_ALPHA_FIXED_ZORDER, + FEAT_FIFO_MERGE, + FEAT_OMAP3_DSI_FIFO_BUG, +}; + +static const enum dispc_feature_id am43xx_dispc_features_list[] = { + FEAT_LCDENABLEPOL, + FEAT_LCDENABLESIGNAL, + FEAT_PCKFREEENABLE, + FEAT_FUNCGATED, + FEAT_LINEBUFFERSPLIT, + FEAT_ROWREPEATENABLE, + FEAT_RESIZECONF, + FEAT_CPR, + FEAT_PRELOAD, + FEAT_FIR_COEF_V, + FEAT_ALPHA_FIXED_ZORDER, + FEAT_FIFO_MERGE, +}; + +static const enum dispc_feature_id omap4_dispc_features_list[] = { + FEAT_MGR_LCD2, + FEAT_CORE_CLK_DIV, + FEAT_HANDLE_UV_SEPARATE, + FEAT_ATTR2, + FEAT_CPR, + FEAT_PRELOAD, + FEAT_FIR_COEF_V, + FEAT_ALPHA_FREE_ZORDER, + FEAT_FIFO_MERGE, + FEAT_BURST_2D, +}; + +static const enum dispc_feature_id omap5_dispc_features_list[] = { + FEAT_MGR_LCD2, + FEAT_MGR_LCD3, + FEAT_CORE_CLK_DIV, + FEAT_HANDLE_UV_SEPARATE, + FEAT_ATTR2, + FEAT_CPR, + FEAT_PRELOAD, + FEAT_FIR_COEF_V, + FEAT_ALPHA_FREE_ZORDER, + FEAT_FIFO_MERGE, + FEAT_BURST_2D, + FEAT_MFLAG, +}; + +static const struct dss_reg_field omap2_dispc_reg_fields[] = { + [FEAT_REG_FIRHINC] = { 11, 0 }, + [FEAT_REG_FIRVINC] = { 27, 16 }, + [FEAT_REG_FIFOLOWTHRESHOLD] = { 8, 0 }, + [FEAT_REG_FIFOHIGHTHRESHOLD] = { 24, 16 }, + [FEAT_REG_FIFOSIZE] = { 8, 0 }, + [FEAT_REG_HORIZONTALACCU] = { 9, 0 }, + [FEAT_REG_VERTICALACCU] = { 25, 16 }, +}; + +static const struct dss_reg_field omap3_dispc_reg_fields[] = { + [FEAT_REG_FIRHINC] = { 12, 0 }, + [FEAT_REG_FIRVINC] = { 28, 16 }, + [FEAT_REG_FIFOLOWTHRESHOLD] = { 11, 0 }, + [FEAT_REG_FIFOHIGHTHRESHOLD] = { 27, 16 }, + [FEAT_REG_FIFOSIZE] = { 10, 0 }, + [FEAT_REG_HORIZONTALACCU] = { 9, 0 }, + [FEAT_REG_VERTICALACCU] = { 25, 16 }, +}; + +static const struct dss_reg_field omap4_dispc_reg_fields[] = { + [FEAT_REG_FIRHINC] = { 12, 0 }, + [FEAT_REG_FIRVINC] = { 28, 16 }, + [FEAT_REG_FIFOLOWTHRESHOLD] = { 15, 0 }, + [FEAT_REG_FIFOHIGHTHRESHOLD] = { 31, 16 }, + [FEAT_REG_FIFOSIZE] = { 15, 0 }, + [FEAT_REG_HORIZONTALACCU] = { 10, 0 }, + [FEAT_REG_VERTICALACCU] = { 26, 16 }, +}; + +static const enum omap_overlay_caps omap2_dispc_overlay_caps[] = { + /* OMAP_DSS_GFX */ + OMAP_DSS_OVL_CAP_POS | OMAP_DSS_OVL_CAP_REPLICATION, + + /* OMAP_DSS_VIDEO1 */ + OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_POS | + OMAP_DSS_OVL_CAP_REPLICATION, + + /* OMAP_DSS_VIDEO2 */ + OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_POS | + OMAP_DSS_OVL_CAP_REPLICATION, +}; + +static const enum omap_overlay_caps omap3430_dispc_overlay_caps[] = { + /* OMAP_DSS_GFX */ + OMAP_DSS_OVL_CAP_GLOBAL_ALPHA | OMAP_DSS_OVL_CAP_POS | + OMAP_DSS_OVL_CAP_REPLICATION, + + /* OMAP_DSS_VIDEO1 */ + OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_POS | + OMAP_DSS_OVL_CAP_REPLICATION, + + /* OMAP_DSS_VIDEO2 */ + OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_GLOBAL_ALPHA | + OMAP_DSS_OVL_CAP_POS | OMAP_DSS_OVL_CAP_REPLICATION, +}; + +static const enum omap_overlay_caps omap3630_dispc_overlay_caps[] = { + /* OMAP_DSS_GFX */ + OMAP_DSS_OVL_CAP_GLOBAL_ALPHA | OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA | + OMAP_DSS_OVL_CAP_POS | OMAP_DSS_OVL_CAP_REPLICATION, + + /* OMAP_DSS_VIDEO1 */ + OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_POS | + OMAP_DSS_OVL_CAP_REPLICATION, + + /* OMAP_DSS_VIDEO2 */ + OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_GLOBAL_ALPHA | + OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA | OMAP_DSS_OVL_CAP_POS | + OMAP_DSS_OVL_CAP_REPLICATION, +}; + +static const enum omap_overlay_caps omap4_dispc_overlay_caps[] = { + /* OMAP_DSS_GFX */ + OMAP_DSS_OVL_CAP_GLOBAL_ALPHA | OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA | + OMAP_DSS_OVL_CAP_ZORDER | OMAP_DSS_OVL_CAP_POS | + OMAP_DSS_OVL_CAP_REPLICATION, + + /* OMAP_DSS_VIDEO1 */ + OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_GLOBAL_ALPHA | + OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA | OMAP_DSS_OVL_CAP_ZORDER | + OMAP_DSS_OVL_CAP_POS | OMAP_DSS_OVL_CAP_REPLICATION, + + /* OMAP_DSS_VIDEO2 */ + OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_GLOBAL_ALPHA | + OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA | OMAP_DSS_OVL_CAP_ZORDER | + OMAP_DSS_OVL_CAP_POS | OMAP_DSS_OVL_CAP_REPLICATION, + + /* OMAP_DSS_VIDEO3 */ + OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_GLOBAL_ALPHA | + OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA | OMAP_DSS_OVL_CAP_ZORDER | + OMAP_DSS_OVL_CAP_POS | OMAP_DSS_OVL_CAP_REPLICATION, +}; + +#define COLOR_ARRAY(arr...) (const u32[]) { arr, 0 } + +static const u32 *omap2_dispc_supported_color_modes[] = { + + /* OMAP_DSS_GFX */ + COLOR_ARRAY( + DRM_FORMAT_RGBX4444, DRM_FORMAT_RGB565, + DRM_FORMAT_XRGB8888, DRM_FORMAT_RGB888), + + /* OMAP_DSS_VIDEO1 */ + COLOR_ARRAY( + DRM_FORMAT_RGB565, DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB888, DRM_FORMAT_YUYV, + DRM_FORMAT_UYVY), + + /* OMAP_DSS_VIDEO2 */ + COLOR_ARRAY( + DRM_FORMAT_RGB565, DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB888, DRM_FORMAT_YUYV, + DRM_FORMAT_UYVY), +}; + +static const u32 *omap3_dispc_supported_color_modes[] = { + /* OMAP_DSS_GFX */ + COLOR_ARRAY( + DRM_FORMAT_RGBX4444, DRM_FORMAT_ARGB4444, + DRM_FORMAT_RGB565, DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB888, DRM_FORMAT_ARGB8888, + DRM_FORMAT_RGBA8888, DRM_FORMAT_RGBX8888), + + /* OMAP_DSS_VIDEO1 */ + COLOR_ARRAY( + DRM_FORMAT_XRGB8888, DRM_FORMAT_RGB888, + DRM_FORMAT_RGBX4444, DRM_FORMAT_RGB565, + DRM_FORMAT_YUYV, DRM_FORMAT_UYVY), + + /* OMAP_DSS_VIDEO2 */ + COLOR_ARRAY( + DRM_FORMAT_RGBX4444, DRM_FORMAT_ARGB4444, + DRM_FORMAT_RGB565, DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB888, DRM_FORMAT_YUYV, + DRM_FORMAT_UYVY, DRM_FORMAT_ARGB8888, + DRM_FORMAT_RGBA8888, DRM_FORMAT_RGBX8888), +}; + +static const u32 *omap4_dispc_supported_color_modes[] = { + /* OMAP_DSS_GFX */ + COLOR_ARRAY( + DRM_FORMAT_RGBX4444, DRM_FORMAT_ARGB4444, + DRM_FORMAT_RGB565, DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB888, DRM_FORMAT_ARGB8888, + DRM_FORMAT_RGBA8888, DRM_FORMAT_RGBX8888, + DRM_FORMAT_ARGB1555, DRM_FORMAT_XRGB4444, + DRM_FORMAT_RGBA4444, DRM_FORMAT_XRGB1555), + + /* OMAP_DSS_VIDEO1 */ + COLOR_ARRAY( + DRM_FORMAT_RGB565, DRM_FORMAT_RGBX4444, + DRM_FORMAT_YUYV, DRM_FORMAT_ARGB1555, + DRM_FORMAT_RGBA8888, DRM_FORMAT_NV12, + DRM_FORMAT_RGBA4444, DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB888, DRM_FORMAT_UYVY, + DRM_FORMAT_ARGB4444, DRM_FORMAT_XRGB1555, + DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB4444, + DRM_FORMAT_RGBX8888), + + /* OMAP_DSS_VIDEO2 */ + COLOR_ARRAY( + DRM_FORMAT_RGB565, DRM_FORMAT_RGBX4444, + DRM_FORMAT_YUYV, DRM_FORMAT_ARGB1555, + DRM_FORMAT_RGBA8888, DRM_FORMAT_NV12, + DRM_FORMAT_RGBA4444, DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB888, DRM_FORMAT_UYVY, + DRM_FORMAT_ARGB4444, DRM_FORMAT_XRGB1555, + DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB4444, + DRM_FORMAT_RGBX8888), + + /* OMAP_DSS_VIDEO3 */ + COLOR_ARRAY( + DRM_FORMAT_RGB565, DRM_FORMAT_RGBX4444, + DRM_FORMAT_YUYV, DRM_FORMAT_ARGB1555, + DRM_FORMAT_RGBA8888, DRM_FORMAT_NV12, + DRM_FORMAT_RGBA4444, DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB888, DRM_FORMAT_UYVY, + DRM_FORMAT_ARGB4444, DRM_FORMAT_XRGB1555, + DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB4444, + DRM_FORMAT_RGBX8888), + + /* OMAP_DSS_WB */ + COLOR_ARRAY( + DRM_FORMAT_RGB565, DRM_FORMAT_RGBX4444, + DRM_FORMAT_YUYV, DRM_FORMAT_ARGB1555, + DRM_FORMAT_RGBA8888, DRM_FORMAT_NV12, + DRM_FORMAT_RGBA4444, DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB888, DRM_FORMAT_UYVY, + DRM_FORMAT_ARGB4444, DRM_FORMAT_XRGB1555, + DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB4444, + DRM_FORMAT_RGBX8888), +}; + +static const u32 omap3_dispc_supported_scaler_color_modes[] = { + DRM_FORMAT_XRGB8888, DRM_FORMAT_RGB565, DRM_FORMAT_YUYV, + DRM_FORMAT_UYVY, + 0, +}; + static const struct dispc_features omap24xx_dispc_feats = { .sw_start = 5, .fp_start = 15, @@ -3700,10 +4102,30 @@ static const struct dispc_features omap24xx_dispc_feats = { .mgr_height_start = 26, .mgr_width_max = 2048, .mgr_height_max = 2048, + .ovl_width_max = 2048, + .ovl_height_max = 2048, .max_lcd_pclk = 66500000, + .max_downscale = 2, + /* + * Assume the line width buffer to be 768 pixels as OMAP2 DISPC scaler + * cannot scale an image width larger than 768. + */ + .max_line_width = 768, + .min_pcd = 2, .calc_scaling = dispc_ovl_calc_scaling_24xx, .calc_core_clk = calc_core_clk_24xx, .num_fifos = 3, + .features = omap2_dispc_features_list, + .num_features = ARRAY_SIZE(omap2_dispc_features_list), + .reg_fields = omap2_dispc_reg_fields, + .num_reg_fields = ARRAY_SIZE(omap2_dispc_reg_fields), + .overlay_caps = omap2_dispc_overlay_caps, + .supported_color_modes = omap2_dispc_supported_color_modes, + .supported_scaler_color_modes = COLOR_ARRAY(DRM_FORMAT_XRGB8888), + .num_mgrs = 2, + .num_ovls = 3, + .buffer_size_unit = 1, + .burst_size_unit = 8, .no_framedone_tv = true, .set_max_preload = false, .last_pixel_inc_missing = true, @@ -3720,11 +4142,27 @@ static const struct dispc_features omap34xx_rev1_0_dispc_feats = { .mgr_height_start = 26, .mgr_width_max = 2048, .mgr_height_max = 2048, + .ovl_width_max = 2048, + .ovl_height_max = 2048, .max_lcd_pclk = 173000000, .max_tv_pclk = 59000000, + .max_downscale = 4, + .max_line_width = 1024, + .min_pcd = 1, .calc_scaling = dispc_ovl_calc_scaling_34xx, .calc_core_clk = calc_core_clk_34xx, .num_fifos = 3, + .features = omap3_dispc_features_list, + .num_features = ARRAY_SIZE(omap3_dispc_features_list), + .reg_fields = omap3_dispc_reg_fields, + .num_reg_fields = ARRAY_SIZE(omap3_dispc_reg_fields), + .overlay_caps = omap3430_dispc_overlay_caps, + .supported_color_modes = omap3_dispc_supported_color_modes, + .supported_scaler_color_modes = omap3_dispc_supported_scaler_color_modes, + .num_mgrs = 2, + .num_ovls = 3, + .buffer_size_unit = 1, + .burst_size_unit = 8, .no_framedone_tv = true, .set_max_preload = false, .last_pixel_inc_missing = true, @@ -3741,11 +4179,101 @@ static const struct dispc_features omap34xx_rev3_0_dispc_feats = { .mgr_height_start = 26, .mgr_width_max = 2048, .mgr_height_max = 2048, + .ovl_width_max = 2048, + .ovl_height_max = 2048, + .max_lcd_pclk = 173000000, + .max_tv_pclk = 59000000, + .max_downscale = 4, + .max_line_width = 1024, + .min_pcd = 1, + .calc_scaling = dispc_ovl_calc_scaling_34xx, + .calc_core_clk = calc_core_clk_34xx, + .num_fifos = 3, + .features = omap3_dispc_features_list, + .num_features = ARRAY_SIZE(omap3_dispc_features_list), + .reg_fields = omap3_dispc_reg_fields, + .num_reg_fields = ARRAY_SIZE(omap3_dispc_reg_fields), + .overlay_caps = omap3430_dispc_overlay_caps, + .supported_color_modes = omap3_dispc_supported_color_modes, + .supported_scaler_color_modes = omap3_dispc_supported_scaler_color_modes, + .num_mgrs = 2, + .num_ovls = 3, + .buffer_size_unit = 1, + .burst_size_unit = 8, + .no_framedone_tv = true, + .set_max_preload = false, + .last_pixel_inc_missing = true, +}; + +static const struct dispc_features omap36xx_dispc_feats = { + .sw_start = 7, + .fp_start = 19, + .bp_start = 31, + .sw_max = 256, + .vp_max = 4095, + .hp_max = 4096, + .mgr_width_start = 10, + .mgr_height_start = 26, + .mgr_width_max = 2048, + .mgr_height_max = 2048, + .ovl_width_max = 2048, + .ovl_height_max = 2048, + .max_lcd_pclk = 173000000, + .max_tv_pclk = 59000000, + .max_downscale = 4, + .max_line_width = 1024, + .min_pcd = 1, + .calc_scaling = dispc_ovl_calc_scaling_34xx, + .calc_core_clk = calc_core_clk_34xx, + .num_fifos = 3, + .features = omap3_dispc_features_list, + .num_features = ARRAY_SIZE(omap3_dispc_features_list), + .reg_fields = omap3_dispc_reg_fields, + .num_reg_fields = ARRAY_SIZE(omap3_dispc_reg_fields), + .overlay_caps = omap3630_dispc_overlay_caps, + .supported_color_modes = omap3_dispc_supported_color_modes, + .supported_scaler_color_modes = omap3_dispc_supported_scaler_color_modes, + .num_mgrs = 2, + .num_ovls = 3, + .buffer_size_unit = 1, + .burst_size_unit = 8, + .no_framedone_tv = true, + .set_max_preload = false, + .last_pixel_inc_missing = true, +}; + +static const struct dispc_features am43xx_dispc_feats = { + .sw_start = 7, + .fp_start = 19, + .bp_start = 31, + .sw_max = 256, + .vp_max = 4095, + .hp_max = 4096, + .mgr_width_start = 10, + .mgr_height_start = 26, + .mgr_width_max = 2048, + .mgr_height_max = 2048, + .ovl_width_max = 2048, + .ovl_height_max = 2048, .max_lcd_pclk = 173000000, .max_tv_pclk = 59000000, + .max_downscale = 4, + .max_line_width = 1024, + .min_pcd = 1, .calc_scaling = dispc_ovl_calc_scaling_34xx, .calc_core_clk = calc_core_clk_34xx, .num_fifos = 3, + .features = am43xx_dispc_features_list, + .num_features = ARRAY_SIZE(am43xx_dispc_features_list), + .reg_fields = omap3_dispc_reg_fields, + .num_reg_fields = ARRAY_SIZE(omap3_dispc_reg_fields), + .overlay_caps = omap3430_dispc_overlay_caps, + .supported_color_modes = omap3_dispc_supported_color_modes, + .supported_scaler_color_modes = omap3_dispc_supported_scaler_color_modes, + .num_mgrs = 1, + .num_ovls = 3, + .buffer_size_unit = 1, + .burst_size_unit = 8, .no_framedone_tv = true, .set_max_preload = false, .last_pixel_inc_missing = true, @@ -3762,11 +4290,26 @@ static const struct dispc_features omap44xx_dispc_feats = { .mgr_height_start = 26, .mgr_width_max = 2048, .mgr_height_max = 2048, + .ovl_width_max = 2048, + .ovl_height_max = 2048, .max_lcd_pclk = 170000000, .max_tv_pclk = 185625000, + .max_downscale = 4, + .max_line_width = 2048, + .min_pcd = 1, .calc_scaling = dispc_ovl_calc_scaling_44xx, .calc_core_clk = calc_core_clk_44xx, .num_fifos = 5, + .features = omap4_dispc_features_list, + .num_features = ARRAY_SIZE(omap4_dispc_features_list), + .reg_fields = omap4_dispc_reg_fields, + .num_reg_fields = ARRAY_SIZE(omap4_dispc_reg_fields), + .overlay_caps = omap4_dispc_overlay_caps, + .supported_color_modes = omap4_dispc_supported_color_modes, + .num_mgrs = 3, + .num_ovls = 4, + .buffer_size_unit = 16, + .burst_size_unit = 16, .gfx_fifo_workaround = true, .set_max_preload = true, .supports_sync_align = true, @@ -3788,11 +4331,26 @@ static const struct dispc_features omap54xx_dispc_feats = { .mgr_height_start = 27, .mgr_width_max = 4096, .mgr_height_max = 4096, + .ovl_width_max = 2048, + .ovl_height_max = 4096, .max_lcd_pclk = 170000000, - .max_tv_pclk = 186000000, + .max_tv_pclk = 192000000, + .max_downscale = 4, + .max_line_width = 2048, + .min_pcd = 1, .calc_scaling = dispc_ovl_calc_scaling_44xx, .calc_core_clk = calc_core_clk_44xx, .num_fifos = 5, + .features = omap5_dispc_features_list, + .num_features = ARRAY_SIZE(omap5_dispc_features_list), + .reg_fields = omap4_dispc_reg_fields, + .num_reg_fields = ARRAY_SIZE(omap4_dispc_reg_fields), + .overlay_caps = omap4_dispc_overlay_caps, + .supported_color_modes = omap4_dispc_supported_color_modes, + .num_mgrs = 4, + .num_ovls = 4, + .buffer_size_unit = 16, + .burst_size_unit = 16, .gfx_fifo_workaround = true, .mstandby_workaround = true, .set_max_preload = true, @@ -3804,91 +4362,57 @@ static const struct dispc_features omap54xx_dispc_feats = { .has_gamma_i734_bug = true, }; -static int dispc_init_features(struct platform_device *pdev) -{ - const struct dispc_features *src; - struct dispc_features *dst; - - dst = devm_kzalloc(&pdev->dev, sizeof(*dst), GFP_KERNEL); - if (!dst) { - dev_err(&pdev->dev, "Failed to allocate DISPC Features\n"); - return -ENOMEM; - } - - switch (omapdss_get_version()) { - case OMAPDSS_VER_OMAP24xx: - src = &omap24xx_dispc_feats; - break; - - case OMAPDSS_VER_OMAP34xx_ES1: - src = &omap34xx_rev1_0_dispc_feats; - break; - - case OMAPDSS_VER_OMAP34xx_ES3: - case OMAPDSS_VER_OMAP3630: - case OMAPDSS_VER_AM35xx: - case OMAPDSS_VER_AM43xx: - src = &omap34xx_rev3_0_dispc_feats; - break; - - case OMAPDSS_VER_OMAP4430_ES1: - case OMAPDSS_VER_OMAP4430_ES2: - case OMAPDSS_VER_OMAP4: - src = &omap44xx_dispc_feats; - break; - - case OMAPDSS_VER_OMAP5: - case OMAPDSS_VER_DRA7xx: - src = &omap54xx_dispc_feats; - break; - - default: - return -ENODEV; - } - - memcpy(dst, src, sizeof(*dst)); - dispc.feat = dst; - - return 0; -} - static irqreturn_t dispc_irq_handler(int irq, void *arg) { - if (!dispc.is_enabled) + struct dispc_device *dispc = arg; + + if (!dispc->is_enabled) return IRQ_NONE; - return dispc.user_handler(irq, dispc.user_data); + return dispc->user_handler(irq, dispc->user_data); } -static int dispc_request_irq(irq_handler_t handler, void *dev_id) +int dispc_request_irq(struct dispc_device *dispc, irq_handler_t handler, + void *dev_id) { int r; - if (dispc.user_handler != NULL) + if (dispc->user_handler != NULL) return -EBUSY; - dispc.user_handler = handler; - dispc.user_data = dev_id; + dispc->user_handler = handler; + dispc->user_data = dev_id; /* ensure the dispc_irq_handler sees the values above */ smp_wmb(); - r = devm_request_irq(&dispc.pdev->dev, dispc.irq, dispc_irq_handler, - IRQF_SHARED, "OMAP DISPC", &dispc); + r = devm_request_irq(&dispc->pdev->dev, dispc->irq, dispc_irq_handler, + IRQF_SHARED, "OMAP DISPC", dispc); if (r) { - dispc.user_handler = NULL; - dispc.user_data = NULL; + dispc->user_handler = NULL; + dispc->user_data = NULL; } return r; } -static void dispc_free_irq(void *dev_id) +void dispc_free_irq(struct dispc_device *dispc, void *dev_id) +{ + devm_free_irq(&dispc->pdev->dev, dispc->irq, dispc); + + dispc->user_handler = NULL; + dispc->user_data = NULL; +} + +u32 dispc_get_memory_bandwidth_limit(struct dispc_device *dispc) { - devm_free_irq(&dispc.pdev->dev, dispc.irq, &dispc); + u32 limit = 0; + + /* Optional maximum memory bandwidth */ + of_property_read_u32(dispc->pdev->dev.of_node, "max-memory-bandwidth", + &limit); - dispc.user_handler = NULL; - dispc.user_data = NULL; + return limit; } /* @@ -3962,18 +4486,18 @@ static struct i734_buf { void *vaddr; } i734_buf; -static int dispc_errata_i734_wa_init(void) +static int dispc_errata_i734_wa_init(struct dispc_device *dispc) { - if (!dispc.feat->has_gamma_i734_bug) + if (!dispc->feat->has_gamma_i734_bug) return 0; i734_buf.size = i734.ovli.width * i734.ovli.height * color_mode_to_bpp(i734.ovli.fourcc) / 8; - i734_buf.vaddr = dma_alloc_writecombine(&dispc.pdev->dev, i734_buf.size, - &i734_buf.paddr, GFP_KERNEL); + i734_buf.vaddr = dma_alloc_wc(&dispc->pdev->dev, i734_buf.size, + &i734_buf.paddr, GFP_KERNEL); if (!i734_buf.vaddr) { - dev_err(&dispc.pdev->dev, "%s: dma_alloc_writecombine failed", + dev_err(&dispc->pdev->dev, "%s: dma_alloc_wc failed\n", __func__); return -ENOMEM; } @@ -3981,190 +4505,201 @@ static int dispc_errata_i734_wa_init(void) return 0; } -static void dispc_errata_i734_wa_fini(void) +static void dispc_errata_i734_wa_fini(struct dispc_device *dispc) { - if (!dispc.feat->has_gamma_i734_bug) + if (!dispc->feat->has_gamma_i734_bug) return; - dma_free_writecombine(&dispc.pdev->dev, i734_buf.size, i734_buf.vaddr, - i734_buf.paddr); + dma_free_wc(&dispc->pdev->dev, i734_buf.size, i734_buf.vaddr, + i734_buf.paddr); } -static void dispc_errata_i734_wa(void) +static void dispc_errata_i734_wa(struct dispc_device *dispc) { - u32 framedone_irq = dispc_mgr_get_framedone_irq(OMAP_DSS_CHANNEL_LCD); + u32 framedone_irq = dispc_mgr_get_framedone_irq(dispc, + OMAP_DSS_CHANNEL_LCD); struct omap_overlay_info ovli; struct dss_lcd_mgr_config lcd_conf; u32 gatestate; unsigned int count; - if (!dispc.feat->has_gamma_i734_bug) + if (!dispc->feat->has_gamma_i734_bug) return; - gatestate = REG_GET(DISPC_CONFIG, 8, 4); + gatestate = REG_GET(dispc, DISPC_CONFIG, 8, 4); ovli = i734.ovli; ovli.paddr = i734_buf.paddr; lcd_conf = i734.lcd_conf; /* Gate all LCD1 outputs */ - REG_FLD_MOD(DISPC_CONFIG, 0x1f, 8, 4); + REG_FLD_MOD(dispc, DISPC_CONFIG, 0x1f, 8, 4); /* Setup and enable GFX plane */ - dispc_ovl_setup(OMAP_DSS_GFX, &ovli, &i734.vm, false, - OMAP_DSS_CHANNEL_LCD); - dispc_ovl_enable(OMAP_DSS_GFX, true); + dispc_ovl_setup(dispc, OMAP_DSS_GFX, &ovli, &i734.vm, false, + OMAP_DSS_CHANNEL_LCD); + dispc_ovl_enable(dispc, OMAP_DSS_GFX, true); /* Set up and enable display manager for LCD1 */ - dispc_mgr_setup(OMAP_DSS_CHANNEL_LCD, &i734.mgri); - dispc_calc_clock_rates(dss_get_dispc_clk_rate(), + dispc_mgr_setup(dispc, OMAP_DSS_CHANNEL_LCD, &i734.mgri); + dispc_calc_clock_rates(dispc, dss_get_dispc_clk_rate(dispc->dss), &lcd_conf.clock_info); - dispc_mgr_set_lcd_config(OMAP_DSS_CHANNEL_LCD, &lcd_conf); - dispc_mgr_set_timings(OMAP_DSS_CHANNEL_LCD, &i734.vm); + dispc_mgr_set_lcd_config(dispc, OMAP_DSS_CHANNEL_LCD, &lcd_conf); + dispc_mgr_set_timings(dispc, OMAP_DSS_CHANNEL_LCD, &i734.vm); - dispc_clear_irqstatus(framedone_irq); + dispc_clear_irqstatus(dispc, framedone_irq); /* Enable and shut the channel to produce just one frame */ - dispc_mgr_enable(OMAP_DSS_CHANNEL_LCD, true); - dispc_mgr_enable(OMAP_DSS_CHANNEL_LCD, false); + dispc_mgr_enable(dispc, OMAP_DSS_CHANNEL_LCD, true); + dispc_mgr_enable(dispc, OMAP_DSS_CHANNEL_LCD, false); /* Busy wait for framedone. We can't fiddle with irq handlers * in PM resume. Typically the loop runs less than 5 times and * waits less than a micro second. */ count = 0; - while (!(dispc_read_irqstatus() & framedone_irq)) { + while (!(dispc_read_irqstatus(dispc) & framedone_irq)) { if (count++ > 10000) { - dev_err(&dispc.pdev->dev, "%s: framedone timeout\n", + dev_err(&dispc->pdev->dev, "%s: framedone timeout\n", __func__); break; } } - dispc_ovl_enable(OMAP_DSS_GFX, false); + dispc_ovl_enable(dispc, OMAP_DSS_GFX, false); /* Clear all irq bits before continuing */ - dispc_clear_irqstatus(0xffffffff); + dispc_clear_irqstatus(dispc, 0xffffffff); /* Restore the original state to LCD1 output gates */ - REG_FLD_MOD(DISPC_CONFIG, gatestate, 8, 4); -} - -static const struct dispc_ops dispc_ops = { - .read_irqstatus = dispc_read_irqstatus, - .clear_irqstatus = dispc_clear_irqstatus, - .write_irqenable = dispc_write_irqenable, - - .request_irq = dispc_request_irq, - .free_irq = dispc_free_irq, - - .runtime_get = dispc_runtime_get, - .runtime_put = dispc_runtime_put, - - .get_num_ovls = dispc_get_num_ovls, - .get_num_mgrs = dispc_get_num_mgrs, - - .mgr_enable = dispc_mgr_enable, - .mgr_is_enabled = dispc_mgr_is_enabled, - .mgr_get_vsync_irq = dispc_mgr_get_vsync_irq, - .mgr_get_framedone_irq = dispc_mgr_get_framedone_irq, - .mgr_get_sync_lost_irq = dispc_mgr_get_sync_lost_irq, - .mgr_go_busy = dispc_mgr_go_busy, - .mgr_go = dispc_mgr_go, - .mgr_set_lcd_config = dispc_mgr_set_lcd_config, - .mgr_set_timings = dispc_mgr_set_timings, - .mgr_setup = dispc_mgr_setup, - .mgr_get_supported_outputs = dispc_mgr_get_supported_outputs, - .mgr_gamma_size = dispc_mgr_gamma_size, - .mgr_set_gamma = dispc_mgr_set_gamma, - - .ovl_enable = dispc_ovl_enable, - .ovl_setup = dispc_ovl_setup, - .ovl_get_color_modes = dispc_ovl_get_color_modes, -}; + REG_FLD_MOD(dispc, DISPC_CONFIG, gatestate, 8, 4); +} /* DISPC HW IP initialisation */ +static const struct of_device_id dispc_of_match[] = { + { .compatible = "ti,omap2-dispc", .data = &omap24xx_dispc_feats }, + { .compatible = "ti,omap3-dispc", .data = &omap36xx_dispc_feats }, + { .compatible = "ti,omap4-dispc", .data = &omap44xx_dispc_feats }, + { .compatible = "ti,omap5-dispc", .data = &omap54xx_dispc_feats }, + { .compatible = "ti,dra7-dispc", .data = &omap54xx_dispc_feats }, + {}, +}; + +static const struct soc_device_attribute dispc_soc_devices[] = { + { .machine = "OMAP3[45]*", + .revision = "ES[12].?", .data = &omap34xx_rev1_0_dispc_feats }, + { .machine = "OMAP3[45]*", .data = &omap34xx_rev3_0_dispc_feats }, + { .machine = "AM35*", .data = &omap34xx_rev3_0_dispc_feats }, + { .machine = "AM43*", .data = &am43xx_dispc_feats }, + { /* sentinel */ } +}; + static int dispc_bind(struct device *dev, struct device *master, void *data) { struct platform_device *pdev = to_platform_device(dev); + const struct soc_device_attribute *soc; + struct dss_device *dss = dss_get_device(master); + struct dispc_device *dispc; u32 rev; int r = 0; - struct resource *dispc_mem; struct device_node *np = pdev->dev.of_node; - dispc.pdev = pdev; + dispc = kzalloc(sizeof(*dispc), GFP_KERNEL); + if (!dispc) + return -ENOMEM; - spin_lock_init(&dispc.control_lock); + dispc->pdev = pdev; + platform_set_drvdata(pdev, dispc); + dispc->dss = dss; - r = dispc_init_features(dispc.pdev); - if (r) - return r; + /* + * The OMAP3-based models can't be told apart using the compatible + * string, use SoC device matching. + */ + soc = soc_device_match(dispc_soc_devices); + if (soc) + dispc->feat = soc->data; + else + dispc->feat = device_get_match_data(&pdev->dev); - r = dispc_errata_i734_wa_init(); + r = dispc_errata_i734_wa_init(dispc); if (r) - return r; + goto err_free; - dispc_mem = platform_get_resource(dispc.pdev, IORESOURCE_MEM, 0); - dispc.base = devm_ioremap_resource(&pdev->dev, dispc_mem); - if (IS_ERR(dispc.base)) - return PTR_ERR(dispc.base); + dispc->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(dispc->base)) { + r = PTR_ERR(dispc->base); + goto err_free; + } - dispc.irq = platform_get_irq(dispc.pdev, 0); - if (dispc.irq < 0) { + dispc->irq = platform_get_irq(dispc->pdev, 0); + if (dispc->irq < 0) { DSSERR("platform_get_irq failed\n"); - return -ENODEV; + r = -ENODEV; + goto err_free; } if (np && of_property_read_bool(np, "syscon-pol")) { - dispc.syscon_pol = syscon_regmap_lookup_by_phandle(np, "syscon-pol"); - if (IS_ERR(dispc.syscon_pol)) { + dispc->syscon_pol = syscon_regmap_lookup_by_phandle(np, "syscon-pol"); + if (IS_ERR(dispc->syscon_pol)) { dev_err(&pdev->dev, "failed to get syscon-pol regmap\n"); - return PTR_ERR(dispc.syscon_pol); + r = PTR_ERR(dispc->syscon_pol); + goto err_free; } if (of_property_read_u32_index(np, "syscon-pol", 1, - &dispc.syscon_pol_offset)) { + &dispc->syscon_pol_offset)) { dev_err(&pdev->dev, "failed to get syscon-pol offset\n"); - return -EINVAL; + r = -EINVAL; + goto err_free; } } - r = dispc_init_gamma_tables(); + r = dispc_init_gamma_tables(dispc); if (r) - return r; + goto err_free; pm_runtime_enable(&pdev->dev); - r = dispc_runtime_get(); + r = dispc_runtime_get(dispc); if (r) goto err_runtime_get; - _omap_dispc_initial_config(); + _omap_dispc_initial_config(dispc); - rev = dispc_read_reg(DISPC_REVISION); + rev = dispc_read_reg(dispc, DISPC_REVISION); dev_dbg(&pdev->dev, "OMAP DISPC rev %d.%d\n", FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); - dispc_runtime_put(); + dispc_runtime_put(dispc); - dispc_set_ops(&dispc_ops); + dss->dispc = dispc; - dss_debugfs_create_file("dispc", dispc_dump_regs); + dispc->debugfs = dss_debugfs_create_file(dss, "dispc", dispc_dump_regs, + dispc); return 0; err_runtime_get: pm_runtime_disable(&pdev->dev); +err_free: + kfree(dispc); return r; } -static void dispc_unbind(struct device *dev, struct device *master, - void *data) +static void dispc_unbind(struct device *dev, struct device *master, void *data) { - dispc_set_ops(NULL); + struct dispc_device *dispc = dev_get_drvdata(dev); + struct dss_device *dss = dispc->dss; + + dss_debugfs_remove_file(dispc->debugfs); + + dss->dispc = NULL; pm_runtime_disable(dev); - dispc_errata_i734_wa_fini(); + dispc_errata_i734_wa_fini(dispc); + + kfree(dispc); } static const struct component_ops dispc_component_ops = { @@ -4177,44 +4712,47 @@ static int dispc_probe(struct platform_device *pdev) return component_add(&pdev->dev, &dispc_component_ops); } -static int dispc_remove(struct platform_device *pdev) +static void dispc_remove(struct platform_device *pdev) { component_del(&pdev->dev, &dispc_component_ops); - return 0; } -static int dispc_runtime_suspend(struct device *dev) +static __maybe_unused int dispc_runtime_suspend(struct device *dev) { - dispc.is_enabled = false; + struct dispc_device *dispc = dev_get_drvdata(dev); + + dispc->is_enabled = false; /* ensure the dispc_irq_handler sees the is_enabled value */ smp_wmb(); /* wait for current handler to finish before turning the DISPC off */ - synchronize_irq(dispc.irq); + synchronize_irq(dispc->irq); - dispc_save_context(); + dispc_save_context(dispc); return 0; } -static int dispc_runtime_resume(struct device *dev) +static __maybe_unused int dispc_runtime_resume(struct device *dev) { + struct dispc_device *dispc = dev_get_drvdata(dev); + /* * The reset value for load mode is 0 (OMAP_DSS_LOAD_CLUT_AND_FRAME) * but we always initialize it to 2 (OMAP_DSS_LOAD_FRAME_ONLY) in * _omap_dispc_initial_config(). We can thus use it to detect if * we have lost register context. */ - if (REG_GET(DISPC_CONFIG, 2, 1) != OMAP_DSS_LOAD_FRAME_ONLY) { - _omap_dispc_initial_config(); + if (REG_GET(dispc, DISPC_CONFIG, 2, 1) != OMAP_DSS_LOAD_FRAME_ONLY) { + _omap_dispc_initial_config(dispc); - dispc_errata_i734_wa(); + dispc_errata_i734_wa(dispc); - dispc_restore_context(); + dispc_restore_context(dispc); - dispc_restore_gamma_tables(); + dispc_restore_gamma_tables(dispc); } - dispc.is_enabled = true; + dispc->is_enabled = true; /* ensure the dispc_irq_handler sees the is_enabled value */ smp_wmb(); @@ -4222,22 +4760,13 @@ static int dispc_runtime_resume(struct device *dev) } static const struct dev_pm_ops dispc_pm_ops = { - .runtime_suspend = dispc_runtime_suspend, - .runtime_resume = dispc_runtime_resume, -}; - -static const struct of_device_id dispc_of_match[] = { - { .compatible = "ti,omap2-dispc", }, - { .compatible = "ti,omap3-dispc", }, - { .compatible = "ti,omap4-dispc", }, - { .compatible = "ti,omap5-dispc", }, - { .compatible = "ti,dra7-dispc", }, - {}, + SET_RUNTIME_PM_OPS(dispc_runtime_suspend, dispc_runtime_resume, NULL) + SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; -static struct platform_driver omap_dispchw_driver = { +struct platform_driver omap_dispchw_driver = { .probe = dispc_probe, - .remove = dispc_remove, + .remove = dispc_remove, .driver = { .name = "omapdss_dispc", .pm = &dispc_pm_ops, @@ -4245,13 +4774,3 @@ static struct platform_driver omap_dispchw_driver = { .suppress_bind_attrs = true, }, }; - -int __init dispc_init_platform_driver(void) -{ - return platform_driver_register(&omap_dispchw_driver); -} - -void dispc_uninit_platform_driver(void) -{ - platform_driver_unregister(&omap_dispchw_driver); -} diff --git a/drivers/gpu/drm/omapdrm/dss/dispc.h b/drivers/gpu/drm/omapdrm/dss/dispc.h index 003adce532f4..3f842c1ff81a 100644 --- a/drivers/gpu/drm/omapdrm/dss/dispc.h +++ b/drivers/gpu/drm/omapdrm/dss/dispc.h @@ -1,21 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* - * linux/drivers/video/omap2/dss/dispc.h - * - * Copyright (C) 2011 Texas Instruments + * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/ * Author: Archit Taneja <archit@ti.com> - * - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __OMAP2_DISPC_REG_H diff --git a/drivers/gpu/drm/omapdrm/dss/dispc_coefs.c b/drivers/gpu/drm/omapdrm/dss/dispc_coefs.c index 34fad2376f8d..d1f3a93b8efd 100644 --- a/drivers/gpu/drm/omapdrm/dss/dispc_coefs.c +++ b/drivers/gpu/drm/omapdrm/dss/dispc_coefs.c @@ -1,20 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* - * linux/drivers/video/omap2/dss/dispc_coefs.c - * - * Copyright (C) 2011 Texas Instruments + * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/ * 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> diff --git a/drivers/gpu/drm/omapdrm/dss/display.c b/drivers/gpu/drm/omapdrm/dss/display.c deleted file mode 100644 index 42279933790e..000000000000 --- a/drivers/gpu/drm/omapdrm/dss/display.c +++ /dev/null @@ -1,191 +0,0 @@ -/* - * linux/drivers/video/omap2/dss/display.c - * - * Copyright (C) 2009 Nokia Corporation - * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> - * - * Some code and ideas taken from drivers/video/omap/ driver - * by Imre Deak. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#define DSS_SUBSYS_NAME "DISPLAY" - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/jiffies.h> -#include <linux/platform_device.h> -#include <linux/of.h> - -#include "omapdss.h" - -void omapdss_default_get_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - *vm = dssdev->panel.vm; -} -EXPORT_SYMBOL(omapdss_default_get_timings); - -static LIST_HEAD(panel_list); -static DEFINE_MUTEX(panel_list_mutex); -static int disp_num_counter; - -int omapdss_register_display(struct omap_dss_device *dssdev) -{ - struct omap_dss_driver *drv = dssdev->driver; - struct list_head *cur; - int id; - - /* - * Note: this presumes that all displays either have an DT alias, or - * none has. - */ - id = of_alias_get_id(dssdev->dev->of_node, "display"); - if (id < 0) - id = disp_num_counter++; - - snprintf(dssdev->alias, sizeof(dssdev->alias), "display%d", id); - - /* Use 'label' property for name, if it exists */ - of_property_read_string(dssdev->dev->of_node, "label", &dssdev->name); - - if (dssdev->name == NULL) - dssdev->name = dssdev->alias; - - if (drv && drv->get_timings == NULL) - drv->get_timings = omapdss_default_get_timings; - - mutex_lock(&panel_list_mutex); - list_for_each(cur, &panel_list) { - struct omap_dss_device *ldev = list_entry(cur, - struct omap_dss_device, - panel_list); - if (strcmp(ldev->alias, dssdev->alias) > 0) - break; - } - list_add_tail(&dssdev->panel_list, cur); - mutex_unlock(&panel_list_mutex); - return 0; -} -EXPORT_SYMBOL(omapdss_register_display); - -void omapdss_unregister_display(struct omap_dss_device *dssdev) -{ - mutex_lock(&panel_list_mutex); - list_del(&dssdev->panel_list); - mutex_unlock(&panel_list_mutex); -} -EXPORT_SYMBOL(omapdss_unregister_display); - -bool omapdss_component_is_display(struct device_node *node) -{ - struct omap_dss_device *dssdev; - bool found = false; - - mutex_lock(&panel_list_mutex); - list_for_each_entry(dssdev, &panel_list, panel_list) { - if (dssdev->dev->of_node == node) { - found = true; - goto out; - } - } -out: - mutex_unlock(&panel_list_mutex); - return found; -} -EXPORT_SYMBOL(omapdss_component_is_display); - -struct omap_dss_device *omap_dss_get_device(struct omap_dss_device *dssdev) -{ - if (!try_module_get(dssdev->owner)) - return NULL; - - if (get_device(dssdev->dev) == NULL) { - module_put(dssdev->owner); - return NULL; - } - - return dssdev; -} -EXPORT_SYMBOL(omap_dss_get_device); - -void omap_dss_put_device(struct omap_dss_device *dssdev) -{ - put_device(dssdev->dev); - module_put(dssdev->owner); -} -EXPORT_SYMBOL(omap_dss_put_device); - -/* - * ref count of the found device is incremented. - * ref count of from-device is decremented. - */ -struct omap_dss_device *omap_dss_get_next_device(struct omap_dss_device *from) -{ - struct list_head *l; - struct omap_dss_device *dssdev; - - mutex_lock(&panel_list_mutex); - - if (list_empty(&panel_list)) { - dssdev = NULL; - goto out; - } - - if (from == NULL) { - dssdev = list_first_entry(&panel_list, struct omap_dss_device, - panel_list); - omap_dss_get_device(dssdev); - goto out; - } - - omap_dss_put_device(from); - - list_for_each(l, &panel_list) { - dssdev = list_entry(l, struct omap_dss_device, panel_list); - if (dssdev == from) { - if (list_is_last(l, &panel_list)) { - dssdev = NULL; - goto out; - } - - dssdev = list_entry(l->next, struct omap_dss_device, - panel_list); - omap_dss_get_device(dssdev); - goto out; - } - } - - WARN(1, "'from' dssdev not found\n"); - - dssdev = NULL; -out: - mutex_unlock(&panel_list_mutex); - return dssdev; -} -EXPORT_SYMBOL(omap_dss_get_next_device); - -struct omap_dss_device *omap_dss_find_device(void *data, - int (*match)(struct omap_dss_device *dssdev, void *data)) -{ - struct omap_dss_device *dssdev = NULL; - - while ((dssdev = omap_dss_get_next_device(dssdev)) != NULL) { - if (match(dssdev, data)) - return dssdev; - } - - return NULL; -} -EXPORT_SYMBOL(omap_dss_find_device); diff --git a/drivers/gpu/drm/omapdrm/dss/dpi.c b/drivers/gpu/drm/omapdrm/dss/dpi.c index 86dbb65a6c28..9f86db774c39 100644 --- a/drivers/gpu/drm/omapdrm/dss/dpi.c +++ b/drivers/gpu/drm/omapdrm/dss/dpi.c @@ -1,66 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0-only /* - * linux/drivers/video/omap2/dss/dpi.c - * * Copyright (C) 2009 Nokia Corporation - * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> * * Some code and ideas taken from drivers/video/omap/ driver * by Imre Deak. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. */ #define DSS_SUBSYS_NAME "DPI" -#include <linux/kernel.h> +#include <linux/clk.h> #include <linux/delay.h> -#include <linux/export.h> #include <linux/err.h> #include <linux/errno.h> +#include <linux/export.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/of_graph.h> #include <linux/platform_device.h> #include <linux/regulator/consumer.h> #include <linux/string.h> -#include <linux/of.h> -#include <linux/clk.h> +#include <linux/sys_soc.h> + +#include <drm/drm_bridge.h> -#include "omapdss.h" #include "dss.h" -#include "dss_features.h" +#include "omapdss.h" struct dpi_data { struct platform_device *pdev; + enum dss_model dss_model; + struct dss_device *dss; + unsigned int id; struct regulator *vdds_dsi_reg; enum dss_clk_source clk_src; struct dss_pll *pll; - struct mutex lock; - - struct videomode vm; struct dss_lcd_mgr_config mgr_config; + unsigned long pixelclock; int data_lines; struct omap_dss_device output; - - bool port_initialized; + struct drm_bridge bridge; }; -static struct dpi_data *dpi_get_data_from_dssdev(struct omap_dss_device *dssdev) -{ - return container_of(dssdev, struct dpi_data, output); -} +#define drm_bridge_to_dpi(bridge) container_of(bridge, struct dpi_data, bridge) -static enum dss_clk_source dpi_get_clk_src_dra7xx(enum omap_channel channel) +/* ----------------------------------------------------------------------------- + * Clock Handling and PLL + */ + +static enum dss_clk_source dpi_get_clk_src_dra7xx(struct dpi_data *dpi, + enum omap_channel channel) { /* * Possible clock sources: @@ -72,23 +64,23 @@ static enum dss_clk_source dpi_get_clk_src_dra7xx(enum omap_channel channel) switch (channel) { case OMAP_DSS_CHANNEL_LCD: { - if (dss_pll_find_by_src(DSS_CLK_SRC_PLL1_1)) + if (dss_pll_find_by_src(dpi->dss, DSS_CLK_SRC_PLL1_1)) return DSS_CLK_SRC_PLL1_1; break; } case OMAP_DSS_CHANNEL_LCD2: { - if (dss_pll_find_by_src(DSS_CLK_SRC_PLL1_3)) + if (dss_pll_find_by_src(dpi->dss, DSS_CLK_SRC_PLL1_3)) return DSS_CLK_SRC_PLL1_3; - if (dss_pll_find_by_src(DSS_CLK_SRC_PLL2_3)) + if (dss_pll_find_by_src(dpi->dss, DSS_CLK_SRC_PLL2_3)) return DSS_CLK_SRC_PLL2_3; break; } case OMAP_DSS_CHANNEL_LCD3: { - if (dss_pll_find_by_src(DSS_CLK_SRC_PLL2_1)) + if (dss_pll_find_by_src(dpi->dss, DSS_CLK_SRC_PLL2_1)) return DSS_CLK_SRC_PLL2_1; - if (dss_pll_find_by_src(DSS_CLK_SRC_PLL1_3)) + if (dss_pll_find_by_src(dpi->dss, DSS_CLK_SRC_PLL1_3)) return DSS_CLK_SRC_PLL1_3; break; } @@ -99,25 +91,21 @@ static enum dss_clk_source dpi_get_clk_src_dra7xx(enum omap_channel channel) return DSS_CLK_SRC_FCK; } -static enum dss_clk_source dpi_get_clk_src(enum omap_channel channel) +static enum dss_clk_source dpi_get_clk_src(struct dpi_data *dpi) { + enum omap_channel channel = dpi->output.dispc_channel; + /* * XXX we can't currently use DSI PLL for DPI with OMAP3, as the DSI PLL * would also be used for DISPC fclk. Meaning, when the DPI output is * disabled, DISPC clock will be disabled, and TV out will stop. */ - switch (omapdss_get_version()) { - case OMAPDSS_VER_OMAP24xx: - case OMAPDSS_VER_OMAP34xx_ES1: - case OMAPDSS_VER_OMAP34xx_ES3: - case OMAPDSS_VER_OMAP3630: - case OMAPDSS_VER_AM35xx: - case OMAPDSS_VER_AM43xx: + switch (dpi->dss_model) { + case DSS_MODEL_OMAP2: + case DSS_MODEL_OMAP3: return DSS_CLK_SRC_FCK; - case OMAPDSS_VER_OMAP4430_ES1: - case OMAPDSS_VER_OMAP4430_ES2: - case OMAPDSS_VER_OMAP4: + case DSS_MODEL_OMAP4: switch (channel) { case OMAP_DSS_CHANNEL_LCD: return DSS_CLK_SRC_PLL1_1; @@ -127,7 +115,7 @@ static enum dss_clk_source dpi_get_clk_src(enum omap_channel channel) return DSS_CLK_SRC_FCK; } - case OMAPDSS_VER_OMAP5: + case DSS_MODEL_OMAP5: switch (channel) { case OMAP_DSS_CHANNEL_LCD: return DSS_CLK_SRC_PLL1_1; @@ -138,8 +126,8 @@ static enum dss_clk_source dpi_get_clk_src(enum omap_channel channel) return DSS_CLK_SRC_FCK; } - case OMAPDSS_VER_DRA7xx: - return dpi_get_clk_src_dra7xx(channel); + case DSS_MODEL_DRA7: + return dpi_get_clk_src_dra7xx(dpi, channel); default: return DSS_CLK_SRC_FCK; @@ -147,8 +135,8 @@ static enum dss_clk_source dpi_get_clk_src(enum omap_channel channel) } struct dpi_clk_calc_ctx { - struct dss_pll *pll; - unsigned clkout_idx; + struct dpi_data *dpi; + unsigned int clkout_idx; /* inputs */ @@ -196,8 +184,9 @@ static bool dpi_calc_hsdiv_cb(int m_dispc, unsigned long dispc, ctx->pll_cinfo.mX[ctx->clkout_idx] = m_dispc; ctx->pll_cinfo.clkout[ctx->clkout_idx] = dispc; - return dispc_div_calc(dispc, ctx->pck_min, ctx->pck_max, - dpi_calc_dispc_cb, ctx); + return dispc_div_calc(ctx->dpi->dss->dispc, dispc, + ctx->pck_min, ctx->pck_max, + dpi_calc_dispc_cb, ctx); } @@ -212,8 +201,8 @@ static bool dpi_calc_pll_cb(int n, int m, unsigned long fint, ctx->pll_cinfo.fint = fint; ctx->pll_cinfo.clkdco = clkdco; - return dss_pll_hsdiv_calc_a(ctx->pll, clkdco, - ctx->pck_min, dss_feat_get_param_max(FEAT_PARAM_DSS_FCK), + return dss_pll_hsdiv_calc_a(ctx->dpi->pll, clkdco, + ctx->pck_min, dss_get_max_fck_rate(ctx->dpi->dss), dpi_calc_hsdiv_cb, ctx); } @@ -223,8 +212,9 @@ static bool dpi_calc_dss_cb(unsigned long fck, void *data) ctx->fck = fck; - return dispc_div_calc(fck, ctx->pck_min, ctx->pck_max, - dpi_calc_dispc_cb, ctx); + return dispc_div_calc(ctx->dpi->dss->dispc, fck, + ctx->pck_min, ctx->pck_max, + dpi_calc_dispc_cb, ctx); } static bool dpi_pll_clk_calc(struct dpi_data *dpi, unsigned long pck, @@ -233,7 +223,7 @@ static bool dpi_pll_clk_calc(struct dpi_data *dpi, unsigned long pck, unsigned long clkin; memset(ctx, 0, sizeof(*ctx)); - ctx->pll = dpi->pll; + ctx->dpi = dpi; ctx->clkout_idx = dss_pll_get_clkout_idx_for_src(dpi->clk_src); clkin = clk_get_rate(dpi->pll->clkin); @@ -247,7 +237,7 @@ static bool dpi_pll_clk_calc(struct dpi_data *dpi, unsigned long pck, pll_min = 0; pll_max = 0; - return dss_pll_calc_a(ctx->pll, clkin, + return dss_pll_calc_a(ctx->dpi->pll, clkin, pll_min, pll_max, dpi_calc_pll_cb, ctx); } else { /* DSS_PLL_TYPE_B */ @@ -262,7 +252,8 @@ static bool dpi_pll_clk_calc(struct dpi_data *dpi, unsigned long pck, } } -static bool dpi_dss_clk_calc(unsigned long pck, struct dpi_clk_calc_ctx *ctx) +static bool dpi_dss_clk_calc(struct dpi_data *dpi, unsigned long pck, + struct dpi_clk_calc_ctx *ctx) { int i; @@ -277,13 +268,15 @@ static bool dpi_dss_clk_calc(unsigned long pck, struct dpi_clk_calc_ctx *ctx) bool ok; memset(ctx, 0, sizeof(*ctx)); + ctx->dpi = dpi; if (pck > 1000 * i * i * i) ctx->pck_min = max(pck - 1000 * i * i * i, 0lu); else ctx->pck_min = 0; ctx->pck_max = pck + 1000 * i * i * i; - ok = dss_div_calc(pck, ctx->pck_min, dpi_calc_dss_cb, ctx); + ok = dss_div_calc(dpi->dss, pck, ctx->pck_min, + dpi_calc_dss_cb, ctx); if (ok) return ok; } @@ -293,9 +286,7 @@ static bool dpi_dss_clk_calc(unsigned long pck, struct dpi_clk_calc_ctx *ctx) -static int dpi_set_pll_clk(struct dpi_data *dpi, enum omap_channel channel, - unsigned long pck_req, unsigned long *fck, int *lck_div, - int *pck_div) +static int dpi_set_pll_clk(struct dpi_data *dpi, unsigned long pck_req) { struct dpi_clk_calc_ctx ctx; int r; @@ -309,79 +300,47 @@ static int dpi_set_pll_clk(struct dpi_data *dpi, enum omap_channel channel, if (r) return r; - dss_select_lcd_clk_source(channel, dpi->clk_src); + dss_select_lcd_clk_source(dpi->dss, dpi->output.dispc_channel, + dpi->clk_src); dpi->mgr_config.clock_info = ctx.dispc_cinfo; - *fck = ctx.pll_cinfo.clkout[ctx.clkout_idx]; - *lck_div = ctx.dispc_cinfo.lck_div; - *pck_div = ctx.dispc_cinfo.pck_div; - return 0; } -static int dpi_set_dispc_clk(struct dpi_data *dpi, unsigned long pck_req, - unsigned long *fck, int *lck_div, int *pck_div) +static int dpi_set_dispc_clk(struct dpi_data *dpi, unsigned long pck_req) { struct dpi_clk_calc_ctx ctx; int r; bool ok; - ok = dpi_dss_clk_calc(pck_req, &ctx); + ok = dpi_dss_clk_calc(dpi, pck_req, &ctx); if (!ok) return -EINVAL; - r = dss_set_fck_rate(ctx.fck); + r = dss_set_fck_rate(dpi->dss, ctx.fck); if (r) return r; dpi->mgr_config.clock_info = ctx.dispc_cinfo; - *fck = ctx.fck; - *lck_div = ctx.dispc_cinfo.lck_div; - *pck_div = ctx.dispc_cinfo.pck_div; - return 0; } static int dpi_set_mode(struct dpi_data *dpi) { - struct omap_dss_device *out = &dpi->output; - enum omap_channel channel = out->dispc_channel; - struct videomode *vm = &dpi->vm; - int lck_div = 0, pck_div = 0; - unsigned long fck = 0; - unsigned long pck; - int r = 0; + int r; if (dpi->pll) - r = dpi_set_pll_clk(dpi, channel, vm->pixelclock, &fck, - &lck_div, &pck_div); + r = dpi_set_pll_clk(dpi, dpi->pixelclock); else - r = dpi_set_dispc_clk(dpi, vm->pixelclock, &fck, - &lck_div, &pck_div); - if (r) - return r; - - pck = fck / lck_div / pck_div; - - if (pck != vm->pixelclock) { - DSSWARN("Could not find exact pixel clock. Requested %lu Hz, got %lu Hz\n", - vm->pixelclock, pck); + r = dpi_set_dispc_clk(dpi, dpi->pixelclock); - vm->pixelclock = pck; - } - - dss_mgr_set_timings(channel, vm); - - return 0; + return r; } static void dpi_config_lcd_manager(struct dpi_data *dpi) { - struct omap_dss_device *out = &dpi->output; - enum omap_channel channel = out->dispc_channel; - dpi->mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS; dpi->mgr_config.stallmode = false; @@ -391,252 +350,248 @@ static void dpi_config_lcd_manager(struct dpi_data *dpi) dpi->mgr_config.lcden_sig_polarity = 0; - dss_mgr_set_lcd_config(channel, &dpi->mgr_config); + dss_mgr_set_lcd_config(&dpi->output, &dpi->mgr_config); } -static int dpi_display_enable(struct omap_dss_device *dssdev) +static int dpi_clock_update(struct dpi_data *dpi, unsigned long *clock) { - struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev); - struct omap_dss_device *out = &dpi->output; - enum omap_channel channel = out->dispc_channel; - int r; - - mutex_lock(&dpi->lock); + int lck_div, pck_div; + unsigned long fck; + struct dpi_clk_calc_ctx ctx; - if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI) && !dpi->vdds_dsi_reg) { - DSSERR("no VDSS_DSI regulator\n"); - r = -ENODEV; - goto err_no_reg; - } + if (dpi->pll) { + if (!dpi_pll_clk_calc(dpi, *clock, &ctx)) + return -EINVAL; - if (!out->dispc_channel_connected) { - DSSERR("failed to enable display: no output/manager\n"); - r = -ENODEV; - goto err_no_out_mgr; - } + fck = ctx.pll_cinfo.clkout[ctx.clkout_idx]; + } else { + if (!dpi_dss_clk_calc(dpi, *clock, &ctx)) + return -EINVAL; - if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI)) { - r = regulator_enable(dpi->vdds_dsi_reg); - if (r) - goto err_reg_enable; + fck = ctx.fck; } - r = dispc_runtime_get(); - if (r) - goto err_get_dispc; - - r = dss_dpi_select_source(out->port_num, channel); - if (r) - goto err_src_sel; + lck_div = ctx.dispc_cinfo.lck_div; + pck_div = ctx.dispc_cinfo.pck_div; - if (dpi->pll) { - r = dss_pll_enable(dpi->pll); - if (r) - goto err_pll_init; - } + *clock = fck / lck_div / pck_div; - r = dpi_set_mode(dpi); - if (r) - goto err_set_mode; + return 0; +} - dpi_config_lcd_manager(dpi); +static int dpi_verify_pll(struct dss_pll *pll) +{ + int r; - mdelay(2); + /* do initial setup with the PLL to see if it is operational */ - r = dss_mgr_enable(channel); + r = dss_pll_enable(pll); if (r) - goto err_mgr_enable; + return r; - mutex_unlock(&dpi->lock); + dss_pll_disable(pll); return 0; - -err_mgr_enable: -err_set_mode: - if (dpi->pll) - dss_pll_disable(dpi->pll); -err_pll_init: -err_src_sel: - dispc_runtime_put(); -err_get_dispc: - if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI)) - regulator_disable(dpi->vdds_dsi_reg); -err_reg_enable: -err_no_out_mgr: -err_no_reg: - mutex_unlock(&dpi->lock); - return r; } -static void dpi_display_disable(struct omap_dss_device *dssdev) +static void dpi_init_pll(struct dpi_data *dpi) { - struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev); - enum omap_channel channel = dpi->output.dispc_channel; - - mutex_lock(&dpi->lock); + struct dss_pll *pll; - dss_mgr_disable(channel); + if (dpi->pll) + return; - if (dpi->pll) { - dss_select_lcd_clk_source(channel, DSS_CLK_SRC_FCK); - dss_pll_disable(dpi->pll); - } + dpi->clk_src = dpi_get_clk_src(dpi); - dispc_runtime_put(); + pll = dss_pll_find_by_src(dpi->dss, dpi->clk_src); + if (!pll) + return; - if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI)) - regulator_disable(dpi->vdds_dsi_reg); + if (dpi_verify_pll(pll)) { + DSSWARN("PLL not operational\n"); + return; + } - mutex_unlock(&dpi->lock); + dpi->pll = pll; } -static void dpi_set_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev); +/* ----------------------------------------------------------------------------- + * DRM Bridge Operations + */ - DSSDBG("dpi_set_timings\n"); +static int dpi_bridge_attach(struct drm_bridge *bridge, + struct drm_encoder *encoder, + enum drm_bridge_attach_flags flags) +{ + struct dpi_data *dpi = drm_bridge_to_dpi(bridge); - mutex_lock(&dpi->lock); + if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) + return -EINVAL; - dpi->vm = *vm; + dpi_init_pll(dpi); - mutex_unlock(&dpi->lock); + return drm_bridge_attach(encoder, dpi->output.next_bridge, + bridge, flags); } -static void dpi_get_timings(struct omap_dss_device *dssdev, - struct videomode *vm) +static enum drm_mode_status +dpi_bridge_mode_valid(struct drm_bridge *bridge, + const struct drm_display_info *info, + const struct drm_display_mode *mode) { - struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev); + struct dpi_data *dpi = drm_bridge_to_dpi(bridge); + unsigned long clock = mode->clock * 1000; + int ret; + + if (mode->hdisplay % 8 != 0) + return MODE_BAD_WIDTH; - mutex_lock(&dpi->lock); + if (mode->clock == 0) + return MODE_NOCLOCK; - *vm = dpi->vm; + ret = dpi_clock_update(dpi, &clock); + if (ret < 0) + return MODE_CLOCK_RANGE; - mutex_unlock(&dpi->lock); + return MODE_OK; } -static int dpi_check_timings(struct omap_dss_device *dssdev, - struct videomode *vm) +static bool dpi_bridge_mode_fixup(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) { - struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev); - enum omap_channel channel = dpi->output.dispc_channel; - int lck_div, pck_div; - unsigned long fck; - unsigned long pck; - struct dpi_clk_calc_ctx ctx; - bool ok; + struct dpi_data *dpi = drm_bridge_to_dpi(bridge); + unsigned long clock = mode->clock * 1000; + int ret; - if (vm->hactive % 8 != 0) - return -EINVAL; + ret = dpi_clock_update(dpi, &clock); + if (ret < 0) + return false; - if (!dispc_mgr_timings_ok(channel, vm)) - return -EINVAL; + adjusted_mode->clock = clock / 1000; - if (vm->pixelclock == 0) - return -EINVAL; + return true; +} - if (dpi->pll) { - ok = dpi_pll_clk_calc(dpi, vm->pixelclock, &ctx); - if (!ok) - return -EINVAL; +static void dpi_bridge_mode_set(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + const struct drm_display_mode *adjusted_mode) +{ + struct dpi_data *dpi = drm_bridge_to_dpi(bridge); - fck = ctx.pll_cinfo.clkout[ctx.clkout_idx]; - } else { - ok = dpi_dss_clk_calc(vm->pixelclock, &ctx); - if (!ok) - return -EINVAL; + dpi->pixelclock = adjusted_mode->clock * 1000; +} - fck = ctx.fck; +static void dpi_bridge_enable(struct drm_bridge *bridge) +{ + struct dpi_data *dpi = drm_bridge_to_dpi(bridge); + int r; + + if (dpi->vdds_dsi_reg) { + r = regulator_enable(dpi->vdds_dsi_reg); + if (r) + return; } - lck_div = ctx.dispc_cinfo.lck_div; - pck_div = ctx.dispc_cinfo.pck_div; + r = dispc_runtime_get(dpi->dss->dispc); + if (r) + goto err_get_dispc; - pck = fck / lck_div / pck_div; + r = dss_dpi_select_source(dpi->dss, dpi->id, dpi->output.dispc_channel); + if (r) + goto err_src_sel; - vm->pixelclock = pck; + if (dpi->pll) { + r = dss_pll_enable(dpi->pll); + if (r) + goto err_pll_init; + } - return 0; -} + r = dpi_set_mode(dpi); + if (r) + goto err_set_mode; -static int dpi_verify_pll(struct dss_pll *pll) -{ - int r; + dpi_config_lcd_manager(dpi); - /* do initial setup with the PLL to see if it is operational */ + mdelay(2); - r = dss_pll_enable(pll); + r = dss_mgr_enable(&dpi->output); if (r) - return r; + goto err_mgr_enable; - dss_pll_disable(pll); + return; - return 0; +err_mgr_enable: +err_set_mode: + if (dpi->pll) + dss_pll_disable(dpi->pll); +err_pll_init: +err_src_sel: + dispc_runtime_put(dpi->dss->dispc); +err_get_dispc: + if (dpi->vdds_dsi_reg) + regulator_disable(dpi->vdds_dsi_reg); } -static int dpi_init_regulator(struct dpi_data *dpi) +static void dpi_bridge_disable(struct drm_bridge *bridge) { - struct regulator *vdds_dsi; + struct dpi_data *dpi = drm_bridge_to_dpi(bridge); - if (!dss_has_feature(FEAT_DPI_USES_VDDS_DSI)) - return 0; + dss_mgr_disable(&dpi->output); - if (dpi->vdds_dsi_reg) - return 0; - - vdds_dsi = devm_regulator_get(&dpi->pdev->dev, "vdds_dsi"); - if (IS_ERR(vdds_dsi)) { - if (PTR_ERR(vdds_dsi) != -EPROBE_DEFER) - DSSERR("can't get VDDS_DSI regulator\n"); - return PTR_ERR(vdds_dsi); + if (dpi->pll) { + dss_select_lcd_clk_source(dpi->dss, dpi->output.dispc_channel, + DSS_CLK_SRC_FCK); + dss_pll_disable(dpi->pll); } - dpi->vdds_dsi_reg = vdds_dsi; + dispc_runtime_put(dpi->dss->dispc); - return 0; + if (dpi->vdds_dsi_reg) + regulator_disable(dpi->vdds_dsi_reg); } -static void dpi_init_pll(struct dpi_data *dpi) -{ - struct dss_pll *pll; - - if (dpi->pll) - return; - - dpi->clk_src = dpi_get_clk_src(dpi->output.dispc_channel); +static const struct drm_bridge_funcs dpi_bridge_funcs = { + .attach = dpi_bridge_attach, + .mode_valid = dpi_bridge_mode_valid, + .mode_fixup = dpi_bridge_mode_fixup, + .mode_set = dpi_bridge_mode_set, + .enable = dpi_bridge_enable, + .disable = dpi_bridge_disable, +}; - pll = dss_pll_find_by_src(dpi->clk_src); - if (!pll) - return; +static void dpi_bridge_init(struct dpi_data *dpi) +{ + dpi->bridge.of_node = dpi->pdev->dev.of_node; + dpi->bridge.type = DRM_MODE_CONNECTOR_DPI; - if (dpi_verify_pll(pll)) { - DSSWARN("PLL not operational\n"); - return; - } + drm_bridge_add(&dpi->bridge); +} - dpi->pll = pll; +static void dpi_bridge_cleanup(struct dpi_data *dpi) +{ + drm_bridge_remove(&dpi->bridge); } +/* ----------------------------------------------------------------------------- + * Initialisation and Cleanup + */ + /* * Return a hardcoded channel for the DPI output. This should work for * current use cases, but this can be later expanded to either resolve * the channel in some more dynamic manner, or get the channel as a user * parameter. */ -static enum omap_channel dpi_get_channel(int port_num) +static enum omap_channel dpi_get_channel(struct dpi_data *dpi) { - switch (omapdss_get_version()) { - case OMAPDSS_VER_OMAP24xx: - case OMAPDSS_VER_OMAP34xx_ES1: - case OMAPDSS_VER_OMAP34xx_ES3: - case OMAPDSS_VER_OMAP3630: - case OMAPDSS_VER_AM35xx: - case OMAPDSS_VER_AM43xx: + switch (dpi->dss_model) { + case DSS_MODEL_OMAP2: + case DSS_MODEL_OMAP3: return OMAP_DSS_CHANNEL_LCD; - case OMAPDSS_VER_DRA7xx: - switch (port_num) { + case DSS_MODEL_DRA7: + switch (dpi->id) { case 2: return OMAP_DSS_CHANNEL_LCD3; case 1: @@ -646,12 +601,10 @@ static enum omap_channel dpi_get_channel(int port_num) return OMAP_DSS_CHANNEL_LCD; } - case OMAPDSS_VER_OMAP4430_ES1: - case OMAPDSS_VER_OMAP4430_ES2: - case OMAPDSS_VER_OMAP4: + case DSS_MODEL_OMAP4: return OMAP_DSS_CHANNEL_LCD2; - case OMAPDSS_VER_OMAP5: + case DSS_MODEL_OMAP5: return OMAP_DSS_CHANNEL_LCD3; default: @@ -660,73 +613,16 @@ static enum omap_channel dpi_get_channel(int port_num) } } -static int dpi_connect(struct omap_dss_device *dssdev, - struct omap_dss_device *dst) +static int dpi_init_output_port(struct dpi_data *dpi, struct device_node *port) { - struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev); - enum omap_channel channel = dpi->output.dispc_channel; - int r; - - r = dpi_init_regulator(dpi); - if (r) - return r; - - dpi_init_pll(dpi); - - r = dss_mgr_connect(channel, dssdev); - if (r) - return r; - - r = omapdss_output_set_device(dssdev, dst); - if (r) { - DSSERR("failed to connect output to new device: %s\n", - dst->name); - dss_mgr_disconnect(channel, dssdev); - return r; - } - - return 0; -} - -static void dpi_disconnect(struct omap_dss_device *dssdev, - struct omap_dss_device *dst) -{ - struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev); - enum omap_channel channel = dpi->output.dispc_channel; - - WARN_ON(dst != dssdev->dst); - - if (dst != dssdev->dst) - return; - - omapdss_output_unset_device(dssdev); - - dss_mgr_disconnect(channel, dssdev); -} - -static const struct omapdss_dpi_ops dpi_ops = { - .connect = dpi_connect, - .disconnect = dpi_disconnect, - - .enable = dpi_display_enable, - .disable = dpi_display_disable, - - .check_timings = dpi_check_timings, - .set_timings = dpi_set_timings, - .get_timings = dpi_get_timings, -}; - -static void dpi_init_output_port(struct platform_device *pdev, - struct device_node *port) -{ - struct dpi_data *dpi = port->data; struct omap_dss_device *out = &dpi->output; + u32 port_num = 0; int r; - u32 port_num; - r = of_property_read_u32(port, "reg", &port_num); - if (r) - port_num = 0; + dpi_bridge_init(dpi); + + of_property_read_u32(port, "reg", &port_num); + dpi->id = port_num <= 2 ? port_num : 0; switch (port_num) { case 2: @@ -741,15 +637,21 @@ static void dpi_init_output_port(struct platform_device *pdev, break; } - out->dev = &pdev->dev; + out->dev = &dpi->pdev->dev; out->id = OMAP_DSS_OUTPUT_DPI; - out->output_type = OMAP_DISPLAY_TYPE_DPI; - out->dispc_channel = dpi_get_channel(port_num); - out->port_num = port_num; - out->ops.dpi = &dpi_ops; - out->owner = THIS_MODULE; + out->type = OMAP_DISPLAY_TYPE_DPI; + out->dispc_channel = dpi_get_channel(dpi); + out->of_port = port_num; + + r = omapdss_device_init_output(out, &dpi->bridge); + if (r < 0) { + dpi_bridge_cleanup(dpi); + return r; + } + + omapdss_device_register(out); - omapdss_register_output(out); + return 0; } static void dpi_uninit_output_port(struct device_node *port) @@ -757,56 +659,87 @@ static void dpi_uninit_output_port(struct device_node *port) struct dpi_data *dpi = port->data; struct omap_dss_device *out = &dpi->output; - omapdss_unregister_output(out); + omapdss_device_unregister(out); + omapdss_device_cleanup_output(out); + + dpi_bridge_cleanup(dpi); } -int dpi_init_port(struct platform_device *pdev, struct device_node *port) +/* ----------------------------------------------------------------------------- + * Initialisation and Cleanup + */ + +static const struct soc_device_attribute dpi_soc_devices[] = { + { .machine = "OMAP3[456]*" }, + { .machine = "[AD]M37*" }, + { /* sentinel */ } +}; + +static int dpi_init_regulator(struct dpi_data *dpi) +{ + struct regulator *vdds_dsi; + + /* + * The DPI uses the DSI VDDS on OMAP34xx, OMAP35xx, OMAP36xx, AM37xx and + * DM37xx only. + */ + if (!soc_device_match(dpi_soc_devices)) + return 0; + + vdds_dsi = devm_regulator_get(&dpi->pdev->dev, "vdds_dsi"); + if (IS_ERR(vdds_dsi)) { + if (PTR_ERR(vdds_dsi) != -EPROBE_DEFER) + DSSERR("can't get VDDS_DSI regulator\n"); + return PTR_ERR(vdds_dsi); + } + + dpi->vdds_dsi_reg = vdds_dsi; + + return 0; +} + +int dpi_init_port(struct dss_device *dss, struct platform_device *pdev, + struct device_node *port, enum dss_model dss_model) { struct dpi_data *dpi; struct device_node *ep; u32 datalines; int r; - dpi = devm_kzalloc(&pdev->dev, sizeof(*dpi), GFP_KERNEL); - if (!dpi) - return -ENOMEM; + dpi = devm_drm_bridge_alloc(&pdev->dev, struct dpi_data, bridge, &dpi_bridge_funcs); + if (IS_ERR(dpi)) + return PTR_ERR(dpi); - ep = of_get_next_child(port, NULL); + ep = of_graph_get_next_port_endpoint(port, NULL); if (!ep) return 0; r = of_property_read_u32(ep, "data-lines", &datalines); + of_node_put(ep); if (r) { DSSERR("failed to parse datalines\n"); - goto err_datalines; + return r; } dpi->data_lines = datalines; - of_node_put(ep); - dpi->pdev = pdev; + dpi->dss_model = dss_model; + dpi->dss = dss; port->data = dpi; - mutex_init(&dpi->lock); - - dpi_init_output_port(pdev, port); - - dpi->port_initialized = true; - - return 0; - -err_datalines: - of_node_put(ep); + r = dpi_init_regulator(dpi); + if (r) + return r; - return r; + return dpi_init_output_port(dpi, port); } void dpi_uninit_port(struct device_node *port) { struct dpi_data *dpi = port->data; - if (!dpi->port_initialized) + if (!dpi) return; dpi_uninit_output_port(port); diff --git a/drivers/gpu/drm/omapdrm/dss/dsi.c b/drivers/gpu/drm/omapdrm/dss/dsi.c index 835f49004bc3..b129e5a8d791 100644 --- a/drivers/gpu/drm/omapdrm/dss/dsi.c +++ b/drivers/gpu/drm/omapdrm/dss/dsi.c @@ -1,31 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0-only /* - * linux/drivers/video/omap2/dss/dsi.c - * * Copyright (C) 2009 Nokia Corporation - * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> */ #define DSS_SUBSYS_NAME "DSI" #include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> #include <linux/io.h> #include <linux/clk.h> #include <linux/device.h> #include <linux/err.h> #include <linux/interrupt.h> +#include <linux/irq.h> #include <linux/delay.h> +#include <linux/gpio/consumer.h> #include <linux/mutex.h> #include <linux/module.h> #include <linux/semaphore.h> @@ -42,408 +33,59 @@ #include <linux/of_graph.h> #include <linux/of_platform.h> #include <linux/component.h> +#include <linux/sys_soc.h> +#include <drm/drm_bridge.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_panel.h> #include <video/mipi_display.h> #include "omapdss.h" #include "dss.h" -#include "dss_features.h" #define DSI_CATCH_MISSING_TE -struct dsi_reg { u16 module; u16 idx; }; - -#define DSI_REG(mod, idx) ((const struct dsi_reg) { mod, idx }) - -/* DSI Protocol Engine */ - -#define DSI_PROTO 0 -#define DSI_PROTO_SZ 0x200 - -#define DSI_REVISION DSI_REG(DSI_PROTO, 0x0000) -#define DSI_SYSCONFIG DSI_REG(DSI_PROTO, 0x0010) -#define DSI_SYSSTATUS DSI_REG(DSI_PROTO, 0x0014) -#define DSI_IRQSTATUS DSI_REG(DSI_PROTO, 0x0018) -#define DSI_IRQENABLE DSI_REG(DSI_PROTO, 0x001C) -#define DSI_CTRL DSI_REG(DSI_PROTO, 0x0040) -#define DSI_GNQ DSI_REG(DSI_PROTO, 0x0044) -#define DSI_COMPLEXIO_CFG1 DSI_REG(DSI_PROTO, 0x0048) -#define DSI_COMPLEXIO_IRQ_STATUS DSI_REG(DSI_PROTO, 0x004C) -#define DSI_COMPLEXIO_IRQ_ENABLE DSI_REG(DSI_PROTO, 0x0050) -#define DSI_CLK_CTRL DSI_REG(DSI_PROTO, 0x0054) -#define DSI_TIMING1 DSI_REG(DSI_PROTO, 0x0058) -#define DSI_TIMING2 DSI_REG(DSI_PROTO, 0x005C) -#define DSI_VM_TIMING1 DSI_REG(DSI_PROTO, 0x0060) -#define DSI_VM_TIMING2 DSI_REG(DSI_PROTO, 0x0064) -#define DSI_VM_TIMING3 DSI_REG(DSI_PROTO, 0x0068) -#define DSI_CLK_TIMING DSI_REG(DSI_PROTO, 0x006C) -#define DSI_TX_FIFO_VC_SIZE DSI_REG(DSI_PROTO, 0x0070) -#define DSI_RX_FIFO_VC_SIZE DSI_REG(DSI_PROTO, 0x0074) -#define DSI_COMPLEXIO_CFG2 DSI_REG(DSI_PROTO, 0x0078) -#define DSI_RX_FIFO_VC_FULLNESS DSI_REG(DSI_PROTO, 0x007C) -#define DSI_VM_TIMING4 DSI_REG(DSI_PROTO, 0x0080) -#define DSI_TX_FIFO_VC_EMPTINESS DSI_REG(DSI_PROTO, 0x0084) -#define DSI_VM_TIMING5 DSI_REG(DSI_PROTO, 0x0088) -#define DSI_VM_TIMING6 DSI_REG(DSI_PROTO, 0x008C) -#define DSI_VM_TIMING7 DSI_REG(DSI_PROTO, 0x0090) -#define DSI_STOPCLK_TIMING DSI_REG(DSI_PROTO, 0x0094) -#define DSI_VC_CTRL(n) DSI_REG(DSI_PROTO, 0x0100 + (n * 0x20)) -#define DSI_VC_TE(n) DSI_REG(DSI_PROTO, 0x0104 + (n * 0x20)) -#define DSI_VC_LONG_PACKET_HEADER(n) DSI_REG(DSI_PROTO, 0x0108 + (n * 0x20)) -#define DSI_VC_LONG_PACKET_PAYLOAD(n) DSI_REG(DSI_PROTO, 0x010C + (n * 0x20)) -#define DSI_VC_SHORT_PACKET_HEADER(n) DSI_REG(DSI_PROTO, 0x0110 + (n * 0x20)) -#define DSI_VC_IRQSTATUS(n) DSI_REG(DSI_PROTO, 0x0118 + (n * 0x20)) -#define DSI_VC_IRQENABLE(n) DSI_REG(DSI_PROTO, 0x011C + (n * 0x20)) - -/* DSIPHY_SCP */ - -#define DSI_PHY 1 -#define DSI_PHY_OFFSET 0x200 -#define DSI_PHY_SZ 0x40 - -#define DSI_DSIPHY_CFG0 DSI_REG(DSI_PHY, 0x0000) -#define DSI_DSIPHY_CFG1 DSI_REG(DSI_PHY, 0x0004) -#define DSI_DSIPHY_CFG2 DSI_REG(DSI_PHY, 0x0008) -#define DSI_DSIPHY_CFG5 DSI_REG(DSI_PHY, 0x0014) -#define DSI_DSIPHY_CFG10 DSI_REG(DSI_PHY, 0x0028) - -/* DSI_PLL_CTRL_SCP */ - -#define DSI_PLL 2 -#define DSI_PLL_OFFSET 0x300 -#define DSI_PLL_SZ 0x20 - -#define DSI_PLL_CONTROL DSI_REG(DSI_PLL, 0x0000) -#define DSI_PLL_STATUS DSI_REG(DSI_PLL, 0x0004) -#define DSI_PLL_GO DSI_REG(DSI_PLL, 0x0008) -#define DSI_PLL_CONFIGURATION1 DSI_REG(DSI_PLL, 0x000C) -#define DSI_PLL_CONFIGURATION2 DSI_REG(DSI_PLL, 0x0010) - -#define REG_GET(dsidev, idx, start, end) \ - FLD_GET(dsi_read_reg(dsidev, idx), start, end) - -#define REG_FLD_MOD(dsidev, idx, val, start, end) \ - dsi_write_reg(dsidev, idx, FLD_MOD(dsi_read_reg(dsidev, idx), val, start, end)) - -/* Global interrupts */ -#define DSI_IRQ_VC0 (1 << 0) -#define DSI_IRQ_VC1 (1 << 1) -#define DSI_IRQ_VC2 (1 << 2) -#define DSI_IRQ_VC3 (1 << 3) -#define DSI_IRQ_WAKEUP (1 << 4) -#define DSI_IRQ_RESYNC (1 << 5) -#define DSI_IRQ_PLL_LOCK (1 << 7) -#define DSI_IRQ_PLL_UNLOCK (1 << 8) -#define DSI_IRQ_PLL_RECALL (1 << 9) -#define DSI_IRQ_COMPLEXIO_ERR (1 << 10) -#define DSI_IRQ_HS_TX_TIMEOUT (1 << 14) -#define DSI_IRQ_LP_RX_TIMEOUT (1 << 15) -#define DSI_IRQ_TE_TRIGGER (1 << 16) -#define DSI_IRQ_ACK_TRIGGER (1 << 17) -#define DSI_IRQ_SYNC_LOST (1 << 18) -#define DSI_IRQ_LDO_POWER_GOOD (1 << 19) -#define DSI_IRQ_TA_TIMEOUT (1 << 20) -#define DSI_IRQ_ERROR_MASK \ - (DSI_IRQ_HS_TX_TIMEOUT | DSI_IRQ_LP_RX_TIMEOUT | DSI_IRQ_SYNC_LOST | \ - DSI_IRQ_TA_TIMEOUT) -#define DSI_IRQ_CHANNEL_MASK 0xf - -/* Virtual channel interrupts */ -#define DSI_VC_IRQ_CS (1 << 0) -#define DSI_VC_IRQ_ECC_CORR (1 << 1) -#define DSI_VC_IRQ_PACKET_SENT (1 << 2) -#define DSI_VC_IRQ_FIFO_TX_OVF (1 << 3) -#define DSI_VC_IRQ_FIFO_RX_OVF (1 << 4) -#define DSI_VC_IRQ_BTA (1 << 5) -#define DSI_VC_IRQ_ECC_NO_CORR (1 << 6) -#define DSI_VC_IRQ_FIFO_TX_UDF (1 << 7) -#define DSI_VC_IRQ_PP_BUSY_CHANGE (1 << 8) -#define DSI_VC_IRQ_ERROR_MASK \ - (DSI_VC_IRQ_CS | DSI_VC_IRQ_ECC_CORR | DSI_VC_IRQ_FIFO_TX_OVF | \ - DSI_VC_IRQ_FIFO_RX_OVF | DSI_VC_IRQ_ECC_NO_CORR | \ - DSI_VC_IRQ_FIFO_TX_UDF) - -/* ComplexIO interrupts */ -#define DSI_CIO_IRQ_ERRSYNCESC1 (1 << 0) -#define DSI_CIO_IRQ_ERRSYNCESC2 (1 << 1) -#define DSI_CIO_IRQ_ERRSYNCESC3 (1 << 2) -#define DSI_CIO_IRQ_ERRSYNCESC4 (1 << 3) -#define DSI_CIO_IRQ_ERRSYNCESC5 (1 << 4) -#define DSI_CIO_IRQ_ERRESC1 (1 << 5) -#define DSI_CIO_IRQ_ERRESC2 (1 << 6) -#define DSI_CIO_IRQ_ERRESC3 (1 << 7) -#define DSI_CIO_IRQ_ERRESC4 (1 << 8) -#define DSI_CIO_IRQ_ERRESC5 (1 << 9) -#define DSI_CIO_IRQ_ERRCONTROL1 (1 << 10) -#define DSI_CIO_IRQ_ERRCONTROL2 (1 << 11) -#define DSI_CIO_IRQ_ERRCONTROL3 (1 << 12) -#define DSI_CIO_IRQ_ERRCONTROL4 (1 << 13) -#define DSI_CIO_IRQ_ERRCONTROL5 (1 << 14) -#define DSI_CIO_IRQ_STATEULPS1 (1 << 15) -#define DSI_CIO_IRQ_STATEULPS2 (1 << 16) -#define DSI_CIO_IRQ_STATEULPS3 (1 << 17) -#define DSI_CIO_IRQ_STATEULPS4 (1 << 18) -#define DSI_CIO_IRQ_STATEULPS5 (1 << 19) -#define DSI_CIO_IRQ_ERRCONTENTIONLP0_1 (1 << 20) -#define DSI_CIO_IRQ_ERRCONTENTIONLP1_1 (1 << 21) -#define DSI_CIO_IRQ_ERRCONTENTIONLP0_2 (1 << 22) -#define DSI_CIO_IRQ_ERRCONTENTIONLP1_2 (1 << 23) -#define DSI_CIO_IRQ_ERRCONTENTIONLP0_3 (1 << 24) -#define DSI_CIO_IRQ_ERRCONTENTIONLP1_3 (1 << 25) -#define DSI_CIO_IRQ_ERRCONTENTIONLP0_4 (1 << 26) -#define DSI_CIO_IRQ_ERRCONTENTIONLP1_4 (1 << 27) -#define DSI_CIO_IRQ_ERRCONTENTIONLP0_5 (1 << 28) -#define DSI_CIO_IRQ_ERRCONTENTIONLP1_5 (1 << 29) -#define DSI_CIO_IRQ_ULPSACTIVENOT_ALL0 (1 << 30) -#define DSI_CIO_IRQ_ULPSACTIVENOT_ALL1 (1 << 31) -#define DSI_CIO_IRQ_ERROR_MASK \ - (DSI_CIO_IRQ_ERRSYNCESC1 | DSI_CIO_IRQ_ERRSYNCESC2 | \ - DSI_CIO_IRQ_ERRSYNCESC3 | DSI_CIO_IRQ_ERRSYNCESC4 | \ - DSI_CIO_IRQ_ERRSYNCESC5 | \ - DSI_CIO_IRQ_ERRESC1 | DSI_CIO_IRQ_ERRESC2 | \ - DSI_CIO_IRQ_ERRESC3 | DSI_CIO_IRQ_ERRESC4 | \ - DSI_CIO_IRQ_ERRESC5 | \ - DSI_CIO_IRQ_ERRCONTROL1 | DSI_CIO_IRQ_ERRCONTROL2 | \ - DSI_CIO_IRQ_ERRCONTROL3 | DSI_CIO_IRQ_ERRCONTROL4 | \ - DSI_CIO_IRQ_ERRCONTROL5 | \ - DSI_CIO_IRQ_ERRCONTENTIONLP0_1 | DSI_CIO_IRQ_ERRCONTENTIONLP1_1 | \ - DSI_CIO_IRQ_ERRCONTENTIONLP0_2 | DSI_CIO_IRQ_ERRCONTENTIONLP1_2 | \ - DSI_CIO_IRQ_ERRCONTENTIONLP0_3 | DSI_CIO_IRQ_ERRCONTENTIONLP1_3 | \ - DSI_CIO_IRQ_ERRCONTENTIONLP0_4 | DSI_CIO_IRQ_ERRCONTENTIONLP1_4 | \ - DSI_CIO_IRQ_ERRCONTENTIONLP0_5 | DSI_CIO_IRQ_ERRCONTENTIONLP1_5) - -typedef void (*omap_dsi_isr_t) (void *arg, u32 mask); - -static int dsi_display_init_dispc(struct platform_device *dsidev, - enum omap_channel channel); -static void dsi_display_uninit_dispc(struct platform_device *dsidev, - enum omap_channel channel); - -static int dsi_vc_send_null(struct omap_dss_device *dssdev, int channel); - -/* DSI PLL HSDIV indices */ -#define HSDIV_DISPC 0 -#define HSDIV_DSI 1 - -#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; - void *arg; - u32 mask; -}; - -enum fifo_size { - DSI_FIFO_SIZE_0 = 0, - DSI_FIFO_SIZE_32 = 1, - DSI_FIFO_SIZE_64 = 2, - DSI_FIFO_SIZE_96 = 3, - DSI_FIFO_SIZE_128 = 4, -}; - -enum dsi_vc_source { - DSI_VC_SOURCE_L4 = 0, - DSI_VC_SOURCE_VP, -}; - -struct dsi_irq_stats { - unsigned long last_reset; - unsigned irq_count; - unsigned dsi_irqs[32]; - unsigned vc_irqs[4][32]; - unsigned cio_irqs[32]; -}; - -struct dsi_isr_tables { - struct dsi_isr_data isr_table[DSI_MAX_NR_ISRS]; - struct dsi_isr_data isr_table_vc[4][DSI_MAX_NR_ISRS]; - struct dsi_isr_data isr_table_cio[DSI_MAX_NR_ISRS]; -}; - -struct dsi_clk_calc_ctx { - struct platform_device *dsidev; - struct dss_pll *pll; - - /* inputs */ - - const struct omap_dss_dsi_config *config; - - unsigned long req_pck_min, req_pck_nom, req_pck_max; - - /* outputs */ - - struct dss_pll_clock_info dsi_cinfo; - struct dispc_clock_info dispc_cinfo; - - struct videomode vm; - struct omap_dss_dsi_videomode_timings dsi_vm; -}; - -struct dsi_lp_clock_info { - unsigned long lp_clk; - u16 lp_clk_div; -}; - -struct dsi_data { - struct platform_device *pdev; - void __iomem *proto_base; - void __iomem *phy_base; - void __iomem *pll_base; - - int module_id; - - int irq; - - bool is_enabled; - - struct clk *dss_clk; +#include "dsi.h" - struct dispc_clock_info user_dispc_cinfo; - struct dss_pll_clock_info user_dsi_cinfo; +#define REG_GET(dsi, idx, start, end) \ + FLD_GET(dsi_read_reg(dsi, idx), start, end) - struct dsi_lp_clock_info user_lp_cinfo; - struct dsi_lp_clock_info current_lp_cinfo; +#define REG_FLD_MOD(dsi, idx, val, start, end) \ + dsi_write_reg(dsi, idx, FLD_MOD(dsi_read_reg(dsi, idx), val, start, end)) - struct dss_pll pll; +static int dsi_init_dispc(struct dsi_data *dsi); +static void dsi_uninit_dispc(struct dsi_data *dsi); - bool vdds_dsi_enabled; - struct regulator *vdds_dsi_reg; - - struct { - enum dsi_vc_source source; - struct omap_dss_device *dssdev; - enum fifo_size tx_fifo_size; - enum fifo_size rx_fifo_size; - int vc_id; - } vc[4]; - - struct mutex lock; - struct semaphore bus_lock; - - spinlock_t irq_lock; - struct dsi_isr_tables isr_tables; - /* space for a copy used by the interrupt handler */ - struct dsi_isr_tables isr_tables_copy; - - int update_channel; -#ifdef DSI_PERF_MEASURE - unsigned update_bytes; -#endif - - bool te_enabled; - bool ulps_enabled; - - void (*framedone_callback)(int, void *); - void *framedone_data; - - struct delayed_work framedone_timeout_work; - -#ifdef DSI_CATCH_MISSING_TE - struct timer_list te_timer; -#endif - - unsigned long cache_req_pck; - unsigned long cache_clk_freq; - struct dss_pll_clock_info cache_cinfo; - - u32 errors; - spinlock_t errors_lock; -#ifdef DSI_PERF_MEASURE - ktime_t perf_setup_time; - ktime_t perf_start_time; -#endif - int debug_read; - int debug_write; - -#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS - spinlock_t irq_stats_lock; - struct dsi_irq_stats irq_stats; -#endif - - unsigned num_lanes_supported; - unsigned line_buffer_size; - - struct dsi_lane_config lanes[DSI_MAX_NR_LANES]; - unsigned num_lanes_used; - - unsigned scp_clk_refcount; - - struct dss_lcd_mgr_config mgr_config; - struct videomode vm; - enum omap_dss_dsi_pixel_format pix_fmt; - enum omap_dss_dsi_mode mode; - struct omap_dss_dsi_videomode_timings vm_timings; - - struct omap_dss_device output; -}; - -struct dsi_packet_sent_handler_data { - struct platform_device *dsidev; - struct completion *completion; -}; - -struct dsi_module_id_data { - u32 address; - int id; -}; +static int dsi_vc_send_null(struct dsi_data *dsi, int vc, int channel); -static const struct of_device_id dsi_of_match[]; +static ssize_t _omap_dsi_host_transfer(struct dsi_data *dsi, int vc, + const struct mipi_dsi_msg *msg); #ifdef DSI_PERF_MEASURE static bool dsi_perf; module_param(dsi_perf, bool, 0644); #endif -static inline struct dsi_data *dsi_get_dsidrv_data(struct platform_device *dsidev) -{ - return dev_get_drvdata(&dsidev->dev); -} +/* Note: for some reason video mode seems to work only if VC_VIDEO is 0 */ +#define VC_VIDEO 0 +#define VC_CMD 1 -static inline struct platform_device *dsi_get_dsidev_from_dssdev(struct omap_dss_device *dssdev) +#define drm_bridge_to_dsi(bridge) \ + container_of(bridge, struct dsi_data, bridge) + +static inline struct dsi_data *to_dsi_data(struct omap_dss_device *dssdev) { - return to_platform_device(dssdev->dev); + return dev_get_drvdata(dssdev->dev); } -static struct platform_device *dsi_get_dsidev_from_id(int module) +static inline struct dsi_data *host_to_omap(struct mipi_dsi_host *host) { - struct omap_dss_device *out; - enum omap_dss_output_id id; - - switch (module) { - case 0: - id = OMAP_DSS_OUTPUT_DSI1; - break; - case 1: - id = OMAP_DSS_OUTPUT_DSI2; - break; - default: - return NULL; - } - - out = omap_dss_get_output(id); - - return out ? to_platform_device(out->dev) : NULL; + return container_of(host, struct dsi_data, host); } -static inline void dsi_write_reg(struct platform_device *dsidev, - const struct dsi_reg idx, u32 val) +static inline void dsi_write_reg(struct dsi_data *dsi, + const struct dsi_reg idx, u32 val) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); void __iomem *base; switch(idx.module) { @@ -456,10 +98,8 @@ static inline void dsi_write_reg(struct platform_device *dsidev, __raw_writel(val, base + idx.idx); } -static inline u32 dsi_read_reg(struct platform_device *dsidev, - const struct dsi_reg idx) +static inline u32 dsi_read_reg(struct dsi_data *dsi, const struct dsi_reg idx) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); void __iomem *base; switch(idx.module) { @@ -472,26 +112,18 @@ static inline u32 dsi_read_reg(struct platform_device *dsidev, return __raw_readl(base + idx.idx); } -static void dsi_bus_lock(struct omap_dss_device *dssdev) +static void dsi_bus_lock(struct dsi_data *dsi) { - struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - down(&dsi->bus_lock); } -static void dsi_bus_unlock(struct omap_dss_device *dssdev) +static void dsi_bus_unlock(struct dsi_data *dsi) { - struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - up(&dsi->bus_lock); } -static bool dsi_bus_is_locked(struct platform_device *dsidev) +static bool dsi_bus_is_locked(struct dsi_data *dsi) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - return dsi->bus_lock.count == 0; } @@ -500,8 +132,9 @@ static void dsi_completion_handler(void *data, u32 mask) complete((struct completion *)data); } -static inline int wait_for_bit_change(struct platform_device *dsidev, - const struct dsi_reg idx, int bitnum, int value) +static inline bool wait_for_bit_change(struct dsi_data *dsi, + const struct dsi_reg idx, + int bitnum, int value) { unsigned long timeout; ktime_t wait; @@ -510,56 +143,37 @@ static inline int wait_for_bit_change(struct platform_device *dsidev, /* 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; + if (REG_GET(dsi, idx, bitnum, bitnum) == value) + return true; } /* 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; + if (REG_GET(dsi, idx, bitnum, bitnum) == value) + return true; wait = ns_to_ktime(1000 * 1000); set_current_state(TASK_UNINTERRUPTIBLE); schedule_hrtimeout(&wait, HRTIMER_MODE_REL); } - return !value; -} - -static u8 dsi_get_pixel_size(enum omap_dss_dsi_pixel_format fmt) -{ - switch (fmt) { - case OMAP_DSS_DSI_FMT_RGB888: - case OMAP_DSS_DSI_FMT_RGB666: - return 24; - case OMAP_DSS_DSI_FMT_RGB666_PACKED: - return 18; - case OMAP_DSS_DSI_FMT_RGB565: - return 16; - default: - BUG(); - return 0; - } + return false; } #ifdef DSI_PERF_MEASURE -static void dsi_perf_mark_setup(struct platform_device *dsidev) +static void dsi_perf_mark_setup(struct dsi_data *dsi) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); dsi->perf_setup_time = ktime_get(); } -static void dsi_perf_mark_start(struct platform_device *dsidev) +static void dsi_perf_mark_start(struct dsi_data *dsi) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); dsi->perf_start_time = ktime_get(); } -static void dsi_perf_show(struct platform_device *dsidev, const char *name) +static void dsi_perf_show(struct dsi_data *dsi, const char *name) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); ktime_t t, setup_time, trans_time; u32 total_bytes; u32 setup_us, trans_us, total_us; @@ -593,16 +207,15 @@ static void dsi_perf_show(struct platform_device *dsidev, const char *name) total_bytes * 1000 / total_us); } #else -static inline void dsi_perf_mark_setup(struct platform_device *dsidev) +static inline void dsi_perf_mark_setup(struct dsi_data *dsi) { } -static inline void dsi_perf_mark_start(struct platform_device *dsidev) +static inline void dsi_perf_mark_start(struct dsi_data *dsi) { } -static inline void dsi_perf_show(struct platform_device *dsidev, - const char *name) +static inline void dsi_perf_show(struct dsi_data *dsi, const char *name) { } #endif @@ -641,7 +254,7 @@ static void print_irq_status(u32 status) #undef PIS } -static void print_irq_status_vc(int channel, u32 status) +static void print_irq_status_vc(int vc, u32 status) { if (status == 0) return; @@ -652,7 +265,7 @@ static void print_irq_status_vc(int channel, u32 status) #define PIS(x) (status & DSI_VC_IRQ_##x) ? (#x " ") : "" pr_debug("DSI VC(%d) IRQ 0x%x: %s%s%s%s%s%s%s%s%s\n", - channel, + vc, status, PIS(CS), PIS(ECC_CORR), @@ -699,10 +312,9 @@ static void print_irq_status_cio(u32 status) } #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS -static void dsi_collect_irq_stats(struct platform_device *dsidev, u32 irqstatus, - u32 *vcstatus, u32 ciostatus) +static void dsi_collect_irq_stats(struct dsi_data *dsi, u32 irqstatus, + u32 *vcstatus, u32 ciostatus) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); int i; spin_lock(&dsi->irq_stats_lock); @@ -718,15 +330,14 @@ static void dsi_collect_irq_stats(struct platform_device *dsidev, u32 irqstatus, spin_unlock(&dsi->irq_stats_lock); } #else -#define dsi_collect_irq_stats(dsidev, irqstatus, vcstatus, ciostatus) +#define dsi_collect_irq_stats(dsi, irqstatus, vcstatus, ciostatus) #endif static int debug_irq; -static void dsi_handle_irq_errors(struct platform_device *dsidev, u32 irqstatus, - u32 *vcstatus, u32 ciostatus) +static void dsi_handle_irq_errors(struct dsi_data *dsi, u32 irqstatus, + u32 *vcstatus, u32 ciostatus) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); int i; if (irqstatus & DSI_IRQ_ERROR_MASK) { @@ -758,7 +369,7 @@ static void dsi_handle_irq_errors(struct platform_device *dsidev, u32 irqstatus, } static void dsi_call_isrs(struct dsi_isr_data *isr_array, - unsigned isr_array_size, u32 irqstatus) + unsigned int isr_array_size, u32 irqstatus) { struct dsi_isr_data *isr_data; int i; @@ -795,20 +406,16 @@ static void dsi_handle_isrs(struct dsi_isr_tables *isr_tables, static irqreturn_t omap_dsi_irq_handler(int irq, void *arg) { - struct platform_device *dsidev; - struct dsi_data *dsi; + struct dsi_data *dsi = arg; u32 irqstatus, vcstatus[4], ciostatus; int i; - dsidev = (struct platform_device *) arg; - dsi = dsi_get_dsidrv_data(dsidev); - if (!dsi->is_enabled) return IRQ_NONE; spin_lock(&dsi->irq_lock); - irqstatus = dsi_read_reg(dsidev, DSI_IRQSTATUS); + irqstatus = dsi_read_reg(dsi, DSI_IRQSTATUS); /* IRQ is not for us */ if (!irqstatus) { @@ -816,9 +423,9 @@ static irqreturn_t omap_dsi_irq_handler(int irq, void *arg) return IRQ_NONE; } - dsi_write_reg(dsidev, DSI_IRQSTATUS, irqstatus & ~DSI_IRQ_CHANNEL_MASK); + dsi_write_reg(dsi, DSI_IRQSTATUS, irqstatus & ~DSI_IRQ_CHANNEL_MASK); /* flush posted write */ - dsi_read_reg(dsidev, DSI_IRQSTATUS); + dsi_read_reg(dsi, DSI_IRQSTATUS); for (i = 0; i < 4; ++i) { if ((irqstatus & (1 << i)) == 0) { @@ -826,26 +433,26 @@ static irqreturn_t omap_dsi_irq_handler(int irq, void *arg) continue; } - vcstatus[i] = dsi_read_reg(dsidev, DSI_VC_IRQSTATUS(i)); + vcstatus[i] = dsi_read_reg(dsi, DSI_VC_IRQSTATUS(i)); - dsi_write_reg(dsidev, DSI_VC_IRQSTATUS(i), vcstatus[i]); + dsi_write_reg(dsi, DSI_VC_IRQSTATUS(i), vcstatus[i]); /* flush posted write */ - dsi_read_reg(dsidev, DSI_VC_IRQSTATUS(i)); + dsi_read_reg(dsi, DSI_VC_IRQSTATUS(i)); } if (irqstatus & DSI_IRQ_COMPLEXIO_ERR) { - ciostatus = dsi_read_reg(dsidev, DSI_COMPLEXIO_IRQ_STATUS); + ciostatus = dsi_read_reg(dsi, DSI_COMPLEXIO_IRQ_STATUS); - dsi_write_reg(dsidev, DSI_COMPLEXIO_IRQ_STATUS, ciostatus); + dsi_write_reg(dsi, DSI_COMPLEXIO_IRQ_STATUS, ciostatus); /* flush posted write */ - dsi_read_reg(dsidev, DSI_COMPLEXIO_IRQ_STATUS); + dsi_read_reg(dsi, DSI_COMPLEXIO_IRQ_STATUS); } else { ciostatus = 0; } #ifdef DSI_CATCH_MISSING_TE if (irqstatus & DSI_IRQ_TE_TRIGGER) - del_timer(&dsi->te_timer); + timer_delete(&dsi->te_timer); #endif /* make a copy and unlock, so that isrs can unregister @@ -857,19 +464,20 @@ static irqreturn_t omap_dsi_irq_handler(int irq, void *arg) dsi_handle_isrs(&dsi->isr_tables_copy, irqstatus, vcstatus, ciostatus); - dsi_handle_irq_errors(dsidev, irqstatus, vcstatus, ciostatus); + dsi_handle_irq_errors(dsi, irqstatus, vcstatus, ciostatus); - dsi_collect_irq_stats(dsidev, irqstatus, vcstatus, ciostatus); + dsi_collect_irq_stats(dsi, irqstatus, vcstatus, ciostatus); return IRQ_HANDLED; } /* dsi->irq_lock has to be locked by the caller */ -static void _omap_dsi_configure_irqs(struct platform_device *dsidev, - struct dsi_isr_data *isr_array, - unsigned isr_array_size, u32 default_mask, - const struct dsi_reg enable_reg, - const struct dsi_reg status_reg) +static void _omap_dsi_configure_irqs(struct dsi_data *dsi, + struct dsi_isr_data *isr_array, + unsigned int isr_array_size, + u32 default_mask, + const struct dsi_reg enable_reg, + const struct dsi_reg status_reg) { struct dsi_isr_data *isr_data; u32 mask; @@ -887,54 +495,48 @@ static void _omap_dsi_configure_irqs(struct platform_device *dsidev, mask |= isr_data->mask; } - old_mask = dsi_read_reg(dsidev, enable_reg); + old_mask = dsi_read_reg(dsi, enable_reg); /* clear the irqstatus for newly enabled irqs */ - dsi_write_reg(dsidev, status_reg, (mask ^ old_mask) & mask); - dsi_write_reg(dsidev, enable_reg, mask); + dsi_write_reg(dsi, status_reg, (mask ^ old_mask) & mask); + dsi_write_reg(dsi, enable_reg, mask); /* flush posted writes */ - dsi_read_reg(dsidev, enable_reg); - dsi_read_reg(dsidev, status_reg); + dsi_read_reg(dsi, enable_reg); + dsi_read_reg(dsi, status_reg); } /* dsi->irq_lock has to be locked by the caller */ -static void _omap_dsi_set_irqs(struct platform_device *dsidev) +static void _omap_dsi_set_irqs(struct dsi_data *dsi) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); u32 mask = DSI_IRQ_ERROR_MASK; #ifdef DSI_CATCH_MISSING_TE mask |= DSI_IRQ_TE_TRIGGER; #endif - _omap_dsi_configure_irqs(dsidev, dsi->isr_tables.isr_table, + _omap_dsi_configure_irqs(dsi, dsi->isr_tables.isr_table, ARRAY_SIZE(dsi->isr_tables.isr_table), mask, DSI_IRQENABLE, DSI_IRQSTATUS); } /* dsi->irq_lock has to be locked by the caller */ -static void _omap_dsi_set_irqs_vc(struct platform_device *dsidev, int vc) +static void _omap_dsi_set_irqs_vc(struct dsi_data *dsi, int vc) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - - _omap_dsi_configure_irqs(dsidev, dsi->isr_tables.isr_table_vc[vc], + _omap_dsi_configure_irqs(dsi, dsi->isr_tables.isr_table_vc[vc], ARRAY_SIZE(dsi->isr_tables.isr_table_vc[vc]), DSI_VC_IRQ_ERROR_MASK, DSI_VC_IRQENABLE(vc), DSI_VC_IRQSTATUS(vc)); } /* dsi->irq_lock has to be locked by the caller */ -static void _omap_dsi_set_irqs_cio(struct platform_device *dsidev) +static void _omap_dsi_set_irqs_cio(struct dsi_data *dsi) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - - _omap_dsi_configure_irqs(dsidev, dsi->isr_tables.isr_table_cio, + _omap_dsi_configure_irqs(dsi, dsi->isr_tables.isr_table_cio, ARRAY_SIZE(dsi->isr_tables.isr_table_cio), DSI_CIO_IRQ_ERROR_MASK, DSI_COMPLEXIO_IRQ_ENABLE, DSI_COMPLEXIO_IRQ_STATUS); } -static void _dsi_initialize_irq(struct platform_device *dsidev) +static void _dsi_initialize_irq(struct dsi_data *dsi) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); unsigned long flags; int vc; @@ -942,16 +544,16 @@ static void _dsi_initialize_irq(struct platform_device *dsidev) memset(&dsi->isr_tables, 0, sizeof(dsi->isr_tables)); - _omap_dsi_set_irqs(dsidev); + _omap_dsi_set_irqs(dsi); for (vc = 0; vc < 4; ++vc) - _omap_dsi_set_irqs_vc(dsidev, vc); - _omap_dsi_set_irqs_cio(dsidev); + _omap_dsi_set_irqs_vc(dsi, vc); + _omap_dsi_set_irqs_cio(dsi); spin_unlock_irqrestore(&dsi->irq_lock, flags); } static int _dsi_register_isr(omap_dsi_isr_t isr, void *arg, u32 mask, - struct dsi_isr_data *isr_array, unsigned isr_array_size) + struct dsi_isr_data *isr_array, unsigned int isr_array_size) { struct dsi_isr_data *isr_data; int free_idx; @@ -985,7 +587,7 @@ static int _dsi_register_isr(omap_dsi_isr_t isr, void *arg, u32 mask, } static int _dsi_unregister_isr(omap_dsi_isr_t isr, void *arg, u32 mask, - struct dsi_isr_data *isr_array, unsigned isr_array_size) + struct dsi_isr_data *isr_array, unsigned int isr_array_size) { struct dsi_isr_data *isr_data; int i; @@ -1006,10 +608,9 @@ static int _dsi_unregister_isr(omap_dsi_isr_t isr, void *arg, u32 mask, return -EINVAL; } -static int dsi_register_isr(struct platform_device *dsidev, omap_dsi_isr_t isr, - void *arg, u32 mask) +static int dsi_register_isr(struct dsi_data *dsi, omap_dsi_isr_t isr, + void *arg, u32 mask) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); unsigned long flags; int r; @@ -1019,17 +620,16 @@ static int dsi_register_isr(struct platform_device *dsidev, omap_dsi_isr_t isr, ARRAY_SIZE(dsi->isr_tables.isr_table)); if (r == 0) - _omap_dsi_set_irqs(dsidev); + _omap_dsi_set_irqs(dsi); spin_unlock_irqrestore(&dsi->irq_lock, flags); return r; } -static int dsi_unregister_isr(struct platform_device *dsidev, - omap_dsi_isr_t isr, void *arg, u32 mask) +static int dsi_unregister_isr(struct dsi_data *dsi, omap_dsi_isr_t isr, + void *arg, u32 mask) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); unsigned long flags; int r; @@ -1039,100 +639,58 @@ static int dsi_unregister_isr(struct platform_device *dsidev, ARRAY_SIZE(dsi->isr_tables.isr_table)); if (r == 0) - _omap_dsi_set_irqs(dsidev); + _omap_dsi_set_irqs(dsi); spin_unlock_irqrestore(&dsi->irq_lock, flags); return r; } -static int dsi_register_isr_vc(struct platform_device *dsidev, int channel, - omap_dsi_isr_t isr, void *arg, u32 mask) +static int dsi_register_isr_vc(struct dsi_data *dsi, int vc, + omap_dsi_isr_t isr, void *arg, u32 mask) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); unsigned long flags; int r; spin_lock_irqsave(&dsi->irq_lock, flags); r = _dsi_register_isr(isr, arg, mask, - dsi->isr_tables.isr_table_vc[channel], - ARRAY_SIZE(dsi->isr_tables.isr_table_vc[channel])); + dsi->isr_tables.isr_table_vc[vc], + ARRAY_SIZE(dsi->isr_tables.isr_table_vc[vc])); if (r == 0) - _omap_dsi_set_irqs_vc(dsidev, channel); + _omap_dsi_set_irqs_vc(dsi, vc); spin_unlock_irqrestore(&dsi->irq_lock, flags); return r; } -static int dsi_unregister_isr_vc(struct platform_device *dsidev, int channel, - omap_dsi_isr_t isr, void *arg, u32 mask) +static int dsi_unregister_isr_vc(struct dsi_data *dsi, int vc, + omap_dsi_isr_t isr, void *arg, u32 mask) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); unsigned long flags; int r; spin_lock_irqsave(&dsi->irq_lock, flags); r = _dsi_unregister_isr(isr, arg, mask, - dsi->isr_tables.isr_table_vc[channel], - ARRAY_SIZE(dsi->isr_tables.isr_table_vc[channel])); - - if (r == 0) - _omap_dsi_set_irqs_vc(dsidev, channel); - - spin_unlock_irqrestore(&dsi->irq_lock, flags); - - return r; -} - -static int dsi_register_isr_cio(struct platform_device *dsidev, - omap_dsi_isr_t isr, void *arg, u32 mask) -{ - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - unsigned long flags; - int r; - - spin_lock_irqsave(&dsi->irq_lock, flags); - - r = _dsi_register_isr(isr, arg, mask, dsi->isr_tables.isr_table_cio, - ARRAY_SIZE(dsi->isr_tables.isr_table_cio)); + dsi->isr_tables.isr_table_vc[vc], + ARRAY_SIZE(dsi->isr_tables.isr_table_vc[vc])); if (r == 0) - _omap_dsi_set_irqs_cio(dsidev); + _omap_dsi_set_irqs_vc(dsi, vc); spin_unlock_irqrestore(&dsi->irq_lock, flags); return r; } -static int dsi_unregister_isr_cio(struct platform_device *dsidev, - omap_dsi_isr_t isr, void *arg, u32 mask) +static u32 dsi_get_errors(struct dsi_data *dsi) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - unsigned long flags; - int r; - - spin_lock_irqsave(&dsi->irq_lock, flags); - - r = _dsi_unregister_isr(isr, arg, mask, dsi->isr_tables.isr_table_cio, - ARRAY_SIZE(dsi->isr_tables.isr_table_cio)); - - if (r == 0) - _omap_dsi_set_irqs_cio(dsidev); - - spin_unlock_irqrestore(&dsi->irq_lock, flags); - - return r; -} - -static u32 dsi_get_errors(struct platform_device *dsidev) -{ - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); unsigned long flags; u32 e; + spin_lock_irqsave(&dsi->errors_lock, flags); e = dsi->errors; dsi->errors = 0; @@ -1140,61 +698,40 @@ static u32 dsi_get_errors(struct platform_device *dsidev) return e; } -static int dsi_runtime_get(struct platform_device *dsidev) +static int dsi_runtime_get(struct dsi_data *dsi) { int r; - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); DSSDBG("dsi_runtime_get\n"); - r = pm_runtime_get_sync(&dsi->pdev->dev); - WARN_ON(r < 0); - return r < 0 ? r : 0; + r = pm_runtime_get_sync(dsi->dev); + if (WARN_ON(r < 0)) { + pm_runtime_put_noidle(dsi->dev); + return r; + } + return 0; } -static void dsi_runtime_put(struct platform_device *dsidev) +static void dsi_runtime_put(struct dsi_data *dsi) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); int r; DSSDBG("dsi_runtime_put\n"); - r = pm_runtime_put_sync(&dsi->pdev->dev); + r = pm_runtime_put_sync(dsi->dev); WARN_ON(r < 0 && r != -ENOSYS); } -static int dsi_regulator_init(struct platform_device *dsidev) +static void _dsi_print_reset_status(struct dsi_data *dsi) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - struct regulator *vdds_dsi; - - if (dsi->vdds_dsi_reg != NULL) - return 0; - - vdds_dsi = devm_regulator_get(&dsi->pdev->dev, "vdd"); - - if (IS_ERR(vdds_dsi)) { - if (PTR_ERR(vdds_dsi) != -EPROBE_DEFER) - DSSERR("can't get DSI VDD regulator\n"); - return PTR_ERR(vdds_dsi); - } - - dsi->vdds_dsi_reg = vdds_dsi; - - return 0; -} - -static void _dsi_print_reset_status(struct platform_device *dsidev) -{ - u32 l; int b0, b1, b2; /* A dummy read using the SCP interface to any DSIPHY register is * required after DSIPHY reset to complete the reset of the DSI complex * I/O. */ - l = dsi_read_reg(dsidev, DSI_DSIPHY_CFG5); + dsi_read_reg(dsi, DSI_DSIPHY_CFG5); - if (dss_has_feature(FEAT_DSI_REVERSE_TXCLKESC)) { + if (dsi->data->quirks & DSI_QUIRK_REVERSE_TXCLKESC) { b0 = 28; b1 = 27; b2 = 26; @@ -1205,7 +742,7 @@ static void _dsi_print_reset_status(struct platform_device *dsidev) } #define DSI_FLD_GET(fld, start, end)\ - FLD_GET(dsi_read_reg(dsidev, DSI_##fld), start, end) + FLD_GET(dsi_read_reg(dsi, DSI_##fld), start, end) pr_debug("DSI resets: PLL (%d) CIO (%d) PHY (%x%x%x, %d, %d, %d)\n", DSI_FLD_GET(PLL_STATUS, 0, 0), @@ -1220,53 +757,48 @@ static void _dsi_print_reset_status(struct platform_device *dsidev) #undef DSI_FLD_GET } -static inline int dsi_if_enable(struct platform_device *dsidev, bool enable) +static inline int dsi_if_enable(struct dsi_data *dsi, bool enable) { DSSDBG("dsi_if_enable(%d)\n", enable); enable = enable ? 1 : 0; - REG_FLD_MOD(dsidev, DSI_CTRL, enable, 0, 0); /* IF_EN */ + REG_FLD_MOD(dsi, DSI_CTRL, enable, 0, 0); /* IF_EN */ - if (wait_for_bit_change(dsidev, DSI_CTRL, 0, enable) != enable) { - DSSERR("Failed to set dsi_if_enable to %d\n", enable); - return -EIO; + if (!wait_for_bit_change(dsi, DSI_CTRL, 0, enable)) { + DSSERR("Failed to set dsi_if_enable to %d\n", enable); + return -EIO; } return 0; } -static unsigned long dsi_get_pll_hsdiv_dispc_rate(struct platform_device *dsidev) +static unsigned long dsi_get_pll_hsdiv_dispc_rate(struct dsi_data *dsi) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - return dsi->pll.cinfo.clkout[HSDIV_DISPC]; } -static unsigned long dsi_get_pll_hsdiv_dsi_rate(struct platform_device *dsidev) +static unsigned long dsi_get_pll_hsdiv_dsi_rate(struct dsi_data *dsi) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - return dsi->pll.cinfo.clkout[HSDIV_DSI]; } -static unsigned long dsi_get_txbyteclkhs(struct platform_device *dsidev) +static unsigned long dsi_get_txbyteclkhs(struct dsi_data *dsi) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - return dsi->pll.cinfo.clkdco / 16; } -static unsigned long dsi_fclk_rate(struct platform_device *dsidev) +static unsigned long dsi_fclk_rate(struct dsi_data *dsi) { unsigned long r; - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + enum dss_clk_source source; - if (dss_get_dsi_clk_source(dsi->module_id) == DSS_CLK_SRC_FCK) { + source = dss_get_dsi_clk_source(dsi->dss, dsi->module_id); + if (source == DSS_CLK_SRC_FCK) { /* DSI FCLK source is DSS_CLK_FCK */ r = clk_get_rate(dsi->dss_clk); } else { /* DSI FCLK source is dsi_pll_hsdiv_dsi_clk */ - r = dsi_get_pll_hsdiv_dsi_rate(dsidev); + r = dsi_get_pll_hsdiv_dsi_rate(dsi); } return r; @@ -1276,7 +808,7 @@ static int dsi_lp_clock_calc(unsigned long dsi_fclk, unsigned long lp_clk_min, unsigned long lp_clk_max, struct dsi_lp_clock_info *lp_cinfo) { - unsigned lp_clk_div; + unsigned int lp_clk_div; unsigned long lp_clk; lp_clk_div = DIV_ROUND_UP(dsi_fclk, lp_clk_max * 2); @@ -1291,13 +823,12 @@ static int dsi_lp_clock_calc(unsigned long dsi_fclk, return 0; } -static int dsi_set_lp_clk_divisor(struct platform_device *dsidev) +static int dsi_set_lp_clk_divisor(struct dsi_data *dsi) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); unsigned long dsi_fclk; - unsigned lp_clk_div; + unsigned int lp_clk_div; unsigned long lp_clk; - unsigned lpdiv_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_LPDIV); + unsigned int lpdiv_max = dsi->data->max_pll_lpdiv; lp_clk_div = dsi->user_lp_cinfo.lp_clk_div; @@ -1305,7 +836,7 @@ static int dsi_set_lp_clk_divisor(struct platform_device *dsidev) if (lp_clk_div == 0 || lp_clk_div > lpdiv_max) return -EINVAL; - dsi_fclk = dsi_fclk_rate(dsidev); + dsi_fclk = dsi_fclk_rate(dsi); lp_clk = dsi_fclk / 2 / lp_clk_div; @@ -1314,29 +845,25 @@ static int dsi_set_lp_clk_divisor(struct platform_device *dsidev) dsi->current_lp_cinfo.lp_clk_div = lp_clk_div; /* LP_CLK_DIVISOR */ - REG_FLD_MOD(dsidev, DSI_CLK_CTRL, lp_clk_div, 12, 0); + REG_FLD_MOD(dsi, DSI_CLK_CTRL, lp_clk_div, 12, 0); /* LP_RX_SYNCHRO_ENABLE */ - REG_FLD_MOD(dsidev, DSI_CLK_CTRL, dsi_fclk > 30000000 ? 1 : 0, 21, 21); + REG_FLD_MOD(dsi, DSI_CLK_CTRL, dsi_fclk > 30000000 ? 1 : 0, 21, 21); return 0; } -static void dsi_enable_scp_clk(struct platform_device *dsidev) +static void dsi_enable_scp_clk(struct dsi_data *dsi) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - if (dsi->scp_clk_refcount++ == 0) - REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 1, 14, 14); /* CIO_CLK_ICG */ + REG_FLD_MOD(dsi, DSI_CLK_CTRL, 1, 14, 14); /* CIO_CLK_ICG */ } -static void dsi_disable_scp_clk(struct platform_device *dsidev) +static void dsi_disable_scp_clk(struct dsi_data *dsi) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - WARN_ON(dsi->scp_clk_refcount == 0); if (--dsi->scp_clk_refcount == 0) - REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 0, 14, 14); /* CIO_CLK_ICG */ + REG_FLD_MOD(dsi, DSI_CLK_CTRL, 0, 14, 14); /* CIO_CLK_ICG */ } enum dsi_pll_power_state { @@ -1346,21 +873,20 @@ enum dsi_pll_power_state { DSI_PLL_POWER_ON_DIV = 0x3, }; -static int dsi_pll_power(struct platform_device *dsidev, - enum dsi_pll_power_state state) +static int dsi_pll_power(struct dsi_data *dsi, enum dsi_pll_power_state state) { int t = 0; /* DSI-PLL power command 0x3 is not working */ - if (dss_has_feature(FEAT_DSI_PLL_PWR_BUG) && - state == DSI_PLL_POWER_ON_DIV) + if ((dsi->data->quirks & DSI_QUIRK_PLL_PWR_BUG) && + state == DSI_PLL_POWER_ON_DIV) state = DSI_PLL_POWER_ON_ALL; /* PLL_PWR_CMD */ - REG_FLD_MOD(dsidev, DSI_CLK_CTRL, state, 31, 30); + REG_FLD_MOD(dsi, DSI_CLK_CTRL, state, 31, 30); /* PLL_PWR_STATUS */ - while (FLD_GET(dsi_read_reg(dsidev, DSI_CLK_CTRL), 29, 28) != state) { + while (FLD_GET(dsi_read_reg(dsi, DSI_CLK_CTRL), 29, 28) != state) { if (++t > 1000) { DSSERR("Failed to set DSI PLL power mode to %d\n", state); @@ -1373,11 +899,12 @@ static int dsi_pll_power(struct platform_device *dsidev, } -static void dsi_pll_calc_dsi_fck(struct dss_pll_clock_info *cinfo) +static void dsi_pll_calc_dsi_fck(struct dsi_data *dsi, + struct dss_pll_clock_info *cinfo) { unsigned long max_dsi_fck; - max_dsi_fck = dss_feat_get_param_max(FEAT_PARAM_DSI_FCK); + max_dsi_fck = dsi->data->max_fck_freq; cinfo->mX[HSDIV_DSI] = DIV_ROUND_UP(cinfo->clkdco, max_dsi_fck); cinfo->clkout[HSDIV_DSI] = cinfo->clkdco / cinfo->mX[HSDIV_DSI]; @@ -1386,46 +913,38 @@ static void dsi_pll_calc_dsi_fck(struct dss_pll_clock_info *cinfo) static int dsi_pll_enable(struct dss_pll *pll) { struct dsi_data *dsi = container_of(pll, struct dsi_data, pll); - struct platform_device *dsidev = dsi->pdev; int r = 0; DSSDBG("PLL init\n"); - r = dsi_regulator_init(dsidev); - if (r) - return r; - - r = dsi_runtime_get(dsidev); + r = dsi_runtime_get(dsi); if (r) return r; /* * Note: SCP CLK is not required on OMAP3, but it is required on OMAP4. */ - dsi_enable_scp_clk(dsidev); + dsi_enable_scp_clk(dsi); - if (!dsi->vdds_dsi_enabled) { - r = regulator_enable(dsi->vdds_dsi_reg); - if (r) - goto err0; - dsi->vdds_dsi_enabled = true; - } + r = regulator_enable(dsi->vdds_dsi_reg); + if (r) + goto err0; /* XXX PLL does not come out of reset without this... */ - dispc_pck_free_enable(1); + dispc_pck_free_enable(dsi->dss->dispc, 1); - if (wait_for_bit_change(dsidev, DSI_PLL_STATUS, 0, 1) != 1) { + if (!wait_for_bit_change(dsi, DSI_PLL_STATUS, 0, 1)) { DSSERR("PLL not coming out of reset.\n"); r = -ENODEV; - dispc_pck_free_enable(0); + dispc_pck_free_enable(dsi->dss->dispc, 0); goto err1; } /* XXX ... but if left on, we get problems when planes do not * fill the whole display. No idea about this */ - dispc_pck_free_enable(0); + dispc_pck_free_enable(dsi->dss->dispc, 0); - r = dsi_pll_power(dsidev, DSI_PLL_POWER_ON_ALL); + r = dsi_pll_power(dsi, DSI_PLL_POWER_ON_ALL); if (r) goto err1; @@ -1434,55 +953,40 @@ static int dsi_pll_enable(struct dss_pll *pll) return 0; err1: - if (dsi->vdds_dsi_enabled) { - regulator_disable(dsi->vdds_dsi_reg); - dsi->vdds_dsi_enabled = false; - } + regulator_disable(dsi->vdds_dsi_reg); err0: - dsi_disable_scp_clk(dsidev); - dsi_runtime_put(dsidev); + dsi_disable_scp_clk(dsi); + dsi_runtime_put(dsi); return r; } -static void dsi_pll_uninit(struct platform_device *dsidev, bool disconnect_lanes) +static void dsi_pll_disable(struct dss_pll *pll) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - - dsi_pll_power(dsidev, DSI_PLL_POWER_OFF); - if (disconnect_lanes) { - WARN_ON(!dsi->vdds_dsi_enabled); - regulator_disable(dsi->vdds_dsi_reg); - dsi->vdds_dsi_enabled = false; - } + struct dsi_data *dsi = container_of(pll, struct dsi_data, pll); - dsi_disable_scp_clk(dsidev); - dsi_runtime_put(dsidev); + dsi_pll_power(dsi, DSI_PLL_POWER_OFF); - DSSDBG("PLL uninit done\n"); -} + regulator_disable(dsi->vdds_dsi_reg); -static void dsi_pll_disable(struct dss_pll *pll) -{ - struct dsi_data *dsi = container_of(pll, struct dsi_data, pll); - struct platform_device *dsidev = dsi->pdev; + dsi_disable_scp_clk(dsi); + dsi_runtime_put(dsi); - dsi_pll_uninit(dsidev, true); + DSSDBG("PLL disable done\n"); } -static void dsi_dump_dsidev_clocks(struct platform_device *dsidev, - struct seq_file *s) +static int dsi_dump_dsi_clocks(struct seq_file *s, void *p) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + struct dsi_data *dsi = s->private; struct dss_pll_clock_info *cinfo = &dsi->pll.cinfo; enum dss_clk_source dispc_clk_src, dsi_clk_src; int dsi_module = dsi->module_id; struct dss_pll *pll = &dsi->pll; - dispc_clk_src = dss_get_dispc_clk_source(); - dsi_clk_src = dss_get_dsi_clk_source(dsi_module); + dispc_clk_src = dss_get_dispc_clk_source(dsi->dss); + dsi_clk_src = dss_get_dsi_clk_source(dsi->dss, dsi_module); - if (dsi_runtime_get(dsidev)) - return; + if (dsi_runtime_get(dsi)) + return 0; seq_printf(s, "- DSI%d PLL -\n", dsi_module + 1); @@ -1516,52 +1020,45 @@ static void dsi_dump_dsidev_clocks(struct platform_device *dsidev, seq_printf(s, "dsi fclk source = %s\n", dss_get_clk_source_name(dsi_clk_src)); - seq_printf(s, "DSI_FCLK\t%lu\n", dsi_fclk_rate(dsidev)); + seq_printf(s, "DSI_FCLK\t%lu\n", dsi_fclk_rate(dsi)); seq_printf(s, "DDR_CLK\t\t%lu\n", cinfo->clkdco / 4); - seq_printf(s, "TxByteClkHS\t%lu\n", dsi_get_txbyteclkhs(dsidev)); + seq_printf(s, "TxByteClkHS\t%lu\n", dsi_get_txbyteclkhs(dsi)); seq_printf(s, "LP_CLK\t\t%lu\n", dsi->current_lp_cinfo.lp_clk); - dsi_runtime_put(dsidev); -} - -void dsi_dump_clocks(struct seq_file *s) -{ - struct platform_device *dsidev; - int i; + dsi_runtime_put(dsi); - for (i = 0; i < MAX_NUM_DSI; i++) { - dsidev = dsi_get_dsidev_from_id(i); - if (dsidev) - dsi_dump_dsidev_clocks(dsidev, s); - } + return 0; } #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS -static void dsi_dump_dsidev_irqs(struct platform_device *dsidev, - struct seq_file *s) +static int dsi_dump_dsi_irqs(struct seq_file *s, void *p) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + struct dsi_data *dsi = s->private; unsigned long flags; - struct dsi_irq_stats stats; + struct dsi_irq_stats *stats; + + stats = kmalloc(sizeof(*stats), GFP_KERNEL); + if (!stats) + return -ENOMEM; spin_lock_irqsave(&dsi->irq_stats_lock, flags); - stats = dsi->irq_stats; + *stats = dsi->irq_stats; memset(&dsi->irq_stats, 0, sizeof(dsi->irq_stats)); dsi->irq_stats.last_reset = jiffies; spin_unlock_irqrestore(&dsi->irq_stats_lock, flags); seq_printf(s, "period %u ms\n", - jiffies_to_msecs(jiffies - stats.last_reset)); + jiffies_to_msecs(jiffies - stats->last_reset)); - seq_printf(s, "irqs %d\n", stats.irq_count); + seq_printf(s, "irqs %d\n", stats->irq_count); #define PIS(x) \ - seq_printf(s, "%-20s %10d\n", #x, stats.dsi_irqs[ffs(DSI_IRQ_##x)-1]); + seq_printf(s, "%-20s %10d\n", #x, stats->dsi_irqs[ffs(DSI_IRQ_##x)-1]); seq_printf(s, "-- DSI%d interrupts --\n", dsi->module_id + 1); PIS(VC0); @@ -1585,10 +1082,10 @@ static void dsi_dump_dsidev_irqs(struct platform_device *dsidev, #define PIS(x) \ seq_printf(s, "%-20s %10d %10d %10d %10d\n", #x, \ - stats.vc_irqs[0][ffs(DSI_VC_IRQ_##x)-1], \ - stats.vc_irqs[1][ffs(DSI_VC_IRQ_##x)-1], \ - stats.vc_irqs[2][ffs(DSI_VC_IRQ_##x)-1], \ - stats.vc_irqs[3][ffs(DSI_VC_IRQ_##x)-1]); + stats->vc_irqs[0][ffs(DSI_VC_IRQ_##x)-1], \ + stats->vc_irqs[1][ffs(DSI_VC_IRQ_##x)-1], \ + stats->vc_irqs[2][ffs(DSI_VC_IRQ_##x)-1], \ + stats->vc_irqs[3][ffs(DSI_VC_IRQ_##x)-1]); seq_printf(s, "-- VC interrupts --\n"); PIS(CS); @@ -1604,7 +1101,7 @@ static void dsi_dump_dsidev_irqs(struct platform_device *dsidev, #define PIS(x) \ seq_printf(s, "%-20s %10d\n", #x, \ - stats.cio_irqs[ffs(DSI_CIO_IRQ_##x)-1]); + stats->cio_irqs[ffs(DSI_CIO_IRQ_##x)-1]); seq_printf(s, "-- CIO interrupts --\n"); PIS(ERRSYNCESC1); @@ -1628,32 +1125,22 @@ static void dsi_dump_dsidev_irqs(struct platform_device *dsidev, PIS(ULPSACTIVENOT_ALL0); PIS(ULPSACTIVENOT_ALL1); #undef PIS -} - -static void dsi1_dump_irqs(struct seq_file *s) -{ - struct platform_device *dsidev = dsi_get_dsidev_from_id(0); - dsi_dump_dsidev_irqs(dsidev, s); -} - -static void dsi2_dump_irqs(struct seq_file *s) -{ - struct platform_device *dsidev = dsi_get_dsidev_from_id(1); + kfree(stats); - dsi_dump_dsidev_irqs(dsidev, s); + return 0; } #endif -static void dsi_dump_dsidev_regs(struct platform_device *dsidev, - struct seq_file *s) +static int dsi_dump_dsi_regs(struct seq_file *s, void *p) { -#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dsi_read_reg(dsidev, r)) + struct dsi_data *dsi = s->private; - if (dsi_runtime_get(dsidev)) - return; - dsi_enable_scp_clk(dsidev); + if (dsi_runtime_get(dsi)) + return 0; + dsi_enable_scp_clk(dsi); +#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dsi_read_reg(dsi, r)) DUMPREG(DSI_REVISION); DUMPREG(DSI_SYSCONFIG); DUMPREG(DSI_SYSSTATUS); @@ -1723,24 +1210,12 @@ static void dsi_dump_dsidev_regs(struct platform_device *dsidev, DUMPREG(DSI_PLL_GO); DUMPREG(DSI_PLL_CONFIGURATION1); DUMPREG(DSI_PLL_CONFIGURATION2); - - dsi_disable_scp_clk(dsidev); - dsi_runtime_put(dsidev); #undef DUMPREG -} -static void dsi1_dump_regs(struct seq_file *s) -{ - struct platform_device *dsidev = dsi_get_dsidev_from_id(0); - - dsi_dump_dsidev_regs(dsidev, s); -} + dsi_disable_scp_clk(dsi); + dsi_runtime_put(dsi); -static void dsi2_dump_regs(struct seq_file *s) -{ - struct platform_device *dsidev = dsi_get_dsidev_from_id(1); - - dsi_dump_dsidev_regs(dsidev, s); + return 0; } enum dsi_cio_power_state { @@ -1749,16 +1224,15 @@ enum dsi_cio_power_state { DSI_COMPLEXIO_POWER_ULPS = 0x2, }; -static int dsi_cio_power(struct platform_device *dsidev, - enum dsi_cio_power_state state) +static int dsi_cio_power(struct dsi_data *dsi, enum dsi_cio_power_state state) { int t = 0; /* PWR_CMD */ - REG_FLD_MOD(dsidev, DSI_COMPLEXIO_CFG1, state, 28, 27); + REG_FLD_MOD(dsi, DSI_COMPLEXIO_CFG1, state, 28, 27); /* PWR_STATUS */ - while (FLD_GET(dsi_read_reg(dsidev, DSI_COMPLEXIO_CFG1), + while (FLD_GET(dsi_read_reg(dsi, DSI_COMPLEXIO_CFG1), 26, 25) != state) { if (++t > 1000) { DSSERR("failed to set complexio power state to " @@ -1771,7 +1245,7 @@ static int dsi_cio_power(struct platform_device *dsidev, return 0; } -static unsigned dsi_get_line_buf_size(struct platform_device *dsidev) +static unsigned int dsi_get_line_buf_size(struct dsi_data *dsi) { int val; @@ -1779,10 +1253,10 @@ static unsigned dsi_get_line_buf_size(struct platform_device *dsidev) /* XXX: for some reason using full buffer size causes * considerable TX slowdown with update sizes that fill the * whole buffer */ - if (!dss_has_feature(FEAT_DSI_GNQ)) + if (!(dsi->data->quirks & DSI_QUIRK_GNQ)) return 1023 * 3; - val = REG_GET(dsidev, DSI_GNQ, 14, 12); /* VP1_LINE_BUFFER_SIZE */ + val = REG_GET(dsi, DSI_GNQ, 14, 12); /* VP1_LINE_BUFFER_SIZE */ switch (val) { case 1: @@ -1805,9 +1279,8 @@ static unsigned dsi_get_line_buf_size(struct platform_device *dsidev) } } -static int dsi_set_lane_config(struct platform_device *dsidev) +static int dsi_set_lane_config(struct dsi_data *dsi) { - 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, @@ -1819,12 +1292,12 @@ static int dsi_set_lane_config(struct platform_device *dsidev) u32 r; int i; - r = dsi_read_reg(dsidev, DSI_COMPLEXIO_CFG1); + r = dsi_read_reg(dsi, DSI_COMPLEXIO_CFG1); for (i = 0; i < dsi->num_lanes_used; ++i) { - unsigned offset = offsets[i]; - unsigned polarity, lane_number; - unsigned t; + unsigned int offset = offsets[i]; + unsigned int polarity, lane_number; + unsigned int t; for (t = 0; t < dsi->num_lanes_supported; ++t) if (dsi->lanes[t].function == functions[i]) @@ -1842,35 +1315,33 @@ static int dsi_set_lane_config(struct platform_device *dsidev) /* clear the unused lanes */ for (; i < dsi->num_lanes_supported; ++i) { - unsigned offset = offsets[i]; + unsigned int 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); + dsi_write_reg(dsi, DSI_COMPLEXIO_CFG1, r); return 0; } -static inline unsigned ns2ddr(struct platform_device *dsidev, unsigned ns) +static inline unsigned int ns2ddr(struct dsi_data *dsi, unsigned int ns) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - /* convert time in ns to ddr ticks, rounding up */ unsigned long ddr_clk = dsi->pll.cinfo.clkdco / 4; + return (ns * (ddr_clk / 1000 / 1000) + 999) / 1000; } -static inline unsigned ddr2ns(struct platform_device *dsidev, unsigned ddr) +static inline unsigned int ddr2ns(struct dsi_data *dsi, unsigned int ddr) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - unsigned long ddr_clk = dsi->pll.cinfo.clkdco / 4; + return ddr * 1000 * 1000 / (ddr_clk / 1000); } -static void dsi_cio_timings(struct platform_device *dsidev) +static void dsi_cio_timings(struct dsi_data *dsi) { u32 r; u32 ths_prepare, ths_prepare_ths_zero, ths_trail, ths_exit; @@ -1882,131 +1353,80 @@ static void dsi_cio_timings(struct platform_device *dsidev) /* 1 * DDR_CLK = 2 * UI */ /* min 40ns + 4*UI max 85ns + 6*UI */ - ths_prepare = ns2ddr(dsidev, 70) + 2; + ths_prepare = ns2ddr(dsi, 70) + 2; /* min 145ns + 10*UI */ - ths_prepare_ths_zero = ns2ddr(dsidev, 175) + 2; + ths_prepare_ths_zero = ns2ddr(dsi, 175) + 2; /* min max(8*UI, 60ns+4*UI) */ - ths_trail = ns2ddr(dsidev, 60) + 5; + ths_trail = ns2ddr(dsi, 60) + 5; /* min 100ns */ - ths_exit = ns2ddr(dsidev, 145); + ths_exit = ns2ddr(dsi, 145); /* tlpx min 50n */ - tlpx_half = ns2ddr(dsidev, 25); + tlpx_half = ns2ddr(dsi, 25); /* min 60ns */ - tclk_trail = ns2ddr(dsidev, 60) + 2; + tclk_trail = ns2ddr(dsi, 60) + 2; /* min 38ns, max 95ns */ - tclk_prepare = ns2ddr(dsidev, 65); + tclk_prepare = ns2ddr(dsi, 65); /* min tclk-prepare + tclk-zero = 300ns */ - tclk_zero = ns2ddr(dsidev, 260); + tclk_zero = ns2ddr(dsi, 260); DSSDBG("ths_prepare %u (%uns), ths_prepare_ths_zero %u (%uns)\n", - ths_prepare, ddr2ns(dsidev, ths_prepare), - ths_prepare_ths_zero, ddr2ns(dsidev, ths_prepare_ths_zero)); + ths_prepare, ddr2ns(dsi, ths_prepare), + ths_prepare_ths_zero, ddr2ns(dsi, ths_prepare_ths_zero)); DSSDBG("ths_trail %u (%uns), ths_exit %u (%uns)\n", - ths_trail, ddr2ns(dsidev, ths_trail), - ths_exit, ddr2ns(dsidev, ths_exit)); + ths_trail, ddr2ns(dsi, ths_trail), + ths_exit, ddr2ns(dsi, ths_exit)); DSSDBG("tlpx_half %u (%uns), tclk_trail %u (%uns), " "tclk_zero %u (%uns)\n", - tlpx_half, ddr2ns(dsidev, tlpx_half), - tclk_trail, ddr2ns(dsidev, tclk_trail), - tclk_zero, ddr2ns(dsidev, tclk_zero)); + tlpx_half, ddr2ns(dsi, tlpx_half), + tclk_trail, ddr2ns(dsi, tclk_trail), + tclk_zero, ddr2ns(dsi, tclk_zero)); DSSDBG("tclk_prepare %u (%uns)\n", - tclk_prepare, ddr2ns(dsidev, tclk_prepare)); + tclk_prepare, ddr2ns(dsi, tclk_prepare)); /* program timings */ - r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG0); + r = dsi_read_reg(dsi, DSI_DSIPHY_CFG0); r = FLD_MOD(r, ths_prepare, 31, 24); r = FLD_MOD(r, ths_prepare_ths_zero, 23, 16); r = FLD_MOD(r, ths_trail, 15, 8); r = FLD_MOD(r, ths_exit, 7, 0); - dsi_write_reg(dsidev, DSI_DSIPHY_CFG0, r); + dsi_write_reg(dsi, DSI_DSIPHY_CFG0, r); - r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG1); + r = dsi_read_reg(dsi, DSI_DSIPHY_CFG1); r = FLD_MOD(r, tlpx_half, 20, 16); r = FLD_MOD(r, tclk_trail, 15, 8); r = FLD_MOD(r, tclk_zero, 7, 0); - if (dss_has_feature(FEAT_DSI_PHY_DCC)) { + if (dsi->data->quirks & DSI_QUIRK_PHY_DCC) { r = FLD_MOD(r, 0, 21, 21); /* DCCEN = disable */ r = FLD_MOD(r, 1, 22, 22); /* CLKINP_DIVBY2EN = enable */ r = FLD_MOD(r, 1, 23, 23); /* CLKINP_SEL = enable */ } - dsi_write_reg(dsidev, DSI_DSIPHY_CFG1, r); + dsi_write_reg(dsi, DSI_DSIPHY_CFG1, r); - r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG2); + r = dsi_read_reg(dsi, DSI_DSIPHY_CFG2); r = FLD_MOD(r, tclk_prepare, 7, 0); - dsi_write_reg(dsidev, DSI_DSIPHY_CFG2, r); + dsi_write_reg(dsi, 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 platform_device *dsidev, - unsigned mask_p, unsigned mask_n) +static int dsi_cio_wait_tx_clk_esc_reset(struct dsi_data *dsi) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - 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 - * 19: DY1 20: DX1 - * 21: DY2 22: DX2 - * 23: DY3 24: DX3 - * 25: DY4 26: DX4 - */ - - /* Set the lane override configuration */ - - /* REGLPTXSCPDAT4TO0DXDY */ - REG_FLD_MOD(dsidev, DSI_DSIPHY_CFG10, l, lptxscp_start, 17); - - /* Enable lane override */ - - /* ENLPTXSCPDAT */ - REG_FLD_MOD(dsidev, DSI_DSIPHY_CFG10, 1, 27, 27); -} - -static void dsi_cio_disable_lane_override(struct platform_device *dsidev) -{ - /* Disable lane override */ - REG_FLD_MOD(dsidev, DSI_DSIPHY_CFG10, 0, 27, 27); /* ENLPTXSCPDAT */ - /* Reset the lane override configuration */ - /* REGLPTXSCPDAT4TO0DXDY */ - REG_FLD_MOD(dsidev, DSI_DSIPHY_CFG10, 0, 22, 17); -} - -static int dsi_cio_wait_tx_clk_esc_reset(struct platform_device *dsidev) -{ - 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)) + if (dsi->data->quirks & DSI_QUIRK_REVERSE_TXCLKESC) offsets = offsets_old; else offsets = offsets_new; @@ -2019,7 +1439,7 @@ static int dsi_cio_wait_tx_clk_esc_reset(struct platform_device *dsidev) u32 l; int ok; - l = dsi_read_reg(dsidev, DSI_DSIPHY_CFG5); + l = dsi_read_reg(dsi, DSI_DSIPHY_CFG5); ok = 0; for (i = 0; i < dsi->num_lanes_supported; ++i) { @@ -2046,10 +1466,9 @@ static int dsi_cio_wait_tx_clk_esc_reset(struct platform_device *dsidev) } /* return bitmask of enabled lanes, lane0 being the lsb */ -static unsigned dsi_get_lane_mask(struct platform_device *dsidev) +static unsigned int dsi_get_lane_mask(struct dsi_data *dsi) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - unsigned mask = 0; + unsigned int mask = 0; int i; for (i = 0; i < dsi->num_lanes_supported; ++i) { @@ -2060,145 +1479,176 @@ static unsigned dsi_get_lane_mask(struct platform_device *dsidev) return mask; } -static int dsi_cio_init(struct platform_device *dsidev) +/* OMAP4 CONTROL_DSIPHY */ +#define OMAP4_DSIPHY_SYSCON_OFFSET 0x78 + +#define OMAP4_DSI2_LANEENABLE_SHIFT 29 +#define OMAP4_DSI2_LANEENABLE_MASK (0x7 << 29) +#define OMAP4_DSI1_LANEENABLE_SHIFT 24 +#define OMAP4_DSI1_LANEENABLE_MASK (0x1f << 24) +#define OMAP4_DSI1_PIPD_SHIFT 19 +#define OMAP4_DSI1_PIPD_MASK (0x1f << 19) +#define OMAP4_DSI2_PIPD_SHIFT 14 +#define OMAP4_DSI2_PIPD_MASK (0x1f << 14) + +static int dsi_omap4_mux_pads(struct dsi_data *dsi, unsigned int lanes) +{ + u32 enable_mask, enable_shift; + u32 pipd_mask, pipd_shift; + + if (dsi->module_id == 0) { + enable_mask = OMAP4_DSI1_LANEENABLE_MASK; + enable_shift = OMAP4_DSI1_LANEENABLE_SHIFT; + pipd_mask = OMAP4_DSI1_PIPD_MASK; + pipd_shift = OMAP4_DSI1_PIPD_SHIFT; + } else if (dsi->module_id == 1) { + enable_mask = OMAP4_DSI2_LANEENABLE_MASK; + enable_shift = OMAP4_DSI2_LANEENABLE_SHIFT; + pipd_mask = OMAP4_DSI2_PIPD_MASK; + pipd_shift = OMAP4_DSI2_PIPD_SHIFT; + } else { + return -ENODEV; + } + + return regmap_update_bits(dsi->syscon, OMAP4_DSIPHY_SYSCON_OFFSET, + enable_mask | pipd_mask, + (lanes << enable_shift) | (lanes << pipd_shift)); +} + +/* OMAP5 CONTROL_DSIPHY */ + +#define OMAP5_DSIPHY_SYSCON_OFFSET 0x74 + +#define OMAP5_DSI1_LANEENABLE_SHIFT 24 +#define OMAP5_DSI2_LANEENABLE_SHIFT 19 +#define OMAP5_DSI_LANEENABLE_MASK 0x1f + +static int dsi_omap5_mux_pads(struct dsi_data *dsi, unsigned int lanes) +{ + u32 enable_shift; + + if (dsi->module_id == 0) + enable_shift = OMAP5_DSI1_LANEENABLE_SHIFT; + else if (dsi->module_id == 1) + enable_shift = OMAP5_DSI2_LANEENABLE_SHIFT; + else + return -ENODEV; + + return regmap_update_bits(dsi->syscon, OMAP5_DSIPHY_SYSCON_OFFSET, + OMAP5_DSI_LANEENABLE_MASK << enable_shift, + lanes << enable_shift); +} + +static int dsi_enable_pads(struct dsi_data *dsi, unsigned int lane_mask) +{ + if (dsi->data->model == DSI_MODEL_OMAP4) + return dsi_omap4_mux_pads(dsi, lane_mask); + if (dsi->data->model == DSI_MODEL_OMAP5) + return dsi_omap5_mux_pads(dsi, lane_mask); + return 0; +} + +static void dsi_disable_pads(struct dsi_data *dsi) +{ + if (dsi->data->model == DSI_MODEL_OMAP4) + dsi_omap4_mux_pads(dsi, 0); + else if (dsi->data->model == DSI_MODEL_OMAP5) + dsi_omap5_mux_pads(dsi, 0); +} + +static int dsi_cio_init(struct dsi_data *dsi) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); int r; u32 l; DSSDBG("DSI CIO init starts"); - r = dss_dsi_enable_pads(dsi->module_id, dsi_get_lane_mask(dsidev)); + r = dsi_enable_pads(dsi, dsi_get_lane_mask(dsi)); if (r) return r; - dsi_enable_scp_clk(dsidev); + dsi_enable_scp_clk(dsi); /* A dummy read using the SCP interface to any DSIPHY register is * required after DSIPHY reset to complete the reset of the DSI complex * I/O. */ - dsi_read_reg(dsidev, DSI_DSIPHY_CFG5); + dsi_read_reg(dsi, DSI_DSIPHY_CFG5); - if (wait_for_bit_change(dsidev, DSI_DSIPHY_CFG5, 30, 1) != 1) { + if (!wait_for_bit_change(dsi, DSI_DSIPHY_CFG5, 30, 1)) { DSSERR("CIO SCP Clock domain not coming out of reset.\n"); r = -EIO; goto err_scp_clk_dom; } - r = dsi_set_lane_config(dsidev); + r = dsi_set_lane_config(dsi); if (r) goto err_scp_clk_dom; /* set TX STOP MODE timer to maximum for this operation */ - l = dsi_read_reg(dsidev, DSI_TIMING1); + l = dsi_read_reg(dsi, DSI_TIMING1); l = FLD_MOD(l, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */ l = FLD_MOD(l, 1, 14, 14); /* STOP_STATE_X16_IO */ l = FLD_MOD(l, 1, 13, 13); /* STOP_STATE_X4_IO */ l = FLD_MOD(l, 0x1fff, 12, 0); /* STOP_STATE_COUNTER_IO */ - dsi_write_reg(dsidev, DSI_TIMING1, l); - - if (dsi->ulps_enabled) { - unsigned mask_p; - int i; - - DSSDBG("manual ulps exit\n"); - - /* ULPS is exited by Mark-1 state for 1ms, followed by - * stop state. DSS HW cannot do this via the normal - * 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 by setting positive lines high and negative lines - * low for 1ms. - */ - - mask_p = 0; - - for (i = 0; i < dsi->num_lanes_supported; ++i) { - if (dsi->lanes[i].function == DSI_LANE_UNUSED) - continue; - mask_p |= 1 << i; - } + dsi_write_reg(dsi, DSI_TIMING1, l); - dsi_cio_enable_lane_override(dsidev, mask_p, 0); - } - - r = dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_ON); + r = dsi_cio_power(dsi, DSI_COMPLEXIO_POWER_ON); if (r) goto err_cio_pwr; - if (wait_for_bit_change(dsidev, DSI_COMPLEXIO_CFG1, 29, 1) != 1) { + if (!wait_for_bit_change(dsi, DSI_COMPLEXIO_CFG1, 29, 1)) { DSSERR("CIO PWR clock domain not coming out of reset.\n"); r = -ENODEV; goto err_cio_pwr_dom; } - dsi_if_enable(dsidev, true); - dsi_if_enable(dsidev, false); - REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 1, 20, 20); /* LP_CLK_ENABLE */ + dsi_if_enable(dsi, true); + dsi_if_enable(dsi, false); + REG_FLD_MOD(dsi, DSI_CLK_CTRL, 1, 20, 20); /* LP_CLK_ENABLE */ - r = dsi_cio_wait_tx_clk_esc_reset(dsidev); + r = dsi_cio_wait_tx_clk_esc_reset(dsi); if (r) goto err_tx_clk_esc_rst; - if (dsi->ulps_enabled) { - /* Keep Mark-1 state for 1ms (as per DSI spec) */ - ktime_t wait = ns_to_ktime(1000 * 1000); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_hrtimeout(&wait, HRTIMER_MODE_REL); - - /* Disable the override. The lanes should be set to Mark-11 - * state by the HW */ - dsi_cio_disable_lane_override(dsidev); - } - /* FORCE_TX_STOP_MODE_IO */ - REG_FLD_MOD(dsidev, DSI_TIMING1, 0, 15, 15); - - dsi_cio_timings(dsidev); + REG_FLD_MOD(dsi, DSI_TIMING1, 0, 15, 15); - if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) { - /* DDR_CLK_ALWAYS_ON */ - REG_FLD_MOD(dsidev, DSI_CLK_CTRL, - dsi->vm_timings.ddr_clk_always_on, 13, 13); - } + dsi_cio_timings(dsi); - dsi->ulps_enabled = false; + /* DDR_CLK_ALWAYS_ON */ + REG_FLD_MOD(dsi, DSI_CLK_CTRL, + !(dsi->dsidev->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS), + 13, 13); DSSDBG("CIO init done\n"); return 0; err_tx_clk_esc_rst: - REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 0, 20, 20); /* LP_CLK_ENABLE */ + REG_FLD_MOD(dsi, DSI_CLK_CTRL, 0, 20, 20); /* LP_CLK_ENABLE */ err_cio_pwr_dom: - dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_OFF); + dsi_cio_power(dsi, DSI_COMPLEXIO_POWER_OFF); err_cio_pwr: - if (dsi->ulps_enabled) - dsi_cio_disable_lane_override(dsidev); err_scp_clk_dom: - dsi_disable_scp_clk(dsidev); - dss_dsi_disable_pads(dsi->module_id, dsi_get_lane_mask(dsidev)); + dsi_disable_scp_clk(dsi); + dsi_disable_pads(dsi); return r; } -static void dsi_cio_uninit(struct platform_device *dsidev) +static void dsi_cio_uninit(struct dsi_data *dsi) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - /* DDR_CLK_ALWAYS_ON */ - REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 0, 13, 13); + REG_FLD_MOD(dsi, DSI_CLK_CTRL, 0, 13, 13); - dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_OFF); - dsi_disable_scp_clk(dsidev); - dss_dsi_disable_pads(dsi->module_id, dsi_get_lane_mask(dsidev)); + dsi_cio_power(dsi, DSI_COMPLEXIO_POWER_OFF); + dsi_disable_scp_clk(dsi); + dsi_disable_pads(dsi); } -static void dsi_config_tx_fifo(struct platform_device *dsidev, - enum fifo_size size1, enum fifo_size size2, - enum fifo_size size3, enum fifo_size size4) +static void dsi_config_tx_fifo(struct dsi_data *dsi, + enum fifo_size size1, enum fifo_size size2, + enum fifo_size size3, enum fifo_size size4) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); u32 r = 0; int add = 0; int i; @@ -2224,14 +1674,13 @@ static void dsi_config_tx_fifo(struct platform_device *dsidev, add += size; } - dsi_write_reg(dsidev, DSI_TX_FIFO_VC_SIZE, r); + dsi_write_reg(dsi, DSI_TX_FIFO_VC_SIZE, r); } -static void dsi_config_rx_fifo(struct platform_device *dsidev, +static void dsi_config_rx_fifo(struct dsi_data *dsi, enum fifo_size size1, enum fifo_size size2, enum fifo_size size3, enum fifo_size size4) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); u32 r = 0; int add = 0; int i; @@ -2257,18 +1706,18 @@ static void dsi_config_rx_fifo(struct platform_device *dsidev, add += size; } - dsi_write_reg(dsidev, DSI_RX_FIFO_VC_SIZE, r); + dsi_write_reg(dsi, DSI_RX_FIFO_VC_SIZE, r); } -static int dsi_force_tx_stop_mode_io(struct platform_device *dsidev) +static int dsi_force_tx_stop_mode_io(struct dsi_data *dsi) { u32 r; - r = dsi_read_reg(dsidev, DSI_TIMING1); + r = dsi_read_reg(dsi, DSI_TIMING1); r = FLD_MOD(r, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */ - dsi_write_reg(dsidev, DSI_TIMING1, r); + dsi_write_reg(dsi, DSI_TIMING1, r); - if (wait_for_bit_change(dsidev, DSI_TIMING1, 15, 0) != 0) { + if (!wait_for_bit_change(dsi, DSI_TIMING1, 15, 0)) { DSSERR("TX_STOP bit not going down\n"); return -EIO; } @@ -2276,29 +1725,28 @@ static int dsi_force_tx_stop_mode_io(struct platform_device *dsidev) return 0; } -static bool dsi_vc_is_enabled(struct platform_device *dsidev, int channel) +static bool dsi_vc_is_enabled(struct dsi_data *dsi, int vc) { - return REG_GET(dsidev, DSI_VC_CTRL(channel), 0, 0); + return REG_GET(dsi, DSI_VC_CTRL(vc), 0, 0); } static void dsi_packet_sent_handler_vp(void *data, u32 mask) { struct dsi_packet_sent_handler_data *vp_data = (struct dsi_packet_sent_handler_data *) data; - struct dsi_data *dsi = dsi_get_dsidrv_data(vp_data->dsidev); - const int channel = dsi->update_channel; + struct dsi_data *dsi = vp_data->dsi; + const int vc = dsi->update_vc; u8 bit = dsi->te_enabled ? 30 : 31; - if (REG_GET(vp_data->dsidev, DSI_VC_TE(channel), bit, bit) == 0) + if (REG_GET(dsi, DSI_VC_TE(vc), bit, bit) == 0) complete(vp_data->completion); } -static int dsi_sync_vc_vp(struct platform_device *dsidev, int channel) +static int dsi_sync_vc_vp(struct dsi_data *dsi, int vc) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); DECLARE_COMPLETION_ONSTACK(completion); struct dsi_packet_sent_handler_data vp_data = { - .dsidev = dsidev, + .dsi = dsi, .completion = &completion }; int r = 0; @@ -2306,13 +1754,13 @@ static int dsi_sync_vc_vp(struct platform_device *dsidev, int channel) bit = dsi->te_enabled ? 30 : 31; - r = dsi_register_isr_vc(dsidev, channel, dsi_packet_sent_handler_vp, + r = dsi_register_isr_vc(dsi, vc, dsi_packet_sent_handler_vp, &vp_data, DSI_VC_IRQ_PACKET_SENT); if (r) goto err0; /* Wait for completion only if TE_EN/TE_START is still set */ - if (REG_GET(dsidev, DSI_VC_TE(channel), bit, bit)) { + if (REG_GET(dsi, DSI_VC_TE(vc), bit, bit)) { if (wait_for_completion_timeout(&completion, msecs_to_jiffies(10)) == 0) { DSSERR("Failed to complete previous frame transfer\n"); @@ -2321,12 +1769,12 @@ static int dsi_sync_vc_vp(struct platform_device *dsidev, int channel) } } - dsi_unregister_isr_vc(dsidev, channel, dsi_packet_sent_handler_vp, + dsi_unregister_isr_vc(dsi, vc, dsi_packet_sent_handler_vp, &vp_data, DSI_VC_IRQ_PACKET_SENT); return 0; err1: - dsi_unregister_isr_vc(dsidev, channel, dsi_packet_sent_handler_vp, + dsi_unregister_isr_vc(dsi, vc, dsi_packet_sent_handler_vp, &vp_data, DSI_VC_IRQ_PACKET_SENT); err0: return r; @@ -2336,29 +1784,29 @@ static void dsi_packet_sent_handler_l4(void *data, u32 mask) { struct dsi_packet_sent_handler_data *l4_data = (struct dsi_packet_sent_handler_data *) data; - struct dsi_data *dsi = dsi_get_dsidrv_data(l4_data->dsidev); - const int channel = dsi->update_channel; + struct dsi_data *dsi = l4_data->dsi; + const int vc = dsi->update_vc; - if (REG_GET(l4_data->dsidev, DSI_VC_CTRL(channel), 5, 5) == 0) + if (REG_GET(dsi, DSI_VC_CTRL(vc), 5, 5) == 0) complete(l4_data->completion); } -static int dsi_sync_vc_l4(struct platform_device *dsidev, int channel) +static int dsi_sync_vc_l4(struct dsi_data *dsi, int vc) { DECLARE_COMPLETION_ONSTACK(completion); struct dsi_packet_sent_handler_data l4_data = { - .dsidev = dsidev, + .dsi = dsi, .completion = &completion }; int r = 0; - r = dsi_register_isr_vc(dsidev, channel, dsi_packet_sent_handler_l4, + r = dsi_register_isr_vc(dsi, vc, dsi_packet_sent_handler_l4, &l4_data, DSI_VC_IRQ_PACKET_SENT); if (r) goto err0; /* Wait for completion only if TX_FIFO_NOT_EMPTY is still set */ - if (REG_GET(dsidev, DSI_VC_CTRL(channel), 5, 5)) { + if (REG_GET(dsi, DSI_VC_CTRL(vc), 5, 5)) { if (wait_for_completion_timeout(&completion, msecs_to_jiffies(10)) == 0) { DSSERR("Failed to complete previous l4 transfer\n"); @@ -2367,70 +1815,65 @@ static int dsi_sync_vc_l4(struct platform_device *dsidev, int channel) } } - dsi_unregister_isr_vc(dsidev, channel, dsi_packet_sent_handler_l4, + dsi_unregister_isr_vc(dsi, vc, dsi_packet_sent_handler_l4, &l4_data, DSI_VC_IRQ_PACKET_SENT); return 0; err1: - dsi_unregister_isr_vc(dsidev, channel, dsi_packet_sent_handler_l4, + dsi_unregister_isr_vc(dsi, vc, dsi_packet_sent_handler_l4, &l4_data, DSI_VC_IRQ_PACKET_SENT); err0: return r; } -static int dsi_sync_vc(struct platform_device *dsidev, int channel) +static int dsi_sync_vc(struct dsi_data *dsi, int vc) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - - WARN_ON(!dsi_bus_is_locked(dsidev)); + WARN_ON(!dsi_bus_is_locked(dsi)); WARN_ON(in_interrupt()); - if (!dsi_vc_is_enabled(dsidev, channel)) + if (!dsi_vc_is_enabled(dsi, vc)) return 0; - switch (dsi->vc[channel].source) { + switch (dsi->vc[vc].source) { case DSI_VC_SOURCE_VP: - return dsi_sync_vc_vp(dsidev, channel); + return dsi_sync_vc_vp(dsi, vc); case DSI_VC_SOURCE_L4: - return dsi_sync_vc_l4(dsidev, channel); + return dsi_sync_vc_l4(dsi, vc); default: BUG(); return -EINVAL; } } -static int dsi_vc_enable(struct platform_device *dsidev, int channel, - bool enable) +static int dsi_vc_enable(struct dsi_data *dsi, int vc, bool enable) { - DSSDBG("dsi_vc_enable channel %d, enable %d\n", - channel, enable); + DSSDBG("dsi_vc_enable vc %d, enable %d\n", + vc, enable); enable = enable ? 1 : 0; - REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), enable, 0, 0); + REG_FLD_MOD(dsi, DSI_VC_CTRL(vc), enable, 0, 0); - if (wait_for_bit_change(dsidev, DSI_VC_CTRL(channel), - 0, enable) != enable) { - DSSERR("Failed to set dsi_vc_enable to %d\n", enable); - return -EIO; + if (!wait_for_bit_change(dsi, DSI_VC_CTRL(vc), 0, enable)) { + DSSERR("Failed to set dsi_vc_enable to %d\n", enable); + return -EIO; } return 0; } -static void dsi_vc_initial_config(struct platform_device *dsidev, int channel) +static void dsi_vc_initial_config(struct dsi_data *dsi, int vc) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); u32 r; - DSSDBG("Initial config of virtual channel %d", channel); + DSSDBG("Initial config of VC %d", vc); - r = dsi_read_reg(dsidev, DSI_VC_CTRL(channel)); + r = dsi_read_reg(dsi, DSI_VC_CTRL(vc)); if (FLD_GET(r, 15, 15)) /* VC_BUSY */ DSSERR("VC(%d) busy when trying to configure it!\n", - channel); + vc); r = FLD_MOD(r, 0, 1, 1); /* SOURCE, 0 = L4 */ r = FLD_MOD(r, 0, 2, 2); /* BTA_SHORT_EN */ @@ -2439,83 +1882,45 @@ static void dsi_vc_initial_config(struct platform_device *dsidev, int channel) r = FLD_MOD(r, 1, 7, 7); /* CS_TX_EN */ r = FLD_MOD(r, 1, 8, 8); /* ECC_TX_EN */ r = FLD_MOD(r, 0, 9, 9); /* MODE_SPEED, high speed on/off */ - if (dss_has_feature(FEAT_DSI_VC_OCP_WIDTH)) + if (dsi->data->quirks & DSI_QUIRK_VC_OCP_WIDTH) r = FLD_MOD(r, 3, 11, 10); /* OCP_WIDTH = 32 bit */ r = FLD_MOD(r, 4, 29, 27); /* DMA_RX_REQ_NB = no dma */ r = FLD_MOD(r, 4, 23, 21); /* DMA_TX_REQ_NB = no dma */ - dsi_write_reg(dsidev, DSI_VC_CTRL(channel), r); - - dsi->vc[channel].source = DSI_VC_SOURCE_L4; -} - -static int dsi_vc_config_source(struct platform_device *dsidev, int channel, - enum dsi_vc_source source) -{ - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - - if (dsi->vc[channel].source == source) - return 0; - - DSSDBG("Source config of virtual channel %d", channel); - - dsi_sync_vc(dsidev, channel); - - dsi_vc_enable(dsidev, channel, 0); - - /* VC_BUSY */ - if (wait_for_bit_change(dsidev, DSI_VC_CTRL(channel), 15, 0) != 0) { - DSSERR("vc(%d) busy when trying to config for VP\n", channel); - return -EIO; - } - - /* SOURCE, 0 = L4, 1 = video port */ - REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), source, 1, 1); + dsi_write_reg(dsi, DSI_VC_CTRL(vc), r); - /* DCS_CMD_ENABLE */ - if (dss_has_feature(FEAT_DSI_DCS_CMD_CONFIG_VC)) { - bool enable = source == DSI_VC_SOURCE_VP; - REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), enable, 30, 30); - } - - dsi_vc_enable(dsidev, channel, 1); - - dsi->vc[channel].source = source; - - return 0; + dsi->vc[vc].source = DSI_VC_SOURCE_L4; } -static void dsi_vc_enable_hs(struct omap_dss_device *dssdev, int channel, +static void dsi_vc_enable_hs(struct omap_dss_device *dssdev, int vc, bool enable) { - struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + struct dsi_data *dsi = to_dsi_data(dssdev); - DSSDBG("dsi_vc_enable_hs(%d, %d)\n", channel, enable); + DSSDBG("dsi_vc_enable_hs(%d, %d)\n", vc, enable); - WARN_ON(!dsi_bus_is_locked(dsidev)); + if (REG_GET(dsi, DSI_VC_CTRL(vc), 9, 9) == enable) + return; - dsi_vc_enable(dsidev, channel, 0); - dsi_if_enable(dsidev, 0); + WARN_ON(!dsi_bus_is_locked(dsi)); - REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), enable, 9, 9); + dsi_vc_enable(dsi, vc, 0); + dsi_if_enable(dsi, 0); - dsi_vc_enable(dsidev, channel, 1); - dsi_if_enable(dsidev, 1); + REG_FLD_MOD(dsi, DSI_VC_CTRL(vc), enable, 9, 9); - dsi_force_tx_stop_mode_io(dsidev); + dsi_vc_enable(dsi, vc, 1); + dsi_if_enable(dsi, 1); - /* start the DDR clock by sending a NULL packet */ - if (dsi->vm_timings.ddr_clk_always_on && enable) - dsi_vc_send_null(dssdev, channel); + dsi_force_tx_stop_mode_io(dsi); } -static void dsi_vc_flush_long_data(struct platform_device *dsidev, int channel) +static void dsi_vc_flush_long_data(struct dsi_data *dsi, int vc) { - while (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20)) { + while (REG_GET(dsi, DSI_VC_CTRL(vc), 20, 20)) { u32 val; - val = dsi_read_reg(dsidev, DSI_VC_SHORT_PACKET_HEADER(channel)); + val = dsi_read_reg(dsi, DSI_VC_SHORT_PACKET_HEADER(vc)); DSSDBG("\t\tb1 %#02x b2 %#02x b3 %#02x b4 %#02x\n", (val >> 0) & 0xff, (val >> 8) & 0xff, @@ -2561,14 +1966,13 @@ static void dsi_show_rx_ack_with_err(u16 err) DSSERR("\t\tDSI Protocol Violation\n"); } -static u16 dsi_vc_flush_receive_data(struct platform_device *dsidev, - int channel) +static u16 dsi_vc_flush_receive_data(struct dsi_data *dsi, int vc) { /* RX_FIFO_NOT_EMPTY */ - while (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20)) { + while (REG_GET(dsi, DSI_VC_CTRL(vc), 20, 20)) { u32 val; u8 dt; - val = dsi_read_reg(dsidev, DSI_VC_SHORT_PACKET_HEADER(channel)); + val = dsi_read_reg(dsi, DSI_VC_SHORT_PACKET_HEADER(vc)); DSSERR("\trawval %#08x\n", val); dt = FLD_GET(val, 5, 0); if (dt == MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT) { @@ -2583,7 +1987,7 @@ static u16 dsi_vc_flush_receive_data(struct platform_device *dsidev, } else if (dt == MIPI_DSI_RX_DCS_LONG_READ_RESPONSE) { DSSERR("\tDCS long response, len %d\n", FLD_GET(val, 23, 8)); - dsi_vc_flush_long_data(dsidev, channel); + dsi_vc_flush_long_data(dsi, vc); } else { DSSERR("\tunknown datatype 0x%02x\n", dt); } @@ -2591,47 +1995,45 @@ static u16 dsi_vc_flush_receive_data(struct platform_device *dsidev, return 0; } -static int dsi_vc_send_bta(struct platform_device *dsidev, int channel) +static int dsi_vc_send_bta(struct dsi_data *dsi, int vc) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - if (dsi->debug_write || dsi->debug_read) - DSSDBG("dsi_vc_send_bta %d\n", channel); + DSSDBG("dsi_vc_send_bta %d\n", vc); - WARN_ON(!dsi_bus_is_locked(dsidev)); + WARN_ON(!dsi_bus_is_locked(dsi)); /* RX_FIFO_NOT_EMPTY */ - if (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20)) { + if (REG_GET(dsi, DSI_VC_CTRL(vc), 20, 20)) { DSSERR("rx fifo not empty when sending BTA, dumping data:\n"); - dsi_vc_flush_receive_data(dsidev, channel); + dsi_vc_flush_receive_data(dsi, vc); } - REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 1, 6, 6); /* BTA_EN */ + REG_FLD_MOD(dsi, DSI_VC_CTRL(vc), 1, 6, 6); /* BTA_EN */ /* flush posted write */ - dsi_read_reg(dsidev, DSI_VC_CTRL(channel)); + dsi_read_reg(dsi, DSI_VC_CTRL(vc)); return 0; } -static int dsi_vc_send_bta_sync(struct omap_dss_device *dssdev, int channel) +static int dsi_vc_send_bta_sync(struct omap_dss_device *dssdev, int vc) { - struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = to_dsi_data(dssdev); DECLARE_COMPLETION_ONSTACK(completion); int r = 0; u32 err; - r = dsi_register_isr_vc(dsidev, channel, dsi_completion_handler, + r = dsi_register_isr_vc(dsi, vc, dsi_completion_handler, &completion, DSI_VC_IRQ_BTA); if (r) goto err0; - r = dsi_register_isr(dsidev, dsi_completion_handler, &completion, + r = dsi_register_isr(dsi, dsi_completion_handler, &completion, DSI_IRQ_ERROR_MASK); if (r) goto err1; - r = dsi_vc_send_bta(dsidev, channel); + r = dsi_vc_send_bta(dsi, vc); if (r) goto err2; @@ -2642,41 +2044,41 @@ static int dsi_vc_send_bta_sync(struct omap_dss_device *dssdev, int channel) goto err2; } - err = dsi_get_errors(dsidev); + err = dsi_get_errors(dsi); if (err) { DSSERR("Error while sending BTA: %x\n", err); r = -EIO; goto err2; } err2: - dsi_unregister_isr(dsidev, dsi_completion_handler, &completion, + dsi_unregister_isr(dsi, dsi_completion_handler, &completion, DSI_IRQ_ERROR_MASK); err1: - dsi_unregister_isr_vc(dsidev, channel, dsi_completion_handler, + dsi_unregister_isr_vc(dsi, vc, dsi_completion_handler, &completion, DSI_VC_IRQ_BTA); err0: return r; } -static inline void dsi_vc_write_long_header(struct platform_device *dsidev, - int channel, u8 data_type, u16 len, u8 ecc) +static inline void dsi_vc_write_long_header(struct dsi_data *dsi, int vc, + int channel, u8 data_type, u16 len, + u8 ecc) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); u32 val; u8 data_id; - WARN_ON(!dsi_bus_is_locked(dsidev)); + WARN_ON(!dsi_bus_is_locked(dsi)); - data_id = data_type | dsi->vc[channel].vc_id << 6; + data_id = data_type | channel << 6; val = FLD_VAL(data_id, 7, 0) | FLD_VAL(len, 23, 8) | FLD_VAL(ecc, 31, 24); - dsi_write_reg(dsidev, DSI_VC_LONG_PACKET_HEADER(channel), val); + dsi_write_reg(dsi, DSI_VC_LONG_PACKET_HEADER(vc), val); } -static inline void dsi_vc_write_long_payload(struct platform_device *dsidev, - int channel, u8 b1, u8 b2, u8 b3, u8 b4) +static inline void dsi_vc_write_long_payload(struct dsi_data *dsi, int vc, + u8 b1, u8 b2, u8 b3, u8 b4) { u32 val; @@ -2685,34 +2087,31 @@ static inline void dsi_vc_write_long_payload(struct platform_device *dsidev, /* DSSDBG("\twriting %02x, %02x, %02x, %02x (%#010x)\n", b1, b2, b3, b4, val); */ - dsi_write_reg(dsidev, DSI_VC_LONG_PACKET_PAYLOAD(channel), val); + dsi_write_reg(dsi, DSI_VC_LONG_PACKET_PAYLOAD(vc), val); } -static int dsi_vc_send_long(struct platform_device *dsidev, int channel, - u8 data_type, u8 *data, u16 len, u8 ecc) +static int dsi_vc_send_long(struct dsi_data *dsi, int vc, + const struct mipi_dsi_msg *msg) { /*u32 val; */ - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); int i; - u8 *p; + const u8 *p; int r = 0; u8 b1, b2, b3, b4; if (dsi->debug_write) - DSSDBG("dsi_vc_send_long, %d bytes\n", len); + DSSDBG("dsi_vc_send_long, %zu bytes\n", msg->tx_len); /* len + header */ - if (dsi->vc[channel].tx_fifo_size * 32 * 4 < len + 4) { + if (dsi->vc[vc].tx_fifo_size * 32 * 4 < msg->tx_len + 4) { DSSERR("unable to send long packet: packet too long.\n"); return -EINVAL; } - dsi_vc_config_source(dsidev, channel, DSI_VC_SOURCE_L4); + dsi_vc_write_long_header(dsi, vc, msg->channel, msg->type, msg->tx_len, 0); - dsi_vc_write_long_header(dsidev, channel, data_type, len, ecc); - - p = data; - for (i = 0; i < len >> 2; i++) { + p = msg->tx_buf; + for (i = 0; i < msg->tx_len >> 2; i++) { if (dsi->debug_write) DSSDBG("\tsending full packet %d\n", i); @@ -2721,10 +2120,10 @@ static int dsi_vc_send_long(struct platform_device *dsidev, int channel, b3 = *p++; b4 = *p++; - dsi_vc_write_long_payload(dsidev, channel, b1, b2, b3, b4); + dsi_vc_write_long_payload(dsi, vc, b1, b2, b3, b4); } - i = len % 4; + i = msg->tx_len % 4; if (i) { b1 = 0; b2 = 0; b3 = 0; @@ -2746,213 +2145,104 @@ static int dsi_vc_send_long(struct platform_device *dsidev, int channel, break; } - dsi_vc_write_long_payload(dsidev, channel, b1, b2, b3, 0); + dsi_vc_write_long_payload(dsi, vc, b1, b2, b3, 0); } return r; } -static int dsi_vc_send_short(struct platform_device *dsidev, int channel, - u8 data_type, u16 data, u8 ecc) +static int dsi_vc_send_short(struct dsi_data *dsi, int vc, + const struct mipi_dsi_msg *msg) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + struct mipi_dsi_packet pkt; + int ret; u32 r; - u8 data_id; - WARN_ON(!dsi_bus_is_locked(dsidev)); + ret = mipi_dsi_create_packet(&pkt, msg); + if (ret < 0) + return ret; - if (dsi->debug_write) - DSSDBG("dsi_vc_send_short(ch%d, dt %#x, b1 %#x, b2 %#x)\n", - channel, - data_type, data & 0xff, (data >> 8) & 0xff); + WARN_ON(!dsi_bus_is_locked(dsi)); - dsi_vc_config_source(dsidev, channel, DSI_VC_SOURCE_L4); + if (dsi->debug_write) + DSSDBG("dsi_vc_send_short(vc%d, dt %#x, b1 %#x, b2 %#x)\n", + vc, msg->type, pkt.header[1], pkt.header[2]); - if (FLD_GET(dsi_read_reg(dsidev, DSI_VC_CTRL(channel)), 16, 16)) { + if (FLD_GET(dsi_read_reg(dsi, DSI_VC_CTRL(vc)), 16, 16)) { DSSERR("ERROR FIFO FULL, aborting transfer\n"); return -EINVAL; } - data_id = data_type | dsi->vc[channel].vc_id << 6; + r = pkt.header[3] << 24 | pkt.header[2] << 16 | pkt.header[1] << 8 | + pkt.header[0]; - r = (data_id << 0) | (data << 8) | (ecc << 24); - - dsi_write_reg(dsidev, DSI_VC_SHORT_PACKET_HEADER(channel), r); + dsi_write_reg(dsi, DSI_VC_SHORT_PACKET_HEADER(vc), r); return 0; } -static int dsi_vc_send_null(struct omap_dss_device *dssdev, int channel) +static int dsi_vc_send_null(struct dsi_data *dsi, int vc, int channel) { - struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); - - return dsi_vc_send_long(dsidev, channel, MIPI_DSI_NULL_PACKET, NULL, - 0, 0); -} - -static int dsi_vc_write_nosync_common(struct platform_device *dsidev, - int channel, u8 *data, int len, enum dss_dsi_content_type type) -{ - int r; - - if (len == 0) { - BUG_ON(type == DSS_DSI_CONTENT_DCS); - r = dsi_vc_send_short(dsidev, channel, - MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM, 0, 0); - } else if (len == 1) { - r = dsi_vc_send_short(dsidev, channel, - type == DSS_DSI_CONTENT_GENERIC ? - MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM : - MIPI_DSI_DCS_SHORT_WRITE, data[0], 0); - } else if (len == 2) { - r = dsi_vc_send_short(dsidev, channel, - type == DSS_DSI_CONTENT_GENERIC ? - MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM : - MIPI_DSI_DCS_SHORT_WRITE_PARAM, - data[0] | (data[1] << 8), 0); - } else { - r = dsi_vc_send_long(dsidev, channel, - type == DSS_DSI_CONTENT_GENERIC ? - MIPI_DSI_GENERIC_LONG_WRITE : - MIPI_DSI_DCS_LONG_WRITE, data, len, 0); - } - - return r; -} - -static int dsi_vc_dcs_write_nosync(struct omap_dss_device *dssdev, int channel, - u8 *data, int len) -{ - struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); - - return dsi_vc_write_nosync_common(dsidev, channel, data, len, - DSS_DSI_CONTENT_DCS); -} - -static int dsi_vc_generic_write_nosync(struct omap_dss_device *dssdev, int channel, - u8 *data, int len) -{ - struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + const struct mipi_dsi_msg msg = { + .channel = channel, + .type = MIPI_DSI_NULL_PACKET, + }; - return dsi_vc_write_nosync_common(dsidev, channel, data, len, - DSS_DSI_CONTENT_GENERIC); + return dsi_vc_send_long(dsi, vc, &msg); } -static int dsi_vc_write_common(struct omap_dss_device *dssdev, int channel, - u8 *data, int len, enum dss_dsi_content_type type) +static int dsi_vc_write_common(struct omap_dss_device *dssdev, int vc, + const struct mipi_dsi_msg *msg) { - struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = to_dsi_data(dssdev); int r; - r = dsi_vc_write_nosync_common(dsidev, channel, data, len, type); - if (r) - goto err; - - r = dsi_vc_send_bta_sync(dssdev, channel); - if (r) - goto err; - - /* RX_FIFO_NOT_EMPTY */ - if (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20)) { - DSSERR("rx fifo not empty after write, dumping data:\n"); - dsi_vc_flush_receive_data(dsidev, channel); - r = -EIO; - goto err; - } - - return 0; -err: - DSSERR("dsi_vc_write_common(ch %d, cmd 0x%02x, len %d) failed\n", - channel, data[0], len); - return r; -} - -static int dsi_vc_dcs_write(struct omap_dss_device *dssdev, int channel, u8 *data, - int len) -{ - return dsi_vc_write_common(dssdev, channel, data, len, - DSS_DSI_CONTENT_DCS); -} - -static int dsi_vc_generic_write(struct omap_dss_device *dssdev, int channel, u8 *data, - int len) -{ - return dsi_vc_write_common(dssdev, channel, data, len, - DSS_DSI_CONTENT_GENERIC); -} + if (mipi_dsi_packet_format_is_short(msg->type)) + r = dsi_vc_send_short(dsi, vc, msg); + else + r = dsi_vc_send_long(dsi, vc, msg); -static int dsi_vc_dcs_send_read_request(struct platform_device *dsidev, - int channel, u8 dcs_cmd) -{ - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - int r; + if (r < 0) + return r; - if (dsi->debug_read) - DSSDBG("dsi_vc_dcs_send_read_request(ch%d, dcs_cmd %x)\n", - channel, dcs_cmd); + /* + * TODO: we do not always have to do the BTA sync, for example + * we can improve performance by setting the update window + * information without sending BTA sync between the commands. + * In that case we can return early. + */ - r = dsi_vc_send_short(dsidev, channel, MIPI_DSI_DCS_READ, dcs_cmd, 0); + r = dsi_vc_send_bta_sync(dssdev, vc); if (r) { - DSSERR("dsi_vc_dcs_send_read_request(ch %d, cmd 0x%02x)" - " failed\n", channel, dcs_cmd); + DSSERR("bta sync failed\n"); return r; } - return 0; -} - -static int dsi_vc_generic_send_read_request(struct platform_device *dsidev, - int channel, u8 *reqdata, int reqlen) -{ - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - u16 data; - u8 data_type; - int r; - - if (dsi->debug_read) - DSSDBG("dsi_vc_generic_send_read_request(ch %d, reqlen %d)\n", - channel, reqlen); - - if (reqlen == 0) { - data_type = MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM; - data = 0; - } else if (reqlen == 1) { - data_type = MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM; - data = reqdata[0]; - } else if (reqlen == 2) { - data_type = MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM; - data = reqdata[0] | (reqdata[1] << 8); - } else { - BUG(); - return -EINVAL; - } - - r = dsi_vc_send_short(dsidev, channel, data_type, data, 0); - if (r) { - DSSERR("dsi_vc_generic_send_read_request(ch %d, reqlen %d)" - " failed\n", channel, reqlen); - return r; + /* RX_FIFO_NOT_EMPTY */ + if (REG_GET(dsi, DSI_VC_CTRL(vc), 20, 20)) { + DSSERR("rx fifo not empty after write, dumping data:\n"); + dsi_vc_flush_receive_data(dsi, vc); + return -EIO; } return 0; } -static int dsi_vc_read_rx_fifo(struct platform_device *dsidev, int channel, - u8 *buf, int buflen, enum dss_dsi_content_type type) +static int dsi_vc_read_rx_fifo(struct dsi_data *dsi, int vc, u8 *buf, + int buflen, enum dss_dsi_content_type type) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); u32 val; u8 dt; int r; /* RX_FIFO_NOT_EMPTY */ - if (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20) == 0) { + if (REG_GET(dsi, DSI_VC_CTRL(vc), 20, 20) == 0) { DSSERR("RX fifo empty when trying to read.\n"); r = -EIO; goto err; } - val = dsi_read_reg(dsidev, DSI_VC_SHORT_PACKET_HEADER(channel)); + val = dsi_read_reg(dsi, DSI_VC_SHORT_PACKET_HEADER(vc)); if (dsi->debug_read) DSSDBG("\theader: %08x\n", val); dt = FLD_GET(val, 5, 0); @@ -3015,8 +2305,8 @@ static int dsi_vc_read_rx_fifo(struct platform_device *dsidev, int channel, /* two byte checksum ends the packet, not included in len */ for (w = 0; w < len + 2;) { int b; - val = dsi_read_reg(dsidev, - DSI_VC_SHORT_PACKET_HEADER(channel)); + val = dsi_read_reg(dsi, + DSI_VC_SHORT_PACKET_HEADER(vc)); if (dsi->debug_read) DSSDBG("\t\t%02x %02x %02x %02x\n", (val >> 0) & 0xff, @@ -3040,174 +2330,78 @@ static int dsi_vc_read_rx_fifo(struct platform_device *dsidev, int channel, } err: - DSSERR("dsi_vc_read_rx_fifo(ch %d type %s) failed\n", channel, + DSSERR("dsi_vc_read_rx_fifo(vc %d type %s) failed\n", vc, type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" : "DCS"); return r; } -static int dsi_vc_dcs_read(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd, - u8 *buf, int buflen) +static int dsi_vc_dcs_read(struct omap_dss_device *dssdev, int vc, + const struct mipi_dsi_msg *msg) { - struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = to_dsi_data(dssdev); + u8 cmd = ((u8 *)msg->tx_buf)[0]; int r; - r = dsi_vc_dcs_send_read_request(dsidev, channel, dcs_cmd); + if (dsi->debug_read) + DSSDBG("%s(vc %d, cmd %x)\n", __func__, vc, cmd); + + r = dsi_vc_send_short(dsi, vc, msg); if (r) goto err; - r = dsi_vc_send_bta_sync(dssdev, channel); + r = dsi_vc_send_bta_sync(dssdev, vc); if (r) goto err; - r = dsi_vc_read_rx_fifo(dsidev, channel, buf, buflen, + r = dsi_vc_read_rx_fifo(dsi, vc, msg->rx_buf, msg->rx_len, DSS_DSI_CONTENT_DCS); if (r < 0) goto err; - if (r != buflen) { + if (r != msg->rx_len) { r = -EIO; goto err; } return 0; err: - DSSERR("dsi_vc_dcs_read(ch %d, cmd 0x%02x) failed\n", channel, dcs_cmd); + DSSERR("%s(vc %d, cmd 0x%02x) failed\n", __func__, vc, cmd); return r; } -static int dsi_vc_generic_read(struct omap_dss_device *dssdev, int channel, - u8 *reqdata, int reqlen, u8 *buf, int buflen) +static int dsi_vc_generic_read(struct omap_dss_device *dssdev, int vc, + const struct mipi_dsi_msg *msg) { - struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = to_dsi_data(dssdev); int r; - r = dsi_vc_generic_send_read_request(dsidev, channel, reqdata, reqlen); + r = dsi_vc_send_short(dsi, vc, msg); if (r) - return r; + goto err; - r = dsi_vc_send_bta_sync(dssdev, channel); + r = dsi_vc_send_bta_sync(dssdev, vc); if (r) - return r; + goto err; - r = dsi_vc_read_rx_fifo(dsidev, channel, buf, buflen, + r = dsi_vc_read_rx_fifo(dsi, vc, msg->rx_buf, msg->rx_len, DSS_DSI_CONTENT_GENERIC); if (r < 0) - return r; - - if (r != buflen) { - r = -EIO; - return r; - } - - return 0; -} - -static int dsi_vc_set_max_rx_packet_size(struct omap_dss_device *dssdev, int channel, - u16 len) -{ - struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); - - return dsi_vc_send_short(dsidev, channel, - MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, len, 0); -} - -static int dsi_enter_ulps(struct platform_device *dsidev) -{ - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - DECLARE_COMPLETION_ONSTACK(completion); - int r, i; - unsigned mask; - - DSSDBG("Entering ULPS"); - - WARN_ON(!dsi_bus_is_locked(dsidev)); - - WARN_ON(dsi->ulps_enabled); - - if (dsi->ulps_enabled) - return 0; - - /* DDR_CLK_ALWAYS_ON */ - if (REG_GET(dsidev, DSI_CLK_CTRL, 13, 13)) { - 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); - dsi_sync_vc(dsidev, 1); - dsi_sync_vc(dsidev, 2); - dsi_sync_vc(dsidev, 3); - - dsi_force_tx_stop_mode_io(dsidev); - - dsi_vc_enable(dsidev, 0, false); - dsi_vc_enable(dsidev, 1, false); - dsi_vc_enable(dsidev, 2, false); - dsi_vc_enable(dsidev, 3, false); - - if (REG_GET(dsidev, DSI_COMPLEXIO_CFG2, 16, 16)) { /* HS_BUSY */ - DSSERR("HS busy when enabling ULPS\n"); - return -EIO; - } - - if (REG_GET(dsidev, DSI_COMPLEXIO_CFG2, 17, 17)) { /* LP_BUSY */ - DSSERR("LP busy when enabling ULPS\n"); - return -EIO; - } - - r = dsi_register_isr_cio(dsidev, dsi_completion_handler, &completion, - DSI_CIO_IRQ_ULPSACTIVENOT_ALL0); - 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, mask, 9, 5); - - /* flush posted write and wait for SCP interface to finish the write */ - dsi_read_reg(dsidev, DSI_COMPLEXIO_CFG2); + goto err; - if (wait_for_completion_timeout(&completion, - msecs_to_jiffies(1000)) == 0) { - DSSERR("ULPS enable timeout\n"); + if (r != msg->rx_len) { r = -EIO; goto err; } - dsi_unregister_isr_cio(dsidev, dsi_completion_handler, &completion, - DSI_CIO_IRQ_ULPSACTIVENOT_ALL0); - - /* Reset LANEx_ULPS_SIG2 */ - 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); - - dsi_if_enable(dsidev, false); - - dsi->ulps_enabled = true; - return 0; - err: - dsi_unregister_isr_cio(dsidev, dsi_completion_handler, &completion, - DSI_CIO_IRQ_ULPSACTIVENOT_ALL0); + DSSERR("%s(vc %d, reqlen %zu) failed\n", __func__, vc, msg->tx_len); return r; } -static void dsi_set_lp_rx_timeout(struct platform_device *dsidev, - unsigned ticks, bool x4, bool x16) +static void dsi_set_lp_rx_timeout(struct dsi_data *dsi, unsigned int ticks, + bool x4, bool x16) { unsigned long fck; unsigned long total_ticks; @@ -3216,14 +2410,14 @@ static void dsi_set_lp_rx_timeout(struct platform_device *dsidev, BUG_ON(ticks > 0x1fff); /* ticks in DSI_FCK */ - fck = dsi_fclk_rate(dsidev); + fck = dsi_fclk_rate(dsi); - r = dsi_read_reg(dsidev, DSI_TIMING2); + r = dsi_read_reg(dsi, DSI_TIMING2); r = FLD_MOD(r, 1, 15, 15); /* LP_RX_TO */ r = FLD_MOD(r, x16 ? 1 : 0, 14, 14); /* LP_RX_TO_X16 */ r = FLD_MOD(r, x4 ? 1 : 0, 13, 13); /* LP_RX_TO_X4 */ r = FLD_MOD(r, ticks, 12, 0); /* LP_RX_COUNTER */ - dsi_write_reg(dsidev, DSI_TIMING2, r); + dsi_write_reg(dsi, DSI_TIMING2, r); total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1); @@ -3233,8 +2427,8 @@ static void dsi_set_lp_rx_timeout(struct platform_device *dsidev, (total_ticks * 1000) / (fck / 1000 / 1000)); } -static void dsi_set_ta_timeout(struct platform_device *dsidev, unsigned ticks, - bool x8, bool x16) +static void dsi_set_ta_timeout(struct dsi_data *dsi, unsigned int ticks, + bool x8, bool x16) { unsigned long fck; unsigned long total_ticks; @@ -3243,14 +2437,14 @@ static void dsi_set_ta_timeout(struct platform_device *dsidev, unsigned ticks, BUG_ON(ticks > 0x1fff); /* ticks in DSI_FCK */ - fck = dsi_fclk_rate(dsidev); + fck = dsi_fclk_rate(dsi); - r = dsi_read_reg(dsidev, DSI_TIMING1); + r = dsi_read_reg(dsi, DSI_TIMING1); r = FLD_MOD(r, 1, 31, 31); /* TA_TO */ r = FLD_MOD(r, x16 ? 1 : 0, 30, 30); /* TA_TO_X16 */ r = FLD_MOD(r, x8 ? 1 : 0, 29, 29); /* TA_TO_X8 */ r = FLD_MOD(r, ticks, 28, 16); /* TA_TO_COUNTER */ - dsi_write_reg(dsidev, DSI_TIMING1, r); + dsi_write_reg(dsi, DSI_TIMING1, r); total_ticks = ticks * (x16 ? 16 : 1) * (x8 ? 8 : 1); @@ -3260,8 +2454,8 @@ static void dsi_set_ta_timeout(struct platform_device *dsidev, unsigned ticks, (total_ticks * 1000) / (fck / 1000 / 1000)); } -static void dsi_set_stop_state_counter(struct platform_device *dsidev, - unsigned ticks, bool x4, bool x16) +static void dsi_set_stop_state_counter(struct dsi_data *dsi, unsigned int ticks, + bool x4, bool x16) { unsigned long fck; unsigned long total_ticks; @@ -3270,14 +2464,14 @@ static void dsi_set_stop_state_counter(struct platform_device *dsidev, BUG_ON(ticks > 0x1fff); /* ticks in DSI_FCK */ - fck = dsi_fclk_rate(dsidev); + fck = dsi_fclk_rate(dsi); - r = dsi_read_reg(dsidev, DSI_TIMING1); + r = dsi_read_reg(dsi, DSI_TIMING1); r = FLD_MOD(r, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */ r = FLD_MOD(r, x16 ? 1 : 0, 14, 14); /* STOP_STATE_X16_IO */ r = FLD_MOD(r, x4 ? 1 : 0, 13, 13); /* STOP_STATE_X4_IO */ r = FLD_MOD(r, ticks, 12, 0); /* STOP_STATE_COUNTER_IO */ - dsi_write_reg(dsidev, DSI_TIMING1, r); + dsi_write_reg(dsi, DSI_TIMING1, r); total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1); @@ -3287,8 +2481,8 @@ static void dsi_set_stop_state_counter(struct platform_device *dsidev, (total_ticks * 1000) / (fck / 1000 / 1000)); } -static void dsi_set_hs_tx_timeout(struct platform_device *dsidev, - unsigned ticks, bool x4, bool x16) +static void dsi_set_hs_tx_timeout(struct dsi_data *dsi, unsigned int ticks, + bool x4, bool x16) { unsigned long fck; unsigned long total_ticks; @@ -3297,14 +2491,14 @@ static void dsi_set_hs_tx_timeout(struct platform_device *dsidev, BUG_ON(ticks > 0x1fff); /* ticks in TxByteClkHS */ - fck = dsi_get_txbyteclkhs(dsidev); + fck = dsi_get_txbyteclkhs(dsi); - r = dsi_read_reg(dsidev, DSI_TIMING2); + r = dsi_read_reg(dsi, DSI_TIMING2); r = FLD_MOD(r, 1, 31, 31); /* HS_TX_TO */ r = FLD_MOD(r, x16 ? 1 : 0, 30, 30); /* HS_TX_TO_X16 */ r = FLD_MOD(r, x4 ? 1 : 0, 29, 29); /* HS_TX_TO_X8 (4 really) */ r = FLD_MOD(r, ticks, 28, 16); /* HS_TX_TO_COUNTER */ - dsi_write_reg(dsidev, DSI_TIMING2, r); + dsi_write_reg(dsi, DSI_TIMING2, r); total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1); @@ -3314,14 +2508,13 @@ static void dsi_set_hs_tx_timeout(struct platform_device *dsidev, (total_ticks * 1000) / (fck / 1000 / 1000)); } -static void dsi_config_vp_num_line_buffers(struct platform_device *dsidev) +static void dsi_config_vp_num_line_buffers(struct dsi_data *dsi) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); int num_line_buffers; if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) { - int bpp = dsi_get_pixel_size(dsi->pix_fmt); - struct videomode *vm = &dsi->vm; + int bpp = mipi_dsi_pixel_format_to_bpp(dsi->pix_fmt); + const struct videomode *vm = &dsi->vm; /* * Don't use line buffers if width is greater than the video * port's line buffer size @@ -3336,12 +2529,11 @@ static void dsi_config_vp_num_line_buffers(struct platform_device *dsidev) } /* LINE_BUFFER */ - REG_FLD_MOD(dsidev, DSI_CTRL, num_line_buffers, 13, 12); + REG_FLD_MOD(dsi, DSI_CTRL, num_line_buffers, 13, 12); } -static void dsi_config_vp_sync_events(struct platform_device *dsidev) +static void dsi_config_vp_sync_events(struct dsi_data *dsi) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); bool sync_end; u32 r; @@ -3350,7 +2542,7 @@ static void dsi_config_vp_sync_events(struct platform_device *dsidev) else sync_end = false; - r = dsi_read_reg(dsidev, DSI_CTRL); + r = dsi_read_reg(dsi, DSI_CTRL); r = FLD_MOD(r, 1, 9, 9); /* VP_DE_POL */ r = FLD_MOD(r, 1, 10, 10); /* VP_HSYNC_POL */ r = FLD_MOD(r, 1, 11, 11); /* VP_VSYNC_POL */ @@ -3358,12 +2550,11 @@ static void dsi_config_vp_sync_events(struct platform_device *dsidev) r = FLD_MOD(r, sync_end, 16, 16); /* VP_VSYNC_END */ r = FLD_MOD(r, 1, 17, 17); /* VP_HSYNC_START */ r = FLD_MOD(r, sync_end, 18, 18); /* VP_HSYNC_END */ - dsi_write_reg(dsidev, DSI_CTRL, r); + dsi_write_reg(dsi, DSI_CTRL, r); } -static void dsi_config_blanking_modes(struct platform_device *dsidev) +static void dsi_config_blanking_modes(struct dsi_data *dsi) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); int blanking_mode = dsi->vm_timings.blanking_mode; int hfp_blanking_mode = dsi->vm_timings.hfp_blanking_mode; int hbp_blanking_mode = dsi->vm_timings.hbp_blanking_mode; @@ -3374,12 +2565,12 @@ static void dsi_config_blanking_modes(struct platform_device *dsidev) * 0 = TX FIFO packets sent or LPS in corresponding blanking periods * 1 = Long blanking packets are sent in corresponding blanking periods */ - r = dsi_read_reg(dsidev, DSI_CTRL); + r = dsi_read_reg(dsi, DSI_CTRL); r = FLD_MOD(r, blanking_mode, 20, 20); /* BLANKING_MODE */ r = FLD_MOD(r, hfp_blanking_mode, 21, 21); /* HFP_BLANKING */ r = FLD_MOD(r, hbp_blanking_mode, 22, 22); /* HBP_BLANKING */ r = FLD_MOD(r, hsa_blanking_mode, 23, 23); /* HSA_BLANKING */ - dsi_write_reg(dsidev, DSI_CTRL, r); + dsi_write_reg(dsi, DSI_CTRL, r); } /* @@ -3444,17 +2635,16 @@ static int dsi_compute_interleave_lp(int blank, int enter_hs, int exit_hs, return max(lp_inter, 0); } -static void dsi_config_cmd_mode_interleaving(struct platform_device *dsidev) +static void dsi_config_cmd_mode_interleaving(struct dsi_data *dsi) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); int blanking_mode; int hfp_blanking_mode, hbp_blanking_mode, hsa_blanking_mode; int hsa, hfp, hbp, width_bytes, bllp, lp_clk_div; int ddr_clk_pre, ddr_clk_post, enter_hs_mode_lat, exit_hs_mode_lat; int tclk_trail, ths_exit, exiths_clk; bool ddr_alwon; - struct videomode *vm = &dsi->vm; - int bpp = dsi_get_pixel_size(dsi->pix_fmt); + const struct videomode *vm = &dsi->vm; + int bpp = mipi_dsi_pixel_format_to_bpp(dsi->pix_fmt); int ndl = dsi->num_lanes_used - 1; int dsi_fclk_hsdiv = dsi->user_dsi_cinfo.mX[HSDIV_DSI] + 1; int hsa_interleave_hs = 0, hsa_interleave_lp = 0; @@ -3463,33 +2653,33 @@ static void dsi_config_cmd_mode_interleaving(struct platform_device *dsidev) int bl_interleave_hs = 0, bl_interleave_lp = 0; u32 r; - r = dsi_read_reg(dsidev, DSI_CTRL); + r = dsi_read_reg(dsi, DSI_CTRL); blanking_mode = FLD_GET(r, 20, 20); hfp_blanking_mode = FLD_GET(r, 21, 21); hbp_blanking_mode = FLD_GET(r, 22, 22); hsa_blanking_mode = FLD_GET(r, 23, 23); - r = dsi_read_reg(dsidev, DSI_VM_TIMING1); + r = dsi_read_reg(dsi, DSI_VM_TIMING1); hbp = FLD_GET(r, 11, 0); hfp = FLD_GET(r, 23, 12); hsa = FLD_GET(r, 31, 24); - r = dsi_read_reg(dsidev, DSI_CLK_TIMING); + r = dsi_read_reg(dsi, DSI_CLK_TIMING); ddr_clk_post = FLD_GET(r, 7, 0); ddr_clk_pre = FLD_GET(r, 15, 8); - r = dsi_read_reg(dsidev, DSI_VM_TIMING7); + r = dsi_read_reg(dsi, DSI_VM_TIMING7); exit_hs_mode_lat = FLD_GET(r, 15, 0); enter_hs_mode_lat = FLD_GET(r, 31, 16); - r = dsi_read_reg(dsidev, DSI_CLK_CTRL); + r = dsi_read_reg(dsi, DSI_CLK_CTRL); lp_clk_div = FLD_GET(r, 12, 0); ddr_alwon = FLD_GET(r, 13, 13); - r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG0); + r = dsi_read_reg(dsi, DSI_DSIPHY_CFG0); ths_exit = FLD_GET(r, 7, 0); - r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG1); + r = dsi_read_reg(dsi, DSI_DSIPHY_CFG1); tclk_trail = FLD_GET(r, 15, 8); exiths_clk = ths_exit + tclk_trail; @@ -3543,47 +2733,46 @@ static void dsi_config_cmd_mode_interleaving(struct platform_device *dsidev) hsa_interleave_lp, hfp_interleave_lp, hbp_interleave_lp, bl_interleave_lp); - r = dsi_read_reg(dsidev, DSI_VM_TIMING4); + r = dsi_read_reg(dsi, DSI_VM_TIMING4); r = FLD_MOD(r, hsa_interleave_hs, 23, 16); r = FLD_MOD(r, hfp_interleave_hs, 15, 8); r = FLD_MOD(r, hbp_interleave_hs, 7, 0); - dsi_write_reg(dsidev, DSI_VM_TIMING4, r); + dsi_write_reg(dsi, DSI_VM_TIMING4, r); - r = dsi_read_reg(dsidev, DSI_VM_TIMING5); + r = dsi_read_reg(dsi, DSI_VM_TIMING5); r = FLD_MOD(r, hsa_interleave_lp, 23, 16); r = FLD_MOD(r, hfp_interleave_lp, 15, 8); r = FLD_MOD(r, hbp_interleave_lp, 7, 0); - dsi_write_reg(dsidev, DSI_VM_TIMING5, r); + dsi_write_reg(dsi, DSI_VM_TIMING5, r); - r = dsi_read_reg(dsidev, DSI_VM_TIMING6); + r = dsi_read_reg(dsi, DSI_VM_TIMING6); r = FLD_MOD(r, bl_interleave_hs, 31, 15); r = FLD_MOD(r, bl_interleave_lp, 16, 0); - dsi_write_reg(dsidev, DSI_VM_TIMING6, r); + dsi_write_reg(dsi, DSI_VM_TIMING6, r); } -static int dsi_proto_config(struct platform_device *dsidev) +static int dsi_proto_config(struct dsi_data *dsi) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); u32 r; int buswidth = 0; - dsi_config_tx_fifo(dsidev, DSI_FIFO_SIZE_32, + dsi_config_tx_fifo(dsi, DSI_FIFO_SIZE_32, DSI_FIFO_SIZE_32, DSI_FIFO_SIZE_32, DSI_FIFO_SIZE_32); - dsi_config_rx_fifo(dsidev, DSI_FIFO_SIZE_32, + dsi_config_rx_fifo(dsi, DSI_FIFO_SIZE_32, DSI_FIFO_SIZE_32, DSI_FIFO_SIZE_32, DSI_FIFO_SIZE_32); /* XXX what values for the timeouts? */ - dsi_set_stop_state_counter(dsidev, 0x1000, false, false); - dsi_set_ta_timeout(dsidev, 0x1fff, true, true); - dsi_set_lp_rx_timeout(dsidev, 0x1fff, true, true); - dsi_set_hs_tx_timeout(dsidev, 0x1fff, true, true); + dsi_set_stop_state_counter(dsi, 0x1000, false, false); + dsi_set_ta_timeout(dsi, 0x1fff, true, true); + dsi_set_lp_rx_timeout(dsi, 0x1fff, true, true); + dsi_set_hs_tx_timeout(dsi, 0x1fff, true, true); - switch (dsi_get_pixel_size(dsi->pix_fmt)) { + switch (mipi_dsi_pixel_format_to_bpp(dsi->pix_fmt)) { case 16: buswidth = 0; break; @@ -3598,7 +2787,7 @@ static int dsi_proto_config(struct platform_device *dsidev) return -EINVAL; } - r = dsi_read_reg(dsidev, DSI_CTRL); + r = dsi_read_reg(dsi, DSI_CTRL); r = FLD_MOD(r, 1, 1, 1); /* CS_RX_EN */ r = FLD_MOD(r, 1, 2, 2); /* ECC_RX_EN */ r = FLD_MOD(r, 1, 3, 3); /* TX_FIFO_ARBITRATION */ @@ -3607,62 +2796,60 @@ static int dsi_proto_config(struct platform_device *dsidev) r = FLD_MOD(r, 0, 8, 8); /* VP_CLK_POL */ r = FLD_MOD(r, 1, 14, 14); /* TRIGGER_RESET_MODE */ r = FLD_MOD(r, 1, 19, 19); /* EOT_ENABLE */ - if (!dss_has_feature(FEAT_DSI_DCS_CMD_CONFIG_VC)) { + if (!(dsi->data->quirks & DSI_QUIRK_DCS_CMD_CONFIG_VC)) { r = FLD_MOD(r, 1, 24, 24); /* DCS_CMD_ENABLE */ /* DCS_CMD_CODE, 1=start, 0=continue */ r = FLD_MOD(r, 0, 25, 25); } - dsi_write_reg(dsidev, DSI_CTRL, r); + dsi_write_reg(dsi, DSI_CTRL, r); - dsi_config_vp_num_line_buffers(dsidev); + dsi_config_vp_num_line_buffers(dsi); if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) { - dsi_config_vp_sync_events(dsidev); - dsi_config_blanking_modes(dsidev); - dsi_config_cmd_mode_interleaving(dsidev); + dsi_config_vp_sync_events(dsi); + dsi_config_blanking_modes(dsi); + dsi_config_cmd_mode_interleaving(dsi); } - dsi_vc_initial_config(dsidev, 0); - dsi_vc_initial_config(dsidev, 1); - dsi_vc_initial_config(dsidev, 2); - dsi_vc_initial_config(dsidev, 3); + dsi_vc_initial_config(dsi, 0); + dsi_vc_initial_config(dsi, 1); + dsi_vc_initial_config(dsi, 2); + dsi_vc_initial_config(dsi, 3); return 0; } -static void dsi_proto_timings(struct platform_device *dsidev) +static void dsi_proto_timings(struct dsi_data *dsi) { - 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; - unsigned ths_trail, ths_exit; - unsigned ddr_clk_pre, ddr_clk_post; - unsigned enter_hs_mode_lat, exit_hs_mode_lat; - unsigned ths_eot; + unsigned int tlpx, tclk_zero, tclk_prepare; + unsigned int tclk_pre, tclk_post; + unsigned int ths_prepare, ths_prepare_ths_zero, ths_zero; + unsigned int ths_trail, ths_exit; + unsigned int ddr_clk_pre, ddr_clk_post; + unsigned int enter_hs_mode_lat, exit_hs_mode_lat; + unsigned int ths_eot; int ndl = dsi->num_lanes_used - 1; u32 r; - r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG0); + r = dsi_read_reg(dsi, DSI_DSIPHY_CFG0); ths_prepare = FLD_GET(r, 31, 24); ths_prepare_ths_zero = FLD_GET(r, 23, 16); ths_zero = ths_prepare_ths_zero - ths_prepare; ths_trail = FLD_GET(r, 15, 8); ths_exit = FLD_GET(r, 7, 0); - r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG1); + r = dsi_read_reg(dsi, DSI_DSIPHY_CFG1); tlpx = FLD_GET(r, 20, 16) * 2; - tclk_trail = FLD_GET(r, 15, 8); tclk_zero = FLD_GET(r, 7, 0); - r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG2); + r = dsi_read_reg(dsi, DSI_DSIPHY_CFG2); tclk_prepare = FLD_GET(r, 7, 0); /* min 8*UI */ tclk_pre = 20; /* min 60ns + 52*UI */ - tclk_post = ns2ddr(dsidev, 60) + 26; + tclk_post = ns2ddr(dsi, 60) + 26; ths_eot = DIV_ROUND_UP(4, ndl); @@ -3673,10 +2860,10 @@ static void dsi_proto_timings(struct platform_device *dsidev) BUG_ON(ddr_clk_pre == 0 || ddr_clk_pre > 255); BUG_ON(ddr_clk_post == 0 || ddr_clk_post > 255); - r = dsi_read_reg(dsidev, DSI_CLK_TIMING); + r = dsi_read_reg(dsi, DSI_CLK_TIMING); r = FLD_MOD(r, ddr_clk_pre, 15, 8); r = FLD_MOD(r, ddr_clk_post, 7, 0); - dsi_write_reg(dsidev, DSI_CLK_TIMING, r); + dsi_write_reg(dsi, DSI_CLK_TIMING, r); DSSDBG("ddr_clk_pre %u, ddr_clk_post %u\n", ddr_clk_pre, @@ -3690,7 +2877,7 @@ static void dsi_proto_timings(struct platform_device *dsidev) r = FLD_VAL(enter_hs_mode_lat, 31, 16) | FLD_VAL(exit_hs_mode_lat, 15, 0); - dsi_write_reg(dsidev, DSI_VM_TIMING7, r); + dsi_write_reg(dsi, DSI_VM_TIMING7, r); DSSDBG("enter_hs_mode_lat %u, exit_hs_mode_lat %u\n", enter_hs_mode_lat, exit_hs_mode_lat); @@ -3705,8 +2892,8 @@ static void dsi_proto_timings(struct platform_device *dsidev) int vbp = dsi->vm_timings.vbp; int window_sync = dsi->vm_timings.window_sync; bool hsync_end; - struct videomode *vm = &dsi->vm; - int bpp = dsi_get_pixel_size(dsi->pix_fmt); + const struct videomode *vm = &dsi->vm; + int bpp = mipi_dsi_pixel_format_to_bpp(dsi->pix_fmt); int tl, t_he, width_bytes; hsync_end = dsi->vm_timings.trans_mode == OMAP_DSS_DSI_PULSE_MODE; @@ -3724,33 +2911,29 @@ static void dsi_proto_timings(struct platform_device *dsidev) DSSDBG("VBP: %d, VFP: %d, VSA: %d, VACT: %d lines\n", vbp, vfp, vsa, vm->vactive); - r = dsi_read_reg(dsidev, DSI_VM_TIMING1); + r = dsi_read_reg(dsi, DSI_VM_TIMING1); r = FLD_MOD(r, hbp, 11, 0); /* HBP */ r = FLD_MOD(r, hfp, 23, 12); /* HFP */ r = FLD_MOD(r, hsync_end ? hsa : 0, 31, 24); /* HSA */ - dsi_write_reg(dsidev, DSI_VM_TIMING1, r); + dsi_write_reg(dsi, DSI_VM_TIMING1, r); - r = dsi_read_reg(dsidev, DSI_VM_TIMING2); + r = dsi_read_reg(dsi, DSI_VM_TIMING2); r = FLD_MOD(r, vbp, 7, 0); /* VBP */ r = FLD_MOD(r, vfp, 15, 8); /* VFP */ r = FLD_MOD(r, vsa, 23, 16); /* VSA */ r = FLD_MOD(r, window_sync, 27, 24); /* WINDOW_SYNC */ - dsi_write_reg(dsidev, DSI_VM_TIMING2, r); + dsi_write_reg(dsi, DSI_VM_TIMING2, r); - r = dsi_read_reg(dsidev, DSI_VM_TIMING3); + r = dsi_read_reg(dsi, DSI_VM_TIMING3); r = FLD_MOD(r, vm->vactive, 14, 0); /* VACT */ r = FLD_MOD(r, tl, 31, 16); /* TL */ - dsi_write_reg(dsidev, DSI_VM_TIMING3, r); + dsi_write_reg(dsi, DSI_VM_TIMING3, r); } } -static int dsi_configure_pins(struct omap_dss_device *dssdev, - const struct omap_dsi_pin_config *pin_cfg) +static int dsi_configure_pins(struct dsi_data *dsi, + int num_pins, const u32 *pins) { - struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - int num_pins; - const int *pins; struct dsi_lane_config lanes[DSI_MAX_NR_LANES]; int num_lanes; int i; @@ -3763,9 +2946,6 @@ static int dsi_configure_pins(struct omap_dss_device *dssdev, DSI_LANE_DATA4, }; - num_pins = pin_cfg->num_pins; - pins = pin_cfg->pins; - if (num_pins < 4 || num_pins > dsi->num_lanes_supported * 2 || num_pins % 2 != 0) return -EINVAL; @@ -3777,15 +2957,15 @@ static int dsi_configure_pins(struct omap_dss_device *dssdev, for (i = 0; i < num_pins; i += 2) { u8 lane, pol; - int dx, dy; + u32 dx, dy; dx = pins[i]; dy = pins[i + 1]; - if (dx < 0 || dx >= dsi->num_lanes_supported * 2) + if (dx >= dsi->num_lanes_supported * 2) return -EINVAL; - if (dy < 0 || dy >= dsi->num_lanes_supported * 2) + if (dy >= dsi->num_lanes_supported * 2) return -EINVAL; if (dx & 1) { @@ -3811,121 +2991,122 @@ static int dsi_configure_pins(struct omap_dss_device *dssdev, return 0; } -static int dsi_enable_video_output(struct omap_dss_device *dssdev, int channel) +static int dsi_enable_video_mode(struct dsi_data *dsi, int vc) { - struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - enum omap_channel dispc_channel = dssdev->dispc_channel; - int bpp = dsi_get_pixel_size(dsi->pix_fmt); - struct omap_dss_device *out = &dsi->output; + int bpp = mipi_dsi_pixel_format_to_bpp(dsi->pix_fmt); u8 data_type; u16 word_count; - int r; - if (!out->dispc_channel_connected) { - DSSERR("failed to enable display: no output/manager\n"); - return -ENODEV; + switch (dsi->pix_fmt) { + case MIPI_DSI_FMT_RGB888: + data_type = MIPI_DSI_PACKED_PIXEL_STREAM_24; + break; + case MIPI_DSI_FMT_RGB666: + data_type = MIPI_DSI_PIXEL_STREAM_3BYTE_18; + break; + case MIPI_DSI_FMT_RGB666_PACKED: + data_type = MIPI_DSI_PACKED_PIXEL_STREAM_18; + break; + case MIPI_DSI_FMT_RGB565: + data_type = MIPI_DSI_PACKED_PIXEL_STREAM_16; + break; + default: + return -EINVAL; } - r = dsi_display_init_dispc(dsidev, dispc_channel); - if (r) - goto err_init_dispc; + dsi_if_enable(dsi, false); + dsi_vc_enable(dsi, vc, false); - if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) { - switch (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: - r = -EINVAL; - goto err_pix_fmt; - } + /* MODE, 1 = video mode */ + REG_FLD_MOD(dsi, DSI_VC_CTRL(vc), 1, 4, 4); - dsi_if_enable(dsidev, false); - dsi_vc_enable(dsidev, channel, false); + word_count = DIV_ROUND_UP(dsi->vm.hactive * bpp, 8); - /* MODE, 1 = video mode */ - REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 1, 4, 4); + dsi_vc_write_long_header(dsi, vc, dsi->dsidev->channel, data_type, + word_count, 0); - word_count = DIV_ROUND_UP(dsi->vm.hactive * bpp, 8); + dsi_vc_enable(dsi, vc, true); + dsi_if_enable(dsi, true); - dsi_vc_write_long_header(dsidev, channel, data_type, - word_count, 0); + return 0; +} + +static void dsi_disable_video_mode(struct dsi_data *dsi, int vc) +{ + dsi_if_enable(dsi, false); + dsi_vc_enable(dsi, vc, false); + + /* MODE, 0 = command mode */ + REG_FLD_MOD(dsi, DSI_VC_CTRL(vc), 0, 4, 4); + + dsi_vc_enable(dsi, vc, true); + dsi_if_enable(dsi, true); +} + +static void dsi_enable_video_output(struct omap_dss_device *dssdev, int vc) +{ + struct dsi_data *dsi = to_dsi_data(dssdev); + int r; + + r = dsi_init_dispc(dsi); + if (r) { + dev_err(dsi->dev, "failed to init dispc!\n"); + return; + } - dsi_vc_enable(dsidev, channel, true); - dsi_if_enable(dsidev, true); + if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) { + r = dsi_enable_video_mode(dsi, vc); + if (r) + goto err_video_mode; } - r = dss_mgr_enable(dispc_channel); + r = dss_mgr_enable(&dsi->output); if (r) goto err_mgr_enable; - return 0; + return; err_mgr_enable: if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) { - dsi_if_enable(dsidev, false); - dsi_vc_enable(dsidev, channel, false); + dsi_if_enable(dsi, false); + dsi_vc_enable(dsi, vc, false); } -err_pix_fmt: - dsi_display_uninit_dispc(dsidev, dispc_channel); -err_init_dispc: - return r; +err_video_mode: + dsi_uninit_dispc(dsi); + dev_err(dsi->dev, "failed to enable DSI encoder!\n"); + return; } -static void dsi_disable_video_output(struct omap_dss_device *dssdev, int channel) +static void dsi_disable_video_output(struct omap_dss_device *dssdev, int vc) { - struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - enum omap_channel dispc_channel = dssdev->dispc_channel; - - if (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); + struct dsi_data *dsi = to_dsi_data(dssdev); - dsi_vc_enable(dsidev, channel, true); - dsi_if_enable(dsidev, true); - } + if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) + dsi_disable_video_mode(dsi, vc); - dss_mgr_disable(dispc_channel); + dss_mgr_disable(&dsi->output); - dsi_display_uninit_dispc(dsidev, dispc_channel); + dsi_uninit_dispc(dsi); } -static void dsi_update_screen_dispc(struct platform_device *dsidev) +static void dsi_update_screen_dispc(struct dsi_data *dsi) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - enum omap_channel dispc_channel = dsi->output.dispc_channel; - unsigned bytespp; - unsigned bytespl; - unsigned bytespf; - unsigned total_len; - unsigned packet_payload; - unsigned packet_len; + unsigned int bytespp; + unsigned int bytespl; + unsigned int bytespf; + unsigned int total_len; + unsigned int packet_payload; + unsigned int packet_len; u32 l; int r; - const unsigned channel = dsi->update_channel; - const unsigned line_buf_size = dsi->line_buffer_size; + const unsigned vc = dsi->update_vc; + const unsigned int line_buf_size = dsi->line_buffer_size; u16 w = dsi->vm.hactive; u16 h = dsi->vm.vactive; DSSDBG("dsi_update_screen_dispc(%dx%d)\n", w, h); - dsi_vc_config_source(dsidev, channel, DSI_VC_SOURCE_VP); - - bytespp = dsi_get_pixel_size(dsi->pix_fmt) / 8; + bytespp = mipi_dsi_pixel_format_to_bpp(dsi->pix_fmt) / 8; bytespl = w * bytespp; bytespf = bytespl * h; @@ -3944,16 +3125,16 @@ static void dsi_update_screen_dispc(struct platform_device *dsidev) total_len += (bytespf % packet_payload) + 1; l = FLD_VAL(total_len, 23, 0); /* TE_SIZE */ - dsi_write_reg(dsidev, DSI_VC_TE(channel), l); + dsi_write_reg(dsi, DSI_VC_TE(vc), l); - dsi_vc_write_long_header(dsidev, channel, MIPI_DSI_DCS_LONG_WRITE, + dsi_vc_write_long_header(dsi, vc, dsi->dsidev->channel, MIPI_DSI_DCS_LONG_WRITE, packet_len, 0); if (dsi->te_enabled) l = FLD_MOD(l, 1, 30, 30); /* TE_EN */ else l = FLD_MOD(l, 1, 31, 31); /* TE_START */ - dsi_write_reg(dsidev, DSI_VC_TE(channel), l); + dsi_write_reg(dsi, DSI_VC_TE(vc), l); /* We put SIDLEMODE to no-idle for the duration of the transfer, * because DSS interrupts are not capable of waking up the CPU and the @@ -3961,24 +3142,22 @@ static void dsi_update_screen_dispc(struct platform_device *dsidev) * the same goes for any DSS interrupts, but for some reason I have not * seen the problem anywhere else than here. */ - dispc_disable_sidle(); + dispc_disable_sidle(dsi->dss->dispc); - dsi_perf_mark_start(dsidev); + dsi_perf_mark_start(dsi); r = schedule_delayed_work(&dsi->framedone_timeout_work, msecs_to_jiffies(250)); BUG_ON(r == 0); - dss_mgr_set_timings(dispc_channel, &dsi->vm); - - dss_mgr_start_update(dispc_channel); + dss_mgr_start_update(&dsi->output); if (dsi->te_enabled) { /* disable LP_RX_TO, so that we can receive TE. Time to wait * for TE is longer than the timer allows */ - REG_FLD_MOD(dsidev, DSI_TIMING2, 0, 15, 15); /* LP_RX_TO */ + REG_FLD_MOD(dsi, DSI_TIMING2, 0, 15, 15); /* LP_RX_TO */ - dsi_vc_send_bta(dsidev, channel); + dsi_vc_send_bta(dsi, vc); #ifdef DSI_CATCH_MISSING_TE mod_timer(&dsi->te_timer, jiffies + msecs_to_jiffies(250)); @@ -3987,28 +3166,26 @@ static void dsi_update_screen_dispc(struct platform_device *dsidev) } #ifdef DSI_CATCH_MISSING_TE -static void dsi_te_timeout(unsigned long arg) +static void dsi_te_timeout(struct timer_list *unused) { DSSERR("TE not received for 250ms!\n"); } #endif -static void dsi_handle_framedone(struct platform_device *dsidev, int error) +static void dsi_handle_framedone(struct dsi_data *dsi, int error) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - /* SIDLEMODE back to smart-idle */ - dispc_enable_sidle(); + dispc_enable_sidle(dsi->dss->dispc); if (dsi->te_enabled) { /* enable LP_RX_TO again after the TE */ - REG_FLD_MOD(dsidev, DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */ + REG_FLD_MOD(dsi, DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */ } - dsi->framedone_callback(error, dsi->framedone_data); + dsi_bus_unlock(dsi); if (!error) - dsi_perf_show(dsidev, "DISPC"); + dsi_perf_show(dsi, "DISPC"); } static void dsi_framedone_timeout_work_callback(struct work_struct *work) @@ -4024,13 +3201,12 @@ static void dsi_framedone_timeout_work_callback(struct work_struct *work) DSSERR("Framedone not received for 250ms!\n"); - dsi_handle_framedone(dsi->pdev, -ETIMEDOUT); + dsi_handle_framedone(dsi, -ETIMEDOUT); } static void dsi_framedone_irq_callback(void *data) { - struct platform_device *dsidev = (struct platform_device *) data; - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + struct dsi_data *dsi = data; /* Note: We get FRAMEDONE when DISPC has finished sending pixels and * turns itself off. However, DSI still has the pixels in its buffers, @@ -4039,50 +3215,105 @@ static void dsi_framedone_irq_callback(void *data) cancel_delayed_work(&dsi->framedone_timeout_work); - dsi_handle_framedone(dsidev, 0); + DSSDBG("Framedone received!\n"); + + dsi_handle_framedone(dsi, 0); } -static int dsi_update(struct omap_dss_device *dssdev, int channel, - void (*callback)(int, void *), void *data) +static int _dsi_update(struct dsi_data *dsi) { - struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - u16 dw, dh; + dsi_perf_mark_setup(dsi); - dsi_perf_mark_setup(dsidev); +#ifdef DSI_PERF_MEASURE + dsi->update_bytes = dsi->vm.hactive * dsi->vm.vactive * + mipi_dsi_pixel_format_to_bpp(dsi->pix_fmt) / 8; +#endif + dsi_update_screen_dispc(dsi); - dsi->update_channel = channel; + return 0; +} - dsi->framedone_callback = callback; - dsi->framedone_data = data; +static int _dsi_send_nop(struct dsi_data *dsi, int vc, int channel) +{ + const u8 payload[] = { MIPI_DCS_NOP }; + const struct mipi_dsi_msg msg = { + .channel = channel, + .type = MIPI_DSI_DCS_SHORT_WRITE, + .tx_len = 1, + .tx_buf = payload, + }; - dw = dsi->vm.hactive; - dh = dsi->vm.vactive; + WARN_ON(!dsi_bus_is_locked(dsi)); -#ifdef DSI_PERF_MEASURE - dsi->update_bytes = dw * dh * - dsi_get_pixel_size(dsi->pix_fmt) / 8; -#endif - dsi_update_screen_dispc(dsidev); + return _omap_dsi_host_transfer(dsi, vc, &msg); +} + +static int dsi_update_channel(struct omap_dss_device *dssdev, int vc) +{ + struct dsi_data *dsi = to_dsi_data(dssdev); + int r; + + dsi_bus_lock(dsi); + + if (!dsi->video_enabled) { + r = -EIO; + goto err; + } + + if (dsi->vm.hactive == 0 || dsi->vm.vactive == 0) { + r = -EINVAL; + goto err; + } + + DSSDBG("dsi_update_channel: %d", vc); + + /* + * Send NOP between the frames. If we don't send something here, the + * updates stop working. This is probably related to DSI spec stating + * that the DSI host should transition to LP at least once per frame. + */ + r = _dsi_send_nop(dsi, VC_CMD, dsi->dsidev->channel); + if (r < 0) { + DSSWARN("failed to send nop between frames: %d\n", r); + goto err; + } + + dsi->update_vc = vc; + + if (dsi->te_enabled && dsi->te_gpio) { + schedule_delayed_work(&dsi->te_timeout_work, + msecs_to_jiffies(250)); + atomic_set(&dsi->do_ext_te_update, 1); + } else { + _dsi_update(dsi); + } return 0; + +err: + dsi_bus_unlock(dsi); + return r; +} + +static int dsi_update_all(struct omap_dss_device *dssdev) +{ + return dsi_update_channel(dssdev, VC_VIDEO); } /* Display funcs */ -static int dsi_configure_dispc_clocks(struct platform_device *dsidev) +static int dsi_configure_dispc_clocks(struct dsi_data *dsi) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); struct dispc_clock_info dispc_cinfo; int r; unsigned long fck; - fck = dsi_get_pll_hsdiv_dispc_rate(dsidev); + fck = dsi_get_pll_hsdiv_dispc_rate(dsi); dispc_cinfo.lck_div = dsi->user_dispc_cinfo.lck_div; dispc_cinfo.pck_div = dsi->user_dispc_cinfo.pck_div; - r = dispc_calc_clock_rates(fck, &dispc_cinfo); + r = dispc_calc_clock_rates(dsi->dss->dispc, fck, &dispc_cinfo); if (r) { DSSERR("Failed to calc dispc clocks\n"); return r; @@ -4093,19 +3324,18 @@ static int dsi_configure_dispc_clocks(struct platform_device *dsidev) return 0; } -static int dsi_display_init_dispc(struct platform_device *dsidev, - enum omap_channel channel) +static int dsi_init_dispc(struct dsi_data *dsi) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + enum omap_channel dispc_channel = dsi->output.dispc_channel; int r; - dss_select_lcd_clk_source(channel, dsi->module_id == 0 ? + dss_select_lcd_clk_source(dsi->dss, dispc_channel, dsi->module_id == 0 ? DSS_CLK_SRC_PLL1_1 : DSS_CLK_SRC_PLL2_1); if (dsi->mode == OMAP_DSS_DSI_CMD_MODE) { - r = dss_mgr_register_framedone_handler(channel, - dsi_framedone_irq_callback, dsidev); + r = dss_mgr_register_framedone_handler(&dsi->output, + dsi_framedone_irq_callback, dsi); if (r) { DSSERR("can't register FRAMEDONE handler\n"); goto err; @@ -4118,60 +3348,40 @@ static int dsi_display_init_dispc(struct platform_device *dsidev, dsi->mgr_config.fifohandcheck = false; } - /* - * override interlace, logic level and edge related parameters in - * videomode with default values - */ - dsi->vm.flags &= ~DISPLAY_FLAGS_INTERLACED; - dsi->vm.flags &= ~DISPLAY_FLAGS_HSYNC_LOW; - dsi->vm.flags |= DISPLAY_FLAGS_HSYNC_HIGH; - dsi->vm.flags &= ~DISPLAY_FLAGS_VSYNC_LOW; - dsi->vm.flags |= DISPLAY_FLAGS_VSYNC_HIGH; - dsi->vm.flags &= ~DISPLAY_FLAGS_PIXDATA_NEGEDGE; - dsi->vm.flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE; - dsi->vm.flags &= ~DISPLAY_FLAGS_DE_LOW; - dsi->vm.flags |= DISPLAY_FLAGS_DE_HIGH; - dsi->vm.flags &= ~DISPLAY_FLAGS_SYNC_POSEDGE; - dsi->vm.flags |= DISPLAY_FLAGS_SYNC_NEGEDGE; - - dss_mgr_set_timings(channel, &dsi->vm); - - r = dsi_configure_dispc_clocks(dsidev); + r = dsi_configure_dispc_clocks(dsi); if (r) goto err1; dsi->mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS; dsi->mgr_config.video_port_width = - dsi_get_pixel_size(dsi->pix_fmt); + mipi_dsi_pixel_format_to_bpp(dsi->pix_fmt); dsi->mgr_config.lcden_sig_polarity = 0; - dss_mgr_set_lcd_config(channel, &dsi->mgr_config); + dss_mgr_set_lcd_config(&dsi->output, &dsi->mgr_config); return 0; err1: if (dsi->mode == OMAP_DSS_DSI_CMD_MODE) - dss_mgr_unregister_framedone_handler(channel, - dsi_framedone_irq_callback, dsidev); + dss_mgr_unregister_framedone_handler(&dsi->output, + dsi_framedone_irq_callback, dsi); err: - dss_select_lcd_clk_source(channel, DSS_CLK_SRC_FCK); + dss_select_lcd_clk_source(dsi->dss, dispc_channel, DSS_CLK_SRC_FCK); return r; } -static void dsi_display_uninit_dispc(struct platform_device *dsidev, - enum omap_channel channel) +static void dsi_uninit_dispc(struct dsi_data *dsi) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + enum omap_channel dispc_channel = dsi->output.dispc_channel; if (dsi->mode == OMAP_DSS_DSI_CMD_MODE) - dss_mgr_unregister_framedone_handler(channel, - dsi_framedone_irq_callback, dsidev); + dss_mgr_unregister_framedone_handler(&dsi->output, + dsi_framedone_irq_callback, dsi); - dss_select_lcd_clk_source(channel, DSS_CLK_SRC_FCK); + dss_select_lcd_clk_source(dsi->dss, dispc_channel, DSS_CLK_SRC_FCK); } -static int dsi_configure_dsi_clocks(struct platform_device *dsidev) +static int dsi_configure_dsi_clocks(struct dsi_data *dsi) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); struct dss_pll_clock_info cinfo; int r; @@ -4186,144 +3396,179 @@ static int dsi_configure_dsi_clocks(struct platform_device *dsidev) return 0; } -static int dsi_display_init_dsi(struct platform_device *dsidev) +static void dsi_setup_dsi_vcs(struct dsi_data *dsi) +{ + /* Setup VC_CMD for LP and cpu transfers */ + REG_FLD_MOD(dsi, DSI_VC_CTRL(VC_CMD), 0, 9, 9); /* LP */ + + REG_FLD_MOD(dsi, DSI_VC_CTRL(VC_CMD), 0, 1, 1); /* SOURCE_L4 */ + dsi->vc[VC_CMD].source = DSI_VC_SOURCE_L4; + + /* Setup VC_VIDEO for HS and dispc transfers */ + REG_FLD_MOD(dsi, DSI_VC_CTRL(VC_VIDEO), 1, 9, 9); /* HS */ + + REG_FLD_MOD(dsi, DSI_VC_CTRL(VC_VIDEO), 1, 1, 1); /* SOURCE_VP */ + dsi->vc[VC_VIDEO].source = DSI_VC_SOURCE_VP; + + if ((dsi->data->quirks & DSI_QUIRK_DCS_CMD_CONFIG_VC) && + !(dsi->dsidev->mode_flags & MIPI_DSI_MODE_VIDEO)) + REG_FLD_MOD(dsi, DSI_VC_CTRL(VC_VIDEO), 1, 30, 30); /* DCS_CMD_ENABLE */ + + dsi_vc_enable(dsi, VC_CMD, 1); + dsi_vc_enable(dsi, VC_VIDEO, 1); + + dsi_if_enable(dsi, 1); + + dsi_force_tx_stop_mode_io(dsi); + + /* start the DDR clock by sending a NULL packet */ + if (!(dsi->dsidev->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)) + dsi_vc_send_null(dsi, VC_CMD, dsi->dsidev->channel); +} + +static int dsi_init_dsi(struct dsi_data *dsi) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); int r; r = dss_pll_enable(&dsi->pll); if (r) - goto err0; + return r; - r = dsi_configure_dsi_clocks(dsidev); + r = dsi_configure_dsi_clocks(dsi); if (r) - goto err1; + goto err0; - dss_select_dsi_clk_source(dsi->module_id, dsi->module_id == 0 ? - DSS_CLK_SRC_PLL1_2 : - DSS_CLK_SRC_PLL2_2); + dss_select_dsi_clk_source(dsi->dss, dsi->module_id, + dsi->module_id == 0 ? + DSS_CLK_SRC_PLL1_2 : DSS_CLK_SRC_PLL2_2); DSSDBG("PLL OK\n"); - r = dsi_cio_init(dsidev); + if (!dsi->vdds_dsi_enabled) { + r = regulator_enable(dsi->vdds_dsi_reg); + if (r) + goto err1; + + dsi->vdds_dsi_enabled = true; + } + + r = dsi_cio_init(dsi); if (r) goto err2; - _dsi_print_reset_status(dsidev); + _dsi_print_reset_status(dsi); - dsi_proto_timings(dsidev); - dsi_set_lp_clk_divisor(dsidev); + dsi_proto_timings(dsi); + dsi_set_lp_clk_divisor(dsi); if (1) - _dsi_print_reset_status(dsidev); + _dsi_print_reset_status(dsi); - r = dsi_proto_config(dsidev); + r = dsi_proto_config(dsi); if (r) goto err3; - /* enable interface */ - dsi_vc_enable(dsidev, 0, 1); - dsi_vc_enable(dsidev, 1, 1); - dsi_vc_enable(dsidev, 2, 1); - dsi_vc_enable(dsidev, 3, 1); - dsi_if_enable(dsidev, 1); - dsi_force_tx_stop_mode_io(dsidev); + dsi_setup_dsi_vcs(dsi); return 0; err3: - dsi_cio_uninit(dsidev); + dsi_cio_uninit(dsi); err2: - dss_select_dsi_clk_source(dsi->module_id, DSS_CLK_SRC_FCK); + regulator_disable(dsi->vdds_dsi_reg); + dsi->vdds_dsi_enabled = false; err1: - dss_pll_disable(&dsi->pll); + dss_select_dsi_clk_source(dsi->dss, dsi->module_id, DSS_CLK_SRC_FCK); err0: + dss_pll_disable(&dsi->pll); + return r; } -static void dsi_display_uninit_dsi(struct platform_device *dsidev, - bool disconnect_lanes, bool enter_ulps) +static void dsi_uninit_dsi(struct dsi_data *dsi) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - - if (enter_ulps && !dsi->ulps_enabled) - dsi_enter_ulps(dsidev); - /* disable interface */ - dsi_if_enable(dsidev, 0); - dsi_vc_enable(dsidev, 0, 0); - dsi_vc_enable(dsidev, 1, 0); - dsi_vc_enable(dsidev, 2, 0); - dsi_vc_enable(dsidev, 3, 0); + dsi_if_enable(dsi, 0); + dsi_vc_enable(dsi, 0, 0); + dsi_vc_enable(dsi, 1, 0); + dsi_vc_enable(dsi, 2, 0); + dsi_vc_enable(dsi, 3, 0); + + dss_select_dsi_clk_source(dsi->dss, dsi->module_id, DSS_CLK_SRC_FCK); + dsi_cio_uninit(dsi); + dss_pll_disable(&dsi->pll); - dss_select_dsi_clk_source(dsi->module_id, DSS_CLK_SRC_FCK); - dsi_cio_uninit(dsidev); - dsi_pll_uninit(dsidev, disconnect_lanes); + regulator_disable(dsi->vdds_dsi_reg); + dsi->vdds_dsi_enabled = false; } -static int dsi_display_enable(struct omap_dss_device *dssdev) +static void dsi_enable(struct dsi_data *dsi) { - struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - int r = 0; + int r; - DSSDBG("dsi_display_enable\n"); + WARN_ON(!dsi_bus_is_locked(dsi)); - WARN_ON(!dsi_bus_is_locked(dsidev)); + if (WARN_ON(dsi->iface_enabled)) + return; mutex_lock(&dsi->lock); - r = dsi_runtime_get(dsidev); + r = dsi_runtime_get(dsi); if (r) goto err_get_dsi; - _dsi_initialize_irq(dsidev); + _dsi_initialize_irq(dsi); - r = dsi_display_init_dsi(dsidev); + r = dsi_init_dsi(dsi); if (r) goto err_init_dsi; + dsi->iface_enabled = true; + mutex_unlock(&dsi->lock); - return 0; + return; err_init_dsi: - dsi_runtime_put(dsidev); + dsi_runtime_put(dsi); err_get_dsi: mutex_unlock(&dsi->lock); - DSSDBG("dsi_display_enable FAILED\n"); - return r; + DSSDBG("dsi_enable FAILED\n"); } -static void dsi_display_disable(struct omap_dss_device *dssdev, - bool disconnect_lanes, bool enter_ulps) +static void dsi_disable(struct dsi_data *dsi) { - struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - - DSSDBG("dsi_display_disable\n"); + WARN_ON(!dsi_bus_is_locked(dsi)); - WARN_ON(!dsi_bus_is_locked(dsidev)); + if (WARN_ON(!dsi->iface_enabled)) + return; mutex_lock(&dsi->lock); - dsi_sync_vc(dsidev, 0); - dsi_sync_vc(dsidev, 1); - dsi_sync_vc(dsidev, 2); - dsi_sync_vc(dsidev, 3); + dsi_sync_vc(dsi, 0); + dsi_sync_vc(dsi, 1); + dsi_sync_vc(dsi, 2); + dsi_sync_vc(dsi, 3); + + dsi_uninit_dsi(dsi); - dsi_display_uninit_dsi(dsidev, disconnect_lanes, enter_ulps); + dsi_runtime_put(dsi); - dsi_runtime_put(dsidev); + dsi->iface_enabled = false; mutex_unlock(&dsi->lock); } -static int dsi_enable_te(struct omap_dss_device *dssdev, bool enable) +static int dsi_enable_te(struct dsi_data *dsi, bool enable) { - struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - dsi->te_enabled = enable; + + if (dsi->te_gpio) { + if (enable) + enable_irq(dsi->te_irq); + else + disable_irq(dsi->te_irq); + } + return 0; } @@ -4442,14 +3687,16 @@ static bool dsi_cm_calc_hsdiv_cb(int m_dispc, unsigned long dispc, ctx->dsi_cinfo.mX[HSDIV_DISPC] = m_dispc; ctx->dsi_cinfo.clkout[HSDIV_DISPC] = dispc; - return dispc_div_calc(dispc, ctx->req_pck_min, ctx->req_pck_max, - dsi_cm_calc_dispc_cb, ctx); + return dispc_div_calc(ctx->dsi->dss->dispc, dispc, + ctx->req_pck_min, ctx->req_pck_max, + dsi_cm_calc_dispc_cb, ctx); } static bool dsi_cm_calc_pll_cb(int n, int m, unsigned long fint, unsigned long clkdco, void *data) { struct dsi_clk_calc_ctx *ctx = data; + struct dsi_data *dsi = ctx->dsi; ctx->dsi_cinfo.n = n; ctx->dsi_cinfo.m = m; @@ -4457,7 +3704,7 @@ static bool dsi_cm_calc_pll_cb(int n, int m, unsigned long fint, ctx->dsi_cinfo.clkdco = clkdco; return dss_pll_hsdiv_calc_a(ctx->pll, clkdco, ctx->req_pck_min, - dss_feat_get_param_max(FEAT_PARAM_DSS_FCK), + dsi->data->max_fck_freq, dsi_cm_calc_hsdiv_cb, ctx); } @@ -4471,7 +3718,7 @@ static bool dsi_cm_calc(struct dsi_data *dsi, unsigned long pck, txbyteclk; clkin = clk_get_rate(dsi->pll.clkin); - bitspp = dsi_get_pixel_size(cfg->pixel_format); + bitspp = mipi_dsi_pixel_format_to_bpp(cfg->pixel_format); ndl = dsi->num_lanes_used - 1; /* @@ -4485,7 +3732,7 @@ static bool dsi_cm_calc(struct dsi_data *dsi, txbyteclk = pck * bitspp / 8 / ndl; memset(ctx, 0, sizeof(*ctx)); - ctx->dsidev = dsi->pdev; + ctx->dsi = dsi; ctx->pll = &dsi->pll; ctx->config = cfg; ctx->req_pck_min = pck; @@ -4502,9 +3749,9 @@ static bool dsi_cm_calc(struct dsi_data *dsi, static bool dsi_vm_calc_blanking(struct dsi_clk_calc_ctx *ctx) { - struct dsi_data *dsi = dsi_get_dsidrv_data(ctx->dsidev); + struct dsi_data *dsi = ctx->dsi; const struct omap_dss_dsi_config *cfg = ctx->config; - int bitspp = dsi_get_pixel_size(cfg->pixel_format); + int bitspp = mipi_dsi_pixel_format_to_bpp(cfg->pixel_format); int ndl = dsi->num_lanes_used - 1; unsigned long hsclk = ctx->dsi_cinfo.clkdco / 4; unsigned long byteclk = hsclk / 4; @@ -4651,7 +3898,6 @@ static bool dsi_vm_calc_blanking(struct dsi_clk_calc_ctx *ctx) dsi_vm->hfp_blanking_mode = 1; dsi_vm->hbp_blanking_mode = 1; - dsi_vm->ddr_clk_always_on = cfg->ddr_clk_always_on; dsi_vm->window_sync = 4; /* setup DISPC videomode */ @@ -4741,14 +3987,16 @@ static bool dsi_vm_calc_hsdiv_cb(int m_dispc, unsigned long dispc, else pck_max = ctx->req_pck_max; - return dispc_div_calc(dispc, ctx->req_pck_min, pck_max, - dsi_vm_calc_dispc_cb, ctx); + return dispc_div_calc(ctx->dsi->dss->dispc, dispc, + ctx->req_pck_min, pck_max, + dsi_vm_calc_dispc_cb, ctx); } static bool dsi_vm_calc_pll_cb(int n, int m, unsigned long fint, unsigned long clkdco, void *data) { struct dsi_clk_calc_ctx *ctx = data; + struct dsi_data *dsi = ctx->dsi; ctx->dsi_cinfo.n = n; ctx->dsi_cinfo.m = m; @@ -4756,7 +4004,7 @@ static bool dsi_vm_calc_pll_cb(int n, int m, unsigned long fint, ctx->dsi_cinfo.clkdco = clkdco; return dss_pll_hsdiv_calc_a(ctx->pll, clkdco, ctx->req_pck_min, - dss_feat_get_param_max(FEAT_PARAM_DSS_FCK), + dsi->data->max_fck_freq, dsi_vm_calc_hsdiv_cb, ctx); } @@ -4769,13 +4017,13 @@ static bool dsi_vm_calc(struct dsi_data *dsi, unsigned long pll_min; unsigned long pll_max; int ndl = dsi->num_lanes_used - 1; - int bitspp = dsi_get_pixel_size(cfg->pixel_format); + int bitspp = mipi_dsi_pixel_format_to_bpp(cfg->pixel_format); unsigned long byteclk_min; clkin = clk_get_rate(dsi->pll.clkin); memset(ctx, 0, sizeof(*ctx)); - ctx->dsidev = dsi->pdev; + ctx->dsi = dsi; ctx->pll = &dsi->pll; ctx->config = cfg; @@ -4802,44 +4050,90 @@ static bool dsi_vm_calc(struct dsi_data *dsi, dsi_vm_calc_pll_cb, ctx); } -static int dsi_set_config(struct omap_dss_device *dssdev, - const struct omap_dss_dsi_config *config) +static bool dsi_is_video_mode(struct omap_dss_device *dssdev) { - struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - struct dsi_clk_calc_ctx ctx; + struct dsi_data *dsi = to_dsi_data(dssdev); + + return dsi->mode == OMAP_DSS_DSI_VIDEO_MODE; +} + +static int __dsi_calc_config(struct dsi_data *dsi, + const struct drm_display_mode *mode, + struct dsi_clk_calc_ctx *ctx) +{ + struct omap_dss_dsi_config cfg = dsi->config; + struct videomode vm; bool ok; int r; - mutex_lock(&dsi->lock); + drm_display_mode_to_videomode(mode, &vm); - dsi->pix_fmt = config->pixel_format; - dsi->mode = config->mode; + cfg.vm = &vm; + cfg.mode = dsi->mode; + cfg.pixel_format = dsi->pix_fmt; - if (config->mode == OMAP_DSS_DSI_VIDEO_MODE) - ok = dsi_vm_calc(dsi, config, &ctx); + if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) + ok = dsi_vm_calc(dsi, &cfg, ctx); else - ok = dsi_cm_calc(dsi, config, &ctx); + ok = dsi_cm_calc(dsi, &cfg, ctx); - if (!ok) { - DSSERR("failed to find suitable DSI clock settings\n"); - r = -EINVAL; - goto err; - } + if (!ok) + return -EINVAL; + + dsi_pll_calc_dsi_fck(dsi, &ctx->dsi_cinfo); + + r = dsi_lp_clock_calc(ctx->dsi_cinfo.clkout[HSDIV_DSI], + cfg.lp_clk_min, cfg.lp_clk_max, &ctx->lp_cinfo); + if (r) + return r; - dsi_pll_calc_dsi_fck(&ctx.dsi_cinfo); + return 0; +} + +static int dsi_set_config(struct omap_dss_device *dssdev, + const struct drm_display_mode *mode) +{ + struct dsi_data *dsi = to_dsi_data(dssdev); + struct dsi_clk_calc_ctx ctx; + int r; + + mutex_lock(&dsi->lock); - r = dsi_lp_clock_calc(ctx.dsi_cinfo.clkout[HSDIV_DSI], - config->lp_clk_min, config->lp_clk_max, &dsi->user_lp_cinfo); + r = __dsi_calc_config(dsi, mode, &ctx); if (r) { - DSSERR("failed to find suitable DSI LP clock settings\n"); + DSSERR("failed to find suitable DSI clock settings\n"); goto err; } + dsi->user_lp_cinfo = ctx.lp_cinfo; dsi->user_dsi_cinfo = ctx.dsi_cinfo; dsi->user_dispc_cinfo = ctx.dispc_cinfo; dsi->vm = ctx.vm; + + /* + * override interlace, logic level and edge related parameters in + * videomode with default values + */ + dsi->vm.flags &= ~DISPLAY_FLAGS_INTERLACED; + dsi->vm.flags &= ~DISPLAY_FLAGS_HSYNC_LOW; + dsi->vm.flags |= DISPLAY_FLAGS_HSYNC_HIGH; + dsi->vm.flags &= ~DISPLAY_FLAGS_VSYNC_LOW; + dsi->vm.flags |= DISPLAY_FLAGS_VSYNC_HIGH; + /* + * HACK: These flags should be handled through the omap_dss_device bus + * flags, but this will only be possible when the DSI encoder will be + * converted to the omapdrm-managed encoder model. + */ + dsi->vm.flags &= ~DISPLAY_FLAGS_PIXDATA_NEGEDGE; + dsi->vm.flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE; + dsi->vm.flags &= ~DISPLAY_FLAGS_DE_LOW; + dsi->vm.flags |= DISPLAY_FLAGS_DE_HIGH; + dsi->vm.flags &= ~DISPLAY_FLAGS_SYNC_POSEDGE; + dsi->vm.flags |= DISPLAY_FLAGS_SYNC_NEGEDGE; + + dss_mgr_set_timings(&dsi->output, &dsi->vm); + dsi->vm_timings = ctx.dsi_vm; mutex_unlock(&dsi->lock); @@ -4852,29 +4146,19 @@ err: } /* - * Return a hardcoded channel for the DSI output. This should work for + * Return a hardcoded dispc channel for the DSI output. This should work for * current use cases, but this can be later expanded to either resolve * the channel in some more dynamic manner, or get the channel as a user * parameter. */ -static enum omap_channel dsi_get_channel(int module_id) +static enum omap_channel dsi_get_dispc_channel(struct dsi_data *dsi) { - switch (omapdss_get_version()) { - case OMAPDSS_VER_OMAP24xx: - case OMAPDSS_VER_AM43xx: - DSSWARN("DSI not supported\n"); - return OMAP_DSS_CHANNEL_LCD; - - case OMAPDSS_VER_OMAP34xx_ES1: - case OMAPDSS_VER_OMAP34xx_ES3: - case OMAPDSS_VER_OMAP3630: - case OMAPDSS_VER_AM35xx: + switch (dsi->data->model) { + case DSI_MODEL_OMAP3: return OMAP_DSS_CHANNEL_LCD; - case OMAPDSS_VER_OMAP4430_ES1: - case OMAPDSS_VER_OMAP4430_ES2: - case OMAPDSS_VER_OMAP4: - switch (module_id) { + case DSI_MODEL_OMAP4: + switch (dsi->module_id) { case 0: return OMAP_DSS_CHANNEL_LCD; case 1: @@ -4884,8 +4168,8 @@ static enum omap_channel dsi_get_channel(int module_id) return OMAP_DSS_CHANNEL_LCD; } - case OMAPDSS_VER_OMAP5: - switch (module_id) { + case DSI_MODEL_OMAP5: + switch (dsi->module_id) { case 0: return OMAP_DSS_CHANNEL_LCD; case 1: @@ -4901,69 +4185,81 @@ static enum omap_channel dsi_get_channel(int module_id) } } -static int dsi_request_vc(struct omap_dss_device *dssdev, int *channel) +static ssize_t _omap_dsi_host_transfer(struct dsi_data *dsi, int vc, + const struct mipi_dsi_msg *msg) { - struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - int i; + struct omap_dss_device *dssdev = &dsi->output; + int r; - for (i = 0; i < ARRAY_SIZE(dsi->vc); i++) { - if (!dsi->vc[i].dssdev) { - dsi->vc[i].dssdev = dssdev; - *channel = i; - return 0; - } + dsi_vc_enable_hs(dssdev, vc, !(msg->flags & MIPI_DSI_MSG_USE_LPM)); + + switch (msg->type) { + case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM: + case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM: + case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM: + case MIPI_DSI_GENERIC_LONG_WRITE: + case MIPI_DSI_DCS_SHORT_WRITE: + case MIPI_DSI_DCS_SHORT_WRITE_PARAM: + case MIPI_DSI_DCS_LONG_WRITE: + case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE: + case MIPI_DSI_NULL_PACKET: + r = dsi_vc_write_common(dssdev, vc, msg); + break; + case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM: + case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM: + case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM: + r = dsi_vc_generic_read(dssdev, vc, msg); + break; + case MIPI_DSI_DCS_READ: + r = dsi_vc_dcs_read(dssdev, vc, msg); + break; + default: + r = -EINVAL; + break; } - DSSERR("cannot get VC for display %s", dssdev->name); - return -ENOSPC; -} - -static int dsi_set_vc_id(struct omap_dss_device *dssdev, int channel, int vc_id) -{ - struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - - if (vc_id < 0 || vc_id > 3) { - DSSERR("VC ID out of range\n"); - return -EINVAL; - } + if (r < 0) + return r; - if (channel < 0 || channel > 3) { - DSSERR("Virtual Channel out of range\n"); - return -EINVAL; - } + if (msg->type == MIPI_DSI_DCS_SHORT_WRITE || + msg->type == MIPI_DSI_DCS_SHORT_WRITE_PARAM) { + u8 cmd = ((u8 *)msg->tx_buf)[0]; - if (dsi->vc[channel].dssdev != dssdev) { - DSSERR("Virtual Channel not allocated to display %s\n", - dssdev->name); - return -EINVAL; + if (cmd == MIPI_DCS_SET_TEAR_OFF) + dsi_enable_te(dsi, false); + else if (cmd == MIPI_DCS_SET_TEAR_ON) + dsi_enable_te(dsi, true); } - dsi->vc[channel].vc_id = vc_id; - return 0; } -static void dsi_release_vc(struct omap_dss_device *dssdev, int channel) +static ssize_t omap_dsi_host_transfer(struct mipi_dsi_host *host, + const struct mipi_dsi_msg *msg) { - struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + struct dsi_data *dsi = host_to_omap(host); + int r; + int vc = VC_CMD; + + dsi_bus_lock(dsi); - if ((channel >= 0 && channel <= 3) && - dsi->vc[channel].dssdev == dssdev) { - dsi->vc[channel].dssdev = NULL; - dsi->vc[channel].vc_id = 0; + if (!dsi->iface_enabled) { + dsi_enable(dsi); + schedule_delayed_work(&dsi->dsi_disable_work, msecs_to_jiffies(2000)); } -} + r = _omap_dsi_host_transfer(dsi, vc, msg); + + dsi_bus_unlock(dsi); + + return r; +} -static int dsi_get_clocks(struct platform_device *dsidev) +static int dsi_get_clocks(struct dsi_data *dsi) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); struct clk *clk; - clk = devm_clk_get(&dsidev->dev, "fck"); + clk = devm_clk_get(dsi->dev, "fck"); if (IS_ERR(clk)) { DSSERR("can't get fck\n"); return PTR_ERR(clk); @@ -4974,168 +4270,174 @@ static int dsi_get_clocks(struct platform_device *dsidev) return 0; } -static int dsi_connect(struct omap_dss_device *dssdev, - struct omap_dss_device *dst) -{ - struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); - enum omap_channel dispc_channel = dssdev->dispc_channel; - int r; - - r = dsi_regulator_init(dsidev); - if (r) - return r; +static const struct omapdss_dsi_ops dsi_ops = { + .update = dsi_update_all, + .is_video_mode = dsi_is_video_mode, +}; - r = dss_mgr_connect(dispc_channel, dssdev); - if (r) - return r; +static irqreturn_t omap_dsi_te_irq_handler(int irq, void *dev_id) +{ + struct dsi_data *dsi = (struct dsi_data *)dev_id; + int old; - r = omapdss_output_set_device(dssdev, dst); - if (r) { - DSSERR("failed to connect output to new device: %s\n", - dssdev->name); - dss_mgr_disconnect(dispc_channel, dssdev); - return r; + old = atomic_cmpxchg(&dsi->do_ext_te_update, 1, 0); + if (old) { + cancel_delayed_work(&dsi->te_timeout_work); + _dsi_update(dsi); } - return 0; + return IRQ_HANDLED; } -static void dsi_disconnect(struct omap_dss_device *dssdev, - struct omap_dss_device *dst) +static void omap_dsi_te_timeout_work_callback(struct work_struct *work) { - enum omap_channel dispc_channel = dssdev->dispc_channel; + struct dsi_data *dsi = + container_of(work, struct dsi_data, te_timeout_work.work); + int old; - WARN_ON(dst != dssdev->dst); + old = atomic_cmpxchg(&dsi->do_ext_te_update, 1, 0); + if (old) { + dev_err(dsi->dev, "TE not received for 250ms!\n"); + _dsi_update(dsi); + } +} - if (dst != dssdev->dst) - return; +static int omap_dsi_register_te_irq(struct dsi_data *dsi, + struct mipi_dsi_device *client) +{ + int err; + int te_irq; - omapdss_output_unset_device(dssdev); + dsi->te_gpio = gpiod_get(&client->dev, "te-gpios", GPIOD_IN); + if (IS_ERR(dsi->te_gpio)) { + err = PTR_ERR(dsi->te_gpio); - dss_mgr_disconnect(dispc_channel, dssdev); -} + if (err == -ENOENT) { + dsi->te_gpio = NULL; + return 0; + } -static const struct omapdss_dsi_ops dsi_ops = { - .connect = dsi_connect, - .disconnect = dsi_disconnect, + dev_err(dsi->dev, "Could not get TE gpio: %d\n", err); + return err; + } - .bus_lock = dsi_bus_lock, - .bus_unlock = dsi_bus_unlock, + te_irq = gpiod_to_irq(dsi->te_gpio); + if (te_irq < 0) { + gpiod_put(dsi->te_gpio); + dsi->te_gpio = NULL; + return -EINVAL; + } - .enable = dsi_display_enable, - .disable = dsi_display_disable, + dsi->te_irq = te_irq; - .enable_hs = dsi_vc_enable_hs, + irq_set_status_flags(te_irq, IRQ_NOAUTOEN); - .configure_pins = dsi_configure_pins, - .set_config = dsi_set_config, + err = request_threaded_irq(te_irq, NULL, omap_dsi_te_irq_handler, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "TE", dsi); + if (err) { + dev_err(dsi->dev, "request irq failed with %d\n", err); + gpiod_put(dsi->te_gpio); + dsi->te_gpio = NULL; + return err; + } - .enable_video_output = dsi_enable_video_output, - .disable_video_output = dsi_disable_video_output, + INIT_DEFERRABLE_WORK(&dsi->te_timeout_work, + omap_dsi_te_timeout_work_callback); - .update = dsi_update, + dev_dbg(dsi->dev, "Using GPIO TE\n"); - .enable_te = dsi_enable_te, + return 0; +} - .request_vc = dsi_request_vc, - .set_vc_id = dsi_set_vc_id, - .release_vc = dsi_release_vc, +static void omap_dsi_unregister_te_irq(struct dsi_data *dsi) +{ + if (dsi->te_gpio) { + free_irq(dsi->te_irq, dsi); + cancel_delayed_work(&dsi->te_timeout_work); + gpiod_put(dsi->te_gpio); + dsi->te_gpio = NULL; + } +} - .dcs_write = dsi_vc_dcs_write, - .dcs_write_nosync = dsi_vc_dcs_write_nosync, - .dcs_read = dsi_vc_dcs_read, +static int omap_dsi_host_attach(struct mipi_dsi_host *host, + struct mipi_dsi_device *client) +{ + struct dsi_data *dsi = host_to_omap(host); + int r; - .gen_write = dsi_vc_generic_write, - .gen_write_nosync = dsi_vc_generic_write_nosync, - .gen_read = dsi_vc_generic_read, + if (dsi->dsidev) { + DSSERR("dsi client already attached\n"); + return -EBUSY; + } - .bta_sync = dsi_vc_send_bta_sync, + if (mipi_dsi_pixel_format_to_bpp(client->format) < 0) { + DSSERR("invalid pixel format\n"); + return -EINVAL; + } - .set_max_rx_packet_size = dsi_vc_set_max_rx_packet_size, -}; + atomic_set(&dsi->do_ext_te_update, 0); -static void dsi_init_output(struct platform_device *dsidev) -{ - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - struct omap_dss_device *out = &dsi->output; + if (client->mode_flags & MIPI_DSI_MODE_VIDEO) { + dsi->mode = OMAP_DSS_DSI_VIDEO_MODE; + } else { + r = omap_dsi_register_te_irq(dsi, client); + if (r) + return r; - out->dev = &dsidev->dev; - out->id = dsi->module_id == 0 ? - OMAP_DSS_OUTPUT_DSI1 : OMAP_DSS_OUTPUT_DSI2; + dsi->mode = OMAP_DSS_DSI_CMD_MODE; + } - out->output_type = OMAP_DISPLAY_TYPE_DSI; - out->name = dsi->module_id == 0 ? "dsi.0" : "dsi.1"; - out->dispc_channel = dsi_get_channel(dsi->module_id); - out->ops.dsi = &dsi_ops; - out->owner = THIS_MODULE; + dsi->dsidev = client; + dsi->pix_fmt = client->format; - omapdss_register_output(out); -} + dsi->config.hs_clk_min = 150000000; // TODO: get from client? + dsi->config.hs_clk_max = client->hs_rate; + dsi->config.lp_clk_min = 7000000; // TODO: get from client? + dsi->config.lp_clk_max = client->lp_rate; -static void dsi_uninit_output(struct platform_device *dsidev) -{ - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - struct omap_dss_device *out = &dsi->output; + if (client->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) + dsi->config.trans_mode = OMAP_DSS_DSI_BURST_MODE; + else if (client->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) + dsi->config.trans_mode = OMAP_DSS_DSI_PULSE_MODE; + else + dsi->config.trans_mode = OMAP_DSS_DSI_EVENT_MODE; - omapdss_unregister_output(out); + return 0; } -static int dsi_probe_of(struct platform_device *pdev) +static int omap_dsi_host_detach(struct mipi_dsi_host *host, + struct mipi_dsi_device *client) { - struct device_node *node = pdev->dev.of_node; - struct dsi_data *dsi = dsi_get_dsidrv_data(pdev); - struct property *prop; - u32 lane_arr[10]; - int len, num_pins; - int r, i; - struct device_node *ep; - struct omap_dsi_pin_config pin_cfg; + struct dsi_data *dsi = host_to_omap(host); - ep = of_graph_get_endpoint_by_regs(node, 0, 0); - if (!ep) - return 0; - - prop = of_find_property(ep, "lanes", &len); - if (prop == NULL) { - dev_err(&pdev->dev, "failed to find lane data\n"); - r = -EINVAL; - goto err; - } - - num_pins = len / sizeof(u32); - - if (num_pins < 4 || num_pins % 2 != 0 || - num_pins > dsi->num_lanes_supported * 2) { - dev_err(&pdev->dev, "bad number of lanes\n"); - r = -EINVAL; - goto err; - } + if (WARN_ON(dsi->dsidev != client)) + return -EINVAL; - r = of_property_read_u32_array(ep, "lanes", lane_arr, num_pins); - if (r) { - dev_err(&pdev->dev, "failed to read lane data\n"); - goto err; - } + cancel_delayed_work_sync(&dsi->dsi_disable_work); - pin_cfg.num_pins = num_pins; - for (i = 0; i < num_pins; ++i) - pin_cfg.pins[i] = (int)lane_arr[i]; + dsi_bus_lock(dsi); - r = dsi_configure_pins(&dsi->output, &pin_cfg); - if (r) { - dev_err(&pdev->dev, "failed to configure pins"); - goto err; - } + if (dsi->iface_enabled) + dsi_disable(dsi); - of_node_put(ep); + dsi_bus_unlock(dsi); + omap_dsi_unregister_te_irq(dsi); + dsi->dsidev = NULL; return 0; - -err: - of_node_put(ep); - return r; } +static const struct mipi_dsi_host_ops omap_dsi_host_ops = { + .attach = omap_dsi_host_attach, + .detach = omap_dsi_host_detach, + .transfer = omap_dsi_host_transfer, +}; + +/* ----------------------------------------------------------------------------- + * PLL + */ + static const struct dss_pll_ops dsi_pll_ops = { .enable = dsi_pll_enable, .disable = dsi_pll_disable, @@ -5223,14 +4525,13 @@ static const struct dss_pll_hw dss_omap5_dsi_pll_hw = { .has_refsel = true, }; -static int dsi_init_pll_data(struct platform_device *dsidev) +static int dsi_init_pll_data(struct dss_device *dss, struct dsi_data *dsi) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); struct dss_pll *pll = &dsi->pll; struct clk *clk; int r; - clk = devm_clk_get(&dsidev->dev, "sys_clk"); + clk = devm_clk_get(dsi->dev, "sys_clk"); if (IS_ERR(clk)) { DSSERR("can't get sys_clk\n"); return PTR_ERR(clk); @@ -5240,55 +4541,364 @@ static int dsi_init_pll_data(struct platform_device *dsidev) pll->id = dsi->module_id == 0 ? DSS_PLL_DSI1 : DSS_PLL_DSI2; pll->clkin = clk; pll->base = dsi->pll_base; + pll->hw = dsi->data->pll_hw; + pll->ops = &dsi_pll_ops; - switch (omapdss_get_version()) { - case OMAPDSS_VER_OMAP34xx_ES1: - case OMAPDSS_VER_OMAP34xx_ES3: - case OMAPDSS_VER_OMAP3630: - case OMAPDSS_VER_AM35xx: - pll->hw = &dss_omap3_dsi_pll_hw; - break; + r = dss_pll_register(dss, pll); + if (r) + return r; - case OMAPDSS_VER_OMAP4430_ES1: - case OMAPDSS_VER_OMAP4430_ES2: - case OMAPDSS_VER_OMAP4: - pll->hw = &dss_omap4_dsi_pll_hw; - break; + return 0; +} - case OMAPDSS_VER_OMAP5: - pll->hw = &dss_omap5_dsi_pll_hw; - break; +/* ----------------------------------------------------------------------------- + * Component Bind & Unbind + */ - default: - return -ENODEV; - } +static int dsi_bind(struct device *dev, struct device *master, void *data) +{ + struct dss_device *dss = dss_get_device(master); + struct dsi_data *dsi = dev_get_drvdata(dev); + char name[10]; + u32 rev; + int r; - pll->ops = &dsi_pll_ops; + dsi->dss = dss; + + dsi_init_pll_data(dss, dsi); - r = dss_pll_register(pll); + r = dsi_runtime_get(dsi); if (r) return r; + rev = dsi_read_reg(dsi, DSI_REVISION); + dev_dbg(dev, "OMAP DSI rev %d.%d\n", + FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); + + dsi->line_buffer_size = dsi_get_line_buf_size(dsi); + + dsi_runtime_put(dsi); + + snprintf(name, sizeof(name), "dsi%u_regs", dsi->module_id + 1); + dsi->debugfs.regs = dss_debugfs_create_file(dss, name, + dsi_dump_dsi_regs, dsi); +#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS + snprintf(name, sizeof(name), "dsi%u_irqs", dsi->module_id + 1); + dsi->debugfs.irqs = dss_debugfs_create_file(dss, name, + dsi_dump_dsi_irqs, dsi); +#endif + snprintf(name, sizeof(name), "dsi%u_clks", dsi->module_id + 1); + dsi->debugfs.clks = dss_debugfs_create_file(dss, name, + dsi_dump_dsi_clocks, dsi); + return 0; } -/* DSI1 HW IP initialisation */ -static int dsi_bind(struct device *dev, struct device *master, void *data) +static void dsi_unbind(struct device *dev, struct device *master, void *data) +{ + struct dsi_data *dsi = dev_get_drvdata(dev); + + dss_debugfs_remove_file(dsi->debugfs.clks); + dss_debugfs_remove_file(dsi->debugfs.irqs); + dss_debugfs_remove_file(dsi->debugfs.regs); + + WARN_ON(dsi->scp_clk_refcount > 0); + + dss_pll_unregister(&dsi->pll); +} + +static const struct component_ops dsi_component_ops = { + .bind = dsi_bind, + .unbind = dsi_unbind, +}; + +/* ----------------------------------------------------------------------------- + * DRM Bridge Operations + */ + +static int dsi_bridge_attach(struct drm_bridge *bridge, + struct drm_encoder *encoder, + enum drm_bridge_attach_flags flags) +{ + struct dsi_data *dsi = drm_bridge_to_dsi(bridge); + + if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) + return -EINVAL; + + return drm_bridge_attach(encoder, dsi->output.next_bridge, + bridge, flags); +} + +static enum drm_mode_status +dsi_bridge_mode_valid(struct drm_bridge *bridge, + const struct drm_display_info *info, + const struct drm_display_mode *mode) +{ + struct dsi_data *dsi = drm_bridge_to_dsi(bridge); + struct dsi_clk_calc_ctx ctx; + int r; + + mutex_lock(&dsi->lock); + r = __dsi_calc_config(dsi, mode, &ctx); + mutex_unlock(&dsi->lock); + + return r ? MODE_CLOCK_RANGE : MODE_OK; +} + +static void dsi_bridge_mode_set(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + const struct drm_display_mode *adjusted_mode) +{ + struct dsi_data *dsi = drm_bridge_to_dsi(bridge); + + dsi_set_config(&dsi->output, adjusted_mode); +} + +static void dsi_bridge_enable(struct drm_bridge *bridge) +{ + struct dsi_data *dsi = drm_bridge_to_dsi(bridge); + struct omap_dss_device *dssdev = &dsi->output; + + cancel_delayed_work_sync(&dsi->dsi_disable_work); + + dsi_bus_lock(dsi); + + if (!dsi->iface_enabled) + dsi_enable(dsi); + + dsi_enable_video_output(dssdev, VC_VIDEO); + + dsi->video_enabled = true; + + dsi_bus_unlock(dsi); +} + +static void dsi_bridge_disable(struct drm_bridge *bridge) +{ + struct dsi_data *dsi = drm_bridge_to_dsi(bridge); + struct omap_dss_device *dssdev = &dsi->output; + + cancel_delayed_work_sync(&dsi->dsi_disable_work); + + dsi_bus_lock(dsi); + + dsi->video_enabled = false; + + dsi_disable_video_output(dssdev, VC_VIDEO); + + dsi_disable(dsi); + + dsi_bus_unlock(dsi); +} + +static const struct drm_bridge_funcs dsi_bridge_funcs = { + .attach = dsi_bridge_attach, + .mode_valid = dsi_bridge_mode_valid, + .mode_set = dsi_bridge_mode_set, + .enable = dsi_bridge_enable, + .disable = dsi_bridge_disable, +}; + +static void dsi_bridge_init(struct dsi_data *dsi) +{ + dsi->bridge.of_node = dsi->host.dev->of_node; + dsi->bridge.type = DRM_MODE_CONNECTOR_DSI; + + drm_bridge_add(&dsi->bridge); +} + +static void dsi_bridge_cleanup(struct dsi_data *dsi) +{ + drm_bridge_remove(&dsi->bridge); +} + +/* ----------------------------------------------------------------------------- + * Probe & Remove, Suspend & Resume + */ + +static int dsi_init_output(struct dsi_data *dsi) +{ + struct omap_dss_device *out = &dsi->output; + int r; + + dsi_bridge_init(dsi); + + out->dev = dsi->dev; + out->id = dsi->module_id == 0 ? + OMAP_DSS_OUTPUT_DSI1 : OMAP_DSS_OUTPUT_DSI2; + + out->type = OMAP_DISPLAY_TYPE_DSI; + out->name = dsi->module_id == 0 ? "dsi.0" : "dsi.1"; + out->dispc_channel = dsi_get_dispc_channel(dsi); + out->dsi_ops = &dsi_ops; + out->of_port = 0; + out->bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE + | DRM_BUS_FLAG_DE_HIGH + | DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE; + + r = omapdss_device_init_output(out, &dsi->bridge); + if (r < 0) { + dsi_bridge_cleanup(dsi); + return r; + } + + omapdss_device_register(out); + + return 0; +} + +static void dsi_uninit_output(struct dsi_data *dsi) +{ + struct omap_dss_device *out = &dsi->output; + + omapdss_device_unregister(out); + omapdss_device_cleanup_output(out); + dsi_bridge_cleanup(dsi); +} + +static int dsi_probe_of(struct dsi_data *dsi) { - struct platform_device *dsidev = to_platform_device(dev); + struct device_node *node = dsi->dev->of_node; + struct property *prop; + u32 lane_arr[10]; + int len, num_pins; + int r; + struct device_node *ep; + + ep = of_graph_get_endpoint_by_regs(node, 0, 0); + if (!ep) + return 0; + + prop = of_find_property(ep, "lanes", &len); + if (prop == NULL) { + dev_err(dsi->dev, "failed to find lane data\n"); + r = -EINVAL; + goto err; + } + + num_pins = len / sizeof(u32); + + if (num_pins < 4 || num_pins % 2 != 0 || + num_pins > dsi->num_lanes_supported * 2) { + dev_err(dsi->dev, "bad number of lanes\n"); + r = -EINVAL; + goto err; + } + + r = of_property_read_u32_array(ep, "lanes", lane_arr, num_pins); + if (r) { + dev_err(dsi->dev, "failed to read lane data\n"); + goto err; + } + + r = dsi_configure_pins(dsi, num_pins, lane_arr); + if (r) { + dev_err(dsi->dev, "failed to configure pins"); + goto err; + } + + of_node_put(ep); + + return 0; + +err: + of_node_put(ep); + return r; +} + +static const struct dsi_of_data dsi_of_data_omap34xx = { + .model = DSI_MODEL_OMAP3, + .pll_hw = &dss_omap3_dsi_pll_hw, + .modules = (const struct dsi_module_id_data[]) { + { .address = 0x4804fc00, .id = 0, }, + { }, + }, + .max_fck_freq = 173000000, + .max_pll_lpdiv = (1 << 13) - 1, + .quirks = DSI_QUIRK_REVERSE_TXCLKESC, +}; + +static const struct dsi_of_data dsi_of_data_omap36xx = { + .model = DSI_MODEL_OMAP3, + .pll_hw = &dss_omap3_dsi_pll_hw, + .modules = (const struct dsi_module_id_data[]) { + { .address = 0x4804fc00, .id = 0, }, + { }, + }, + .max_fck_freq = 173000000, + .max_pll_lpdiv = (1 << 13) - 1, + .quirks = DSI_QUIRK_PLL_PWR_BUG, +}; + +static const struct dsi_of_data dsi_of_data_omap4 = { + .model = DSI_MODEL_OMAP4, + .pll_hw = &dss_omap4_dsi_pll_hw, + .modules = (const struct dsi_module_id_data[]) { + { .address = 0x58004000, .id = 0, }, + { .address = 0x58005000, .id = 1, }, + { }, + }, + .max_fck_freq = 170000000, + .max_pll_lpdiv = (1 << 13) - 1, + .quirks = DSI_QUIRK_DCS_CMD_CONFIG_VC | DSI_QUIRK_VC_OCP_WIDTH + | DSI_QUIRK_GNQ, +}; + +static const struct dsi_of_data dsi_of_data_omap5 = { + .model = DSI_MODEL_OMAP5, + .pll_hw = &dss_omap5_dsi_pll_hw, + .modules = (const struct dsi_module_id_data[]) { + { .address = 0x58004000, .id = 0, }, + { .address = 0x58009000, .id = 1, }, + { }, + }, + .max_fck_freq = 209250000, + .max_pll_lpdiv = (1 << 13) - 1, + .quirks = DSI_QUIRK_DCS_CMD_CONFIG_VC | DSI_QUIRK_VC_OCP_WIDTH + | DSI_QUIRK_GNQ | DSI_QUIRK_PHY_DCC, +}; + +static const struct of_device_id dsi_of_match[] = { + { .compatible = "ti,omap3-dsi", .data = &dsi_of_data_omap36xx, }, + { .compatible = "ti,omap4-dsi", .data = &dsi_of_data_omap4, }, + { .compatible = "ti,omap5-dsi", .data = &dsi_of_data_omap5, }, + {}, +}; + +static const struct soc_device_attribute dsi_soc_devices[] = { + { .machine = "OMAP3[45]*", .data = &dsi_of_data_omap34xx }, + { .machine = "AM35*", .data = &dsi_of_data_omap34xx }, + { /* sentinel */ } +}; + +static void omap_dsi_disable_work_callback(struct work_struct *work) +{ + struct dsi_data *dsi = container_of(work, struct dsi_data, dsi_disable_work.work); + + dsi_bus_lock(dsi); + + if (dsi->iface_enabled && !dsi->video_enabled) + dsi_disable(dsi); + + dsi_bus_unlock(dsi); +} + +static int dsi_probe(struct platform_device *pdev) +{ + const struct soc_device_attribute *soc; const struct dsi_module_id_data *d; - u32 rev; - int r, i; + struct device *dev = &pdev->dev; struct dsi_data *dsi; struct resource *dsi_mem; - struct resource *res; + unsigned int i; + int r; - dsi = devm_kzalloc(&dsidev->dev, sizeof(*dsi), GFP_KERNEL); - if (!dsi) - return -ENOMEM; + dsi = devm_drm_bridge_alloc(dev, struct dsi_data, bridge, &dsi_bridge_funcs); + if (IS_ERR(dsi)) + return PTR_ERR(dsi); - dsi->pdev = dsidev; - dev_set_drvdata(&dsidev->dev, dsi); + dsi->dev = dev; + dev_set_drvdata(dev, dsi); spin_lock_init(&dsi->irq_lock); spin_lock_init(&dsi->errors_lock); @@ -5305,41 +4915,52 @@ static int dsi_bind(struct device *dev, struct device *master, void *data) INIT_DEFERRABLE_WORK(&dsi->framedone_timeout_work, dsi_framedone_timeout_work_callback); + INIT_DEFERRABLE_WORK(&dsi->dsi_disable_work, omap_dsi_disable_work_callback); + #ifdef DSI_CATCH_MISSING_TE - init_timer(&dsi->te_timer); - dsi->te_timer.function = dsi_te_timeout; - dsi->te_timer.data = 0; + timer_setup(&dsi->te_timer, dsi_te_timeout, 0); #endif - dsi_mem = platform_get_resource_byname(dsidev, IORESOURCE_MEM, "proto"); - dsi->proto_base = devm_ioremap_resource(&dsidev->dev, dsi_mem); + dsi_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "proto"); + dsi->proto_base = devm_ioremap_resource(dev, dsi_mem); if (IS_ERR(dsi->proto_base)) return PTR_ERR(dsi->proto_base); - res = platform_get_resource_byname(dsidev, IORESOURCE_MEM, "phy"); - dsi->phy_base = devm_ioremap_resource(&dsidev->dev, res); + dsi->phy_base = devm_platform_ioremap_resource_byname(pdev, "phy"); if (IS_ERR(dsi->phy_base)) return PTR_ERR(dsi->phy_base); - res = platform_get_resource_byname(dsidev, IORESOURCE_MEM, "pll"); - dsi->pll_base = devm_ioremap_resource(&dsidev->dev, res); + dsi->pll_base = devm_platform_ioremap_resource_byname(pdev, "pll"); if (IS_ERR(dsi->pll_base)) return PTR_ERR(dsi->pll_base); - dsi->irq = platform_get_irq(dsi->pdev, 0); + dsi->irq = platform_get_irq(pdev, 0); if (dsi->irq < 0) { DSSERR("platform_get_irq failed\n"); return -ENODEV; } - r = devm_request_irq(&dsidev->dev, dsi->irq, omap_dsi_irq_handler, - IRQF_SHARED, dev_name(&dsidev->dev), dsi->pdev); + r = devm_request_irq(dev, dsi->irq, omap_dsi_irq_handler, + IRQF_SHARED, dev_name(dev), dsi); if (r < 0) { DSSERR("request_irq failed\n"); return r; } - d = of_match_node(dsi_of_match, dsidev->dev.of_node)->data; + dsi->vdds_dsi_reg = devm_regulator_get(dev, "vdd"); + if (IS_ERR(dsi->vdds_dsi_reg)) { + if (PTR_ERR(dsi->vdds_dsi_reg) != -EPROBE_DEFER) + DSSERR("can't get DSI VDD regulator\n"); + return PTR_ERR(dsi->vdds_dsi_reg); + } + + soc = soc_device_match(dsi_soc_devices); + if (soc) + dsi->data = soc->data; + else + dsi->data = of_match_node(dsi_of_match, dev->of_node)->data; + + d = dsi->data->modules; while (d->address != 0 && d->address != dsi_mem->start) d++; @@ -5350,90 +4971,90 @@ static int dsi_bind(struct device *dev, struct device *master, void *data) dsi->module_id = d->id; - /* DSI VCs initialization */ - for (i = 0; i < ARRAY_SIZE(dsi->vc); i++) { - dsi->vc[i].source = DSI_VC_SOURCE_L4; - dsi->vc[i].dssdev = NULL; - dsi->vc[i].vc_id = 0; - } + if (dsi->data->model == DSI_MODEL_OMAP4 || + dsi->data->model == DSI_MODEL_OMAP5) { + struct device_node *np; - r = dsi_get_clocks(dsidev); - if (r) - return r; + /* + * The OMAP4/5 display DT bindings don't reference the padconf + * syscon. Our only option to retrieve it is to find it by name. + */ + np = of_find_node_by_name(NULL, + dsi->data->model == DSI_MODEL_OMAP4 ? + "omap4_padconf_global" : "omap5_padconf_global"); + if (!np) + return -ENODEV; - dsi_init_pll_data(dsidev); + dsi->syscon = syscon_node_to_regmap(np); + of_node_put(np); + } - pm_runtime_enable(&dsidev->dev); + /* DSI VCs initialization */ + for (i = 0; i < ARRAY_SIZE(dsi->vc); i++) + dsi->vc[i].source = DSI_VC_SOURCE_L4; - r = dsi_runtime_get(dsidev); + r = dsi_get_clocks(dsi); if (r) - goto err_runtime_get; + return r; - 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)); + pm_runtime_enable(dev); /* DSI on OMAP3 doesn't have register DSI_GNQ, set number * of data to 3 by default */ - if (dss_has_feature(FEAT_DSI_GNQ)) + if (dsi->data->quirks & DSI_QUIRK_GNQ) { + dsi_runtime_get(dsi); /* NB_DATA_LANES */ - dsi->num_lanes_supported = 1 + REG_GET(dsidev, DSI_GNQ, 11, 9); - else + dsi->num_lanes_supported = 1 + REG_GET(dsi, DSI_GNQ, 11, 9); + dsi_runtime_put(dsi); + } else { dsi->num_lanes_supported = 3; + } - dsi->line_buffer_size = dsi_get_line_buf_size(dsidev); - - dsi_init_output(dsidev); + dsi->host.ops = &omap_dsi_host_ops; + dsi->host.dev = &pdev->dev; - r = dsi_probe_of(dsidev); + r = dsi_probe_of(dsi); if (r) { DSSERR("Invalid DSI DT data\n"); - goto err_probe_of; + goto err_pm_disable; } - r = of_platform_populate(dsidev->dev.of_node, NULL, NULL, &dsidev->dev); - if (r) - DSSERR("Failed to populate DSI child devices: %d\n", r); - - dsi_runtime_put(dsidev); + r = mipi_dsi_host_register(&dsi->host); + if (r < 0) { + dev_err(&pdev->dev, "failed to register DSI host: %d\n", r); + goto err_pm_disable; + } - if (dsi->module_id == 0) - dss_debugfs_create_file("dsi1_regs", dsi1_dump_regs); - else if (dsi->module_id == 1) - dss_debugfs_create_file("dsi2_regs", dsi2_dump_regs); + r = dsi_init_output(dsi); + if (r) + goto err_dsi_host_unregister; -#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS - if (dsi->module_id == 0) - dss_debugfs_create_file("dsi1_irqs", dsi1_dump_irqs); - else if (dsi->module_id == 1) - dss_debugfs_create_file("dsi2_irqs", dsi2_dump_irqs); -#endif + r = component_add(&pdev->dev, &dsi_component_ops); + if (r) + goto err_uninit_output; return 0; -err_probe_of: - dsi_uninit_output(dsidev); - dsi_runtime_put(dsidev); - -err_runtime_get: - pm_runtime_disable(&dsidev->dev); +err_uninit_output: + dsi_uninit_output(dsi); +err_dsi_host_unregister: + mipi_dsi_host_unregister(&dsi->host); +err_pm_disable: + pm_runtime_disable(dev); return r; } -static void dsi_unbind(struct device *dev, struct device *master, void *data) +static void dsi_remove(struct platform_device *pdev) { - struct platform_device *dsidev = to_platform_device(dev); - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + struct dsi_data *dsi = platform_get_drvdata(pdev); - of_platform_depopulate(&dsidev->dev); - - WARN_ON(dsi->scp_clk_refcount > 0); + component_del(&pdev->dev, &dsi_component_ops); - dss_pll_unregister(&dsi->pll); + dsi_uninit_output(dsi); - dsi_uninit_output(dsidev); + mipi_dsi_host_unregister(&dsi->host); - pm_runtime_disable(&dsidev->dev); + pm_runtime_disable(&pdev->dev); if (dsi->vdds_dsi_reg != NULL && dsi->vdds_dsi_enabled) { regulator_disable(dsi->vdds_dsi_reg); @@ -5441,26 +5062,9 @@ static void dsi_unbind(struct device *dev, struct device *master, void *data) } } -static const struct component_ops dsi_component_ops = { - .bind = dsi_bind, - .unbind = dsi_unbind, -}; - -static int dsi_probe(struct platform_device *pdev) -{ - return component_add(&pdev->dev, &dsi_component_ops); -} - -static int dsi_remove(struct platform_device *pdev) +static __maybe_unused int dsi_runtime_suspend(struct device *dev) { - component_del(&pdev->dev, &dsi_component_ops); - return 0; -} - -static int dsi_runtime_suspend(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct dsi_data *dsi = dsi_get_dsidrv_data(pdev); + struct dsi_data *dsi = dev_get_drvdata(dev); dsi->is_enabled = false; /* ensure the irq handler sees the is_enabled value */ @@ -5468,20 +5072,12 @@ static int dsi_runtime_suspend(struct device *dev) /* wait for current handler to finish before turning the DSI off */ synchronize_irq(dsi->irq); - dispc_runtime_put(); - return 0; } -static int dsi_runtime_resume(struct device *dev) +static __maybe_unused int dsi_runtime_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct dsi_data *dsi = dsi_get_dsidrv_data(pdev); - int r; - - r = dispc_runtime_get(); - if (r) - return r; + struct dsi_data *dsi = dev_get_drvdata(dev); dsi->is_enabled = true; /* ensure the irq handler sees the is_enabled value */ @@ -5491,35 +5087,11 @@ static int dsi_runtime_resume(struct device *dev) } static const struct dev_pm_ops dsi_pm_ops = { - .runtime_suspend = dsi_runtime_suspend, - .runtime_resume = dsi_runtime_resume, -}; - -static const struct dsi_module_id_data dsi_of_data_omap3[] = { - { .address = 0x4804fc00, .id = 0, }, - { }, -}; - -static const struct dsi_module_id_data dsi_of_data_omap4[] = { - { .address = 0x58004000, .id = 0, }, - { .address = 0x58005000, .id = 1, }, - { }, -}; - -static const struct dsi_module_id_data dsi_of_data_omap5[] = { - { .address = 0x58004000, .id = 0, }, - { .address = 0x58009000, .id = 1, }, - { }, + SET_RUNTIME_PM_OPS(dsi_runtime_suspend, dsi_runtime_resume, NULL) + SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; -static const struct of_device_id dsi_of_match[] = { - { .compatible = "ti,omap3-dsi", .data = dsi_of_data_omap3, }, - { .compatible = "ti,omap4-dsi", .data = dsi_of_data_omap4, }, - { .compatible = "ti,omap5-dsi", .data = dsi_of_data_omap5, }, - {}, -}; - -static struct platform_driver omap_dsihw_driver = { +struct platform_driver omap_dsihw_driver = { .probe = dsi_probe, .remove = dsi_remove, .driver = { @@ -5529,13 +5101,3 @@ static struct platform_driver omap_dsihw_driver = { .suppress_bind_attrs = true, }, }; - -int __init dsi_init_platform_driver(void) -{ - return platform_driver_register(&omap_dsihw_driver); -} - -void dsi_uninit_platform_driver(void) -{ - platform_driver_unregister(&omap_dsihw_driver); -} diff --git a/drivers/gpu/drm/omapdrm/dss/dsi.h b/drivers/gpu/drm/omapdrm/dss/dsi.h new file mode 100644 index 000000000000..601707c0ecc4 --- /dev/null +++ b/drivers/gpu/drm/omapdrm/dss/dsi.h @@ -0,0 +1,456 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> + */ + +#ifndef __OMAP_DRM_DSS_DSI_H +#define __OMAP_DRM_DSS_DSI_H + +#include <drm/drm_mipi_dsi.h> + +struct dsi_reg { + u16 module; + u16 idx; +}; + +#define DSI_REG(mod, idx) ((const struct dsi_reg) { mod, idx }) + +/* DSI Protocol Engine */ + +#define DSI_PROTO 0 +#define DSI_PROTO_SZ 0x200 + +#define DSI_REVISION DSI_REG(DSI_PROTO, 0x0000) +#define DSI_SYSCONFIG DSI_REG(DSI_PROTO, 0x0010) +#define DSI_SYSSTATUS DSI_REG(DSI_PROTO, 0x0014) +#define DSI_IRQSTATUS DSI_REG(DSI_PROTO, 0x0018) +#define DSI_IRQENABLE DSI_REG(DSI_PROTO, 0x001C) +#define DSI_CTRL DSI_REG(DSI_PROTO, 0x0040) +#define DSI_GNQ DSI_REG(DSI_PROTO, 0x0044) +#define DSI_COMPLEXIO_CFG1 DSI_REG(DSI_PROTO, 0x0048) +#define DSI_COMPLEXIO_IRQ_STATUS DSI_REG(DSI_PROTO, 0x004C) +#define DSI_COMPLEXIO_IRQ_ENABLE DSI_REG(DSI_PROTO, 0x0050) +#define DSI_CLK_CTRL DSI_REG(DSI_PROTO, 0x0054) +#define DSI_TIMING1 DSI_REG(DSI_PROTO, 0x0058) +#define DSI_TIMING2 DSI_REG(DSI_PROTO, 0x005C) +#define DSI_VM_TIMING1 DSI_REG(DSI_PROTO, 0x0060) +#define DSI_VM_TIMING2 DSI_REG(DSI_PROTO, 0x0064) +#define DSI_VM_TIMING3 DSI_REG(DSI_PROTO, 0x0068) +#define DSI_CLK_TIMING DSI_REG(DSI_PROTO, 0x006C) +#define DSI_TX_FIFO_VC_SIZE DSI_REG(DSI_PROTO, 0x0070) +#define DSI_RX_FIFO_VC_SIZE DSI_REG(DSI_PROTO, 0x0074) +#define DSI_COMPLEXIO_CFG2 DSI_REG(DSI_PROTO, 0x0078) +#define DSI_RX_FIFO_VC_FULLNESS DSI_REG(DSI_PROTO, 0x007C) +#define DSI_VM_TIMING4 DSI_REG(DSI_PROTO, 0x0080) +#define DSI_TX_FIFO_VC_EMPTINESS DSI_REG(DSI_PROTO, 0x0084) +#define DSI_VM_TIMING5 DSI_REG(DSI_PROTO, 0x0088) +#define DSI_VM_TIMING6 DSI_REG(DSI_PROTO, 0x008C) +#define DSI_VM_TIMING7 DSI_REG(DSI_PROTO, 0x0090) +#define DSI_STOPCLK_TIMING DSI_REG(DSI_PROTO, 0x0094) +#define DSI_VC_CTRL(n) DSI_REG(DSI_PROTO, 0x0100 + (n * 0x20)) +#define DSI_VC_TE(n) DSI_REG(DSI_PROTO, 0x0104 + (n * 0x20)) +#define DSI_VC_LONG_PACKET_HEADER(n) DSI_REG(DSI_PROTO, 0x0108 + (n * 0x20)) +#define DSI_VC_LONG_PACKET_PAYLOAD(n) DSI_REG(DSI_PROTO, 0x010C + (n * 0x20)) +#define DSI_VC_SHORT_PACKET_HEADER(n) DSI_REG(DSI_PROTO, 0x0110 + (n * 0x20)) +#define DSI_VC_IRQSTATUS(n) DSI_REG(DSI_PROTO, 0x0118 + (n * 0x20)) +#define DSI_VC_IRQENABLE(n) DSI_REG(DSI_PROTO, 0x011C + (n * 0x20)) + +/* DSIPHY_SCP */ + +#define DSI_PHY 1 +#define DSI_PHY_OFFSET 0x200 +#define DSI_PHY_SZ 0x40 + +#define DSI_DSIPHY_CFG0 DSI_REG(DSI_PHY, 0x0000) +#define DSI_DSIPHY_CFG1 DSI_REG(DSI_PHY, 0x0004) +#define DSI_DSIPHY_CFG2 DSI_REG(DSI_PHY, 0x0008) +#define DSI_DSIPHY_CFG5 DSI_REG(DSI_PHY, 0x0014) +#define DSI_DSIPHY_CFG10 DSI_REG(DSI_PHY, 0x0028) + +/* DSI_PLL_CTRL_SCP */ + +#define DSI_PLL 2 +#define DSI_PLL_OFFSET 0x300 +#define DSI_PLL_SZ 0x20 + +#define DSI_PLL_CONTROL DSI_REG(DSI_PLL, 0x0000) +#define DSI_PLL_STATUS DSI_REG(DSI_PLL, 0x0004) +#define DSI_PLL_GO DSI_REG(DSI_PLL, 0x0008) +#define DSI_PLL_CONFIGURATION1 DSI_REG(DSI_PLL, 0x000C) +#define DSI_PLL_CONFIGURATION2 DSI_REG(DSI_PLL, 0x0010) + +/* Global interrupts */ +#define DSI_IRQ_VC0 (1 << 0) +#define DSI_IRQ_VC1 (1 << 1) +#define DSI_IRQ_VC2 (1 << 2) +#define DSI_IRQ_VC3 (1 << 3) +#define DSI_IRQ_WAKEUP (1 << 4) +#define DSI_IRQ_RESYNC (1 << 5) +#define DSI_IRQ_PLL_LOCK (1 << 7) +#define DSI_IRQ_PLL_UNLOCK (1 << 8) +#define DSI_IRQ_PLL_RECALL (1 << 9) +#define DSI_IRQ_COMPLEXIO_ERR (1 << 10) +#define DSI_IRQ_HS_TX_TIMEOUT (1 << 14) +#define DSI_IRQ_LP_RX_TIMEOUT (1 << 15) +#define DSI_IRQ_TE_TRIGGER (1 << 16) +#define DSI_IRQ_ACK_TRIGGER (1 << 17) +#define DSI_IRQ_SYNC_LOST (1 << 18) +#define DSI_IRQ_LDO_POWER_GOOD (1 << 19) +#define DSI_IRQ_TA_TIMEOUT (1 << 20) +#define DSI_IRQ_ERROR_MASK \ + (DSI_IRQ_HS_TX_TIMEOUT | DSI_IRQ_LP_RX_TIMEOUT | DSI_IRQ_SYNC_LOST | \ + DSI_IRQ_TA_TIMEOUT) +#define DSI_IRQ_CHANNEL_MASK 0xf + +/* Virtual channel interrupts */ +#define DSI_VC_IRQ_CS (1 << 0) +#define DSI_VC_IRQ_ECC_CORR (1 << 1) +#define DSI_VC_IRQ_PACKET_SENT (1 << 2) +#define DSI_VC_IRQ_FIFO_TX_OVF (1 << 3) +#define DSI_VC_IRQ_FIFO_RX_OVF (1 << 4) +#define DSI_VC_IRQ_BTA (1 << 5) +#define DSI_VC_IRQ_ECC_NO_CORR (1 << 6) +#define DSI_VC_IRQ_FIFO_TX_UDF (1 << 7) +#define DSI_VC_IRQ_PP_BUSY_CHANGE (1 << 8) +#define DSI_VC_IRQ_ERROR_MASK \ + (DSI_VC_IRQ_CS | DSI_VC_IRQ_ECC_CORR | DSI_VC_IRQ_FIFO_TX_OVF | \ + DSI_VC_IRQ_FIFO_RX_OVF | DSI_VC_IRQ_ECC_NO_CORR | \ + DSI_VC_IRQ_FIFO_TX_UDF) + +/* ComplexIO interrupts */ +#define DSI_CIO_IRQ_ERRSYNCESC1 (1 << 0) +#define DSI_CIO_IRQ_ERRSYNCESC2 (1 << 1) +#define DSI_CIO_IRQ_ERRSYNCESC3 (1 << 2) +#define DSI_CIO_IRQ_ERRSYNCESC4 (1 << 3) +#define DSI_CIO_IRQ_ERRSYNCESC5 (1 << 4) +#define DSI_CIO_IRQ_ERRESC1 (1 << 5) +#define DSI_CIO_IRQ_ERRESC2 (1 << 6) +#define DSI_CIO_IRQ_ERRESC3 (1 << 7) +#define DSI_CIO_IRQ_ERRESC4 (1 << 8) +#define DSI_CIO_IRQ_ERRESC5 (1 << 9) +#define DSI_CIO_IRQ_ERRCONTROL1 (1 << 10) +#define DSI_CIO_IRQ_ERRCONTROL2 (1 << 11) +#define DSI_CIO_IRQ_ERRCONTROL3 (1 << 12) +#define DSI_CIO_IRQ_ERRCONTROL4 (1 << 13) +#define DSI_CIO_IRQ_ERRCONTROL5 (1 << 14) +#define DSI_CIO_IRQ_STATEULPS1 (1 << 15) +#define DSI_CIO_IRQ_STATEULPS2 (1 << 16) +#define DSI_CIO_IRQ_STATEULPS3 (1 << 17) +#define DSI_CIO_IRQ_STATEULPS4 (1 << 18) +#define DSI_CIO_IRQ_STATEULPS5 (1 << 19) +#define DSI_CIO_IRQ_ERRCONTENTIONLP0_1 (1 << 20) +#define DSI_CIO_IRQ_ERRCONTENTIONLP1_1 (1 << 21) +#define DSI_CIO_IRQ_ERRCONTENTIONLP0_2 (1 << 22) +#define DSI_CIO_IRQ_ERRCONTENTIONLP1_2 (1 << 23) +#define DSI_CIO_IRQ_ERRCONTENTIONLP0_3 (1 << 24) +#define DSI_CIO_IRQ_ERRCONTENTIONLP1_3 (1 << 25) +#define DSI_CIO_IRQ_ERRCONTENTIONLP0_4 (1 << 26) +#define DSI_CIO_IRQ_ERRCONTENTIONLP1_4 (1 << 27) +#define DSI_CIO_IRQ_ERRCONTENTIONLP0_5 (1 << 28) +#define DSI_CIO_IRQ_ERRCONTENTIONLP1_5 (1 << 29) +#define DSI_CIO_IRQ_ULPSACTIVENOT_ALL0 (1 << 30) +#define DSI_CIO_IRQ_ULPSACTIVENOT_ALL1 (1 << 31) +#define DSI_CIO_IRQ_ERROR_MASK \ + (DSI_CIO_IRQ_ERRSYNCESC1 | DSI_CIO_IRQ_ERRSYNCESC2 | \ + DSI_CIO_IRQ_ERRSYNCESC3 | DSI_CIO_IRQ_ERRSYNCESC4 | \ + DSI_CIO_IRQ_ERRSYNCESC5 | \ + DSI_CIO_IRQ_ERRESC1 | DSI_CIO_IRQ_ERRESC2 | \ + DSI_CIO_IRQ_ERRESC3 | DSI_CIO_IRQ_ERRESC4 | \ + DSI_CIO_IRQ_ERRESC5 | \ + DSI_CIO_IRQ_ERRCONTROL1 | DSI_CIO_IRQ_ERRCONTROL2 | \ + DSI_CIO_IRQ_ERRCONTROL3 | DSI_CIO_IRQ_ERRCONTROL4 | \ + DSI_CIO_IRQ_ERRCONTROL5 | \ + DSI_CIO_IRQ_ERRCONTENTIONLP0_1 | DSI_CIO_IRQ_ERRCONTENTIONLP1_1 | \ + DSI_CIO_IRQ_ERRCONTENTIONLP0_2 | DSI_CIO_IRQ_ERRCONTENTIONLP1_2 | \ + DSI_CIO_IRQ_ERRCONTENTIONLP0_3 | DSI_CIO_IRQ_ERRCONTENTIONLP1_3 | \ + DSI_CIO_IRQ_ERRCONTENTIONLP0_4 | DSI_CIO_IRQ_ERRCONTENTIONLP1_4 | \ + DSI_CIO_IRQ_ERRCONTENTIONLP0_5 | DSI_CIO_IRQ_ERRCONTENTIONLP1_5) + +enum omap_dss_dsi_mode { + OMAP_DSS_DSI_CMD_MODE = 0, + OMAP_DSS_DSI_VIDEO_MODE, +}; + +enum omap_dss_dsi_trans_mode { + /* Sync Pulses: both sync start and end packets sent */ + OMAP_DSS_DSI_PULSE_MODE, + /* Sync Events: only sync start packets sent */ + OMAP_DSS_DSI_EVENT_MODE, + /* Burst: only sync start packets sent, pixels are time compressed */ + OMAP_DSS_DSI_BURST_MODE, +}; + +struct omap_dss_dsi_videomode_timings { + unsigned long hsclk; + + unsigned int ndl; + unsigned int bitspp; + + /* pixels */ + u16 hact; + /* lines */ + u16 vact; + + /* DSI video mode blanking data */ + /* Unit: byte clock cycles */ + u16 hss; + u16 hsa; + u16 hse; + u16 hfp; + u16 hbp; + /* Unit: line clocks */ + u16 vsa; + u16 vfp; + u16 vbp; + + /* DSI blanking modes */ + int blanking_mode; + int hsa_blanking_mode; + int hbp_blanking_mode; + int hfp_blanking_mode; + + enum omap_dss_dsi_trans_mode trans_mode; + + int window_sync; +}; + +struct omap_dss_dsi_config { + enum omap_dss_dsi_mode mode; + enum mipi_dsi_pixel_format pixel_format; + const struct videomode *vm; + + unsigned long hs_clk_min, hs_clk_max; + unsigned long lp_clk_min, lp_clk_max; + + enum omap_dss_dsi_trans_mode trans_mode; +}; + +/* DSI PLL HSDIV indices */ +#define HSDIV_DISPC 0 +#define HSDIV_DSI 1 + +#define DSI_MAX_NR_ISRS 2 +#define DSI_MAX_NR_LANES 5 + +enum dsi_model { + DSI_MODEL_OMAP3, + DSI_MODEL_OMAP4, + DSI_MODEL_OMAP5, +}; + +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; +}; + +typedef void (*omap_dsi_isr_t) (void *arg, u32 mask); + +struct dsi_isr_data { + omap_dsi_isr_t isr; + void *arg; + u32 mask; +}; + +enum fifo_size { + DSI_FIFO_SIZE_0 = 0, + DSI_FIFO_SIZE_32 = 1, + DSI_FIFO_SIZE_64 = 2, + DSI_FIFO_SIZE_96 = 3, + DSI_FIFO_SIZE_128 = 4, +}; + +enum dsi_vc_source { + DSI_VC_SOURCE_L4 = 0, + DSI_VC_SOURCE_VP, +}; + +struct dsi_irq_stats { + unsigned long last_reset; + unsigned int irq_count; + unsigned int dsi_irqs[32]; + unsigned int vc_irqs[4][32]; + unsigned int cio_irqs[32]; +}; + +struct dsi_isr_tables { + struct dsi_isr_data isr_table[DSI_MAX_NR_ISRS]; + struct dsi_isr_data isr_table_vc[4][DSI_MAX_NR_ISRS]; + struct dsi_isr_data isr_table_cio[DSI_MAX_NR_ISRS]; +}; + +struct dsi_lp_clock_info { + unsigned long lp_clk; + u16 lp_clk_div; +}; + +struct dsi_clk_calc_ctx { + struct dsi_data *dsi; + struct dss_pll *pll; + + /* inputs */ + + const struct omap_dss_dsi_config *config; + + unsigned long req_pck_min, req_pck_nom, req_pck_max; + + /* outputs */ + + struct dss_pll_clock_info dsi_cinfo; + struct dispc_clock_info dispc_cinfo; + struct dsi_lp_clock_info lp_cinfo; + + struct videomode vm; + struct omap_dss_dsi_videomode_timings dsi_vm; +}; + +struct dsi_module_id_data { + u32 address; + int id; +}; + +enum dsi_quirks { + DSI_QUIRK_PLL_PWR_BUG = (1 << 0), /* DSI-PLL power command 0x3 is not working */ + DSI_QUIRK_DCS_CMD_CONFIG_VC = (1 << 1), + DSI_QUIRK_VC_OCP_WIDTH = (1 << 2), + DSI_QUIRK_REVERSE_TXCLKESC = (1 << 3), + DSI_QUIRK_GNQ = (1 << 4), + DSI_QUIRK_PHY_DCC = (1 << 5), +}; + +struct dsi_of_data { + enum dsi_model model; + const struct dss_pll_hw *pll_hw; + const struct dsi_module_id_data *modules; + unsigned int max_fck_freq; + unsigned int max_pll_lpdiv; + enum dsi_quirks quirks; +}; + +struct dsi_data { + struct device *dev; + void __iomem *proto_base; + void __iomem *phy_base; + void __iomem *pll_base; + + const struct dsi_of_data *data; + int module_id; + + int irq; + + bool is_enabled; + + struct clk *dss_clk; + struct regmap *syscon; + struct dss_device *dss; + + struct mipi_dsi_host host; + + struct dispc_clock_info user_dispc_cinfo; + struct dss_pll_clock_info user_dsi_cinfo; + + struct dsi_lp_clock_info user_lp_cinfo; + struct dsi_lp_clock_info current_lp_cinfo; + + struct dss_pll pll; + + bool vdds_dsi_enabled; + struct regulator *vdds_dsi_reg; + + struct mipi_dsi_device *dsidev; + + struct { + enum dsi_vc_source source; + enum fifo_size tx_fifo_size; + enum fifo_size rx_fifo_size; + } vc[4]; + + struct mutex lock; + struct semaphore bus_lock; + + spinlock_t irq_lock; + struct dsi_isr_tables isr_tables; + /* space for a copy used by the interrupt handler */ + struct dsi_isr_tables isr_tables_copy; + + int update_vc; +#ifdef DSI_PERF_MEASURE + unsigned int update_bytes; +#endif + + /* external TE GPIO */ + struct gpio_desc *te_gpio; + int te_irq; + struct delayed_work te_timeout_work; + atomic_t do_ext_te_update; + + bool te_enabled; + bool iface_enabled; + bool video_enabled; + + struct delayed_work framedone_timeout_work; + +#ifdef DSI_CATCH_MISSING_TE + struct timer_list te_timer; +#endif + + unsigned long cache_req_pck; + unsigned long cache_clk_freq; + struct dss_pll_clock_info cache_cinfo; + + u32 errors; + spinlock_t errors_lock; +#ifdef DSI_PERF_MEASURE + ktime_t perf_setup_time; + ktime_t perf_start_time; +#endif + int debug_read; + int debug_write; + struct { + struct dss_debugfs_entry *irqs; + struct dss_debugfs_entry *regs; + struct dss_debugfs_entry *clks; + } debugfs; + +#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS + spinlock_t irq_stats_lock; + struct dsi_irq_stats irq_stats; +#endif + + unsigned int num_lanes_supported; + unsigned int line_buffer_size; + + struct dsi_lane_config lanes[DSI_MAX_NR_LANES]; + unsigned int num_lanes_used; + + unsigned int scp_clk_refcount; + + struct omap_dss_dsi_config config; + + struct dss_lcd_mgr_config mgr_config; + struct videomode vm; + enum mipi_dsi_pixel_format pix_fmt; + enum omap_dss_dsi_mode mode; + struct omap_dss_dsi_videomode_timings vm_timings; + + struct omap_dss_device output; + struct drm_bridge bridge; + + struct delayed_work dsi_disable_work; +}; + +struct dsi_packet_sent_handler_data { + struct dsi_data *dsi; + struct completion *completion; +}; + +#endif /* __OMAP_DRM_DSS_DSI_H */ diff --git a/drivers/gpu/drm/omapdrm/dss/dss-of.c b/drivers/gpu/drm/omapdrm/dss/dss-of.c deleted file mode 100644 index c6b86f348a5c..000000000000 --- a/drivers/gpu/drm/omapdrm/dss/dss-of.c +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2013 Texas Instruments - * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - */ - -#include <linux/device.h> -#include <linux/err.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/of_graph.h> -#include <linux/seq_file.h> - -#include "omapdss.h" - -struct device_node *dss_of_port_get_parent_device(struct device_node *port) -{ - struct device_node *np; - int i; - - if (!port) - return NULL; - - np = of_get_parent(port); - - for (i = 0; i < 2 && np; ++i) { - struct property *prop; - - prop = of_find_property(np, "compatible", NULL); - - if (prop) - return np; - - np = of_get_next_parent(np); - } - - return NULL; -} -EXPORT_SYMBOL_GPL(dss_of_port_get_parent_device); - -u32 dss_of_port_get_port_number(struct device_node *port) -{ - int r; - u32 reg; - - r = of_property_read_u32(port, "reg", ®); - if (r) - reg = 0; - - return reg; -} -EXPORT_SYMBOL_GPL(dss_of_port_get_port_number); - -struct omap_dss_device * -omapdss_of_find_source_for_first_ep(struct device_node *node) -{ - struct device_node *ep; - struct device_node *src_port; - struct omap_dss_device *src; - - ep = of_graph_get_endpoint_by_regs(node, 0, 0); - if (!ep) - return ERR_PTR(-EINVAL); - - src_port = of_graph_get_remote_port(ep); - if (!src_port) { - of_node_put(ep); - return ERR_PTR(-EINVAL); - } - - of_node_put(ep); - - src = omap_dss_find_output_by_port_node(src_port); - - of_node_put(src_port); - - return src ? src : ERR_PTR(-EPROBE_DEFER); -} -EXPORT_SYMBOL_GPL(omapdss_of_find_source_for_first_ep); diff --git a/drivers/gpu/drm/omapdrm/dss/dss.c b/drivers/gpu/drm/omapdrm/dss/dss.c index 99e22ca972c7..692df747e2ae 100644 --- a/drivers/gpu/drm/omapdrm/dss/dss.c +++ b/drivers/gpu/drm/omapdrm/dss/dss.c @@ -1,27 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0-only /* - * linux/drivers/video/omap2/dss/dss.c - * * Copyright (C) 2009 Nokia Corporation - * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> * * Some code and ideas taken from drivers/video/omap/ driver * by Imre Deak. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. */ #define DSS_SUBSYS_NAME "DSS" +#include <linux/debugfs.h> +#include <linux/dma-mapping.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/io.h> @@ -33,21 +22,21 @@ #include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/property.h> #include <linux/gfp.h> #include <linux/sizes.h> #include <linux/mfd/syscon.h> #include <linux/regmap.h> #include <linux/of.h> +#include <linux/of_platform.h> #include <linux/of_graph.h> #include <linux/regulator/consumer.h> #include <linux/suspend.h> #include <linux/component.h> +#include <linux/sys_soc.h> #include "omapdss.h" #include "dss.h" -#include "dss_features.h" - -#define DSS_SZ_REGS SZ_512 struct dss_reg { u16 idx; @@ -63,50 +52,35 @@ struct dss_reg { #define DSS_PLL_CONTROL DSS_REG(0x0048) #define DSS_SDI_STATUS DSS_REG(0x005C) -#define REG_GET(idx, start, end) \ - FLD_GET(dss_read_reg(idx), start, end) +#define REG_GET(dss, idx, start, end) \ + FLD_GET(dss_read_reg(dss, idx), start, end) -#define REG_FLD_MOD(idx, val, start, end) \ - dss_write_reg(idx, FLD_MOD(dss_read_reg(idx), val, start, end)) +#define REG_FLD_MOD(dss, idx, val, start, end) \ + dss_write_reg(dss, idx, \ + FLD_MOD(dss_read_reg(dss, idx), val, start, end)) + +struct dss_ops { + int (*dpi_select_source)(struct dss_device *dss, int port, + enum omap_channel channel); + int (*select_lcd_source)(struct dss_device *dss, + enum omap_channel channel, + enum dss_clk_source clk_src); +}; struct dss_features { + enum dss_model model; u8 fck_div_max; + unsigned int fck_freq_max; u8 dss_fck_multiplier; const char *parent_clk_name; const enum omap_display_type *ports; int num_ports; - int (*dpi_select_source)(int port, enum omap_channel channel); - int (*select_lcd_source)(enum omap_channel channel, - enum dss_clk_source clk_src); + const enum omap_dss_output_id *outputs; + const struct dss_ops *ops; + struct dss_reg_field dispc_clk_switch; + bool has_lcd_clk_src; }; -static struct { - struct platform_device *pdev; - void __iomem *base; - struct regmap *syscon_pll_ctrl; - u32 syscon_pll_ctrl_offset; - - struct clk *parent_clk; - struct clk *dss_clk; - unsigned long dss_clk_rate; - - unsigned long cache_req_pck; - unsigned long cache_prate; - struct dispc_clock_info cache_dispc_cinfo; - - enum dss_clk_source dsi_clk_source[MAX_NUM_DSI]; - enum dss_clk_source dispc_clk_source; - enum dss_clk_source lcd_clk_source[MAX_DSS_LCD_MANAGERS]; - - bool ctx_valid; - u32 ctx[DSS_SZ_REGS / sizeof(u32)]; - - const struct dss_features *feat; - - struct dss_pll *video1_pll; - struct dss_pll *video2_pll; -} dss; - static const char * const dss_generic_clk_source_names[] = { [DSS_CLK_SRC_FCK] = "FCK", [DSS_CLK_SRC_PLL1_1] = "PLL1:1", @@ -118,51 +92,50 @@ static const char * const dss_generic_clk_source_names[] = { [DSS_CLK_SRC_HDMI_PLL] = "HDMI PLL", }; -static inline void dss_write_reg(const struct dss_reg idx, u32 val) +static inline void dss_write_reg(struct dss_device *dss, + const struct dss_reg idx, u32 val) { - __raw_writel(val, dss.base + idx.idx); + __raw_writel(val, dss->base + idx.idx); } -static inline u32 dss_read_reg(const struct dss_reg idx) +static inline u32 dss_read_reg(struct dss_device *dss, const struct dss_reg idx) { - return __raw_readl(dss.base + idx.idx); + return __raw_readl(dss->base + idx.idx); } -#define SR(reg) \ - dss.ctx[(DSS_##reg).idx / sizeof(u32)] = dss_read_reg(DSS_##reg) -#define RR(reg) \ - dss_write_reg(DSS_##reg, dss.ctx[(DSS_##reg).idx / sizeof(u32)]) +#define SR(dss, reg) \ + dss->ctx[(DSS_##reg).idx / sizeof(u32)] = dss_read_reg(dss, DSS_##reg) +#define RR(dss, reg) \ + dss_write_reg(dss, DSS_##reg, dss->ctx[(DSS_##reg).idx / sizeof(u32)]) -static void dss_save_context(void) +static void dss_save_context(struct dss_device *dss) { DSSDBG("dss_save_context\n"); - SR(CONTROL); + SR(dss, CONTROL); - if (dss_feat_get_supported_displays(OMAP_DSS_CHANNEL_LCD) & - OMAP_DISPLAY_TYPE_SDI) { - SR(SDI_CONTROL); - SR(PLL_CONTROL); + if (dss->feat->outputs[OMAP_DSS_CHANNEL_LCD] & OMAP_DSS_OUTPUT_SDI) { + SR(dss, SDI_CONTROL); + SR(dss, PLL_CONTROL); } - dss.ctx_valid = true; + dss->ctx_valid = true; DSSDBG("context saved\n"); } -static void dss_restore_context(void) +static void dss_restore_context(struct dss_device *dss) { DSSDBG("dss_restore_context\n"); - if (!dss.ctx_valid) + if (!dss->ctx_valid) return; - RR(CONTROL); + RR(dss, CONTROL); - if (dss_feat_get_supported_displays(OMAP_DSS_CHANNEL_LCD) & - OMAP_DISPLAY_TYPE_SDI) { - RR(SDI_CONTROL); - RR(PLL_CONTROL); + if (dss->feat->outputs[OMAP_DSS_CHANNEL_LCD] & OMAP_DSS_OUTPUT_SDI) { + RR(dss, SDI_CONTROL); + RR(dss, PLL_CONTROL); } DSSDBG("context restored\n"); @@ -171,17 +144,17 @@ static void dss_restore_context(void) #undef SR #undef RR -void dss_ctrl_pll_enable(enum dss_pll_id pll_id, bool enable) +void dss_ctrl_pll_enable(struct dss_pll *pll, bool enable) { - unsigned shift; - unsigned val; + unsigned int shift; + unsigned int val; - if (!dss.syscon_pll_ctrl) + if (!pll->dss->syscon_pll_ctrl) return; val = !enable; - switch (pll_id) { + switch (pll->id) { case DSS_PLL_VIDEO1: shift = 0; break; @@ -192,20 +165,22 @@ void dss_ctrl_pll_enable(enum dss_pll_id pll_id, bool enable) shift = 2; break; default: - DSSERR("illegal DSS PLL ID %d\n", pll_id); + DSSERR("illegal DSS PLL ID %d\n", pll->id); return; } - regmap_update_bits(dss.syscon_pll_ctrl, dss.syscon_pll_ctrl_offset, - 1 << shift, val << shift); + regmap_update_bits(pll->dss->syscon_pll_ctrl, + pll->dss->syscon_pll_ctrl_offset, + 1 << shift, val << shift); } -static int dss_ctrl_pll_set_control_mux(enum dss_clk_source clk_src, - enum omap_channel channel) +static int dss_ctrl_pll_set_control_mux(struct dss_device *dss, + enum dss_clk_source clk_src, + enum omap_channel channel) { - unsigned shift, val; + unsigned int shift, val; - if (!dss.syscon_pll_ctrl) + if (!dss->syscon_pll_ctrl) return -EINVAL; switch (channel) { @@ -260,47 +235,47 @@ static int dss_ctrl_pll_set_control_mux(enum dss_clk_source clk_src, return -EINVAL; } - regmap_update_bits(dss.syscon_pll_ctrl, dss.syscon_pll_ctrl_offset, + regmap_update_bits(dss->syscon_pll_ctrl, dss->syscon_pll_ctrl_offset, 0x3 << shift, val << shift); return 0; } -void dss_sdi_init(int datapairs) +void dss_sdi_init(struct dss_device *dss, int datapairs) { u32 l; BUG_ON(datapairs > 3 || datapairs < 1); - l = dss_read_reg(DSS_SDI_CONTROL); + l = dss_read_reg(dss, DSS_SDI_CONTROL); l = FLD_MOD(l, 0xf, 19, 15); /* SDI_PDIV */ l = FLD_MOD(l, datapairs-1, 3, 2); /* SDI_PRSEL */ l = FLD_MOD(l, 2, 1, 0); /* SDI_BWSEL */ - dss_write_reg(DSS_SDI_CONTROL, l); + dss_write_reg(dss, DSS_SDI_CONTROL, l); - l = dss_read_reg(DSS_PLL_CONTROL); + l = dss_read_reg(dss, DSS_PLL_CONTROL); l = FLD_MOD(l, 0x7, 25, 22); /* SDI_PLL_FREQSEL */ l = FLD_MOD(l, 0xb, 16, 11); /* SDI_PLL_REGN */ l = FLD_MOD(l, 0xb4, 10, 1); /* SDI_PLL_REGM */ - dss_write_reg(DSS_PLL_CONTROL, l); + dss_write_reg(dss, DSS_PLL_CONTROL, l); } -int dss_sdi_enable(void) +int dss_sdi_enable(struct dss_device *dss) { unsigned long timeout; - dispc_pck_free_enable(1); + dispc_pck_free_enable(dss->dispc, 1); /* Reset SDI PLL */ - REG_FLD_MOD(DSS_PLL_CONTROL, 1, 18, 18); /* SDI_PLL_SYSRESET */ + REG_FLD_MOD(dss, DSS_PLL_CONTROL, 1, 18, 18); /* SDI_PLL_SYSRESET */ udelay(1); /* wait 2x PCLK */ /* Lock SDI PLL */ - REG_FLD_MOD(DSS_PLL_CONTROL, 1, 28, 28); /* SDI_PLL_GOBIT */ + REG_FLD_MOD(dss, DSS_PLL_CONTROL, 1, 28, 28); /* SDI_PLL_GOBIT */ /* Waiting for PLL lock request to complete */ timeout = jiffies + msecs_to_jiffies(500); - while (dss_read_reg(DSS_SDI_STATUS) & (1 << 6)) { + while (dss_read_reg(dss, DSS_SDI_STATUS) & (1 << 6)) { if (time_after_eq(jiffies, timeout)) { DSSERR("PLL lock request timed out\n"); goto err1; @@ -308,22 +283,22 @@ int dss_sdi_enable(void) } /* Clearing PLL_GO bit */ - REG_FLD_MOD(DSS_PLL_CONTROL, 0, 28, 28); + REG_FLD_MOD(dss, DSS_PLL_CONTROL, 0, 28, 28); /* Waiting for PLL to lock */ timeout = jiffies + msecs_to_jiffies(500); - while (!(dss_read_reg(DSS_SDI_STATUS) & (1 << 5))) { + while (!(dss_read_reg(dss, DSS_SDI_STATUS) & (1 << 5))) { if (time_after_eq(jiffies, timeout)) { DSSERR("PLL lock timed out\n"); goto err1; } } - dispc_lcd_enable_signal(1); + dispc_lcd_enable_signal(dss->dispc, 1); /* Waiting for SDI reset to complete */ timeout = jiffies + msecs_to_jiffies(500); - while (!(dss_read_reg(DSS_SDI_STATUS) & (1 << 2))) { + while (!(dss_read_reg(dss, DSS_SDI_STATUS) & (1 << 2))) { if (time_after_eq(jiffies, timeout)) { DSSERR("SDI reset timed out\n"); goto err2; @@ -333,24 +308,24 @@ int dss_sdi_enable(void) return 0; err2: - dispc_lcd_enable_signal(0); + dispc_lcd_enable_signal(dss->dispc, 0); err1: /* Reset SDI PLL */ - REG_FLD_MOD(DSS_PLL_CONTROL, 0, 18, 18); /* SDI_PLL_SYSRESET */ + REG_FLD_MOD(dss, DSS_PLL_CONTROL, 0, 18, 18); /* SDI_PLL_SYSRESET */ - dispc_pck_free_enable(0); + dispc_pck_free_enable(dss->dispc, 0); return -ETIMEDOUT; } -void dss_sdi_disable(void) +void dss_sdi_disable(struct dss_device *dss) { - dispc_lcd_enable_signal(0); + dispc_lcd_enable_signal(dss->dispc, 0); - dispc_pck_free_enable(0); + dispc_pck_free_enable(dss->dispc, 0); /* Reset SDI PLL */ - REG_FLD_MOD(DSS_PLL_CONTROL, 0, 18, 18); /* SDI_PLL_SYSRESET */ + REG_FLD_MOD(dss, DSS_PLL_CONTROL, 0, 18, 18); /* SDI_PLL_SYSRESET */ } const char *dss_get_clk_source_name(enum dss_clk_source clk_src) @@ -358,47 +333,58 @@ const char *dss_get_clk_source_name(enum dss_clk_source clk_src) return dss_generic_clk_source_names[clk_src]; } -void dss_dump_clocks(struct seq_file *s) +static void dss_dump_clocks(struct dss_device *dss, struct seq_file *s) { const char *fclk_name; unsigned long fclk_rate; - if (dss_runtime_get()) + if (dss_runtime_get(dss)) return; seq_printf(s, "- DSS -\n"); fclk_name = dss_get_clk_source_name(DSS_CLK_SRC_FCK); - fclk_rate = clk_get_rate(dss.dss_clk); + fclk_rate = clk_get_rate(dss->dss_clk); seq_printf(s, "%s = %lu\n", fclk_name, fclk_rate); - dss_runtime_put(); + dss_runtime_put(dss); } -static void dss_dump_regs(struct seq_file *s) +static int dss_dump_regs(struct seq_file *s, void *p) { -#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dss_read_reg(r)) + struct dss_device *dss = s->private; - if (dss_runtime_get()) - return; +#define DUMPREG(dss, r) seq_printf(s, "%-35s %08x\n", #r, dss_read_reg(dss, r)) - DUMPREG(DSS_REVISION); - DUMPREG(DSS_SYSCONFIG); - DUMPREG(DSS_SYSSTATUS); - DUMPREG(DSS_CONTROL); + if (dss_runtime_get(dss)) + return 0; + + DUMPREG(dss, DSS_REVISION); + DUMPREG(dss, DSS_SYSCONFIG); + DUMPREG(dss, DSS_SYSSTATUS); + DUMPREG(dss, DSS_CONTROL); - if (dss_feat_get_supported_displays(OMAP_DSS_CHANNEL_LCD) & - OMAP_DISPLAY_TYPE_SDI) { - DUMPREG(DSS_SDI_CONTROL); - DUMPREG(DSS_PLL_CONTROL); - DUMPREG(DSS_SDI_STATUS); + if (dss->feat->outputs[OMAP_DSS_CHANNEL_LCD] & OMAP_DSS_OUTPUT_SDI) { + DUMPREG(dss, DSS_SDI_CONTROL); + DUMPREG(dss, DSS_PLL_CONTROL); + DUMPREG(dss, DSS_SDI_STATUS); } - dss_runtime_put(); + dss_runtime_put(dss); #undef DUMPREG + return 0; +} + +static int dss_debug_dump_clocks(struct seq_file *s, void *p) +{ + struct dss_device *dss = s->private; + + dss_dump_clocks(dss, s); + dispc_dump_clocks(dss->dispc, s); + return 0; } static int dss_get_channel_index(enum omap_channel channel) @@ -416,17 +402,16 @@ static int dss_get_channel_index(enum omap_channel channel) } } -static void dss_select_dispc_clk_source(enum dss_clk_source clk_src) +static void dss_select_dispc_clk_source(struct dss_device *dss, + enum dss_clk_source clk_src) { int b; - u8 start, end; /* * We always use PRCM clock as the DISPC func clock, except on DSS3, * where we don't have separate DISPC and LCD clock sources. */ - if (WARN_ON(dss_has_feature(FEAT_LCD_CLK_SRC) && - clk_src != DSS_CLK_SRC_FCK)) + if (WARN_ON(dss->feat->has_lcd_clk_src && clk_src != DSS_CLK_SRC_FCK)) return; switch (clk_src) { @@ -444,15 +429,15 @@ static void dss_select_dispc_clk_source(enum dss_clk_source clk_src) return; } - dss_feat_get_reg_field(FEAT_REG_DISPC_CLK_SWITCH, &start, &end); - - REG_FLD_MOD(DSS_CONTROL, b, start, end); /* DISPC_CLK_SWITCH */ + REG_FLD_MOD(dss, DSS_CONTROL, b, /* DISPC_CLK_SWITCH */ + dss->feat->dispc_clk_switch.start, + dss->feat->dispc_clk_switch.end); - dss.dispc_clk_source = clk_src; + dss->dispc_clk_source = clk_src; } -void dss_select_dsi_clk_source(int dsi_module, - enum dss_clk_source clk_src) +void dss_select_dsi_clk_source(struct dss_device *dss, int dsi_module, + enum dss_clk_source clk_src) { int b, pos; @@ -474,13 +459,14 @@ void dss_select_dsi_clk_source(int dsi_module, } pos = dsi_module == 0 ? 1 : 10; - REG_FLD_MOD(DSS_CONTROL, b, pos, pos); /* DSIx_CLK_SWITCH */ + REG_FLD_MOD(dss, DSS_CONTROL, b, pos, pos); /* DSIx_CLK_SWITCH */ - dss.dsi_clk_source[dsi_module] = clk_src; + dss->dsi_clk_source[dsi_module] = clk_src; } -static int dss_lcd_clk_mux_dra7(enum omap_channel channel, - enum dss_clk_source clk_src) +static int dss_lcd_clk_mux_dra7(struct dss_device *dss, + enum omap_channel channel, + enum dss_clk_source clk_src) { const u8 ctrl_bits[] = { [OMAP_DSS_CHANNEL_LCD] = 0, @@ -493,21 +479,22 @@ static int dss_lcd_clk_mux_dra7(enum omap_channel channel, if (clk_src == DSS_CLK_SRC_FCK) { /* LCDx_CLK_SWITCH */ - REG_FLD_MOD(DSS_CONTROL, 0, ctrl_bit, ctrl_bit); + REG_FLD_MOD(dss, DSS_CONTROL, 0, ctrl_bit, ctrl_bit); return -EINVAL; } - r = dss_ctrl_pll_set_control_mux(clk_src, channel); + r = dss_ctrl_pll_set_control_mux(dss, clk_src, channel); if (r) return r; - REG_FLD_MOD(DSS_CONTROL, 1, ctrl_bit, ctrl_bit); + REG_FLD_MOD(dss, DSS_CONTROL, 1, ctrl_bit, ctrl_bit); return 0; } -static int dss_lcd_clk_mux_omap5(enum omap_channel channel, - enum dss_clk_source clk_src) +static int dss_lcd_clk_mux_omap5(struct dss_device *dss, + enum omap_channel channel, + enum dss_clk_source clk_src) { const u8 ctrl_bits[] = { [OMAP_DSS_CHANNEL_LCD] = 0, @@ -524,20 +511,21 @@ static int dss_lcd_clk_mux_omap5(enum omap_channel channel, if (clk_src == DSS_CLK_SRC_FCK) { /* LCDx_CLK_SWITCH */ - REG_FLD_MOD(DSS_CONTROL, 0, ctrl_bit, ctrl_bit); + REG_FLD_MOD(dss, DSS_CONTROL, 0, ctrl_bit, ctrl_bit); return -EINVAL; } if (WARN_ON(allowed_plls[channel] != clk_src)) return -EINVAL; - REG_FLD_MOD(DSS_CONTROL, 1, ctrl_bit, ctrl_bit); + REG_FLD_MOD(dss, DSS_CONTROL, 1, ctrl_bit, ctrl_bit); return 0; } -static int dss_lcd_clk_mux_omap4(enum omap_channel channel, - enum dss_clk_source clk_src) +static int dss_lcd_clk_mux_omap4(struct dss_device *dss, + enum omap_channel channel, + enum dss_clk_source clk_src) { const u8 ctrl_bits[] = { [OMAP_DSS_CHANNEL_LCD] = 0, @@ -552,87 +540,90 @@ static int dss_lcd_clk_mux_omap4(enum omap_channel channel, if (clk_src == DSS_CLK_SRC_FCK) { /* LCDx_CLK_SWITCH */ - REG_FLD_MOD(DSS_CONTROL, 0, ctrl_bit, ctrl_bit); + REG_FLD_MOD(dss, DSS_CONTROL, 0, ctrl_bit, ctrl_bit); return 0; } if (WARN_ON(allowed_plls[channel] != clk_src)) return -EINVAL; - REG_FLD_MOD(DSS_CONTROL, 1, ctrl_bit, ctrl_bit); + REG_FLD_MOD(dss, DSS_CONTROL, 1, ctrl_bit, ctrl_bit); return 0; } -void dss_select_lcd_clk_source(enum omap_channel channel, - enum dss_clk_source clk_src) +void dss_select_lcd_clk_source(struct dss_device *dss, + enum omap_channel channel, + enum dss_clk_source clk_src) { int idx = dss_get_channel_index(channel); int r; - if (!dss_has_feature(FEAT_LCD_CLK_SRC)) { - dss_select_dispc_clk_source(clk_src); - dss.lcd_clk_source[idx] = clk_src; + if (!dss->feat->has_lcd_clk_src) { + dss_select_dispc_clk_source(dss, clk_src); + dss->lcd_clk_source[idx] = clk_src; return; } - r = dss.feat->select_lcd_source(channel, clk_src); + r = dss->feat->ops->select_lcd_source(dss, channel, clk_src); if (r) return; - dss.lcd_clk_source[idx] = clk_src; + dss->lcd_clk_source[idx] = clk_src; } -enum dss_clk_source dss_get_dispc_clk_source(void) +enum dss_clk_source dss_get_dispc_clk_source(struct dss_device *dss) { - return dss.dispc_clk_source; + return dss->dispc_clk_source; } -enum dss_clk_source dss_get_dsi_clk_source(int dsi_module) +enum dss_clk_source dss_get_dsi_clk_source(struct dss_device *dss, + int dsi_module) { - return dss.dsi_clk_source[dsi_module]; + return dss->dsi_clk_source[dsi_module]; } -enum dss_clk_source dss_get_lcd_clk_source(enum omap_channel channel) +enum dss_clk_source dss_get_lcd_clk_source(struct dss_device *dss, + enum omap_channel channel) { - if (dss_has_feature(FEAT_LCD_CLK_SRC)) { + if (dss->feat->has_lcd_clk_src) { int idx = dss_get_channel_index(channel); - return dss.lcd_clk_source[idx]; + return dss->lcd_clk_source[idx]; } else { /* LCD_CLK source is the same as DISPC_FCLK source for * OMAP2 and OMAP3 */ - return dss.dispc_clk_source; + return dss->dispc_clk_source; } } -bool dss_div_calc(unsigned long pck, unsigned long fck_min, - dss_div_calc_func func, void *data) +bool dss_div_calc(struct dss_device *dss, unsigned long pck, + unsigned long fck_min, dss_div_calc_func func, void *data) { int fckd, fckd_start, fckd_stop; unsigned long fck; unsigned long fck_hw_max; unsigned long fckd_hw_max; unsigned long prate; - unsigned m; + unsigned int m; - fck_hw_max = dss_feat_get_param_max(FEAT_PARAM_DSS_FCK); + fck_hw_max = dss->feat->fck_freq_max; - if (dss.parent_clk == NULL) { - unsigned pckd; + if (dss->parent_clk == NULL) { + unsigned int pckd; pckd = fck_hw_max / pck; fck = pck * pckd; - fck = clk_round_rate(dss.dss_clk, fck); + fck = clk_round_rate(dss->dss_clk, fck); return func(fck, data); } - fckd_hw_max = dss.feat->fck_div_max; + fckd_hw_max = dss->feat->fck_div_max; - m = dss.feat->dss_fck_multiplier; - prate = clk_get_rate(dss.parent_clk); + m = dss->feat->dss_fck_multiplier; + prate = clk_get_rate(dss->parent_clk); fck_min = fck_min ? fck_min : 1; @@ -649,57 +640,62 @@ bool dss_div_calc(unsigned long pck, unsigned long fck_min, return false; } -int dss_set_fck_rate(unsigned long rate) +int dss_set_fck_rate(struct dss_device *dss, unsigned long rate) { int r; DSSDBG("set fck to %lu\n", rate); - r = clk_set_rate(dss.dss_clk, rate); + r = clk_set_rate(dss->dss_clk, rate); if (r) return r; - dss.dss_clk_rate = clk_get_rate(dss.dss_clk); + dss->dss_clk_rate = clk_get_rate(dss->dss_clk); - WARN_ONCE(dss.dss_clk_rate != rate, - "clk rate mismatch: %lu != %lu", dss.dss_clk_rate, - rate); + WARN_ONCE(dss->dss_clk_rate != rate, "clk rate mismatch: %lu != %lu", + dss->dss_clk_rate, rate); return 0; } -unsigned long dss_get_dispc_clk_rate(void) +unsigned long dss_get_dispc_clk_rate(struct dss_device *dss) { - return dss.dss_clk_rate; + return dss->dss_clk_rate; } -static int dss_setup_default_clock(void) +unsigned long dss_get_max_fck_rate(struct dss_device *dss) +{ + return dss->feat->fck_freq_max; +} + +static int dss_setup_default_clock(struct dss_device *dss) { unsigned long max_dss_fck, prate; unsigned long fck; - unsigned fck_div; + unsigned int fck_div; int r; - max_dss_fck = dss_feat_get_param_max(FEAT_PARAM_DSS_FCK); + max_dss_fck = dss->feat->fck_freq_max; - if (dss.parent_clk == NULL) { - fck = clk_round_rate(dss.dss_clk, max_dss_fck); + if (dss->parent_clk == NULL) { + fck = clk_round_rate(dss->dss_clk, max_dss_fck); } else { - prate = clk_get_rate(dss.parent_clk); + prate = clk_get_rate(dss->parent_clk); - fck_div = DIV_ROUND_UP(prate * dss.feat->dss_fck_multiplier, + fck_div = DIV_ROUND_UP(prate * dss->feat->dss_fck_multiplier, max_dss_fck); - fck = DIV_ROUND_UP(prate, fck_div) * dss.feat->dss_fck_multiplier; + fck = DIV_ROUND_UP(prate, fck_div) + * dss->feat->dss_fck_multiplier; } - r = dss_set_fck_rate(fck); + r = dss_set_fck_rate(dss, fck); if (r) return r; return 0; } -void dss_set_venc_output(enum omap_dss_venc_type type) +void dss_set_venc_output(struct dss_device *dss, enum omap_dss_venc_type type) { int l = 0; @@ -711,43 +707,35 @@ void dss_set_venc_output(enum omap_dss_venc_type type) BUG(); /* venc out selection. 0 = comp, 1 = svideo */ - REG_FLD_MOD(DSS_CONTROL, l, 6, 6); + REG_FLD_MOD(dss, DSS_CONTROL, l, 6, 6); } -void dss_set_dac_pwrdn_bgz(bool enable) +void dss_set_dac_pwrdn_bgz(struct dss_device *dss, bool enable) { - REG_FLD_MOD(DSS_CONTROL, enable, 5, 5); /* DAC Power-Down Control */ + /* DAC Power-Down Control */ + REG_FLD_MOD(dss, DSS_CONTROL, enable, 5, 5); } -void dss_select_hdmi_venc_clk_source(enum dss_hdmi_venc_clk_source_select src) +void dss_select_hdmi_venc_clk_source(struct dss_device *dss, + enum dss_hdmi_venc_clk_source_select src) { - enum omap_display_type dp; - dp = dss_feat_get_supported_displays(OMAP_DSS_CHANNEL_DIGIT); + enum omap_dss_output_id outputs; + + outputs = dss->feat->outputs[OMAP_DSS_CHANNEL_DIGIT]; /* Complain about invalid selections */ - WARN_ON((src == DSS_VENC_TV_CLK) && !(dp & OMAP_DISPLAY_TYPE_VENC)); - WARN_ON((src == DSS_HDMI_M_PCLK) && !(dp & OMAP_DISPLAY_TYPE_HDMI)); + WARN_ON((src == DSS_VENC_TV_CLK) && !(outputs & OMAP_DSS_OUTPUT_VENC)); + WARN_ON((src == DSS_HDMI_M_PCLK) && !(outputs & OMAP_DSS_OUTPUT_HDMI)); /* Select only if we have options */ - if ((dp & OMAP_DISPLAY_TYPE_VENC) && (dp & OMAP_DISPLAY_TYPE_HDMI)) - REG_FLD_MOD(DSS_CONTROL, src, 15, 15); /* VENC_HDMI_SWITCH */ + if ((outputs & OMAP_DSS_OUTPUT_VENC) && + (outputs & OMAP_DSS_OUTPUT_HDMI)) + /* VENC_HDMI_SWITCH */ + REG_FLD_MOD(dss, DSS_CONTROL, src, 15, 15); } -enum dss_hdmi_venc_clk_source_select dss_get_hdmi_venc_clk_source(void) -{ - enum omap_display_type displays; - - displays = dss_feat_get_supported_displays(OMAP_DSS_CHANNEL_DIGIT); - if ((displays & OMAP_DISPLAY_TYPE_HDMI) == 0) - return DSS_VENC_TV_CLK; - - if ((displays & OMAP_DISPLAY_TYPE_VENC) == 0) - return DSS_HDMI_M_PCLK; - - return REG_GET(DSS_CONTROL, 15, 15); -} - -static int dss_dpi_select_source_omap2_omap3(int port, enum omap_channel channel) +static int dss_dpi_select_source_omap2_omap3(struct dss_device *dss, int port, + enum omap_channel channel) { if (channel != OMAP_DSS_CHANNEL_LCD) return -EINVAL; @@ -755,7 +743,8 @@ static int dss_dpi_select_source_omap2_omap3(int port, enum omap_channel channel return 0; } -static int dss_dpi_select_source_omap4(int port, enum omap_channel channel) +static int dss_dpi_select_source_omap4(struct dss_device *dss, int port, + enum omap_channel channel) { int val; @@ -770,12 +759,13 @@ static int dss_dpi_select_source_omap4(int port, enum omap_channel channel) return -EINVAL; } - REG_FLD_MOD(DSS_CONTROL, val, 17, 17); + REG_FLD_MOD(dss, DSS_CONTROL, val, 17, 17); return 0; } -static int dss_dpi_select_source_omap5(int port, enum omap_channel channel) +static int dss_dpi_select_source_omap5(struct dss_device *dss, int port, + enum omap_channel channel) { int val; @@ -796,16 +786,17 @@ static int dss_dpi_select_source_omap5(int port, enum omap_channel channel) return -EINVAL; } - REG_FLD_MOD(DSS_CONTROL, val, 17, 16); + REG_FLD_MOD(dss, DSS_CONTROL, val, 17, 16); return 0; } -static int dss_dpi_select_source_dra7xx(int port, enum omap_channel channel) +static int dss_dpi_select_source_dra7xx(struct dss_device *dss, int port, + enum omap_channel channel) { switch (port) { case 0: - return dss_dpi_select_source_omap5(port, channel); + return dss_dpi_select_source_omap5(dss, port, channel); case 1: if (channel != OMAP_DSS_CHANNEL_LCD2) return -EINVAL; @@ -821,77 +812,171 @@ static int dss_dpi_select_source_dra7xx(int port, enum omap_channel channel) return 0; } -int dss_dpi_select_source(int port, enum omap_channel channel) +int dss_dpi_select_source(struct dss_device *dss, int port, + enum omap_channel channel) { - return dss.feat->dpi_select_source(port, channel); + return dss->feat->ops->dpi_select_source(dss, port, channel); } -static int dss_get_clocks(void) +static int dss_get_clocks(struct dss_device *dss) { struct clk *clk; - clk = devm_clk_get(&dss.pdev->dev, "fck"); + clk = devm_clk_get(&dss->pdev->dev, "fck"); if (IS_ERR(clk)) { DSSERR("can't get clock fck\n"); return PTR_ERR(clk); } - dss.dss_clk = clk; + dss->dss_clk = clk; - if (dss.feat->parent_clk_name) { - clk = clk_get(NULL, dss.feat->parent_clk_name); + if (dss->feat->parent_clk_name) { + clk = clk_get(NULL, dss->feat->parent_clk_name); if (IS_ERR(clk)) { - DSSERR("Failed to get %s\n", dss.feat->parent_clk_name); + DSSERR("Failed to get %s\n", + dss->feat->parent_clk_name); return PTR_ERR(clk); } } else { clk = NULL; } - dss.parent_clk = clk; + dss->parent_clk = clk; return 0; } -static void dss_put_clocks(void) +static void dss_put_clocks(struct dss_device *dss) { - if (dss.parent_clk) - clk_put(dss.parent_clk); + if (dss->parent_clk) + clk_put(dss->parent_clk); } -int dss_runtime_get(void) +int dss_runtime_get(struct dss_device *dss) { int r; DSSDBG("dss_runtime_get\n"); - r = pm_runtime_get_sync(&dss.pdev->dev); - WARN_ON(r < 0); - return r < 0 ? r : 0; + r = pm_runtime_get_sync(&dss->pdev->dev); + if (WARN_ON(r < 0)) { + pm_runtime_put_noidle(&dss->pdev->dev); + return r; + } + return 0; } -void dss_runtime_put(void) +void dss_runtime_put(struct dss_device *dss) { int r; DSSDBG("dss_runtime_put\n"); - r = pm_runtime_put_sync(&dss.pdev->dev); + r = pm_runtime_put_sync(&dss->pdev->dev); WARN_ON(r < 0 && r != -ENOSYS && r != -EBUSY); } +struct dss_device *dss_get_device(struct device *dev) +{ + return dev_get_drvdata(dev); +} + /* DEBUGFS */ #if defined(CONFIG_OMAP2_DSS_DEBUGFS) -void dss_debug_dump_clocks(struct seq_file *s) +static int dss_initialize_debugfs(struct dss_device *dss) { - dss_dump_clocks(s); - dispc_dump_clocks(s); -#ifdef CONFIG_OMAP2_DSS_DSI - dsi_dump_clocks(s); -#endif + struct dentry *dir; + + dir = debugfs_create_dir("omapdss", NULL); + if (IS_ERR(dir)) + return PTR_ERR(dir); + + dss->debugfs.root = dir; + + return 0; +} + +static void dss_uninitialize_debugfs(struct dss_device *dss) +{ + debugfs_remove_recursive(dss->debugfs.root); +} + +struct dss_debugfs_entry { + struct dentry *dentry; + int (*show_fn)(struct seq_file *s, void *data); + void *data; +}; + +static int dss_debug_open(struct inode *inode, struct file *file) +{ + struct dss_debugfs_entry *entry = inode->i_private; + + return single_open(file, entry->show_fn, entry->data); +} + +static const struct file_operations dss_debug_fops = { + .open = dss_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +struct dss_debugfs_entry * +dss_debugfs_create_file(struct dss_device *dss, const char *name, + int (*show_fn)(struct seq_file *s, void *data), + void *data) +{ + struct dss_debugfs_entry *entry; + + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return ERR_PTR(-ENOMEM); + + entry->show_fn = show_fn; + entry->data = data; + entry->dentry = debugfs_create_file(name, 0444, dss->debugfs.root, + entry, &dss_debug_fops); + + return entry; } -#endif +void dss_debugfs_remove_file(struct dss_debugfs_entry *entry) +{ + if (IS_ERR_OR_NULL(entry)) + return; + + debugfs_remove(entry->dentry); + kfree(entry); +} + +#else /* CONFIG_OMAP2_DSS_DEBUGFS */ +static inline int dss_initialize_debugfs(struct dss_device *dss) +{ + return 0; +} +static inline void dss_uninitialize_debugfs(struct dss_device *dss) +{ +} +#endif /* CONFIG_OMAP2_DSS_DEBUGFS */ + +static const struct dss_ops dss_ops_omap2_omap3 = { + .dpi_select_source = &dss_dpi_select_source_omap2_omap3, +}; + +static const struct dss_ops dss_ops_omap4 = { + .dpi_select_source = &dss_dpi_select_source_omap4, + .select_lcd_source = &dss_lcd_clk_mux_omap4, +}; + +static const struct dss_ops dss_ops_omap5 = { + .dpi_select_source = &dss_dpi_select_source_omap5, + .select_lcd_source = &dss_lcd_clk_mux_omap5, +}; + +static const struct dss_ops dss_ops_dra7 = { + .dpi_select_source = &dss_dpi_select_source_dra7xx, + .select_lcd_source = &dss_lcd_clk_mux_dra7, +}; static const enum omap_display_type omap2plus_ports[] = { OMAP_DISPLAY_TYPE_DPI, @@ -908,182 +993,242 @@ static const enum omap_display_type dra7xx_ports[] = { OMAP_DISPLAY_TYPE_DPI, }; +static const enum omap_dss_output_id omap2_dss_supported_outputs[] = { + /* OMAP_DSS_CHANNEL_LCD */ + OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI, + + /* OMAP_DSS_CHANNEL_DIGIT */ + OMAP_DSS_OUTPUT_VENC, +}; + +static const enum omap_dss_output_id omap3430_dss_supported_outputs[] = { + /* OMAP_DSS_CHANNEL_LCD */ + OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI | + OMAP_DSS_OUTPUT_SDI | OMAP_DSS_OUTPUT_DSI1, + + /* OMAP_DSS_CHANNEL_DIGIT */ + OMAP_DSS_OUTPUT_VENC, +}; + +static const enum omap_dss_output_id omap3630_dss_supported_outputs[] = { + /* OMAP_DSS_CHANNEL_LCD */ + OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI | + OMAP_DSS_OUTPUT_DSI1, + + /* OMAP_DSS_CHANNEL_DIGIT */ + OMAP_DSS_OUTPUT_VENC, +}; + +static const enum omap_dss_output_id am43xx_dss_supported_outputs[] = { + /* OMAP_DSS_CHANNEL_LCD */ + OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI, +}; + +static const enum omap_dss_output_id omap4_dss_supported_outputs[] = { + /* OMAP_DSS_CHANNEL_LCD */ + OMAP_DSS_OUTPUT_DBI | OMAP_DSS_OUTPUT_DSI1, + + /* OMAP_DSS_CHANNEL_DIGIT */ + OMAP_DSS_OUTPUT_VENC | OMAP_DSS_OUTPUT_HDMI, + + /* OMAP_DSS_CHANNEL_LCD2 */ + OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI | + OMAP_DSS_OUTPUT_DSI2, +}; + +static const enum omap_dss_output_id omap5_dss_supported_outputs[] = { + /* OMAP_DSS_CHANNEL_LCD */ + OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI | + OMAP_DSS_OUTPUT_DSI1 | OMAP_DSS_OUTPUT_DSI2, + + /* OMAP_DSS_CHANNEL_DIGIT */ + OMAP_DSS_OUTPUT_HDMI, + + /* OMAP_DSS_CHANNEL_LCD2 */ + OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI | + OMAP_DSS_OUTPUT_DSI1, + + /* OMAP_DSS_CHANNEL_LCD3 */ + OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI | + OMAP_DSS_OUTPUT_DSI2, +}; + static const struct dss_features omap24xx_dss_feats = { + .model = DSS_MODEL_OMAP2, /* * fck div max is really 16, but the divider range has gaps. The range * from 1 to 6 has no gaps, so let's use that as a max. */ .fck_div_max = 6, + .fck_freq_max = 133000000, .dss_fck_multiplier = 2, .parent_clk_name = "core_ck", - .dpi_select_source = &dss_dpi_select_source_omap2_omap3, .ports = omap2plus_ports, .num_ports = ARRAY_SIZE(omap2plus_ports), + .outputs = omap2_dss_supported_outputs, + .ops = &dss_ops_omap2_omap3, + .dispc_clk_switch = { 0, 0 }, + .has_lcd_clk_src = false, }; static const struct dss_features omap34xx_dss_feats = { + .model = DSS_MODEL_OMAP3, .fck_div_max = 16, + .fck_freq_max = 173000000, .dss_fck_multiplier = 2, .parent_clk_name = "dpll4_ck", - .dpi_select_source = &dss_dpi_select_source_omap2_omap3, .ports = omap34xx_ports, + .outputs = omap3430_dss_supported_outputs, .num_ports = ARRAY_SIZE(omap34xx_ports), + .ops = &dss_ops_omap2_omap3, + .dispc_clk_switch = { 0, 0 }, + .has_lcd_clk_src = false, }; static const struct dss_features omap3630_dss_feats = { - .fck_div_max = 32, + .model = DSS_MODEL_OMAP3, + .fck_div_max = 31, + .fck_freq_max = 173000000, .dss_fck_multiplier = 1, .parent_clk_name = "dpll4_ck", - .dpi_select_source = &dss_dpi_select_source_omap2_omap3, .ports = omap2plus_ports, .num_ports = ARRAY_SIZE(omap2plus_ports), + .outputs = omap3630_dss_supported_outputs, + .ops = &dss_ops_omap2_omap3, + .dispc_clk_switch = { 0, 0 }, + .has_lcd_clk_src = false, }; static const struct dss_features omap44xx_dss_feats = { + .model = DSS_MODEL_OMAP4, .fck_div_max = 32, + .fck_freq_max = 186000000, .dss_fck_multiplier = 1, .parent_clk_name = "dpll_per_x2_ck", - .dpi_select_source = &dss_dpi_select_source_omap4, .ports = omap2plus_ports, .num_ports = ARRAY_SIZE(omap2plus_ports), - .select_lcd_source = &dss_lcd_clk_mux_omap4, + .outputs = omap4_dss_supported_outputs, + .ops = &dss_ops_omap4, + .dispc_clk_switch = { 9, 8 }, + .has_lcd_clk_src = true, }; static const struct dss_features omap54xx_dss_feats = { + .model = DSS_MODEL_OMAP5, .fck_div_max = 64, + .fck_freq_max = 209250000, .dss_fck_multiplier = 1, .parent_clk_name = "dpll_per_x2_ck", - .dpi_select_source = &dss_dpi_select_source_omap5, .ports = omap2plus_ports, .num_ports = ARRAY_SIZE(omap2plus_ports), - .select_lcd_source = &dss_lcd_clk_mux_omap5, + .outputs = omap5_dss_supported_outputs, + .ops = &dss_ops_omap5, + .dispc_clk_switch = { 9, 7 }, + .has_lcd_clk_src = true, }; static const struct dss_features am43xx_dss_feats = { + .model = DSS_MODEL_OMAP3, .fck_div_max = 0, + .fck_freq_max = 200000000, .dss_fck_multiplier = 0, .parent_clk_name = NULL, - .dpi_select_source = &dss_dpi_select_source_omap2_omap3, .ports = omap2plus_ports, .num_ports = ARRAY_SIZE(omap2plus_ports), + .outputs = am43xx_dss_supported_outputs, + .ops = &dss_ops_omap2_omap3, + .dispc_clk_switch = { 0, 0 }, + .has_lcd_clk_src = true, }; static const struct dss_features dra7xx_dss_feats = { + .model = DSS_MODEL_DRA7, .fck_div_max = 64, + .fck_freq_max = 209250000, .dss_fck_multiplier = 1, .parent_clk_name = "dpll_per_x2_ck", - .dpi_select_source = &dss_dpi_select_source_dra7xx, .ports = dra7xx_ports, .num_ports = ARRAY_SIZE(dra7xx_ports), - .select_lcd_source = &dss_lcd_clk_mux_dra7, + .outputs = omap5_dss_supported_outputs, + .ops = &dss_ops_dra7, + .dispc_clk_switch = { 9, 7 }, + .has_lcd_clk_src = true, }; -static int dss_init_features(struct platform_device *pdev) -{ - const struct dss_features *src; - struct dss_features *dst; - - dst = devm_kzalloc(&pdev->dev, sizeof(*dst), GFP_KERNEL); - if (!dst) { - dev_err(&pdev->dev, "Failed to allocate local DSS Features\n"); - return -ENOMEM; - } - - switch (omapdss_get_version()) { - case OMAPDSS_VER_OMAP24xx: - src = &omap24xx_dss_feats; - break; - - case OMAPDSS_VER_OMAP34xx_ES1: - case OMAPDSS_VER_OMAP34xx_ES3: - case OMAPDSS_VER_AM35xx: - src = &omap34xx_dss_feats; - break; - - case OMAPDSS_VER_OMAP3630: - src = &omap3630_dss_feats; - break; - - case OMAPDSS_VER_OMAP4430_ES1: - case OMAPDSS_VER_OMAP4430_ES2: - case OMAPDSS_VER_OMAP4: - src = &omap44xx_dss_feats; - break; - - case OMAPDSS_VER_OMAP5: - src = &omap54xx_dss_feats; - break; - - case OMAPDSS_VER_AM43xx: - src = &am43xx_dss_feats; - break; - - case OMAPDSS_VER_DRA7xx: - src = &dra7xx_dss_feats; - break; - - default: - return -ENODEV; - } - - memcpy(dst, src, sizeof(*dst)); - dss.feat = dst; - - return 0; -} - -static int dss_init_ports(struct platform_device *pdev) +static void __dss_uninit_ports(struct dss_device *dss, unsigned int num_ports) { + struct platform_device *pdev = dss->pdev; struct device_node *parent = pdev->dev.of_node; struct device_node *port; - int i; + unsigned int i; - for (i = 0; i < dss.feat->num_ports; i++) { + for (i = 0; i < num_ports; i++) { port = of_graph_get_port_by_id(parent, i); if (!port) continue; - switch (dss.feat->ports[i]) { + switch (dss->feat->ports[i]) { case OMAP_DISPLAY_TYPE_DPI: - dpi_init_port(pdev, port); + dpi_uninit_port(port); break; case OMAP_DISPLAY_TYPE_SDI: - sdi_init_port(pdev, port); + sdi_uninit_port(port); break; default: break; } + of_node_put(port); } - - return 0; } -static void dss_uninit_ports(struct platform_device *pdev) +static int dss_init_ports(struct dss_device *dss) { + struct platform_device *pdev = dss->pdev; struct device_node *parent = pdev->dev.of_node; struct device_node *port; - int i; + unsigned int i; + int r; - for (i = 0; i < dss.feat->num_ports; i++) { + for (i = 0; i < dss->feat->num_ports; i++) { port = of_graph_get_port_by_id(parent, i); if (!port) continue; - switch (dss.feat->ports[i]) { + switch (dss->feat->ports[i]) { case OMAP_DISPLAY_TYPE_DPI: - dpi_uninit_port(port); + r = dpi_init_port(dss, pdev, port, dss->feat->model); + if (r) + goto error; break; + case OMAP_DISPLAY_TYPE_SDI: - sdi_uninit_port(port); + r = sdi_init_port(dss, pdev, port); + if (r) + goto error; break; + default: break; } + of_node_put(port); } + + return 0; + +error: + of_node_put(port); + __dss_uninit_ports(dss, i); + return r; +} + +static void dss_uninit_ports(struct dss_device *dss) +{ + __dss_uninit_ports(dss, dss->feat->num_ports); } -static int dss_video_pll_probe(struct platform_device *pdev) +static int dss_video_pll_probe(struct dss_device *dss) { + struct platform_device *pdev = dss->pdev; struct device_node *np = pdev->dev.of_node; struct regulator *pll_regulator; int r; @@ -1091,20 +1236,14 @@ static int dss_video_pll_probe(struct platform_device *pdev) if (!np) return 0; - if (of_property_read_bool(np, "syscon-pll-ctrl")) { - dss.syscon_pll_ctrl = syscon_regmap_lookup_by_phandle(np, - "syscon-pll-ctrl"); - if (IS_ERR(dss.syscon_pll_ctrl)) { + if (of_property_present(np, "syscon-pll-ctrl")) { + dss->syscon_pll_ctrl = + syscon_regmap_lookup_by_phandle_args(np, "syscon-pll-ctrl", + 1, &dss->syscon_pll_ctrl_offset); + if (IS_ERR(dss->syscon_pll_ctrl)) { dev_err(&pdev->dev, "failed to get syscon-pll-ctrl regmap\n"); - return PTR_ERR(dss.syscon_pll_ctrl); - } - - if (of_property_read_u32_index(np, "syscon-pll-ctrl", 1, - &dss.syscon_pll_ctrl_offset)) { - dev_err(&pdev->dev, - "failed to get syscon-pll-ctrl offset\n"); - return -EINVAL; + return PTR_ERR(dss->syscon_pll_ctrl); } } @@ -1127,16 +1266,18 @@ static int dss_video_pll_probe(struct platform_device *pdev) } if (of_property_match_string(np, "reg-names", "pll1") >= 0) { - dss.video1_pll = dss_video_pll_init(pdev, 0, pll_regulator); - if (IS_ERR(dss.video1_pll)) - return PTR_ERR(dss.video1_pll); + dss->video1_pll = dss_video_pll_init(dss, pdev, 0, + pll_regulator); + if (IS_ERR(dss->video1_pll)) + return PTR_ERR(dss->video1_pll); } if (of_property_match_string(np, "reg-names", "pll2") >= 0) { - dss.video2_pll = dss_video_pll_init(pdev, 1, pll_regulator); - if (IS_ERR(dss.video2_pll)) { - dss_video_pll_uninit(dss.video1_pll); - return PTR_ERR(dss.video2_pll); + dss->video2_pll = dss_video_pll_init(dss, pdev, 1, + pll_regulator); + if (IS_ERR(dss->video2_pll)) { + dss_video_pll_uninit(dss->video1_pll); + return PTR_ERR(dss->video2_pll); } } @@ -1144,117 +1285,56 @@ static int dss_video_pll_probe(struct platform_device *pdev) } /* DSS HW IP initialisation */ +static const struct of_device_id dss_of_match[] = { + { .compatible = "ti,omap2-dss", .data = &omap24xx_dss_feats }, + { .compatible = "ti,omap3-dss", .data = &omap3630_dss_feats }, + { .compatible = "ti,omap4-dss", .data = &omap44xx_dss_feats }, + { .compatible = "ti,omap5-dss", .data = &omap54xx_dss_feats }, + { .compatible = "ti,dra7-dss", .data = &dra7xx_dss_feats }, + {}, +}; +MODULE_DEVICE_TABLE(of, dss_of_match); + +static const struct soc_device_attribute dss_soc_devices[] = { + { .machine = "OMAP3430/3530", .data = &omap34xx_dss_feats }, + { .machine = "AM35??", .data = &omap34xx_dss_feats }, + { .family = "AM43xx", .data = &am43xx_dss_feats }, + { /* sentinel */ } +}; + static int dss_bind(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct resource *dss_mem; - u32 rev; + struct dss_device *dss = dev_get_drvdata(dev); + struct platform_device *drm_pdev; + struct dss_pdata pdata; int r; - dss.pdev = pdev; - - r = dss_init_features(dss.pdev); + r = component_bind_all(dev, NULL); if (r) return r; - dss_mem = platform_get_resource(dss.pdev, IORESOURCE_MEM, 0); - dss.base = devm_ioremap_resource(&pdev->dev, dss_mem); - if (IS_ERR(dss.base)) - return PTR_ERR(dss.base); - - r = dss_get_clocks(); - if (r) - return r; - - r = dss_setup_default_clock(); - if (r) - goto err_setup_clocks; - - r = dss_video_pll_probe(pdev); - if (r) - goto err_pll_init; - - r = dss_init_ports(pdev); - if (r) - goto err_init_ports; - - pm_runtime_enable(&pdev->dev); - - r = dss_runtime_get(); - if (r) - goto err_runtime_get; - - dss.dss_clk_rate = clk_get_rate(dss.dss_clk); - - /* Select DPLL */ - REG_FLD_MOD(DSS_CONTROL, 0, 0, 0); - - dss_select_dispc_clk_source(DSS_CLK_SRC_FCK); - -#ifdef CONFIG_OMAP2_DSS_VENC - REG_FLD_MOD(DSS_CONTROL, 1, 4, 4); /* venc dac demen */ - REG_FLD_MOD(DSS_CONTROL, 1, 3, 3); /* venc clock 4x enable */ - REG_FLD_MOD(DSS_CONTROL, 0, 2, 2); /* venc clock mode = normal */ -#endif - dss.dsi_clk_source[0] = DSS_CLK_SRC_FCK; - dss.dsi_clk_source[1] = DSS_CLK_SRC_FCK; - dss.dispc_clk_source = DSS_CLK_SRC_FCK; - dss.lcd_clk_source[0] = DSS_CLK_SRC_FCK; - dss.lcd_clk_source[1] = DSS_CLK_SRC_FCK; - - rev = dss_read_reg(DSS_REVISION); - pr_info("OMAP DSS rev %d.%d\n", FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); - - dss_runtime_put(); - - r = component_bind_all(&pdev->dev, NULL); - if (r) - goto err_component; - - dss_debugfs_create_file("dss", dss_dump_regs); - pm_set_vt_switch(0); - omapdss_gather_components(dev); - omapdss_set_is_initialized(true); + pdata.dss = dss; + drm_pdev = platform_device_register_data(NULL, "omapdrm", 0, + &pdata, sizeof(pdata)); + if (IS_ERR(drm_pdev)) { + component_unbind_all(dev, NULL); + return PTR_ERR(drm_pdev); + } - return 0; + dss->drm_pdev = drm_pdev; -err_component: -err_runtime_get: - pm_runtime_disable(&pdev->dev); - dss_uninit_ports(pdev); -err_init_ports: - if (dss.video1_pll) - dss_video_pll_uninit(dss.video1_pll); - - if (dss.video2_pll) - dss_video_pll_uninit(dss.video2_pll); -err_pll_init: -err_setup_clocks: - dss_put_clocks(); - return r; + return 0; } static void dss_unbind(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - - omapdss_set_is_initialized(false); + struct dss_device *dss = dev_get_drvdata(dev); - component_unbind_all(&pdev->dev, NULL); + platform_device_unregister(dss->drm_pdev); - if (dss.video1_pll) - dss_video_pll_uninit(dss.video1_pll); - - if (dss.video2_pll) - dss_video_pll_uninit(dss.video2_pll); - - dss_uninit_ports(pdev); - - pm_runtime_disable(&pdev->dev); - - dss_put_clocks(); + component_unbind_all(dev, NULL); } static const struct component_master_ops dss_component_ops = { @@ -1262,15 +1342,15 @@ static const struct component_master_ops dss_component_ops = { .unbind = dss_unbind, }; -static int dss_component_compare(struct device *dev, void *data) -{ - struct device *child = data; - return dev == child; -} +struct dss_component_match_data { + struct device *dev; + struct component_match **match; +}; static int dss_add_child_component(struct device *dev, void *data) { - struct component_match **match = data; + struct dss_component_match_data *cmatch = data; + struct component_match **match = cmatch->match; /* * HACK @@ -1281,35 +1361,209 @@ static int dss_add_child_component(struct device *dev, void *data) if (strstr(dev_name(dev), "rfbi")) return 0; - component_match_add(dev->parent, match, dss_component_compare, dev); + /* + * Handle possible interconnect target modules defined within the DSS. + * The DSS components can be children of an interconnect target module + * after the device tree has been updated for the module data. + * See also omapdss_boot_init() for compatible fixup. + */ + if (strstr(dev_name(dev), "target-module")) + return device_for_each_child(dev, cmatch, + dss_add_child_component); + + component_match_add(cmatch->dev, match, component_compare_dev, dev); + + return 0; +} + +static int dss_probe_hardware(struct dss_device *dss) +{ + u32 rev; + int r; + + r = dss_runtime_get(dss); + if (r) + return r; + + dss->dss_clk_rate = clk_get_rate(dss->dss_clk); + + /* Select DPLL */ + REG_FLD_MOD(dss, DSS_CONTROL, 0, 0, 0); + + dss_select_dispc_clk_source(dss, DSS_CLK_SRC_FCK); + +#ifdef CONFIG_OMAP2_DSS_VENC + REG_FLD_MOD(dss, DSS_CONTROL, 1, 4, 4); /* venc dac demen */ + REG_FLD_MOD(dss, DSS_CONTROL, 1, 3, 3); /* venc clock 4x enable */ + REG_FLD_MOD(dss, DSS_CONTROL, 0, 2, 2); /* venc clock mode = normal */ +#endif + dss->dsi_clk_source[0] = DSS_CLK_SRC_FCK; + dss->dsi_clk_source[1] = DSS_CLK_SRC_FCK; + dss->dispc_clk_source = DSS_CLK_SRC_FCK; + dss->lcd_clk_source[0] = DSS_CLK_SRC_FCK; + dss->lcd_clk_source[1] = DSS_CLK_SRC_FCK; + + rev = dss_read_reg(dss, DSS_REVISION); + pr_info("OMAP DSS rev %d.%d\n", FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); + + dss_runtime_put(dss); return 0; } static int dss_probe(struct platform_device *pdev) { + const struct soc_device_attribute *soc; + struct dss_component_match_data cmatch; struct component_match *match = NULL; + struct dss_device *dss; int r; - /* add all the child devices as components */ - device_for_each_child(&pdev->dev, &match, dss_add_child_component); + dss = kzalloc(sizeof(*dss), GFP_KERNEL); + if (!dss) + return -ENOMEM; + + dss->pdev = pdev; + platform_set_drvdata(pdev, dss); + + r = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (r) { + dev_err(&pdev->dev, "Failed to set the DMA mask\n"); + goto err_free_dss; + } + + /* + * The various OMAP3-based SoCs can't be told apart using the compatible + * string, use SoC device matching. + */ + soc = soc_device_match(dss_soc_devices); + if (soc) + dss->feat = soc->data; + else + dss->feat = device_get_match_data(&pdev->dev); + + /* Map I/O registers, get and setup clocks. */ + dss->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(dss->base)) { + r = PTR_ERR(dss->base); + goto err_free_dss; + } + + r = dss_get_clocks(dss); + if (r) + goto err_free_dss; + + r = dss_setup_default_clock(dss); + if (r) + goto err_put_clocks; + + /* Setup the video PLLs and the DPI and SDI ports. */ + r = dss_video_pll_probe(dss); + if (r) + goto err_put_clocks; + + r = dss_init_ports(dss); + if (r) + goto err_uninit_plls; + + /* Enable runtime PM and probe the hardware. */ + pm_runtime_enable(&pdev->dev); + + r = dss_probe_hardware(dss); + if (r) + goto err_pm_runtime_disable; + + /* Initialize debugfs. */ + r = dss_initialize_debugfs(dss); + if (r) + goto err_pm_runtime_disable; + + dss->debugfs.clk = dss_debugfs_create_file(dss, "clk", + dss_debug_dump_clocks, dss); + dss->debugfs.dss = dss_debugfs_create_file(dss, "dss", dss_dump_regs, + dss); + + /* Add all the child devices as components. */ + r = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + if (r) + goto err_uninit_debugfs; + + omapdss_gather_components(&pdev->dev); + + cmatch.dev = &pdev->dev; + cmatch.match = &match; + device_for_each_child(&pdev->dev, &cmatch, dss_add_child_component); r = component_master_add_with_match(&pdev->dev, &dss_component_ops, match); if (r) - return r; + goto err_of_depopulate; return 0; + +err_of_depopulate: + of_platform_depopulate(&pdev->dev); + +err_uninit_debugfs: + dss_debugfs_remove_file(dss->debugfs.clk); + dss_debugfs_remove_file(dss->debugfs.dss); + dss_uninitialize_debugfs(dss); + +err_pm_runtime_disable: + pm_runtime_disable(&pdev->dev); + dss_uninit_ports(dss); + +err_uninit_plls: + if (dss->video1_pll) + dss_video_pll_uninit(dss->video1_pll); + if (dss->video2_pll) + dss_video_pll_uninit(dss->video2_pll); + +err_put_clocks: + dss_put_clocks(dss); + +err_free_dss: + kfree(dss); + + return r; } -static int dss_remove(struct platform_device *pdev) +static void dss_remove(struct platform_device *pdev) { + struct dss_device *dss = platform_get_drvdata(pdev); + + of_platform_depopulate(&pdev->dev); + component_master_del(&pdev->dev, &dss_component_ops); - return 0; + + dss_debugfs_remove_file(dss->debugfs.clk); + dss_debugfs_remove_file(dss->debugfs.dss); + dss_uninitialize_debugfs(dss); + + pm_runtime_disable(&pdev->dev); + + dss_uninit_ports(dss); + + if (dss->video1_pll) + dss_video_pll_uninit(dss->video1_pll); + + if (dss->video2_pll) + dss_video_pll_uninit(dss->video2_pll); + + dss_put_clocks(dss); + + kfree(dss); } -static int dss_runtime_suspend(struct device *dev) +static void dss_shutdown(struct platform_device *pdev) { - dss_save_context(); + DSSDBG("shutdown\n"); +} + +static __maybe_unused int dss_runtime_suspend(struct device *dev) +{ + struct dss_device *dss = dev_get_drvdata(dev); + + dss_save_context(dss); dss_set_min_bus_tput(dev, 0); pinctrl_pm_select_sleep_state(dev); @@ -1317,8 +1571,9 @@ static int dss_runtime_suspend(struct device *dev) return 0; } -static int dss_runtime_resume(struct device *dev) +static __maybe_unused int dss_runtime_resume(struct device *dev) { + struct dss_device *dss = dev_get_drvdata(dev); int r; pinctrl_pm_select_default_state(dev); @@ -1334,29 +1589,19 @@ static int dss_runtime_resume(struct device *dev) if (r) return r; - dss_restore_context(); + dss_restore_context(dss); return 0; } static const struct dev_pm_ops dss_pm_ops = { - .runtime_suspend = dss_runtime_suspend, - .runtime_resume = dss_runtime_resume, + SET_RUNTIME_PM_OPS(dss_runtime_suspend, dss_runtime_resume, NULL) + SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; -static const struct of_device_id dss_of_match[] = { - { .compatible = "ti,omap2-dss", }, - { .compatible = "ti,omap3-dss", }, - { .compatible = "ti,omap4-dss", }, - { .compatible = "ti,omap5-dss", }, - { .compatible = "ti,dra7-dss", }, - {}, -}; - -MODULE_DEVICE_TABLE(of, dss_of_match); - -static struct platform_driver omap_dsshw_driver = { +struct platform_driver omap_dsshw_driver = { .probe = dss_probe, .remove = dss_remove, + .shutdown = dss_shutdown, .driver = { .name = "omapdss_dss", .pm = &dss_pm_ops, @@ -1365,12 +1610,32 @@ static struct platform_driver omap_dsshw_driver = { }, }; -int __init dss_init_platform_driver(void) +/* INIT */ +static struct platform_driver * const omap_dss_drivers[] = { + &omap_dsshw_driver, + &omap_dispchw_driver, +#ifdef CONFIG_OMAP2_DSS_DSI + &omap_dsihw_driver, +#endif +#ifdef CONFIG_OMAP2_DSS_VENC + &omap_venchw_driver, +#endif +#ifdef CONFIG_OMAP4_DSS_HDMI + &omapdss_hdmi4hw_driver, +#endif +#ifdef CONFIG_OMAP5_DSS_HDMI + &omapdss_hdmi5hw_driver, +#endif +}; + +int __init omap_dss_init(void) { - return platform_driver_register(&omap_dsshw_driver); + return platform_register_drivers(omap_dss_drivers, + ARRAY_SIZE(omap_dss_drivers)); } -void dss_uninit_platform_driver(void) +void omap_dss_exit(void) { - platform_driver_unregister(&omap_dsshw_driver); + platform_unregister_drivers(omap_dss_drivers, + ARRAY_SIZE(omap_dss_drivers)); } diff --git a/drivers/gpu/drm/omapdrm/dss/dss.h b/drivers/gpu/drm/omapdrm/dss/dss.h index 8dbf35f3ab23..a8b231ed4f4b 100644 --- a/drivers/gpu/drm/omapdrm/dss/dss.h +++ b/drivers/gpu/drm/omapdrm/dss/dss.h @@ -1,23 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* - * linux/drivers/video/omap2/dss/dss.h - * * Copyright (C) 2009 Nokia Corporation - * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> * * Some code and ideas taken from drivers/video/omap/ driver * by Imre Deak. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __OMAP2_DSS_H @@ -27,6 +14,14 @@ #include "omapdss.h" +struct dispc_device; +struct dss_debugfs_entry; +struct platform_device; +struct seq_file; + +#define MAX_DSS_LCD_MANAGERS 3 +#define MAX_NUM_DSI 2 + #ifdef pr_fmt #undef pr_fmt #endif @@ -72,6 +67,14 @@ #define FLD_MOD(orig, val, start, end) \ (((orig) & ~FLD_MASK(start, end)) | FLD_VAL(val, start, end)) +enum dss_model { + DSS_MODEL_OMAP2, + DSS_MODEL_OMAP3, + DSS_MODEL_OMAP4, + DSS_MODEL_OMAP5, + DSS_MODEL_DRA7, +}; + enum dss_io_pad_mode { DSS_IO_PAD_MODE_RESET, DSS_IO_PAD_MODE_RFBI, @@ -88,17 +91,6 @@ enum dss_dsi_content_type { DSS_DSI_CONTENT_GENERIC, }; -enum dss_writeback_channel { - DSS_WB_LCD1_MGR = 0, - DSS_WB_LCD2_MGR = 1, - DSS_WB_TV_MGR = 2, - DSS_WB_OVL0 = 3, - DSS_WB_OVL1 = 4, - DSS_WB_OVL2 = 5, - DSS_WB_OVL3 = 6, - DSS_WB_LCD3_MGR = 7, -}; - enum dss_clk_source { DSS_CLK_SRC_FCK = 0, @@ -158,10 +150,10 @@ struct dss_pll_ops { struct dss_pll_hw { enum dss_pll_type type; - unsigned n_max; - unsigned m_min; - unsigned m_max; - unsigned mX_max; + unsigned int n_max; + unsigned int m_min; + unsigned int m_max; + unsigned int mX_max; unsigned long fint_min, fint_max; unsigned long clkdco_min, clkdco_low, clkdco_max; @@ -174,11 +166,18 @@ struct dss_pll_hw { bool has_freqsel; bool has_selfreqdco; bool has_refsel; + + /* DRA7 errata i886: use high N & M to avoid jitter */ + bool errata_i886; + + /* DRA7 errata i932: retry pll lock on failure */ + bool errata_i932; }; struct dss_pll { const char *name; enum dss_pll_id id; + struct dss_device *dss; struct clk *clkin; struct regulator *regulator; @@ -192,6 +191,11 @@ struct dss_pll { struct dss_pll_clock_info cinfo; }; +/* Defines a generic omap register field */ +struct dss_reg_field { + u8 start, end; +}; + struct dispc_clock_info { /* rates that we get with dividers below */ unsigned long lck; @@ -215,14 +219,53 @@ struct dss_lcd_mgr_config { int lcden_sig_polarity; }; -struct seq_file; -struct platform_device; +#define DSS_SZ_REGS SZ_512 + +struct dss_device { + struct platform_device *pdev; + void __iomem *base; + struct regmap *syscon_pll_ctrl; + u32 syscon_pll_ctrl_offset; + + struct platform_device *drm_pdev; + + struct clk *parent_clk; + struct clk *dss_clk; + unsigned long dss_clk_rate; + + unsigned long cache_req_pck; + unsigned long cache_prate; + struct dispc_clock_info cache_dispc_cinfo; + + enum dss_clk_source dsi_clk_source[MAX_NUM_DSI]; + enum dss_clk_source dispc_clk_source; + enum dss_clk_source lcd_clk_source[MAX_DSS_LCD_MANAGERS]; + + bool ctx_valid; + u32 ctx[DSS_SZ_REGS / sizeof(u32)]; + + const struct dss_features *feat; + + struct { + struct dentry *root; + struct dss_debugfs_entry *clk; + struct dss_debugfs_entry *dss; + } debugfs; + + struct dss_pll *plls[4]; + struct dss_pll *video1_pll; + struct dss_pll *video2_pll; + + struct dispc_device *dispc; + struct omap_drm_private *mgr_ops_priv; +}; /* core */ -int dss_dsi_enable_pads(int dsi_id, unsigned lane_mask); -void dss_dsi_disable_pads(int dsi_id, unsigned lane_mask); -int dss_set_min_bus_tput(struct device *dev, unsigned long tput); -int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *)); +static inline int dss_set_min_bus_tput(struct device *dev, unsigned long tput) +{ + /* To be implemented when the OMAP platform will provide this feature */ + return 0; +} static inline bool dss_mgr_is_lcd(enum omap_channel id) { @@ -234,58 +277,80 @@ static inline bool dss_mgr_is_lcd(enum omap_channel id) } /* DSS */ -int dss_init_platform_driver(void) __init; -void dss_uninit_platform_driver(void); +#if defined(CONFIG_OMAP2_DSS_DEBUGFS) +struct dss_debugfs_entry * +dss_debugfs_create_file(struct dss_device *dss, const char *name, + int (*show_fn)(struct seq_file *s, void *data), + void *data); +void dss_debugfs_remove_file(struct dss_debugfs_entry *entry); +#else +static inline struct dss_debugfs_entry * +dss_debugfs_create_file(struct dss_device *dss, const char *name, + int (*show_fn)(struct seq_file *s, void *data), + void *data) +{ + return NULL; +} -int dss_runtime_get(void); -void dss_runtime_put(void); +static inline void dss_debugfs_remove_file(struct dss_debugfs_entry *entry) +{ +} +#endif /* CONFIG_OMAP2_DSS_DEBUGFS */ -unsigned long dss_get_dispc_clk_rate(void); -int dss_dpi_select_source(int port, enum omap_channel channel); -void dss_select_hdmi_venc_clk_source(enum dss_hdmi_venc_clk_source_select); -enum dss_hdmi_venc_clk_source_select dss_get_hdmi_venc_clk_source(void); +struct dss_device *dss_get_device(struct device *dev); + +int dss_runtime_get(struct dss_device *dss); +void dss_runtime_put(struct dss_device *dss); + +unsigned long dss_get_dispc_clk_rate(struct dss_device *dss); +unsigned long dss_get_max_fck_rate(struct dss_device *dss); +int dss_dpi_select_source(struct dss_device *dss, int port, + enum omap_channel channel); +void dss_select_hdmi_venc_clk_source(struct dss_device *dss, + enum dss_hdmi_venc_clk_source_select src); const char *dss_get_clk_source_name(enum dss_clk_source clk_src); -void dss_dump_clocks(struct seq_file *s); /* DSS VIDEO PLL */ -struct dss_pll *dss_video_pll_init(struct platform_device *pdev, int id, - struct regulator *regulator); +struct dss_pll *dss_video_pll_init(struct dss_device *dss, + struct platform_device *pdev, int id, + struct regulator *regulator); void dss_video_pll_uninit(struct dss_pll *pll); -#if defined(CONFIG_OMAP2_DSS_DEBUGFS) -void dss_debug_dump_clocks(struct seq_file *s); -#endif +void dss_ctrl_pll_enable(struct dss_pll *pll, bool enable); -void dss_ctrl_pll_enable(enum dss_pll_id pll_id, bool enable); +void dss_sdi_init(struct dss_device *dss, int datapairs); +int dss_sdi_enable(struct dss_device *dss); +void dss_sdi_disable(struct dss_device *dss); -void dss_sdi_init(int datapairs); -int dss_sdi_enable(void); -void dss_sdi_disable(void); +void dss_select_dsi_clk_source(struct dss_device *dss, int dsi_module, + enum dss_clk_source clk_src); +void dss_select_lcd_clk_source(struct dss_device *dss, + enum omap_channel channel, + enum dss_clk_source clk_src); +enum dss_clk_source dss_get_dispc_clk_source(struct dss_device *dss); +enum dss_clk_source dss_get_dsi_clk_source(struct dss_device *dss, + int dsi_module); +enum dss_clk_source dss_get_lcd_clk_source(struct dss_device *dss, + enum omap_channel channel); -void dss_select_dsi_clk_source(int dsi_module, - enum dss_clk_source clk_src); -void dss_select_lcd_clk_source(enum omap_channel channel, - enum dss_clk_source clk_src); -enum dss_clk_source dss_get_dispc_clk_source(void); -enum dss_clk_source dss_get_dsi_clk_source(int dsi_module); -enum dss_clk_source dss_get_lcd_clk_source(enum omap_channel channel); +void dss_set_venc_output(struct dss_device *dss, enum omap_dss_venc_type type); +void dss_set_dac_pwrdn_bgz(struct dss_device *dss, bool enable); -void dss_set_venc_output(enum omap_dss_venc_type type); -void dss_set_dac_pwrdn_bgz(bool enable); - -int dss_set_fck_rate(unsigned long rate); +int dss_set_fck_rate(struct dss_device *dss, unsigned long rate); typedef bool (*dss_div_calc_func)(unsigned long fck, void *data); -bool dss_div_calc(unsigned long pck, unsigned long fck_min, - dss_div_calc_func func, void *data); +bool dss_div_calc(struct dss_device *dss, unsigned long pck, + unsigned long fck_min, dss_div_calc_func func, void *data); /* SDI */ #ifdef CONFIG_OMAP2_DSS_SDI -int sdi_init_port(struct platform_device *pdev, struct device_node *port); +int sdi_init_port(struct dss_device *dss, struct platform_device *pdev, + struct device_node *port); void sdi_uninit_port(struct device_node *port); #else -static inline int sdi_init_port(struct platform_device *pdev, - struct device_node *port) +static inline int sdi_init_port(struct dss_device *dss, + struct platform_device *pdev, + struct device_node *port) { return 0; } @@ -298,25 +363,20 @@ static inline void sdi_uninit_port(struct device_node *port) #ifdef CONFIG_OMAP2_DSS_DSI -struct dentry; -struct file_operations; - -int dsi_init_platform_driver(void) __init; -void dsi_uninit_platform_driver(void); - -void dsi_dump_clocks(struct seq_file *s); - void dsi_irq_handler(void); #endif /* DPI */ #ifdef CONFIG_OMAP2_DSS_DPI -int dpi_init_port(struct platform_device *pdev, struct device_node *port); +int dpi_init_port(struct dss_device *dss, struct platform_device *pdev, + struct device_node *port, enum dss_model dss_model); void dpi_uninit_port(struct device_node *port); #else -static inline int dpi_init_port(struct platform_device *pdev, - struct device_node *port) +static inline int dpi_init_port(struct dss_device *dss, + struct platform_device *pdev, + struct device_node *port, + enum dss_model dss_model) { return 0; } @@ -326,65 +386,108 @@ static inline void dpi_uninit_port(struct device_node *port) #endif /* DISPC */ -int dispc_init_platform_driver(void) __init; -void dispc_uninit_platform_driver(void); -void dispc_dump_clocks(struct seq_file *s); +void dispc_dump_clocks(struct dispc_device *dispc, struct seq_file *s); -int dispc_runtime_get(void); -void dispc_runtime_put(void); +int dispc_runtime_get(struct dispc_device *dispc); +void dispc_runtime_put(struct dispc_device *dispc); -void dispc_enable_sidle(void); -void dispc_disable_sidle(void); +int dispc_get_num_ovls(struct dispc_device *dispc); +int dispc_get_num_mgrs(struct dispc_device *dispc); -void dispc_lcd_enable_signal(bool enable); -void dispc_pck_free_enable(bool enable); -void dispc_enable_fifomerge(bool enable); -void dispc_enable_gamma_table(bool enable); +const u32 *dispc_ovl_get_color_modes(struct dispc_device *dispc, + enum omap_plane_id plane); -typedef bool (*dispc_div_calc_func)(int lckd, int pckd, unsigned long lck, - unsigned long pck, void *data); -bool dispc_div_calc(unsigned long dispc, - unsigned long pck_min, unsigned long pck_max, - dispc_div_calc_func func, void *data); +void dispc_ovl_get_max_size(struct dispc_device *dispc, u16 *width, u16 *height); +bool dispc_ovl_color_mode_supported(struct dispc_device *dispc, + enum omap_plane_id plane, u32 fourcc); +enum omap_overlay_caps dispc_ovl_get_caps(struct dispc_device *dispc, enum omap_plane_id plane); + +u32 dispc_read_irqstatus(struct dispc_device *dispc); +void dispc_clear_irqstatus(struct dispc_device *dispc, u32 mask); +void dispc_write_irqenable(struct dispc_device *dispc, u32 mask); + +int dispc_request_irq(struct dispc_device *dispc, irq_handler_t handler, + void *dev_id); +void dispc_free_irq(struct dispc_device *dispc, void *dev_id); -bool dispc_mgr_timings_ok(enum omap_channel channel, const struct videomode *vm); -int dispc_calc_clock_rates(unsigned long dispc_fclk_rate, - struct dispc_clock_info *cinfo); +u32 dispc_mgr_get_vsync_irq(struct dispc_device *dispc, + enum omap_channel channel); +u32 dispc_mgr_get_framedone_irq(struct dispc_device *dispc, + enum omap_channel channel); +u32 dispc_mgr_get_sync_lost_irq(struct dispc_device *dispc, + enum omap_channel channel); +u32 dispc_get_memory_bandwidth_limit(struct dispc_device *dispc); -void dispc_ovl_set_fifo_threshold(enum omap_plane_id plane, u32 low, - u32 high); -void dispc_ovl_compute_fifo_thresholds(enum omap_plane_id plane, - u32 *fifo_low, u32 *fifo_high, bool use_fifomerge, - bool manual_update); +void dispc_mgr_enable(struct dispc_device *dispc, + enum omap_channel channel, bool enable); -void dispc_mgr_set_clock_div(enum omap_channel channel, - const struct dispc_clock_info *cinfo); -int dispc_mgr_get_clock_div(enum omap_channel channel, - struct dispc_clock_info *cinfo); -void dispc_set_tv_pclk(unsigned long pclk); +bool dispc_mgr_go_busy(struct dispc_device *dispc, + enum omap_channel channel); -u32 dispc_wb_get_framedone_irq(void); -bool dispc_wb_go_busy(void); -void dispc_wb_go(void); -void dispc_wb_set_channel_in(enum dss_writeback_channel channel); -int dispc_wb_setup(const struct omap_dss_writeback_info *wi, - bool mem_to_mem, const struct videomode *vm); +void dispc_mgr_go(struct dispc_device *dispc, enum omap_channel channel); -/* VENC */ -int venc_init_platform_driver(void) __init; -void venc_uninit_platform_driver(void); +void dispc_mgr_set_lcd_config(struct dispc_device *dispc, + enum omap_channel channel, + const struct dss_lcd_mgr_config *config); +void dispc_mgr_set_timings(struct dispc_device *dispc, + enum omap_channel channel, + const struct videomode *vm); +void dispc_mgr_setup(struct dispc_device *dispc, + enum omap_channel channel, + const struct omap_overlay_manager_info *info); -/* HDMI */ -int hdmi4_init_platform_driver(void) __init; -void hdmi4_uninit_platform_driver(void); +int dispc_mgr_check_timings(struct dispc_device *dispc, + enum omap_channel channel, + const struct videomode *vm); -int hdmi5_init_platform_driver(void) __init; -void hdmi5_uninit_platform_driver(void); +u32 dispc_mgr_gamma_size(struct dispc_device *dispc, + enum omap_channel channel); +void dispc_mgr_set_gamma(struct dispc_device *dispc, + enum omap_channel channel, + const struct drm_color_lut *lut, + unsigned int length); +int dispc_ovl_setup(struct dispc_device *dispc, + enum omap_plane_id plane, + const struct omap_overlay_info *oi, + const struct videomode *vm, bool mem_to_mem, + enum omap_channel channel); + +int dispc_ovl_enable(struct dispc_device *dispc, + enum omap_plane_id plane, bool enable); + +void dispc_enable_sidle(struct dispc_device *dispc); +void dispc_disable_sidle(struct dispc_device *dispc); + +void dispc_lcd_enable_signal(struct dispc_device *dispc, bool enable); +void dispc_pck_free_enable(struct dispc_device *dispc, bool enable); + +typedef bool (*dispc_div_calc_func)(int lckd, int pckd, unsigned long lck, + unsigned long pck, void *data); +bool dispc_div_calc(struct dispc_device *dispc, unsigned long dispc_freq, + unsigned long pck_min, unsigned long pck_max, + dispc_div_calc_func func, void *data); + +int dispc_calc_clock_rates(struct dispc_device *dispc, + unsigned long dispc_fclk_rate, + struct dispc_clock_info *cinfo); + + +void dispc_ovl_set_fifo_threshold(struct dispc_device *dispc, + enum omap_plane_id plane, u32 low, u32 high); +void dispc_ovl_compute_fifo_thresholds(struct dispc_device *dispc, + enum omap_plane_id plane, + u32 *fifo_low, u32 *fifo_high, + bool use_fifomerge, bool manual_update); + +void dispc_mgr_set_clock_div(struct dispc_device *dispc, + enum omap_channel channel, + const struct dispc_clock_info *cinfo); +void dispc_set_tv_pclk(struct dispc_device *dispc, unsigned long pclk); #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS -static inline void dss_collect_irq_stats(u32 irqstatus, unsigned *irq_arr) +static inline void dss_collect_irq_stats(u32 irqstatus, unsigned int *irq_arr) { int b; for (b = 0; b < 32; ++b) { @@ -400,11 +503,12 @@ typedef bool (*dss_pll_calc_func)(int n, int m, unsigned long fint, typedef bool (*dss_hsdiv_calc_func)(int m_dispc, unsigned long dispc, void *data); -int dss_pll_register(struct dss_pll *pll); +int dss_pll_register(struct dss_device *dss, struct dss_pll *pll); void dss_pll_unregister(struct dss_pll *pll); -struct dss_pll *dss_pll_find(const char *name); -struct dss_pll *dss_pll_find_by_src(enum dss_clk_source src); -unsigned dss_pll_get_clkout_idx_for_src(enum dss_clk_source src); +struct dss_pll *dss_pll_find(struct dss_device *dss, const char *name); +struct dss_pll *dss_pll_find_by_src(struct dss_device *dss, + enum dss_clk_source src); +unsigned int dss_pll_get_clkout_idx_for_src(enum dss_clk_source src); int dss_pll_enable(struct dss_pll *pll); void dss_pll_disable(struct dss_pll *pll); int dss_pll_set_config(struct dss_pll *pll, @@ -426,4 +530,19 @@ int dss_pll_write_config_type_b(struct dss_pll *pll, const struct dss_pll_clock_info *cinfo); int dss_pll_wait_reset_done(struct dss_pll *pll); +extern struct platform_driver omap_dsshw_driver; +extern struct platform_driver omap_dispchw_driver; +#ifdef CONFIG_OMAP2_DSS_DSI +extern struct platform_driver omap_dsihw_driver; +#endif +#ifdef CONFIG_OMAP2_DSS_VENC +extern struct platform_driver omap_venchw_driver; +#endif +#ifdef CONFIG_OMAP4_DSS_HDMI +extern struct platform_driver omapdss_hdmi4hw_driver; +#endif +#ifdef CONFIG_OMAP5_DSS_HDMI +extern struct platform_driver omapdss_hdmi5hw_driver; +#endif + #endif diff --git a/drivers/gpu/drm/omapdrm/dss/dss_features.c b/drivers/gpu/drm/omapdrm/dss/dss_features.c deleted file mode 100644 index 0e599710dd95..000000000000 --- a/drivers/gpu/drm/omapdrm/dss/dss_features.c +++ /dev/null @@ -1,905 +0,0 @@ -/* - * linux/drivers/video/omap2/dss/dss_features.c - * - * Copyright (C) 2010 Texas Instruments - * Author: Archit Taneja <archit@ti.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/types.h> -#include <linux/err.h> -#include <linux/slab.h> -#include <drm/drm_fourcc.h> - -#include "omapdss.h" -#include "dss.h" -#include "dss_features.h" - -/* Defines a generic omap register field */ -struct dss_reg_field { - u8 start, end; -}; - -struct dss_param_range { - int min, max; -}; - -struct omap_dss_features { - const struct dss_reg_field *reg_fields; - const int num_reg_fields; - - const enum dss_feat_id *features; - const int num_features; - - const int num_mgrs; - const int num_ovls; - const enum omap_display_type *supported_displays; - const enum omap_dss_output_id *supported_outputs; - const u32 **supported_color_modes; - const enum omap_overlay_caps *overlay_caps; - const struct dss_param_range *dss_params; - - const u32 buffer_size_unit; - const u32 burst_size_unit; -}; - -/* This struct is assigned to one of the below during initialization */ -static const struct omap_dss_features *omap_current_dss_features; - -static const struct dss_reg_field omap2_dss_reg_fields[] = { - [FEAT_REG_FIRHINC] = { 11, 0 }, - [FEAT_REG_FIRVINC] = { 27, 16 }, - [FEAT_REG_FIFOLOWTHRESHOLD] = { 8, 0 }, - [FEAT_REG_FIFOHIGHTHRESHOLD] = { 24, 16 }, - [FEAT_REG_FIFOSIZE] = { 8, 0 }, - [FEAT_REG_HORIZONTALACCU] = { 9, 0 }, - [FEAT_REG_VERTICALACCU] = { 25, 16 }, - [FEAT_REG_DISPC_CLK_SWITCH] = { 0, 0 }, -}; - -static const struct dss_reg_field omap3_dss_reg_fields[] = { - [FEAT_REG_FIRHINC] = { 12, 0 }, - [FEAT_REG_FIRVINC] = { 28, 16 }, - [FEAT_REG_FIFOLOWTHRESHOLD] = { 11, 0 }, - [FEAT_REG_FIFOHIGHTHRESHOLD] = { 27, 16 }, - [FEAT_REG_FIFOSIZE] = { 10, 0 }, - [FEAT_REG_HORIZONTALACCU] = { 9, 0 }, - [FEAT_REG_VERTICALACCU] = { 25, 16 }, - [FEAT_REG_DISPC_CLK_SWITCH] = { 0, 0 }, -}; - -static const struct dss_reg_field am43xx_dss_reg_fields[] = { - [FEAT_REG_FIRHINC] = { 12, 0 }, - [FEAT_REG_FIRVINC] = { 28, 16 }, - [FEAT_REG_FIFOLOWTHRESHOLD] = { 11, 0 }, - [FEAT_REG_FIFOHIGHTHRESHOLD] = { 27, 16 }, - [FEAT_REG_FIFOSIZE] = { 10, 0 }, - [FEAT_REG_HORIZONTALACCU] = { 9, 0 }, - [FEAT_REG_VERTICALACCU] = { 25, 16 }, - [FEAT_REG_DISPC_CLK_SWITCH] = { 0, 0 }, -}; - -static const struct dss_reg_field omap4_dss_reg_fields[] = { - [FEAT_REG_FIRHINC] = { 12, 0 }, - [FEAT_REG_FIRVINC] = { 28, 16 }, - [FEAT_REG_FIFOLOWTHRESHOLD] = { 15, 0 }, - [FEAT_REG_FIFOHIGHTHRESHOLD] = { 31, 16 }, - [FEAT_REG_FIFOSIZE] = { 15, 0 }, - [FEAT_REG_HORIZONTALACCU] = { 10, 0 }, - [FEAT_REG_VERTICALACCU] = { 26, 16 }, - [FEAT_REG_DISPC_CLK_SWITCH] = { 9, 8 }, -}; - -static const struct dss_reg_field omap5_dss_reg_fields[] = { - [FEAT_REG_FIRHINC] = { 12, 0 }, - [FEAT_REG_FIRVINC] = { 28, 16 }, - [FEAT_REG_FIFOLOWTHRESHOLD] = { 15, 0 }, - [FEAT_REG_FIFOHIGHTHRESHOLD] = { 31, 16 }, - [FEAT_REG_FIFOSIZE] = { 15, 0 }, - [FEAT_REG_HORIZONTALACCU] = { 10, 0 }, - [FEAT_REG_VERTICALACCU] = { 26, 16 }, - [FEAT_REG_DISPC_CLK_SWITCH] = { 9, 7 }, -}; - -static const enum omap_display_type omap2_dss_supported_displays[] = { - /* OMAP_DSS_CHANNEL_LCD */ - OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI, - - /* OMAP_DSS_CHANNEL_DIGIT */ - OMAP_DISPLAY_TYPE_VENC, -}; - -static const enum omap_display_type omap3430_dss_supported_displays[] = { - /* OMAP_DSS_CHANNEL_LCD */ - OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI | - OMAP_DISPLAY_TYPE_SDI | OMAP_DISPLAY_TYPE_DSI, - - /* OMAP_DSS_CHANNEL_DIGIT */ - OMAP_DISPLAY_TYPE_VENC, -}; - -static const enum omap_display_type omap3630_dss_supported_displays[] = { - /* OMAP_DSS_CHANNEL_LCD */ - OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI | - OMAP_DISPLAY_TYPE_DSI, - - /* OMAP_DSS_CHANNEL_DIGIT */ - OMAP_DISPLAY_TYPE_VENC, -}; - -static const enum omap_display_type am43xx_dss_supported_displays[] = { - /* OMAP_DSS_CHANNEL_LCD */ - OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI, -}; - -static const enum omap_display_type omap4_dss_supported_displays[] = { - /* OMAP_DSS_CHANNEL_LCD */ - OMAP_DISPLAY_TYPE_DBI | OMAP_DISPLAY_TYPE_DSI, - - /* OMAP_DSS_CHANNEL_DIGIT */ - OMAP_DISPLAY_TYPE_VENC | OMAP_DISPLAY_TYPE_HDMI, - - /* OMAP_DSS_CHANNEL_LCD2 */ - OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI | - OMAP_DISPLAY_TYPE_DSI, -}; - -static const enum omap_display_type omap5_dss_supported_displays[] = { - /* OMAP_DSS_CHANNEL_LCD */ - OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI | - OMAP_DISPLAY_TYPE_DSI, - - /* OMAP_DSS_CHANNEL_DIGIT */ - OMAP_DISPLAY_TYPE_HDMI | OMAP_DISPLAY_TYPE_DPI, - - /* OMAP_DSS_CHANNEL_LCD2 */ - OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI | - OMAP_DISPLAY_TYPE_DSI, -}; - -static const enum omap_dss_output_id omap2_dss_supported_outputs[] = { - /* OMAP_DSS_CHANNEL_LCD */ - OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI, - - /* OMAP_DSS_CHANNEL_DIGIT */ - OMAP_DSS_OUTPUT_VENC, -}; - -static const enum omap_dss_output_id omap3430_dss_supported_outputs[] = { - /* OMAP_DSS_CHANNEL_LCD */ - OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI | - OMAP_DSS_OUTPUT_SDI | OMAP_DSS_OUTPUT_DSI1, - - /* OMAP_DSS_CHANNEL_DIGIT */ - OMAP_DSS_OUTPUT_VENC, -}; - -static const enum omap_dss_output_id omap3630_dss_supported_outputs[] = { - /* OMAP_DSS_CHANNEL_LCD */ - OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI | - OMAP_DSS_OUTPUT_DSI1, - - /* OMAP_DSS_CHANNEL_DIGIT */ - OMAP_DSS_OUTPUT_VENC, -}; - -static const enum omap_dss_output_id am43xx_dss_supported_outputs[] = { - /* OMAP_DSS_CHANNEL_LCD */ - OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI, -}; - -static const enum omap_dss_output_id omap4_dss_supported_outputs[] = { - /* OMAP_DSS_CHANNEL_LCD */ - OMAP_DSS_OUTPUT_DBI | OMAP_DSS_OUTPUT_DSI1, - - /* OMAP_DSS_CHANNEL_DIGIT */ - OMAP_DSS_OUTPUT_VENC | OMAP_DSS_OUTPUT_HDMI, - - /* OMAP_DSS_CHANNEL_LCD2 */ - OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI | - OMAP_DSS_OUTPUT_DSI2, -}; - -static const enum omap_dss_output_id omap5_dss_supported_outputs[] = { - /* OMAP_DSS_CHANNEL_LCD */ - OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI | - OMAP_DSS_OUTPUT_DSI1 | OMAP_DSS_OUTPUT_DSI2, - - /* OMAP_DSS_CHANNEL_DIGIT */ - OMAP_DSS_OUTPUT_HDMI, - - /* OMAP_DSS_CHANNEL_LCD2 */ - OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI | - OMAP_DSS_OUTPUT_DSI1, - - /* OMAP_DSS_CHANNEL_LCD3 */ - OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI | - OMAP_DSS_OUTPUT_DSI2, -}; - -#define COLOR_ARRAY(arr...) (const u32[]) { arr, 0 } - -static const u32 *omap2_dss_supported_color_modes[] = { - - /* OMAP_DSS_GFX */ - COLOR_ARRAY( - DRM_FORMAT_RGBX4444, DRM_FORMAT_RGB565, - DRM_FORMAT_XRGB8888, DRM_FORMAT_RGB888), - - /* OMAP_DSS_VIDEO1 */ - COLOR_ARRAY( - DRM_FORMAT_RGB565, DRM_FORMAT_XRGB8888, - DRM_FORMAT_RGB888, DRM_FORMAT_YUYV, - DRM_FORMAT_UYVY), - - /* OMAP_DSS_VIDEO2 */ - COLOR_ARRAY( - DRM_FORMAT_RGB565, DRM_FORMAT_XRGB8888, - DRM_FORMAT_RGB888, DRM_FORMAT_YUYV, - DRM_FORMAT_UYVY), -}; - -static const u32 *omap3_dss_supported_color_modes[] = { - /* OMAP_DSS_GFX */ - COLOR_ARRAY( - DRM_FORMAT_RGBX4444, DRM_FORMAT_ARGB4444, - DRM_FORMAT_RGB565, DRM_FORMAT_XRGB8888, - DRM_FORMAT_RGB888, DRM_FORMAT_ARGB8888, - DRM_FORMAT_RGBA8888, DRM_FORMAT_RGBX8888), - - /* OMAP_DSS_VIDEO1 */ - COLOR_ARRAY( - DRM_FORMAT_XRGB8888, DRM_FORMAT_RGB888, - DRM_FORMAT_RGBX4444, DRM_FORMAT_RGB565, - DRM_FORMAT_YUYV, DRM_FORMAT_UYVY), - - /* OMAP_DSS_VIDEO2 */ - COLOR_ARRAY( - DRM_FORMAT_RGBX4444, DRM_FORMAT_ARGB4444, - DRM_FORMAT_RGB565, DRM_FORMAT_XRGB8888, - DRM_FORMAT_RGB888, DRM_FORMAT_YUYV, - DRM_FORMAT_UYVY, DRM_FORMAT_ARGB8888, - DRM_FORMAT_RGBA8888, DRM_FORMAT_RGBX8888), -}; - -static const u32 *omap4_dss_supported_color_modes[] = { - /* OMAP_DSS_GFX */ - COLOR_ARRAY( - DRM_FORMAT_RGBX4444, DRM_FORMAT_ARGB4444, - DRM_FORMAT_RGB565, DRM_FORMAT_XRGB8888, - DRM_FORMAT_RGB888, DRM_FORMAT_ARGB8888, - DRM_FORMAT_RGBA8888, DRM_FORMAT_RGBX8888, - DRM_FORMAT_ARGB1555, DRM_FORMAT_XRGB4444, - DRM_FORMAT_RGBA4444, DRM_FORMAT_XRGB1555), - - /* OMAP_DSS_VIDEO1 */ - COLOR_ARRAY( - DRM_FORMAT_RGB565, DRM_FORMAT_RGBX4444, - DRM_FORMAT_YUYV, DRM_FORMAT_ARGB1555, - DRM_FORMAT_RGBA8888, DRM_FORMAT_NV12, - DRM_FORMAT_RGBA4444, DRM_FORMAT_XRGB8888, - DRM_FORMAT_RGB888, DRM_FORMAT_UYVY, - DRM_FORMAT_ARGB4444, DRM_FORMAT_XRGB1555, - DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB4444, - DRM_FORMAT_RGBX8888), - - /* OMAP_DSS_VIDEO2 */ - COLOR_ARRAY( - DRM_FORMAT_RGB565, DRM_FORMAT_RGBX4444, - DRM_FORMAT_YUYV, DRM_FORMAT_ARGB1555, - DRM_FORMAT_RGBA8888, DRM_FORMAT_NV12, - DRM_FORMAT_RGBA4444, DRM_FORMAT_XRGB8888, - DRM_FORMAT_RGB888, DRM_FORMAT_UYVY, - DRM_FORMAT_ARGB4444, DRM_FORMAT_XRGB1555, - DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB4444, - DRM_FORMAT_RGBX8888), - - /* OMAP_DSS_VIDEO3 */ - COLOR_ARRAY( - DRM_FORMAT_RGB565, DRM_FORMAT_RGBX4444, - DRM_FORMAT_YUYV, DRM_FORMAT_ARGB1555, - DRM_FORMAT_RGBA8888, DRM_FORMAT_NV12, - DRM_FORMAT_RGBA4444, DRM_FORMAT_XRGB8888, - DRM_FORMAT_RGB888, DRM_FORMAT_UYVY, - DRM_FORMAT_ARGB4444, DRM_FORMAT_XRGB1555, - DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB4444, - DRM_FORMAT_RGBX8888), - - /* OMAP_DSS_WB */ - COLOR_ARRAY( - DRM_FORMAT_RGB565, DRM_FORMAT_RGBX4444, - DRM_FORMAT_YUYV, DRM_FORMAT_ARGB1555, - DRM_FORMAT_RGBA8888, DRM_FORMAT_NV12, - DRM_FORMAT_RGBA4444, DRM_FORMAT_XRGB8888, - DRM_FORMAT_RGB888, DRM_FORMAT_UYVY, - DRM_FORMAT_ARGB4444, DRM_FORMAT_XRGB1555, - DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB4444, - DRM_FORMAT_RGBX8888), -}; - -static const enum omap_overlay_caps omap2_dss_overlay_caps[] = { - /* OMAP_DSS_GFX */ - OMAP_DSS_OVL_CAP_POS | OMAP_DSS_OVL_CAP_REPLICATION, - - /* OMAP_DSS_VIDEO1 */ - OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_POS | - OMAP_DSS_OVL_CAP_REPLICATION, - - /* OMAP_DSS_VIDEO2 */ - OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_POS | - OMAP_DSS_OVL_CAP_REPLICATION, -}; - -static const enum omap_overlay_caps omap3430_dss_overlay_caps[] = { - /* OMAP_DSS_GFX */ - OMAP_DSS_OVL_CAP_GLOBAL_ALPHA | OMAP_DSS_OVL_CAP_POS | - OMAP_DSS_OVL_CAP_REPLICATION, - - /* OMAP_DSS_VIDEO1 */ - OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_POS | - OMAP_DSS_OVL_CAP_REPLICATION, - - /* OMAP_DSS_VIDEO2 */ - OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_GLOBAL_ALPHA | - OMAP_DSS_OVL_CAP_POS | OMAP_DSS_OVL_CAP_REPLICATION, -}; - -static const enum omap_overlay_caps omap3630_dss_overlay_caps[] = { - /* OMAP_DSS_GFX */ - OMAP_DSS_OVL_CAP_GLOBAL_ALPHA | OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA | - OMAP_DSS_OVL_CAP_POS | OMAP_DSS_OVL_CAP_REPLICATION, - - /* OMAP_DSS_VIDEO1 */ - OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_POS | - OMAP_DSS_OVL_CAP_REPLICATION, - - /* OMAP_DSS_VIDEO2 */ - OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_GLOBAL_ALPHA | - OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA | OMAP_DSS_OVL_CAP_POS | - OMAP_DSS_OVL_CAP_REPLICATION, -}; - -static const enum omap_overlay_caps omap4_dss_overlay_caps[] = { - /* OMAP_DSS_GFX */ - OMAP_DSS_OVL_CAP_GLOBAL_ALPHA | OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA | - OMAP_DSS_OVL_CAP_ZORDER | OMAP_DSS_OVL_CAP_POS | - OMAP_DSS_OVL_CAP_REPLICATION, - - /* OMAP_DSS_VIDEO1 */ - OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_GLOBAL_ALPHA | - OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA | OMAP_DSS_OVL_CAP_ZORDER | - OMAP_DSS_OVL_CAP_POS | OMAP_DSS_OVL_CAP_REPLICATION, - - /* OMAP_DSS_VIDEO2 */ - OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_GLOBAL_ALPHA | - OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA | OMAP_DSS_OVL_CAP_ZORDER | - OMAP_DSS_OVL_CAP_POS | OMAP_DSS_OVL_CAP_REPLICATION, - - /* OMAP_DSS_VIDEO3 */ - OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_GLOBAL_ALPHA | - OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA | OMAP_DSS_OVL_CAP_ZORDER | - OMAP_DSS_OVL_CAP_POS | OMAP_DSS_OVL_CAP_REPLICATION, -}; - -static const struct dss_param_range omap2_dss_param_range[] = { - [FEAT_PARAM_DSS_FCK] = { 0, 133000000 }, - [FEAT_PARAM_DSS_PCD] = { 2, 255 }, - [FEAT_PARAM_DOWNSCALE] = { 1, 2 }, - /* - * Assuming the line width buffer to be 768 pixels as OMAP2 DISPC - * scaler cannot scale a image with width more than 768. - */ - [FEAT_PARAM_LINEWIDTH] = { 1, 768 }, -}; - -static const struct dss_param_range omap3_dss_param_range[] = { - [FEAT_PARAM_DSS_FCK] = { 0, 173000000 }, - [FEAT_PARAM_DSS_PCD] = { 1, 255 }, - [FEAT_PARAM_DSIPLL_LPDIV] = { 1, (1 << 13) - 1}, - [FEAT_PARAM_DSI_FCK] = { 0, 173000000 }, - [FEAT_PARAM_DOWNSCALE] = { 1, 4 }, - [FEAT_PARAM_LINEWIDTH] = { 1, 1024 }, -}; - -static const struct dss_param_range am43xx_dss_param_range[] = { - [FEAT_PARAM_DSS_FCK] = { 0, 200000000 }, - [FEAT_PARAM_DSS_PCD] = { 1, 255 }, - [FEAT_PARAM_DOWNSCALE] = { 1, 4 }, - [FEAT_PARAM_LINEWIDTH] = { 1, 1024 }, -}; - -static const struct dss_param_range omap4_dss_param_range[] = { - [FEAT_PARAM_DSS_FCK] = { 0, 186000000 }, - [FEAT_PARAM_DSS_PCD] = { 1, 255 }, - [FEAT_PARAM_DSIPLL_LPDIV] = { 0, (1 << 13) - 1 }, - [FEAT_PARAM_DSI_FCK] = { 0, 170000000 }, - [FEAT_PARAM_DOWNSCALE] = { 1, 4 }, - [FEAT_PARAM_LINEWIDTH] = { 1, 2048 }, -}; - -static const struct dss_param_range omap5_dss_param_range[] = { - [FEAT_PARAM_DSS_FCK] = { 0, 209250000 }, - [FEAT_PARAM_DSS_PCD] = { 1, 255 }, - [FEAT_PARAM_DSIPLL_LPDIV] = { 0, (1 << 13) - 1 }, - [FEAT_PARAM_DSI_FCK] = { 0, 209250000 }, - [FEAT_PARAM_DOWNSCALE] = { 1, 4 }, - [FEAT_PARAM_LINEWIDTH] = { 1, 2048 }, -}; - -static const enum dss_feat_id omap2_dss_feat_list[] = { - FEAT_LCDENABLEPOL, - FEAT_LCDENABLESIGNAL, - FEAT_PCKFREEENABLE, - FEAT_FUNCGATED, - FEAT_ROWREPEATENABLE, - FEAT_RESIZECONF, -}; - -static const enum dss_feat_id omap3430_dss_feat_list[] = { - FEAT_LCDENABLEPOL, - FEAT_LCDENABLESIGNAL, - FEAT_PCKFREEENABLE, - FEAT_FUNCGATED, - FEAT_LINEBUFFERSPLIT, - FEAT_ROWREPEATENABLE, - FEAT_RESIZECONF, - FEAT_DSI_REVERSE_TXCLKESC, - FEAT_VENC_REQUIRES_TV_DAC_CLK, - FEAT_CPR, - FEAT_PRELOAD, - FEAT_FIR_COEF_V, - FEAT_ALPHA_FIXED_ZORDER, - FEAT_FIFO_MERGE, - FEAT_OMAP3_DSI_FIFO_BUG, - FEAT_DPI_USES_VDDS_DSI, -}; - -static const enum dss_feat_id am35xx_dss_feat_list[] = { - FEAT_LCDENABLEPOL, - FEAT_LCDENABLESIGNAL, - FEAT_PCKFREEENABLE, - FEAT_FUNCGATED, - FEAT_LINEBUFFERSPLIT, - FEAT_ROWREPEATENABLE, - FEAT_RESIZECONF, - FEAT_DSI_REVERSE_TXCLKESC, - FEAT_VENC_REQUIRES_TV_DAC_CLK, - FEAT_CPR, - FEAT_PRELOAD, - FEAT_FIR_COEF_V, - FEAT_ALPHA_FIXED_ZORDER, - FEAT_FIFO_MERGE, - FEAT_OMAP3_DSI_FIFO_BUG, -}; - -static const enum dss_feat_id am43xx_dss_feat_list[] = { - FEAT_LCDENABLEPOL, - FEAT_LCDENABLESIGNAL, - FEAT_PCKFREEENABLE, - FEAT_FUNCGATED, - FEAT_LINEBUFFERSPLIT, - FEAT_ROWREPEATENABLE, - FEAT_RESIZECONF, - FEAT_CPR, - FEAT_PRELOAD, - FEAT_FIR_COEF_V, - FEAT_ALPHA_FIXED_ZORDER, - FEAT_FIFO_MERGE, -}; - -static const enum dss_feat_id omap3630_dss_feat_list[] = { - FEAT_LCDENABLEPOL, - FEAT_LCDENABLESIGNAL, - FEAT_PCKFREEENABLE, - FEAT_FUNCGATED, - FEAT_LINEBUFFERSPLIT, - FEAT_ROWREPEATENABLE, - FEAT_RESIZECONF, - FEAT_DSI_PLL_PWR_BUG, - FEAT_CPR, - FEAT_PRELOAD, - FEAT_FIR_COEF_V, - FEAT_ALPHA_FIXED_ZORDER, - FEAT_FIFO_MERGE, - FEAT_OMAP3_DSI_FIFO_BUG, - FEAT_DPI_USES_VDDS_DSI, -}; - -static const enum dss_feat_id omap4430_es1_0_dss_feat_list[] = { - FEAT_MGR_LCD2, - FEAT_CORE_CLK_DIV, - FEAT_LCD_CLK_SRC, - FEAT_DSI_DCS_CMD_CONFIG_VC, - FEAT_DSI_VC_OCP_WIDTH, - FEAT_DSI_GNQ, - FEAT_HANDLE_UV_SEPARATE, - FEAT_ATTR2, - FEAT_CPR, - FEAT_PRELOAD, - FEAT_FIR_COEF_V, - FEAT_ALPHA_FREE_ZORDER, - FEAT_FIFO_MERGE, - FEAT_BURST_2D, -}; - -static const enum dss_feat_id omap4430_es2_0_1_2_dss_feat_list[] = { - FEAT_MGR_LCD2, - FEAT_CORE_CLK_DIV, - FEAT_LCD_CLK_SRC, - FEAT_DSI_DCS_CMD_CONFIG_VC, - FEAT_DSI_VC_OCP_WIDTH, - FEAT_DSI_GNQ, - FEAT_HDMI_CTS_SWMODE, - FEAT_HANDLE_UV_SEPARATE, - FEAT_ATTR2, - FEAT_CPR, - FEAT_PRELOAD, - FEAT_FIR_COEF_V, - FEAT_ALPHA_FREE_ZORDER, - FEAT_FIFO_MERGE, - FEAT_BURST_2D, -}; - -static const enum dss_feat_id omap4_dss_feat_list[] = { - FEAT_MGR_LCD2, - FEAT_CORE_CLK_DIV, - FEAT_LCD_CLK_SRC, - FEAT_DSI_DCS_CMD_CONFIG_VC, - FEAT_DSI_VC_OCP_WIDTH, - FEAT_DSI_GNQ, - FEAT_HDMI_CTS_SWMODE, - FEAT_HDMI_AUDIO_USE_MCLK, - FEAT_HANDLE_UV_SEPARATE, - FEAT_ATTR2, - FEAT_CPR, - FEAT_PRELOAD, - FEAT_FIR_COEF_V, - FEAT_ALPHA_FREE_ZORDER, - FEAT_FIFO_MERGE, - FEAT_BURST_2D, -}; - -static const enum dss_feat_id omap5_dss_feat_list[] = { - FEAT_MGR_LCD2, - FEAT_MGR_LCD3, - FEAT_CORE_CLK_DIV, - FEAT_LCD_CLK_SRC, - FEAT_DSI_DCS_CMD_CONFIG_VC, - FEAT_DSI_VC_OCP_WIDTH, - FEAT_DSI_GNQ, - FEAT_HDMI_CTS_SWMODE, - FEAT_HDMI_AUDIO_USE_MCLK, - FEAT_HANDLE_UV_SEPARATE, - FEAT_ATTR2, - FEAT_CPR, - FEAT_PRELOAD, - FEAT_FIR_COEF_V, - FEAT_ALPHA_FREE_ZORDER, - FEAT_FIFO_MERGE, - FEAT_BURST_2D, - FEAT_DSI_PHY_DCC, - FEAT_MFLAG, -}; - -/* OMAP2 DSS Features */ -static const struct omap_dss_features omap2_dss_features = { - .reg_fields = omap2_dss_reg_fields, - .num_reg_fields = ARRAY_SIZE(omap2_dss_reg_fields), - - .features = omap2_dss_feat_list, - .num_features = ARRAY_SIZE(omap2_dss_feat_list), - - .num_mgrs = 2, - .num_ovls = 3, - .supported_displays = omap2_dss_supported_displays, - .supported_outputs = omap2_dss_supported_outputs, - .supported_color_modes = omap2_dss_supported_color_modes, - .overlay_caps = omap2_dss_overlay_caps, - .dss_params = omap2_dss_param_range, - .buffer_size_unit = 1, - .burst_size_unit = 8, -}; - -/* OMAP3 DSS Features */ -static const struct omap_dss_features omap3430_dss_features = { - .reg_fields = omap3_dss_reg_fields, - .num_reg_fields = ARRAY_SIZE(omap3_dss_reg_fields), - - .features = omap3430_dss_feat_list, - .num_features = ARRAY_SIZE(omap3430_dss_feat_list), - - .num_mgrs = 2, - .num_ovls = 3, - .supported_displays = omap3430_dss_supported_displays, - .supported_outputs = omap3430_dss_supported_outputs, - .supported_color_modes = omap3_dss_supported_color_modes, - .overlay_caps = omap3430_dss_overlay_caps, - .dss_params = omap3_dss_param_range, - .buffer_size_unit = 1, - .burst_size_unit = 8, -}; - -/* - * AM35xx DSS Features. This is basically OMAP3 DSS Features without the - * vdds_dsi regulator. - */ -static const struct omap_dss_features am35xx_dss_features = { - .reg_fields = omap3_dss_reg_fields, - .num_reg_fields = ARRAY_SIZE(omap3_dss_reg_fields), - - .features = am35xx_dss_feat_list, - .num_features = ARRAY_SIZE(am35xx_dss_feat_list), - - .num_mgrs = 2, - .num_ovls = 3, - .supported_displays = omap3430_dss_supported_displays, - .supported_outputs = omap3430_dss_supported_outputs, - .supported_color_modes = omap3_dss_supported_color_modes, - .overlay_caps = omap3430_dss_overlay_caps, - .dss_params = omap3_dss_param_range, - .buffer_size_unit = 1, - .burst_size_unit = 8, -}; - -static const struct omap_dss_features am43xx_dss_features = { - .reg_fields = am43xx_dss_reg_fields, - .num_reg_fields = ARRAY_SIZE(am43xx_dss_reg_fields), - - .features = am43xx_dss_feat_list, - .num_features = ARRAY_SIZE(am43xx_dss_feat_list), - - .num_mgrs = 1, - .num_ovls = 3, - .supported_displays = am43xx_dss_supported_displays, - .supported_outputs = am43xx_dss_supported_outputs, - .supported_color_modes = omap3_dss_supported_color_modes, - .overlay_caps = omap3430_dss_overlay_caps, - .dss_params = am43xx_dss_param_range, - .buffer_size_unit = 1, - .burst_size_unit = 8, -}; - -static const struct omap_dss_features omap3630_dss_features = { - .reg_fields = omap3_dss_reg_fields, - .num_reg_fields = ARRAY_SIZE(omap3_dss_reg_fields), - - .features = omap3630_dss_feat_list, - .num_features = ARRAY_SIZE(omap3630_dss_feat_list), - - .num_mgrs = 2, - .num_ovls = 3, - .supported_displays = omap3630_dss_supported_displays, - .supported_outputs = omap3630_dss_supported_outputs, - .supported_color_modes = omap3_dss_supported_color_modes, - .overlay_caps = omap3630_dss_overlay_caps, - .dss_params = omap3_dss_param_range, - .buffer_size_unit = 1, - .burst_size_unit = 8, -}; - -/* OMAP4 DSS Features */ -/* For OMAP4430 ES 1.0 revision */ -static const struct omap_dss_features omap4430_es1_0_dss_features = { - .reg_fields = omap4_dss_reg_fields, - .num_reg_fields = ARRAY_SIZE(omap4_dss_reg_fields), - - .features = omap4430_es1_0_dss_feat_list, - .num_features = ARRAY_SIZE(omap4430_es1_0_dss_feat_list), - - .num_mgrs = 3, - .num_ovls = 4, - .supported_displays = omap4_dss_supported_displays, - .supported_outputs = omap4_dss_supported_outputs, - .supported_color_modes = omap4_dss_supported_color_modes, - .overlay_caps = omap4_dss_overlay_caps, - .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, - .supported_displays = omap4_dss_supported_displays, - .supported_outputs = omap4_dss_supported_outputs, - .supported_color_modes = omap4_dss_supported_color_modes, - .overlay_caps = omap4_dss_overlay_caps, - .dss_params = omap4_dss_param_range, - .buffer_size_unit = 16, - .burst_size_unit = 16, -}; - -/* For all the other OMAP4 versions */ -static const struct omap_dss_features omap4_dss_features = { - .reg_fields = omap4_dss_reg_fields, - .num_reg_fields = ARRAY_SIZE(omap4_dss_reg_fields), - - .features = omap4_dss_feat_list, - .num_features = ARRAY_SIZE(omap4_dss_feat_list), - - .num_mgrs = 3, - .num_ovls = 4, - .supported_displays = omap4_dss_supported_displays, - .supported_outputs = omap4_dss_supported_outputs, - .supported_color_modes = omap4_dss_supported_color_modes, - .overlay_caps = omap4_dss_overlay_caps, - .dss_params = omap4_dss_param_range, - .buffer_size_unit = 16, - .burst_size_unit = 16, -}; - -/* OMAP5 DSS Features */ -static const struct omap_dss_features omap5_dss_features = { - .reg_fields = omap5_dss_reg_fields, - .num_reg_fields = ARRAY_SIZE(omap5_dss_reg_fields), - - .features = omap5_dss_feat_list, - .num_features = ARRAY_SIZE(omap5_dss_feat_list), - - .num_mgrs = 4, - .num_ovls = 4, - .supported_displays = omap5_dss_supported_displays, - .supported_outputs = omap5_dss_supported_outputs, - .supported_color_modes = omap4_dss_supported_color_modes, - .overlay_caps = omap4_dss_overlay_caps, - .dss_params = omap5_dss_param_range, - .buffer_size_unit = 16, - .burst_size_unit = 16, -}; - -/* Functions returning values related to a DSS feature */ -int dss_feat_get_num_mgrs(void) -{ - return omap_current_dss_features->num_mgrs; -} - -int dss_feat_get_num_ovls(void) -{ - return omap_current_dss_features->num_ovls; -} - -unsigned long dss_feat_get_param_min(enum dss_range_param param) -{ - return omap_current_dss_features->dss_params[param].min; -} - -unsigned long dss_feat_get_param_max(enum dss_range_param param) -{ - return omap_current_dss_features->dss_params[param].max; -} - -enum omap_display_type dss_feat_get_supported_displays(enum omap_channel channel) -{ - return omap_current_dss_features->supported_displays[channel]; -} - -enum omap_dss_output_id dss_feat_get_supported_outputs(enum omap_channel channel) -{ - return omap_current_dss_features->supported_outputs[channel]; -} - -const u32 *dss_feat_get_supported_color_modes(enum omap_plane_id plane) -{ - return omap_current_dss_features->supported_color_modes[plane]; -} - -enum omap_overlay_caps dss_feat_get_overlay_caps(enum omap_plane_id plane) -{ - return omap_current_dss_features->overlay_caps[plane]; -} - -bool dss_feat_color_mode_supported(enum omap_plane_id plane, u32 fourcc) -{ - const u32 *modes; - unsigned int i; - - modes = omap_current_dss_features->supported_color_modes[plane]; - - for (i = 0; modes[i]; ++i) { - if (modes[i] == fourcc) - return true; - } - - return false; -} - -u32 dss_feat_get_buffer_size_unit(void) -{ - return omap_current_dss_features->buffer_size_unit; -} - -u32 dss_feat_get_burst_size_unit(void) -{ - return omap_current_dss_features->burst_size_unit; -} - -/* DSS has_feature check */ -bool dss_has_feature(enum dss_feat_id id) -{ - int i; - const enum dss_feat_id *features = omap_current_dss_features->features; - const int num_features = omap_current_dss_features->num_features; - - for (i = 0; i < num_features; i++) { - if (features[i] == id) - return true; - } - - return false; -} - -void dss_feat_get_reg_field(enum dss_feat_reg_field id, u8 *start, u8 *end) -{ - if (id >= omap_current_dss_features->num_reg_fields) - BUG(); - - *start = omap_current_dss_features->reg_fields[id].start; - *end = omap_current_dss_features->reg_fields[id].end; -} - -void dss_features_init(enum omapdss_version version) -{ - switch (version) { - case OMAPDSS_VER_OMAP24xx: - omap_current_dss_features = &omap2_dss_features; - break; - - case OMAPDSS_VER_OMAP34xx_ES1: - case OMAPDSS_VER_OMAP34xx_ES3: - omap_current_dss_features = &omap3430_dss_features; - break; - - case OMAPDSS_VER_OMAP3630: - omap_current_dss_features = &omap3630_dss_features; - break; - - case OMAPDSS_VER_OMAP4430_ES1: - omap_current_dss_features = &omap4430_es1_0_dss_features; - break; - - case OMAPDSS_VER_OMAP4430_ES2: - omap_current_dss_features = &omap4430_es2_0_1_2_dss_features; - break; - - case OMAPDSS_VER_OMAP4: - omap_current_dss_features = &omap4_dss_features; - break; - - case OMAPDSS_VER_OMAP5: - case OMAPDSS_VER_DRA7xx: - omap_current_dss_features = &omap5_dss_features; - break; - - case OMAPDSS_VER_AM35xx: - omap_current_dss_features = &am35xx_dss_features; - break; - - case OMAPDSS_VER_AM43xx: - omap_current_dss_features = &am43xx_dss_features; - break; - - default: - DSSWARN("Unsupported OMAP version"); - break; - } -} diff --git a/drivers/gpu/drm/omapdrm/dss/dss_features.h b/drivers/gpu/drm/omapdrm/dss/dss_features.h deleted file mode 100644 index c36436d27ff5..000000000000 --- a/drivers/gpu/drm/omapdrm/dss/dss_features.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - * linux/drivers/video/omap2/dss/dss_features.h - * - * Copyright (C) 2010 Texas Instruments - * Author: Archit Taneja <archit@ti.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef __OMAP2_DSS_FEATURES_H -#define __OMAP2_DSS_FEATURES_H - -#define MAX_DSS_MANAGERS 4 -#define MAX_DSS_OVERLAYS 4 -#define MAX_DSS_LCD_MANAGERS 3 -#define MAX_NUM_DSI 2 - -/* DSS has feature id */ -enum dss_feat_id { - FEAT_LCDENABLEPOL, - FEAT_LCDENABLESIGNAL, - FEAT_PCKFREEENABLE, - FEAT_FUNCGATED, - FEAT_MGR_LCD2, - FEAT_MGR_LCD3, - FEAT_LINEBUFFERSPLIT, - FEAT_ROWREPEATENABLE, - FEAT_RESIZECONF, - /* Independent core clk divider */ - FEAT_CORE_CLK_DIV, - FEAT_LCD_CLK_SRC, - /* DSI-PLL power command 0x3 is not working */ - FEAT_DSI_PLL_PWR_BUG, - FEAT_DSI_DCS_CMD_CONFIG_VC, - FEAT_DSI_VC_OCP_WIDTH, - FEAT_DSI_REVERSE_TXCLKESC, - FEAT_DSI_GNQ, - FEAT_DPI_USES_VDDS_DSI, - FEAT_HDMI_CTS_SWMODE, - FEAT_HDMI_AUDIO_USE_MCLK, - FEAT_HANDLE_UV_SEPARATE, - FEAT_ATTR2, - FEAT_VENC_REQUIRES_TV_DAC_CLK, - FEAT_CPR, - FEAT_PRELOAD, - FEAT_FIR_COEF_V, - FEAT_ALPHA_FIXED_ZORDER, - FEAT_ALPHA_FREE_ZORDER, - FEAT_FIFO_MERGE, - /* An unknown HW bug causing the normal FIFO thresholds not to work */ - FEAT_OMAP3_DSI_FIFO_BUG, - FEAT_BURST_2D, - FEAT_DSI_PHY_DCC, - FEAT_MFLAG, -}; - -/* DSS register field id */ -enum dss_feat_reg_field { - FEAT_REG_FIRHINC, - FEAT_REG_FIRVINC, - FEAT_REG_FIFOHIGHTHRESHOLD, - FEAT_REG_FIFOLOWTHRESHOLD, - FEAT_REG_FIFOSIZE, - FEAT_REG_HORIZONTALACCU, - FEAT_REG_VERTICALACCU, - FEAT_REG_DISPC_CLK_SWITCH, -}; - -enum dss_range_param { - FEAT_PARAM_DSS_FCK, - FEAT_PARAM_DSS_PCD, - FEAT_PARAM_DSIPLL_LPDIV, - FEAT_PARAM_DSI_FCK, - FEAT_PARAM_DOWNSCALE, - FEAT_PARAM_LINEWIDTH, -}; - -/* DSS Feature Functions */ -unsigned long dss_feat_get_param_min(enum dss_range_param param); -unsigned long dss_feat_get_param_max(enum dss_range_param param); -enum omap_overlay_caps dss_feat_get_overlay_caps(enum omap_plane_id plane); -bool dss_feat_color_mode_supported(enum omap_plane_id plane, - u32 fourcc); - -u32 dss_feat_get_buffer_size_unit(void); /* in bytes */ -u32 dss_feat_get_burst_size_unit(void); /* in bytes */ - -bool dss_has_feature(enum dss_feat_id id); -void dss_feat_get_reg_field(enum dss_feat_reg_field id, u8 *start, u8 *end); -void dss_features_init(enum omapdss_version version); - -enum omap_display_type dss_feat_get_supported_displays(enum omap_channel channel); -enum omap_dss_output_id dss_feat_get_supported_outputs(enum omap_channel channel); - -int dss_feat_get_num_mgrs(void); -int dss_feat_get_num_ovls(void); -const u32 *dss_feat_get_supported_color_modes(enum omap_plane_id plane); - -#endif diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi.h b/drivers/gpu/drm/omapdrm/dss/hdmi.h index fb6cccd02374..c4a4e07f0b99 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi.h +++ b/drivers/gpu/drm/omapdrm/dss/hdmi.h @@ -1,19 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * HDMI driver definition for TI OMAP4 Processor. * - * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/ - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. + * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com/ */ #ifndef _HDMI_H @@ -24,10 +13,14 @@ #include <linux/platform_device.h> #include <linux/hdmi.h> #include <sound/omap-hdmi-audio.h> +#include <media/cec.h> +#include <drm/drm_bridge.h> #include "omapdss.h" #include "dss.h" +struct dss_device; + /* HDMI Wrapper */ #define HDMI_WP_REVISION 0x0 @@ -234,6 +227,7 @@ struct hdmi_core_audio_config { struct hdmi_wp_data { void __iomem *base; phys_addr_t phys_base; + unsigned int version; }; struct hdmi_pll_data { @@ -245,15 +239,28 @@ struct hdmi_pll_data { struct hdmi_wp_data *wp; }; +struct hdmi_phy_features { + bool bist_ctrl; + bool ldo_voltage; + unsigned long max_phy; +}; + struct hdmi_phy_data { void __iomem *base; + const struct hdmi_phy_features *features; u8 lane_function[4]; u8 lane_polarity[4]; }; struct hdmi_core_data { void __iomem *base; + bool cts_swmode; + bool audio_use_mclk; + + struct hdmi_wp_data *wp; + unsigned int core_pwr_cnt; + struct cec_adapter *adap; }; static inline void hdmi_write_reg(void __iomem *base_addr, const u32 idx, @@ -296,27 +303,29 @@ void hdmi_wp_clear_irqenable(struct hdmi_wp_data *wp, u32 mask); int hdmi_wp_set_phy_pwr(struct hdmi_wp_data *wp, enum hdmi_phy_pwr val); int hdmi_wp_set_pll_pwr(struct hdmi_wp_data *wp, enum hdmi_pll_pwr val); void hdmi_wp_video_config_format(struct hdmi_wp_data *wp, - struct hdmi_video_format *video_fmt); + const struct hdmi_video_format *video_fmt); void hdmi_wp_video_config_interface(struct hdmi_wp_data *wp, - struct videomode *vm); + const struct videomode *vm); void hdmi_wp_video_config_timing(struct hdmi_wp_data *wp, - struct videomode *vm); + const struct videomode *vm); void hdmi_wp_init_vid_fmt_timings(struct hdmi_video_format *video_fmt, - struct videomode *vm, struct hdmi_config *param); -int hdmi_wp_init(struct platform_device *pdev, struct hdmi_wp_data *wp); + struct videomode *vm, const struct hdmi_config *param); +int hdmi_wp_init(struct platform_device *pdev, struct hdmi_wp_data *wp, + unsigned int version); phys_addr_t hdmi_wp_get_audio_dma_addr(struct hdmi_wp_data *wp); /* HDMI PLL funcs */ void hdmi_pll_dump(struct hdmi_pll_data *pll, struct seq_file *s); -int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll, - struct hdmi_wp_data *wp); +int hdmi_pll_init(struct dss_device *dss, struct platform_device *pdev, + struct hdmi_pll_data *pll, struct hdmi_wp_data *wp); void hdmi_pll_uninit(struct hdmi_pll_data *hpll); /* HDMI PHY funcs */ int hdmi_phy_configure(struct hdmi_phy_data *phy, unsigned long hfbitclk, unsigned long lfbitclk); void hdmi_phy_dump(struct hdmi_phy_data *phy, struct seq_file *s); -int hdmi_phy_init(struct platform_device *pdev, struct hdmi_phy_data *phy); +int hdmi_phy_init(struct platform_device *pdev, struct hdmi_phy_data *phy, + unsigned int version); int hdmi_phy_parse_lanes(struct hdmi_phy_data *phy, const u32 *lanes); /* HDMI common funcs */ @@ -340,6 +349,9 @@ static inline bool hdmi_mode_has_audio(struct hdmi_config *cfg) struct omap_hdmi { struct mutex lock; struct platform_device *pdev; + struct dss_device *dss; + + struct dss_debugfs_entry *debugfs; struct hdmi_wp_data wp; struct hdmi_pll_data pll; @@ -353,6 +365,7 @@ struct omap_hdmi { bool core_enabled; struct omap_dss_device output; + struct drm_bridge bridge; struct platform_device *audio_pdev; void (*audio_abort_cb)(struct device *dev); @@ -361,10 +374,12 @@ struct omap_hdmi { bool audio_configured; struct omap_dss_audio audio_config; - /* This lock should be taken when booleans bellow are touched. */ + /* This lock should be taken when booleans below are touched. */ spinlock_t audio_playing_lock; bool audio_playing; bool display_enabled; }; +#define drm_bridge_to_hdmi(b) container_of(b, struct omap_hdmi, bridge) + #endif diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c b/drivers/gpu/drm/omapdrm/dss/hdmi4.c index 284b4942b9ac..3cd612af2449 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c @@ -1,20 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * HDMI interface DSS driver for TI's OMAP4 family of SoCs. - * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/ + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com/ * Authors: Yong Zhi * Mythri pk <mythripk@ti.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. */ #define DSS_SUBSYS_NAME "HDMI" @@ -30,48 +20,51 @@ #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/clk.h> -#include <linux/gpio.h> #include <linux/regulator/consumer.h> #include <linux/component.h> #include <linux/of.h> #include <linux/of_graph.h> #include <sound/omap-hdmi-audio.h> +#include <media/cec.h> + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_state_helper.h> +#include <drm/drm_edid.h> #include "omapdss.h" #include "hdmi4_core.h" +#include "hdmi4_cec.h" #include "dss.h" -#include "dss_features.h" #include "hdmi.h" -static struct omap_hdmi hdmi; - -static int hdmi_runtime_get(void) +static int hdmi_runtime_get(struct omap_hdmi *hdmi) { int r; DSSDBG("hdmi_runtime_get\n"); - r = pm_runtime_get_sync(&hdmi.pdev->dev); - WARN_ON(r < 0); - if (r < 0) + r = pm_runtime_get_sync(&hdmi->pdev->dev); + if (WARN_ON(r < 0)) { + pm_runtime_put_noidle(&hdmi->pdev->dev); return r; - + } return 0; } -static void hdmi_runtime_put(void) +static void hdmi_runtime_put(struct omap_hdmi *hdmi) { int r; DSSDBG("hdmi_runtime_put\n"); - r = pm_runtime_put_sync(&hdmi.pdev->dev); + r = pm_runtime_put_sync(&hdmi->pdev->dev); WARN_ON(r < 0 && r != -ENOSYS); } static irqreturn_t hdmi_irq_handler(int irq, void *data) { - struct hdmi_wp_data *wp = data; + struct omap_hdmi *hdmi = data; + struct hdmi_wp_data *wp = &hdmi->wp; u32 irqstatus; irqstatus = hdmi_wp_get_irqstatus(wp); @@ -96,81 +89,77 @@ static irqreturn_t hdmi_irq_handler(int irq, void *data) } else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) { hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON); } + if (irqstatus & HDMI_IRQ_CORE) { + u32 intr4 = hdmi_read_reg(hdmi->core.base, HDMI_CORE_SYS_INTR4); - return IRQ_HANDLED; -} - -static int hdmi_init_regulator(void) -{ - struct regulator *reg; - - if (hdmi.vdda_reg != NULL) - return 0; - - reg = devm_regulator_get(&hdmi.pdev->dev, "vdda"); - - if (IS_ERR(reg)) { - if (PTR_ERR(reg) != -EPROBE_DEFER) - DSSERR("can't get VDDA regulator\n"); - return PTR_ERR(reg); + hdmi_write_reg(hdmi->core.base, HDMI_CORE_SYS_INTR4, intr4); + if (intr4 & 8) + hdmi4_cec_irq(&hdmi->core); } - hdmi.vdda_reg = reg; - - return 0; + return IRQ_HANDLED; } -static int hdmi_power_on_core(struct omap_dss_device *dssdev) +static int hdmi_power_on_core(struct omap_hdmi *hdmi) { int r; - r = regulator_enable(hdmi.vdda_reg); + if (hdmi->core.core_pwr_cnt++) + return 0; + + r = regulator_enable(hdmi->vdda_reg); if (r) - return r; + goto err_reg_enable; - r = hdmi_runtime_get(); + r = hdmi_runtime_get(hdmi); if (r) goto err_runtime_get; + hdmi4_core_powerdown_disable(&hdmi->core); + /* Make selection of HDMI in DSS */ - dss_select_hdmi_venc_clk_source(DSS_HDMI_M_PCLK); + dss_select_hdmi_venc_clk_source(hdmi->dss, DSS_HDMI_M_PCLK); - hdmi.core_enabled = true; + hdmi->core_enabled = true; return 0; err_runtime_get: - regulator_disable(hdmi.vdda_reg); + regulator_disable(hdmi->vdda_reg); +err_reg_enable: + hdmi->core.core_pwr_cnt--; return r; } -static void hdmi_power_off_core(struct omap_dss_device *dssdev) +static void hdmi_power_off_core(struct omap_hdmi *hdmi) { - hdmi.core_enabled = false; + if (--hdmi->core.core_pwr_cnt) + return; + + hdmi->core_enabled = false; - hdmi_runtime_put(); - regulator_disable(hdmi.vdda_reg); + hdmi_runtime_put(hdmi); + regulator_disable(hdmi->vdda_reg); } -static int hdmi_power_on_full(struct omap_dss_device *dssdev) +static int hdmi_power_on_full(struct omap_hdmi *hdmi) { int r; - struct videomode *vm; - enum omap_channel channel = dssdev->dispc_channel; - struct hdmi_wp_data *wp = &hdmi.wp; + const struct videomode *vm; + struct hdmi_wp_data *wp = &hdmi->wp; struct dss_pll_clock_info hdmi_cinfo = { 0 }; - unsigned pc; + unsigned int pc; - r = hdmi_power_on_core(dssdev); + r = hdmi_power_on_core(hdmi); if (r) return r; /* disable and clear irqs */ - hdmi_wp_clear_irqenable(wp, 0xffffffff); - hdmi_wp_set_irqstatus(wp, 0xffffffff); + hdmi_wp_clear_irqenable(wp, ~HDMI_IRQ_CORE); + hdmi_wp_set_irqstatus(wp, ~HDMI_IRQ_CORE); - vm = &hdmi.cfg.vm; + vm = &hdmi->cfg.vm; DSSDBG("hdmi_power_on hactive= %d vactive = %d\n", vm->hactive, vm->vactive); @@ -182,22 +171,22 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev) /* DSS_HDMI_TCLK is bitclk / 10 */ pc *= 10; - dss_pll_calc_b(&hdmi.pll.pll, clk_get_rate(hdmi.pll.pll.clkin), + dss_pll_calc_b(&hdmi->pll.pll, clk_get_rate(hdmi->pll.pll.clkin), pc, &hdmi_cinfo); - r = dss_pll_enable(&hdmi.pll.pll); + r = dss_pll_enable(&hdmi->pll.pll); if (r) { DSSERR("Failed to enable PLL\n"); goto err_pll_enable; } - r = dss_pll_set_config(&hdmi.pll.pll, &hdmi_cinfo); + r = dss_pll_set_config(&hdmi->pll.pll, &hdmi_cinfo); if (r) { DSSERR("Failed to configure PLL\n"); goto err_pll_cfg; } - r = hdmi_phy_configure(&hdmi.phy, hdmi_cinfo.clkdco, + r = hdmi_phy_configure(&hdmi->phy, hdmi_cinfo.clkdco, hdmi_cinfo.clkout[0]); if (r) { DSSDBG("Failed to configure PHY\n"); @@ -208,16 +197,13 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev) if (r) goto err_phy_pwr; - hdmi4_configure(&hdmi.core, &hdmi.wp, &hdmi.cfg); + hdmi4_configure(&hdmi->core, &hdmi->wp, &hdmi->cfg); - /* tv size */ - dss_mgr_set_timings(channel, vm); - - r = dss_mgr_enable(channel); + r = dss_mgr_enable(&hdmi->output); if (r) goto err_mgr_enable; - r = hdmi_wp_video_start(&hdmi.wp); + r = hdmi_wp_video_start(&hdmi->wp); if (r) goto err_vid_enable; @@ -227,95 +213,52 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev) return 0; err_vid_enable: - dss_mgr_disable(channel); + dss_mgr_disable(&hdmi->output); err_mgr_enable: - hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF); + hdmi_wp_set_phy_pwr(&hdmi->wp, HDMI_PHYPWRCMD_OFF); err_phy_pwr: err_phy_cfg: err_pll_cfg: - dss_pll_disable(&hdmi.pll.pll); + dss_pll_disable(&hdmi->pll.pll); err_pll_enable: - hdmi_power_off_core(dssdev); + hdmi_power_off_core(hdmi); return -EIO; } -static void hdmi_power_off_full(struct omap_dss_device *dssdev) -{ - enum omap_channel channel = dssdev->dispc_channel; - - hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff); - - hdmi_wp_video_stop(&hdmi.wp); - - dss_mgr_disable(channel); - - hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF); - - dss_pll_disable(&hdmi.pll.pll); - - hdmi_power_off_core(dssdev); -} - -static int hdmi_display_check_timing(struct omap_dss_device *dssdev, - struct videomode *vm) +static void hdmi_power_off_full(struct omap_hdmi *hdmi) { - if (!dispc_mgr_timings_ok(dssdev->dispc_channel, vm)) - return -EINVAL; + hdmi_wp_clear_irqenable(&hdmi->wp, ~HDMI_IRQ_CORE); - return 0; -} + hdmi_wp_video_stop(&hdmi->wp); -static void hdmi_display_set_timing(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - mutex_lock(&hdmi.lock); + dss_mgr_disable(&hdmi->output); - hdmi.cfg.vm = *vm; + hdmi_wp_set_phy_pwr(&hdmi->wp, HDMI_PHYPWRCMD_OFF); - dispc_set_tv_pclk(vm->pixelclock); + dss_pll_disable(&hdmi->pll.pll); - mutex_unlock(&hdmi.lock); + hdmi_power_off_core(hdmi); } -static void hdmi_display_get_timings(struct omap_dss_device *dssdev, - struct videomode *vm) +static int hdmi_dump_regs(struct seq_file *s, void *p) { - *vm = hdmi.cfg.vm; -} + struct omap_hdmi *hdmi = s->private; -static void hdmi_dump_regs(struct seq_file *s) -{ - mutex_lock(&hdmi.lock); + mutex_lock(&hdmi->lock); - if (hdmi_runtime_get()) { - mutex_unlock(&hdmi.lock); - return; + if (hdmi_runtime_get(hdmi)) { + mutex_unlock(&hdmi->lock); + return 0; } - hdmi_wp_dump(&hdmi.wp, s); - hdmi_pll_dump(&hdmi.pll, s); - hdmi_phy_dump(&hdmi.phy, s); - hdmi4_core_dump(&hdmi.core, s); - - hdmi_runtime_put(); - mutex_unlock(&hdmi.lock); -} - -static int read_edid(u8 *buf, int len) -{ - int r; - - mutex_lock(&hdmi.lock); - - r = hdmi_runtime_get(); - BUG_ON(r); - - r = hdmi4_read_edid(&hdmi.core, buf, len); - - hdmi_runtime_put(); - mutex_unlock(&hdmi.lock); + hdmi_wp_dump(&hdmi->wp, s); + hdmi_pll_dump(&hdmi->pll, s); + hdmi_phy_dump(&hdmi->phy, s); + hdmi4_core_dump(&hdmi->core, s); - return r; + hdmi_runtime_put(hdmi); + mutex_unlock(&hdmi->lock); + return 0; } static void hdmi_start_audio_stream(struct omap_hdmi *hd) @@ -330,259 +273,268 @@ static void hdmi_stop_audio_stream(struct omap_hdmi *hd) hdmi_wp_audio_enable(&hd->wp, false); } -static int hdmi_display_enable(struct omap_dss_device *dssdev) +int hdmi4_core_enable(struct hdmi_core_data *core) { - struct omap_dss_device *out = &hdmi.output; - unsigned long flags; + struct omap_hdmi *hdmi = container_of(core, struct omap_hdmi, core); int r = 0; - DSSDBG("ENTER hdmi_display_enable\n"); - - mutex_lock(&hdmi.lock); + DSSDBG("ENTER omapdss_hdmi4_core_enable\n"); - if (!out->dispc_channel_connected) { - DSSERR("failed to enable display: no output/manager\n"); - r = -ENODEV; - goto err0; - } + mutex_lock(&hdmi->lock); - r = hdmi_power_on_full(dssdev); + r = hdmi_power_on_core(hdmi); if (r) { DSSERR("failed to power on device\n"); goto err0; } - if (hdmi.audio_configured) { - r = hdmi4_audio_config(&hdmi.core, &hdmi.wp, &hdmi.audio_config, - hdmi.cfg.vm.pixelclock); - if (r) { - DSSERR("Error restoring audio configuration: %d", r); - hdmi.audio_abort_cb(&hdmi.pdev->dev); - hdmi.audio_configured = false; - } - } - - spin_lock_irqsave(&hdmi.audio_playing_lock, flags); - if (hdmi.audio_configured && hdmi.audio_playing) - hdmi_start_audio_stream(&hdmi); - hdmi.display_enabled = true; - spin_unlock_irqrestore(&hdmi.audio_playing_lock, flags); - - mutex_unlock(&hdmi.lock); + mutex_unlock(&hdmi->lock); return 0; err0: - mutex_unlock(&hdmi.lock); + mutex_unlock(&hdmi->lock); return r; } -static void hdmi_display_disable(struct omap_dss_device *dssdev) +void hdmi4_core_disable(struct hdmi_core_data *core) { - unsigned long flags; - - DSSDBG("Enter hdmi_display_disable\n"); + struct omap_hdmi *hdmi = container_of(core, struct omap_hdmi, core); - mutex_lock(&hdmi.lock); + DSSDBG("Enter omapdss_hdmi4_core_disable\n"); - spin_lock_irqsave(&hdmi.audio_playing_lock, flags); - hdmi_stop_audio_stream(&hdmi); - hdmi.display_enabled = false; - spin_unlock_irqrestore(&hdmi.audio_playing_lock, flags); + mutex_lock(&hdmi->lock); - hdmi_power_off_full(dssdev); + hdmi_power_off_core(hdmi); - mutex_unlock(&hdmi.lock); + mutex_unlock(&hdmi->lock); } -static int hdmi_core_enable(struct omap_dss_device *dssdev) +/* ----------------------------------------------------------------------------- + * DRM Bridge Operations + */ + +static int hdmi4_bridge_attach(struct drm_bridge *bridge, + struct drm_encoder *encoder, + enum drm_bridge_attach_flags flags) { - int r = 0; + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); - DSSDBG("ENTER omapdss_hdmi_core_enable\n"); + if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) + return -EINVAL; - mutex_lock(&hdmi.lock); + return drm_bridge_attach(encoder, hdmi->output.next_bridge, + bridge, flags); +} - r = hdmi_power_on_core(dssdev); - if (r) { - DSSERR("failed to power on device\n"); - goto err0; - } +static void hdmi4_bridge_mode_set(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + const struct drm_display_mode *adjusted_mode) +{ + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); - mutex_unlock(&hdmi.lock); - return 0; + mutex_lock(&hdmi->lock); -err0: - mutex_unlock(&hdmi.lock); - return r; + drm_display_mode_to_videomode(adjusted_mode, &hdmi->cfg.vm); + + dispc_set_tv_pclk(hdmi->dss->dispc, adjusted_mode->clock * 1000); + + mutex_unlock(&hdmi->lock); } -static void hdmi_core_disable(struct omap_dss_device *dssdev) +static void hdmi4_bridge_enable(struct drm_bridge *bridge, + struct drm_atomic_state *state) { - DSSDBG("Enter omapdss_hdmi_core_disable\n"); + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); + struct drm_connector_state *conn_state; + struct drm_connector *connector; + struct drm_crtc_state *crtc_state; + unsigned long flags; + int ret; - mutex_lock(&hdmi.lock); + /* + * None of these should fail, as the bridge can't be enabled without a + * valid CRTC to connector path with fully populated new states. + */ + connector = drm_atomic_get_new_connector_for_encoder(state, + bridge->encoder); + if (WARN_ON(!connector)) + return; + conn_state = drm_atomic_get_new_connector_state(state, connector); + if (WARN_ON(!conn_state)) + return; + crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); + if (WARN_ON(!crtc_state)) + return; - hdmi_power_off_core(dssdev); + hdmi->cfg.hdmi_dvi_mode = connector->display_info.is_hdmi + ? HDMI_HDMI : HDMI_DVI; - mutex_unlock(&hdmi.lock); -} + if (connector->display_info.is_hdmi) { + const struct drm_display_mode *mode; + struct hdmi_avi_infoframe avi; -static int hdmi_connect(struct omap_dss_device *dssdev, - struct omap_dss_device *dst) -{ - enum omap_channel channel = dssdev->dispc_channel; - int r; + mode = &crtc_state->adjusted_mode; + ret = drm_hdmi_avi_infoframe_from_display_mode(&avi, connector, + mode); + if (ret == 0) + hdmi->cfg.infoframe = avi; + } - r = hdmi_init_regulator(); - if (r) - return r; + mutex_lock(&hdmi->lock); - r = dss_mgr_connect(channel, dssdev); - if (r) - return r; + ret = hdmi_power_on_full(hdmi); + if (ret) { + DSSERR("failed to power on device\n"); + goto done; + } - r = omapdss_output_set_device(dssdev, dst); - if (r) { - DSSERR("failed to connect output to new device: %s\n", - dst->name); - dss_mgr_disconnect(channel, dssdev); - return r; + if (hdmi->audio_configured) { + ret = hdmi4_audio_config(&hdmi->core, &hdmi->wp, + &hdmi->audio_config, + hdmi->cfg.vm.pixelclock); + if (ret) { + DSSERR("Error restoring audio configuration: %d", ret); + hdmi->audio_abort_cb(&hdmi->pdev->dev); + hdmi->audio_configured = false; + } } - return 0; + spin_lock_irqsave(&hdmi->audio_playing_lock, flags); + if (hdmi->audio_configured && hdmi->audio_playing) + hdmi_start_audio_stream(hdmi); + hdmi->display_enabled = true; + spin_unlock_irqrestore(&hdmi->audio_playing_lock, flags); + +done: + mutex_unlock(&hdmi->lock); } -static void hdmi_disconnect(struct omap_dss_device *dssdev, - struct omap_dss_device *dst) +static void hdmi4_bridge_disable(struct drm_bridge *bridge, + struct drm_atomic_state *state) { - enum omap_channel channel = dssdev->dispc_channel; + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); + unsigned long flags; - WARN_ON(dst != dssdev->dst); + mutex_lock(&hdmi->lock); - if (dst != dssdev->dst) - return; + spin_lock_irqsave(&hdmi->audio_playing_lock, flags); + hdmi_stop_audio_stream(hdmi); + hdmi->display_enabled = false; + spin_unlock_irqrestore(&hdmi->audio_playing_lock, flags); + + hdmi_power_off_full(hdmi); - omapdss_output_unset_device(dssdev); + mutex_unlock(&hdmi->lock); +} + +static void hdmi4_bridge_hpd_notify(struct drm_bridge *bridge, + enum drm_connector_status status) +{ + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); - dss_mgr_disconnect(channel, dssdev); + if (status == connector_status_disconnected) + hdmi4_cec_set_phys_addr(&hdmi->core, CEC_PHYS_ADDR_INVALID); } -static int hdmi_read_edid(struct omap_dss_device *dssdev, - u8 *edid, int len) +static const struct drm_edid *hdmi4_bridge_edid_read(struct drm_bridge *bridge, + struct drm_connector *connector) { + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); + const struct drm_edid *drm_edid = NULL; + unsigned int cec_addr; bool need_enable; int r; - need_enable = hdmi.core_enabled == false; + need_enable = hdmi->core_enabled == false; if (need_enable) { - r = hdmi_core_enable(dssdev); + r = hdmi4_core_enable(&hdmi->core); if (r) - return r; + return NULL; } - r = read_edid(edid, len); + mutex_lock(&hdmi->lock); + r = hdmi_runtime_get(hdmi); + BUG_ON(r); - if (need_enable) - hdmi_core_disable(dssdev); + r = hdmi4_core_ddc_init(&hdmi->core); + if (r) + goto done; - return r; -} + drm_edid = drm_edid_read_custom(connector, hdmi4_core_ddc_read, &hdmi->core); -static int hdmi_set_infoframe(struct omap_dss_device *dssdev, - const struct hdmi_avi_infoframe *avi) -{ - hdmi.cfg.infoframe = *avi; - return 0; -} +done: + hdmi_runtime_put(hdmi); + mutex_unlock(&hdmi->lock); -static int hdmi_set_hdmi_mode(struct omap_dss_device *dssdev, - bool hdmi_mode) -{ - hdmi.cfg.hdmi_dvi_mode = hdmi_mode ? HDMI_HDMI : HDMI_DVI; - return 0; -} + if (drm_edid) { + /* + * FIXME: The CEC physical address should be set using + * hdmi4_cec_set_phys_addr(&hdmi->core, + * connector->display_info.source_physical_address) from a path + * that has read the EDID and called + * drm_edid_connector_update(). + */ + const struct edid *edid = drm_edid_raw(drm_edid); + unsigned int len = (edid->extensions + 1) * EDID_LENGTH; -static const struct omapdss_hdmi_ops hdmi_ops = { - .connect = hdmi_connect, - .disconnect = hdmi_disconnect, + cec_addr = cec_get_edid_phys_addr((u8 *)edid, len, NULL); + } else { + cec_addr = CEC_PHYS_ADDR_INVALID; + } - .enable = hdmi_display_enable, - .disable = hdmi_display_disable, + hdmi4_cec_set_phys_addr(&hdmi->core, cec_addr); - .check_timings = hdmi_display_check_timing, - .set_timings = hdmi_display_set_timing, - .get_timings = hdmi_display_get_timings, + if (need_enable) + hdmi4_core_disable(&hdmi->core); + + return drm_edid; +} - .read_edid = hdmi_read_edid, - .set_infoframe = hdmi_set_infoframe, - .set_hdmi_mode = hdmi_set_hdmi_mode, +static const struct drm_bridge_funcs hdmi4_bridge_funcs = { + .attach = hdmi4_bridge_attach, + .mode_set = hdmi4_bridge_mode_set, + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_enable = hdmi4_bridge_enable, + .atomic_disable = hdmi4_bridge_disable, + .hpd_notify = hdmi4_bridge_hpd_notify, + .edid_read = hdmi4_bridge_edid_read, }; -static void hdmi_init_output(struct platform_device *pdev) +static void hdmi4_bridge_init(struct omap_hdmi *hdmi) { - struct omap_dss_device *out = &hdmi.output; - - out->dev = &pdev->dev; - out->id = OMAP_DSS_OUTPUT_HDMI; - out->output_type = OMAP_DISPLAY_TYPE_HDMI; - out->name = "hdmi.0"; - out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT; - out->ops.hdmi = &hdmi_ops; - out->owner = THIS_MODULE; + hdmi->bridge.of_node = hdmi->pdev->dev.of_node; + hdmi->bridge.ops = DRM_BRIDGE_OP_EDID; + hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA; - omapdss_register_output(out); + drm_bridge_add(&hdmi->bridge); } -static void hdmi_uninit_output(struct platform_device *pdev) +static void hdmi4_bridge_cleanup(struct omap_hdmi *hdmi) { - struct omap_dss_device *out = &hdmi.output; - - omapdss_unregister_output(out); + drm_bridge_remove(&hdmi->bridge); } -static int hdmi_probe_of(struct platform_device *pdev) -{ - struct device_node *node = pdev->dev.of_node; - struct device_node *ep; - int r; - - ep = of_graph_get_endpoint_by_regs(node, 0, 0); - if (!ep) - return 0; - - r = hdmi_parse_lanes_of(pdev, ep, &hdmi.phy); - if (r) - goto err; - - of_node_put(ep); - return 0; - -err: - of_node_put(ep); - return r; -} +/* ----------------------------------------------------------------------------- + * Audio Callbacks + */ -/* Audio callbacks */ static int hdmi_audio_startup(struct device *dev, void (*abort_cb)(struct device *dev)) { struct omap_hdmi *hd = dev_get_drvdata(dev); - int ret = 0; mutex_lock(&hd->lock); - if (!hdmi_mode_has_audio(&hd->cfg) || !hd->display_enabled) { - ret = -EPERM; - goto out; - } + WARN_ON(hd->audio_abort_cb != NULL); hd->audio_abort_cb = abort_cb; -out: mutex_unlock(&hd->lock); - return ret; + return 0; } static int hdmi_audio_shutdown(struct device *dev) @@ -603,12 +555,14 @@ static int hdmi_audio_start(struct device *dev) struct omap_hdmi *hd = dev_get_drvdata(dev); unsigned long flags; - WARN_ON(!hdmi_mode_has_audio(&hd->cfg)); - spin_lock_irqsave(&hd->audio_playing_lock, flags); - if (hd->display_enabled) + if (hd->display_enabled) { + if (!hdmi_mode_has_audio(&hd->cfg)) + DSSERR("%s: Video mode does not support audio\n", + __func__); hdmi_start_audio_stream(hd); + } hd->audio_playing = true; spin_unlock_irqrestore(&hd->audio_playing_lock, flags); @@ -635,21 +589,19 @@ static int hdmi_audio_config(struct device *dev, struct omap_dss_audio *dss_audio) { struct omap_hdmi *hd = dev_get_drvdata(dev); - int ret; + int ret = 0; mutex_lock(&hd->lock); - if (!hdmi_mode_has_audio(&hd->cfg) || !hd->display_enabled) { - ret = -EPERM; - goto out; + if (hd->display_enabled) { + ret = hdmi4_audio_config(&hd->core, &hd->wp, dss_audio, + hd->cfg.vm.pixelclock); + if (ret) + goto out; } - ret = hdmi4_audio_config(&hd->core, &hd->wp, dss_audio, - hd->cfg.vm.pixelclock); - if (!ret) { - hd->audio_configured = true; - hd->audio_config = *dss_audio; - } + hd->audio_configured = true; + hd->audio_config = *dss_audio; out: mutex_unlock(&hd->lock); @@ -664,105 +616,82 @@ static const struct omap_hdmi_audio_ops hdmi_audio_ops = { .audio_config = hdmi_audio_config, }; -static int hdmi_audio_register(struct device *dev) +static int hdmi_audio_register(struct omap_hdmi *hdmi) { struct omap_hdmi_audio_pdata pdata = { - .dev = dev, - .dss_version = omapdss_get_version(), - .audio_dma_addr = hdmi_wp_get_audio_dma_addr(&hdmi.wp), + .dev = &hdmi->pdev->dev, + .version = 4, + .audio_dma_addr = hdmi_wp_get_audio_dma_addr(&hdmi->wp), .ops = &hdmi_audio_ops, }; - hdmi.audio_pdev = platform_device_register_data( - dev, "omap-hdmi-audio", PLATFORM_DEVID_AUTO, + hdmi->audio_pdev = platform_device_register_data( + &hdmi->pdev->dev, "omap-hdmi-audio", PLATFORM_DEVID_AUTO, &pdata, sizeof(pdata)); - if (IS_ERR(hdmi.audio_pdev)) - return PTR_ERR(hdmi.audio_pdev); + if (IS_ERR(hdmi->audio_pdev)) + return PTR_ERR(hdmi->audio_pdev); return 0; } -/* HDMI HW IP initialisation */ +/* ----------------------------------------------------------------------------- + * Component Bind & Unbind + */ + static int hdmi4_bind(struct device *dev, struct device *master, void *data) { - struct platform_device *pdev = to_platform_device(dev); + struct dss_device *dss = dss_get_device(master); + struct omap_hdmi *hdmi = dev_get_drvdata(dev); int r; - int irq; - hdmi.pdev = pdev; - dev_set_drvdata(&pdev->dev, &hdmi); + hdmi->dss = dss; - mutex_init(&hdmi.lock); - spin_lock_init(&hdmi.audio_playing_lock); - - r = hdmi_probe_of(pdev); - if (r) - return r; - - r = hdmi_wp_init(pdev, &hdmi.wp); + r = hdmi_runtime_get(hdmi); if (r) return r; - r = hdmi_pll_init(pdev, &hdmi.pll, &hdmi.wp); + r = hdmi_pll_init(dss, hdmi->pdev, &hdmi->pll, &hdmi->wp); if (r) - return r; - - r = hdmi_phy_init(pdev, &hdmi.phy); - if (r) - goto err; + goto err_runtime_put; - r = hdmi4_core_init(pdev, &hdmi.core); + r = hdmi4_cec_init(hdmi->pdev, &hdmi->core, &hdmi->wp); if (r) - goto err; - - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - DSSERR("platform_get_irq failed\n"); - r = -ENODEV; - goto err; - } + goto err_pll_uninit; - r = devm_request_threaded_irq(&pdev->dev, irq, - NULL, hdmi_irq_handler, - IRQF_ONESHOT, "OMAP HDMI", &hdmi.wp); - if (r) { - DSSERR("HDMI IRQ request failed\n"); - goto err; - } - - pm_runtime_enable(&pdev->dev); - - hdmi_init_output(pdev); - - r = hdmi_audio_register(&pdev->dev); + r = hdmi_audio_register(hdmi); if (r) { DSSERR("Registering HDMI audio failed\n"); - hdmi_uninit_output(pdev); - pm_runtime_disable(&pdev->dev); - return r; + goto err_cec_uninit; } - dss_debugfs_create_file("hdmi", hdmi_dump_regs); + hdmi->debugfs = dss_debugfs_create_file(dss, "hdmi", hdmi_dump_regs, + hdmi); + + hdmi_runtime_put(hdmi); return 0; -err: - hdmi_pll_uninit(&hdmi.pll); + +err_cec_uninit: + hdmi4_cec_uninit(&hdmi->core); +err_pll_uninit: + hdmi_pll_uninit(&hdmi->pll); +err_runtime_put: + hdmi_runtime_put(hdmi); return r; } static void hdmi4_unbind(struct device *dev, struct device *master, void *data) { - struct platform_device *pdev = to_platform_device(dev); - - if (hdmi.audio_pdev) - platform_device_unregister(hdmi.audio_pdev); + struct omap_hdmi *hdmi = dev_get_drvdata(dev); - hdmi_uninit_output(pdev); + dss_debugfs_remove_file(hdmi->debugfs); - hdmi_pll_uninit(&hdmi.pll); + if (hdmi->audio_pdev) + platform_device_unregister(hdmi->audio_pdev); - pm_runtime_disable(&pdev->dev); + hdmi4_cec_uninit(&hdmi->core); + hdmi_pll_uninit(&hdmi->pll); } static const struct component_ops hdmi4_component_ops = { @@ -770,62 +699,157 @@ static const struct component_ops hdmi4_component_ops = { .unbind = hdmi4_unbind, }; -static int hdmi4_probe(struct platform_device *pdev) +/* ----------------------------------------------------------------------------- + * Probe & Remove, Suspend & Resume + */ + +static int hdmi4_init_output(struct omap_hdmi *hdmi) { - return component_add(&pdev->dev, &hdmi4_component_ops); + struct omap_dss_device *out = &hdmi->output; + int r; + + hdmi4_bridge_init(hdmi); + + out->dev = &hdmi->pdev->dev; + out->id = OMAP_DSS_OUTPUT_HDMI; + out->type = OMAP_DISPLAY_TYPE_HDMI; + out->name = "hdmi.0"; + out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT; + out->of_port = 0; + + r = omapdss_device_init_output(out, &hdmi->bridge); + if (r < 0) { + hdmi4_bridge_cleanup(hdmi); + return r; + } + + omapdss_device_register(out); + + return 0; } -static int hdmi4_remove(struct platform_device *pdev) +static void hdmi4_uninit_output(struct omap_hdmi *hdmi) { - component_del(&pdev->dev, &hdmi4_component_ops); - return 0; + struct omap_dss_device *out = &hdmi->output; + + omapdss_device_unregister(out); + omapdss_device_cleanup_output(out); + + hdmi4_bridge_cleanup(hdmi); } -static int hdmi_runtime_suspend(struct device *dev) +static int hdmi4_probe_of(struct omap_hdmi *hdmi) { - dispc_runtime_put(); + struct platform_device *pdev = hdmi->pdev; + struct device_node *node = pdev->dev.of_node; + struct device_node *ep; + int r; - return 0; + ep = of_graph_get_endpoint_by_regs(node, 0, 0); + if (!ep) + return 0; + + r = hdmi_parse_lanes_of(pdev, ep, &hdmi->phy); + of_node_put(ep); + return r; } -static int hdmi_runtime_resume(struct device *dev) +static int hdmi4_probe(struct platform_device *pdev) { + struct omap_hdmi *hdmi; + int irq; int r; - r = dispc_runtime_get(); - if (r < 0) + hdmi = devm_drm_bridge_alloc(&pdev->dev, struct omap_hdmi, bridge, &hdmi4_bridge_funcs); + if (IS_ERR(hdmi)) + return PTR_ERR(hdmi); + + hdmi->pdev = pdev; + + dev_set_drvdata(&pdev->dev, hdmi); + + mutex_init(&hdmi->lock); + spin_lock_init(&hdmi->audio_playing_lock); + + r = hdmi4_probe_of(hdmi); + if (r) + return r; + + r = hdmi_wp_init(pdev, &hdmi->wp, 4); + if (r) + return r; + + r = hdmi_phy_init(pdev, &hdmi->phy, 4); + if (r) + return r; + + r = hdmi4_core_init(pdev, &hdmi->core); + if (r) + return r; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + DSSERR("platform_get_irq failed\n"); + return -ENODEV; + } + + r = devm_request_threaded_irq(&pdev->dev, irq, + NULL, hdmi_irq_handler, + IRQF_ONESHOT, "OMAP HDMI", hdmi); + if (r) { + DSSERR("HDMI IRQ request failed\n"); return r; + } + + hdmi->vdda_reg = devm_regulator_get(&pdev->dev, "vdda"); + if (IS_ERR(hdmi->vdda_reg)) { + r = PTR_ERR(hdmi->vdda_reg); + if (r != -EPROBE_DEFER) + DSSERR("can't get VDDA regulator\n"); + return r; + } + + pm_runtime_enable(&pdev->dev); + + r = hdmi4_init_output(hdmi); + if (r) + goto err_pm_disable; + + r = component_add(&pdev->dev, &hdmi4_component_ops); + if (r) + goto err_uninit_output; return 0; + +err_uninit_output: + hdmi4_uninit_output(hdmi); +err_pm_disable: + pm_runtime_disable(&pdev->dev); + return r; } -static const struct dev_pm_ops hdmi_pm_ops = { - .runtime_suspend = hdmi_runtime_suspend, - .runtime_resume = hdmi_runtime_resume, -}; +static void hdmi4_remove(struct platform_device *pdev) +{ + struct omap_hdmi *hdmi = platform_get_drvdata(pdev); + + component_del(&pdev->dev, &hdmi4_component_ops); + + hdmi4_uninit_output(hdmi); + + pm_runtime_disable(&pdev->dev); +} static const struct of_device_id hdmi_of_match[] = { { .compatible = "ti,omap4-hdmi", }, {}, }; -static struct platform_driver omapdss_hdmihw_driver = { +struct platform_driver omapdss_hdmi4hw_driver = { .probe = hdmi4_probe, .remove = hdmi4_remove, .driver = { .name = "omapdss_hdmi", - .pm = &hdmi_pm_ops, .of_match_table = hdmi_of_match, .suppress_bind_attrs = true, }, }; - -int __init hdmi4_init_platform_driver(void) -{ - return platform_driver_register(&omapdss_hdmihw_driver); -} - -void hdmi4_uninit_platform_driver(void) -{ - platform_driver_unregister(&omapdss_hdmihw_driver); -} diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4_cec.c b/drivers/gpu/drm/omapdrm/dss/hdmi4_cec.c new file mode 100644 index 000000000000..852987e67e40 --- /dev/null +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4_cec.c @@ -0,0 +1,353 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * HDMI CEC + * + * Based on the CEC code from hdmi_ti_4xxx_ip.c from Android. + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com/ + * Authors: Yong Zhi + * Mythri pk <mythripk@ti.com> + * + * Heavily modified to use the linux CEC framework: + * + * Copyright 2016-2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + */ + +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include "dss.h" +#include "hdmi.h" +#include "hdmi4_core.h" +#include "hdmi4_cec.h" + +/* HDMI CEC */ +#define HDMI_CEC_DEV_ID 0x900 +#define HDMI_CEC_SPEC 0x904 + +/* Not really a debug register, more a low-level control register */ +#define HDMI_CEC_DBG_3 0x91C +#define HDMI_CEC_TX_INIT 0x920 +#define HDMI_CEC_TX_DEST 0x924 +#define HDMI_CEC_SETUP 0x938 +#define HDMI_CEC_TX_COMMAND 0x93C +#define HDMI_CEC_TX_OPERAND 0x940 +#define HDMI_CEC_TRANSMIT_DATA 0x97C +#define HDMI_CEC_CA_7_0 0x988 +#define HDMI_CEC_CA_15_8 0x98C +#define HDMI_CEC_INT_STATUS_0 0x998 +#define HDMI_CEC_INT_STATUS_1 0x99C +#define HDMI_CEC_INT_ENABLE_0 0x990 +#define HDMI_CEC_INT_ENABLE_1 0x994 +#define HDMI_CEC_RX_CONTROL 0x9B0 +#define HDMI_CEC_RX_COUNT 0x9B4 +#define HDMI_CEC_RX_CMD_HEADER 0x9B8 +#define HDMI_CEC_RX_COMMAND 0x9BC +#define HDMI_CEC_RX_OPERAND 0x9C0 + +#define HDMI_CEC_TX_FIFO_INT_MASK 0x64 +#define HDMI_CEC_RETRANSMIT_CNT_INT_MASK 0x2 + +#define HDMI_CORE_CEC_RETRY 200 + +static void hdmi_cec_received_msg(struct hdmi_core_data *core) +{ + u32 cnt = hdmi_read_reg(core->base, HDMI_CEC_RX_COUNT) & 0xff; + + /* While there are CEC frames in the FIFO */ + while (cnt & 0x70) { + /* and the frame doesn't have an error */ + if (!(cnt & 0x80)) { + struct cec_msg msg = {}; + unsigned int i; + + /* then read the message */ + msg.len = cnt & 0xf; + if (msg.len > CEC_MAX_MSG_SIZE - 2) + msg.len = CEC_MAX_MSG_SIZE - 2; + msg.msg[0] = hdmi_read_reg(core->base, + HDMI_CEC_RX_CMD_HEADER); + msg.msg[1] = hdmi_read_reg(core->base, + HDMI_CEC_RX_COMMAND); + for (i = 0; i < msg.len; i++) { + unsigned int reg = HDMI_CEC_RX_OPERAND + i * 4; + + msg.msg[2 + i] = + hdmi_read_reg(core->base, reg); + } + msg.len += 2; + cec_received_msg(core->adap, &msg); + } + /* Clear the current frame from the FIFO */ + hdmi_write_reg(core->base, HDMI_CEC_RX_CONTROL, 1); + /* Wait until the current frame is cleared */ + while (hdmi_read_reg(core->base, HDMI_CEC_RX_CONTROL) & 1) + udelay(1); + /* + * Re-read the count register and loop to see if there are + * more messages in the FIFO. + */ + cnt = hdmi_read_reg(core->base, HDMI_CEC_RX_COUNT) & 0xff; + } +} + +void hdmi4_cec_irq(struct hdmi_core_data *core) +{ + u32 stat0 = hdmi_read_reg(core->base, HDMI_CEC_INT_STATUS_0); + u32 stat1 = hdmi_read_reg(core->base, HDMI_CEC_INT_STATUS_1); + + hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_0, stat0); + hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_1, stat1); + + if (stat0 & 0x20) { + cec_transmit_done(core->adap, CEC_TX_STATUS_OK, + 0, 0, 0, 0); + REG_FLD_MOD(core->base, HDMI_CEC_DBG_3, 0x1, 7, 7); + } else if (stat1 & 0x02) { + u32 dbg3 = hdmi_read_reg(core->base, HDMI_CEC_DBG_3); + + cec_transmit_done(core->adap, + CEC_TX_STATUS_NACK | + CEC_TX_STATUS_MAX_RETRIES, + 0, (dbg3 >> 4) & 7, 0, 0); + REG_FLD_MOD(core->base, HDMI_CEC_DBG_3, 0x1, 7, 7); + } + if (stat0 & 0x02) + hdmi_cec_received_msg(core); +} + +static bool hdmi_cec_clear_tx_fifo(struct cec_adapter *adap) +{ + struct hdmi_core_data *core = cec_get_drvdata(adap); + int retry = HDMI_CORE_CEC_RETRY; + int temp; + + REG_FLD_MOD(core->base, HDMI_CEC_DBG_3, 0x1, 7, 7); + while (retry) { + temp = hdmi_read_reg(core->base, HDMI_CEC_DBG_3); + if (FLD_GET(temp, 7, 7) == 0) + break; + retry--; + } + return retry != 0; +} + +static bool hdmi_cec_clear_rx_fifo(struct cec_adapter *adap) +{ + struct hdmi_core_data *core = cec_get_drvdata(adap); + int retry = HDMI_CORE_CEC_RETRY; + int temp; + + hdmi_write_reg(core->base, HDMI_CEC_RX_CONTROL, 0x3); + retry = HDMI_CORE_CEC_RETRY; + while (retry) { + temp = hdmi_read_reg(core->base, HDMI_CEC_RX_CONTROL); + if (FLD_GET(temp, 1, 0) == 0) + break; + retry--; + } + return retry != 0; +} + +static int hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable) +{ + struct hdmi_core_data *core = cec_get_drvdata(adap); + int temp, err; + + if (!enable) { + hdmi_write_reg(core->base, HDMI_CEC_INT_ENABLE_0, 0); + hdmi_write_reg(core->base, HDMI_CEC_INT_ENABLE_1, 0); + REG_FLD_MOD(core->base, HDMI_CORE_SYS_INTR_UNMASK4, 0, 3, 3); + hdmi_wp_clear_irqenable(core->wp, HDMI_IRQ_CORE); + hdmi_wp_set_irqstatus(core->wp, HDMI_IRQ_CORE); + REG_FLD_MOD(core->wp->base, HDMI_WP_CLK, 0, 5, 0); + hdmi4_core_disable(core); + return 0; + } + err = hdmi4_core_enable(core); + if (err) + return err; + + /* + * Initialize CEC clock divider: CEC needs 2MHz clock hence + * set the divider to 24 to get 48/24=2MHz clock + */ + REG_FLD_MOD(core->wp->base, HDMI_WP_CLK, 0x18, 5, 0); + + /* Clear TX FIFO */ + if (!hdmi_cec_clear_tx_fifo(adap)) { + pr_err("cec-%s: could not clear TX FIFO\n", adap->name); + err = -EIO; + goto err_disable_clk; + } + + /* Clear RX FIFO */ + if (!hdmi_cec_clear_rx_fifo(adap)) { + pr_err("cec-%s: could not clear RX FIFO\n", adap->name); + err = -EIO; + goto err_disable_clk; + } + + /* Clear CEC interrupts */ + hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_1, + hdmi_read_reg(core->base, HDMI_CEC_INT_STATUS_1)); + hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_0, + hdmi_read_reg(core->base, HDMI_CEC_INT_STATUS_0)); + + /* Enable HDMI core interrupts */ + hdmi_wp_set_irqenable(core->wp, HDMI_IRQ_CORE); + /* Unmask CEC interrupt */ + REG_FLD_MOD(core->base, HDMI_CORE_SYS_INTR_UNMASK4, 0x1, 3, 3); + /* + * Enable CEC interrupts: + * Transmit Buffer Full/Empty Change event + * Receiver FIFO Not Empty event + */ + hdmi_write_reg(core->base, HDMI_CEC_INT_ENABLE_0, 0x22); + /* + * Enable CEC interrupts: + * Frame Retransmit Count Exceeded event + */ + hdmi_write_reg(core->base, HDMI_CEC_INT_ENABLE_1, 0x02); + + /* cec calibration enable (self clearing) */ + hdmi_write_reg(core->base, HDMI_CEC_SETUP, 0x03); + msleep(20); + hdmi_write_reg(core->base, HDMI_CEC_SETUP, 0x04); + + temp = hdmi_read_reg(core->base, HDMI_CEC_SETUP); + if (FLD_GET(temp, 4, 4) != 0) { + temp = FLD_MOD(temp, 0, 4, 4); + hdmi_write_reg(core->base, HDMI_CEC_SETUP, temp); + + /* + * If we enabled CEC in middle of a CEC message on the bus, + * we could have start bit irregularity and/or short + * pulse event. Clear them now. + */ + temp = hdmi_read_reg(core->base, HDMI_CEC_INT_STATUS_1); + temp = FLD_MOD(0x0, 0x5, 2, 0); + hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_1, temp); + } + return 0; + +err_disable_clk: + REG_FLD_MOD(core->wp->base, HDMI_WP_CLK, 0, 5, 0); + hdmi4_core_disable(core); + + return err; +} + +static int hdmi_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr) +{ + struct hdmi_core_data *core = cec_get_drvdata(adap); + u32 v; + + if (log_addr == CEC_LOG_ADDR_INVALID) { + hdmi_write_reg(core->base, HDMI_CEC_CA_7_0, 0); + hdmi_write_reg(core->base, HDMI_CEC_CA_15_8, 0); + return 0; + } + if (log_addr <= 7) { + v = hdmi_read_reg(core->base, HDMI_CEC_CA_7_0); + v |= 1 << log_addr; + hdmi_write_reg(core->base, HDMI_CEC_CA_7_0, v); + } else { + v = hdmi_read_reg(core->base, HDMI_CEC_CA_15_8); + v |= 1 << (log_addr - 8); + hdmi_write_reg(core->base, HDMI_CEC_CA_15_8, v); + } + return 0; +} + +static int hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, + u32 signal_free_time, struct cec_msg *msg) +{ + struct hdmi_core_data *core = cec_get_drvdata(adap); + int temp; + u32 i; + + /* Clear TX FIFO */ + if (!hdmi_cec_clear_tx_fifo(adap)) { + pr_err("cec-%s: could not clear TX FIFO for transmit\n", + adap->name); + return -EIO; + } + + /* Clear TX interrupts */ + hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_0, + HDMI_CEC_TX_FIFO_INT_MASK); + + hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_1, + HDMI_CEC_RETRANSMIT_CNT_INT_MASK); + + /* Set the retry count */ + REG_FLD_MOD(core->base, HDMI_CEC_DBG_3, attempts - 1, 6, 4); + + /* Set the initiator addresses */ + hdmi_write_reg(core->base, HDMI_CEC_TX_INIT, cec_msg_initiator(msg)); + + /* Set destination id */ + temp = cec_msg_destination(msg); + if (msg->len == 1) + temp |= 0x80; + hdmi_write_reg(core->base, HDMI_CEC_TX_DEST, temp); + if (msg->len == 1) + return 0; + + /* Setup command and arguments for the command */ + hdmi_write_reg(core->base, HDMI_CEC_TX_COMMAND, msg->msg[1]); + + for (i = 0; i < msg->len - 2; i++) + hdmi_write_reg(core->base, HDMI_CEC_TX_OPERAND + i * 4, + msg->msg[2 + i]); + + /* Operand count */ + hdmi_write_reg(core->base, HDMI_CEC_TRANSMIT_DATA, + (msg->len - 2) | 0x10); + return 0; +} + +static const struct cec_adap_ops hdmi_cec_adap_ops = { + .adap_enable = hdmi_cec_adap_enable, + .adap_log_addr = hdmi_cec_adap_log_addr, + .adap_transmit = hdmi_cec_adap_transmit, +}; + +void hdmi4_cec_set_phys_addr(struct hdmi_core_data *core, u16 pa) +{ + cec_s_phys_addr(core->adap, pa, false); +} + +int hdmi4_cec_init(struct platform_device *pdev, struct hdmi_core_data *core, + struct hdmi_wp_data *wp) +{ + const u32 caps = CEC_CAP_TRANSMIT | CEC_CAP_LOG_ADDRS | + CEC_CAP_PASSTHROUGH | CEC_CAP_RC; + int ret; + + core->adap = cec_allocate_adapter(&hdmi_cec_adap_ops, core, + "omap4", caps, CEC_MAX_LOG_ADDRS); + ret = PTR_ERR_OR_ZERO(core->adap); + if (ret < 0) + return ret; + core->wp = wp; + + /* Disable clock initially, hdmi_cec_adap_enable() manages it */ + REG_FLD_MOD(core->wp->base, HDMI_WP_CLK, 0, 5, 0); + + ret = cec_register_adapter(core->adap, &pdev->dev); + if (ret < 0) { + cec_delete_adapter(core->adap); + return ret; + } + return 0; +} + +void hdmi4_cec_uninit(struct hdmi_core_data *core) +{ + cec_unregister_adapter(core->adap); +} diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4_cec.h b/drivers/gpu/drm/omapdrm/dss/hdmi4_cec.h new file mode 100644 index 000000000000..1ca5b5ca8a99 --- /dev/null +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4_cec.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * HDMI header definition for OMAP4 HDMI CEC IP + * + * Copyright 2016-2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + */ + +#ifndef _HDMI4_CEC_H_ +#define _HDMI4_CEC_H_ + +struct hdmi_core_data; +struct hdmi_wp_data; +struct platform_device; + +/* HDMI CEC funcs */ +#ifdef CONFIG_OMAP4_DSS_HDMI_CEC +void hdmi4_cec_set_phys_addr(struct hdmi_core_data *core, u16 pa); +void hdmi4_cec_irq(struct hdmi_core_data *core); +int hdmi4_cec_init(struct platform_device *pdev, struct hdmi_core_data *core, + struct hdmi_wp_data *wp); +void hdmi4_cec_uninit(struct hdmi_core_data *core); +#else +static inline void hdmi4_cec_set_phys_addr(struct hdmi_core_data *core, u16 pa) +{ +} + +static inline void hdmi4_cec_irq(struct hdmi_core_data *core) +{ +} + +static inline int hdmi4_cec_init(struct platform_device *pdev, + struct hdmi_core_data *core, + struct hdmi_wp_data *wp) +{ + return 0; +} + +static inline void hdmi4_cec_uninit(struct hdmi_core_data *core) +{ +} +#endif + +#endif diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4_core.c b/drivers/gpu/drm/omapdrm/dss/hdmi4_core.c index ed6001613405..8720bf4f18fe 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi4_core.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4_core.c @@ -1,22 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* - * ti_hdmi_4xxx_ip.c - * * HDMI TI81xx, TI38xx, TI OMAP4 etc IP driver Library - * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/ + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com/ * Authors: Yong Zhi * Mythri pk <mythripk@ti.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. */ #define DSS_SUBSYS_NAME "HDMICORE" @@ -31,11 +19,11 @@ #include <linux/platform_device.h> #include <linux/string.h> #include <linux/seq_file.h> +#include <linux/sys_soc.h> #include <sound/asound.h> #include <sound/asoundef.h> #include "hdmi4_core.h" -#include "dss_features.h" #define HDMI_CORE_AV 0x500 @@ -44,7 +32,7 @@ static inline void __iomem *hdmi_av_base(struct hdmi_core_data *core) return core->base + HDMI_CORE_AV; } -static int hdmi_core_ddc_init(struct hdmi_core_data *core) +int hdmi4_core_ddc_init(struct hdmi_core_data *core) { void __iomem *base = core->base; @@ -86,13 +74,11 @@ static int hdmi_core_ddc_init(struct hdmi_core_data *core) return 0; } -static int hdmi_core_ddc_edid(struct hdmi_core_data *core, - u8 *pedid, int ext) +int hdmi4_core_ddc_read(void *data, u8 *buf, unsigned int block, size_t len) { + struct hdmi_core_data *core = data; void __iomem *base = core->base; u32 i; - char checksum; - u32 offset = 0; /* HDMI_CORE_DDC_STATUS_IN_PROG */ if (hdmi_wait_for_bit_change(base, HDMI_CORE_DDC_STATUS, @@ -101,24 +87,21 @@ static int hdmi_core_ddc_edid(struct hdmi_core_data *core, return -ETIMEDOUT; } - if (ext % 2 != 0) - offset = 0x80; - /* Load Segment Address Register */ - REG_FLD_MOD(base, HDMI_CORE_DDC_SEGM, ext / 2, 7, 0); + REG_FLD_MOD(base, HDMI_CORE_DDC_SEGM, block / 2, 7, 0); /* Load Slave Address Register */ REG_FLD_MOD(base, HDMI_CORE_DDC_ADDR, 0xA0 >> 1, 7, 1); /* Load Offset Address Register */ - REG_FLD_MOD(base, HDMI_CORE_DDC_OFFSET, offset, 7, 0); + REG_FLD_MOD(base, HDMI_CORE_DDC_OFFSET, block % 2 ? 0x80 : 0, 7, 0); /* Load Byte Count */ - REG_FLD_MOD(base, HDMI_CORE_DDC_COUNT1, 0x80, 7, 0); + REG_FLD_MOD(base, HDMI_CORE_DDC_COUNT1, len, 7, 0); REG_FLD_MOD(base, HDMI_CORE_DDC_COUNT2, 0x0, 1, 0); /* Set DDC_CMD */ - if (ext) + if (block) REG_FLD_MOD(base, HDMI_CORE_DDC_CMD, 0x4, 3, 0); else REG_FLD_MOD(base, HDMI_CORE_DDC_CMD, 0x2, 3, 0); @@ -134,7 +117,7 @@ static int hdmi_core_ddc_edid(struct hdmi_core_data *core, return -EIO; } - for (i = 0; i < 0x80; ++i) { + for (i = 0; i < len; ++i) { int t; /* IN_PROG */ @@ -153,48 +136,12 @@ static int hdmi_core_ddc_edid(struct hdmi_core_data *core, udelay(1); } - pedid[i] = REG_GET(base, HDMI_CORE_DDC_DATA, 7, 0); - } - - checksum = 0; - for (i = 0; i < 0x80; ++i) - checksum += pedid[i]; - - if (checksum != 0) { - DSSERR("E-EDID checksum failed!!\n"); - return -EIO; + buf[i] = REG_GET(base, HDMI_CORE_DDC_DATA, 7, 0); } return 0; } -int hdmi4_read_edid(struct hdmi_core_data *core, u8 *edid, int len) -{ - int r, l; - - if (len < 128) - return -EINVAL; - - r = hdmi_core_ddc_init(core); - if (r) - return r; - - r = hdmi_core_ddc_edid(core, edid, 0); - if (r) - return r; - - l = 128; - - if (len >= 128 * 2 && edid[0x7e] > 0) { - r = hdmi_core_ddc_edid(core, edid + 0x80, 1); - if (r) - return r; - l += 128; - } - - return l; -} - static void hdmi_core_init(struct hdmi_core_video_config *video_cfg) { DSSDBG("Enter hdmi_core_init\n"); @@ -208,9 +155,9 @@ static void hdmi_core_init(struct hdmi_core_video_config *video_cfg) video_cfg->tclk_sel_clkmult = HDMI_FPLL10IDCK; } -static void hdmi_core_powerdown_disable(struct hdmi_core_data *core) +void hdmi4_core_powerdown_disable(struct hdmi_core_data *core) { - DSSDBG("Enter hdmi_core_powerdown_disable\n"); + DSSDBG("Enter hdmi4_core_powerdown_disable\n"); REG_FLD_MOD(core->base, HDMI_CORE_SYS_SYS_CTRL1, 0x1, 0, 0); } @@ -335,9 +282,6 @@ void hdmi4_configure(struct hdmi_core_data *core, */ hdmi_core_swreset_assert(core); - /* power down off */ - hdmi_core_powerdown_disable(core); - v_core_cfg.pkt_mode = HDMI_PACKETMODE24BITPERPIXEL; v_core_cfg.hdmi_dvi = cfg->hdmi_dvi_mode; @@ -557,8 +501,9 @@ static void hdmi_core_audio_config(struct hdmi_core_data *core, } /* Set ACR clock divisor */ - REG_FLD_MOD(av_base, - HDMI_CORE_AV_FREQ_SVAL, cfg->mclk_mode, 2, 0); + if (cfg->use_mclk) + 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); /* @@ -690,7 +635,7 @@ int hdmi4_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp, struct hdmi_audio_format audio_format; struct hdmi_audio_dma audio_dma; struct hdmi_core_audio_config acore; - int err, n, cts, channel_count; + int n, cts, channel_count; unsigned int fs_nr; bool word_length_16b = false; @@ -712,7 +657,7 @@ int hdmi4_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp, else acore.i2s_cfg.justification = HDMI_AUDIO_JUSTIFY_RIGHT; /* - * The I2S input word length is twice the lenght given in the IEC-60958 + * The I2S input word length is twice the length given in the IEC-60958 * status word. If the word size is greater than * 20 bits, increment by one. */ @@ -752,15 +697,15 @@ int hdmi4_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp, return -EINVAL; } - err = hdmi_compute_acr(pclk, fs_nr, &n, &cts); + hdmi_compute_acr(pclk, fs_nr, &n, &cts); /* Audio clock regeneration settings */ acore.n = n; acore.cts = cts; - if (dss_has_feature(FEAT_HDMI_CTS_SWMODE)) { + if (core->cts_swmode) { acore.aud_par_busclk = 0; acore.cts_mode = HDMI_AUDIO_CTS_MODE_SW; - acore.use_mclk = dss_has_feature(FEAT_HDMI_AUDIO_USE_MCLK); + acore.use_mclk = core->audio_use_mclk; } else { acore.aud_par_busclk = (((128 * 31) - 1) << 8); acore.cts_mode = HDMI_AUDIO_CTS_MODE_HW; @@ -884,12 +829,58 @@ void hdmi4_audio_stop(struct hdmi_core_data *core, struct hdmi_wp_data *wp) hdmi_wp_audio_core_req_enable(wp, false); } +struct hdmi4_features { + bool cts_swmode; + bool audio_use_mclk; +}; + +static const struct hdmi4_features hdmi4430_es1_features = { + .cts_swmode = false, + .audio_use_mclk = false, +}; + +static const struct hdmi4_features hdmi4430_es2_features = { + .cts_swmode = true, + .audio_use_mclk = false, +}; + +static const struct hdmi4_features hdmi4_features = { + .cts_swmode = true, + .audio_use_mclk = true, +}; + +static const struct soc_device_attribute hdmi4_soc_devices[] = { + { + .machine = "OMAP4430", + .revision = "ES1.?", + .data = &hdmi4430_es1_features, + }, + { + .machine = "OMAP4430", + .revision = "ES2.?", + .data = &hdmi4430_es2_features, + }, + { + .family = "OMAP4", + .data = &hdmi4_features, + }, + { /* sentinel */ } +}; + int hdmi4_core_init(struct platform_device *pdev, struct hdmi_core_data *core) { - struct resource *res; + const struct hdmi4_features *features; + const struct soc_device_attribute *soc; + + soc = soc_device_match(hdmi4_soc_devices); + if (!soc) + return -ENODEV; + + features = soc->data; + core->cts_swmode = features->cts_swmode; + core->audio_use_mclk = features->audio_use_mclk; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core"); - core->base = devm_ioremap_resource(&pdev->dev, res); + core->base = devm_platform_ioremap_resource_byname(pdev, "core"); if (IS_ERR(core->base)) return PTR_ERR(core->base); diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4_core.h b/drivers/gpu/drm/omapdrm/dss/hdmi4_core.h index a069f96ec6f6..3c9e1f600fbe 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi4_core.h +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4_core.h @@ -1,19 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * HDMI header definition for OMAP4 HDMI core IP * - * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/ - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. + * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com/ */ #ifndef _HDMI4_CORE_H_ @@ -260,12 +249,18 @@ struct hdmi_core_packet_enable_repeat { u32 generic_pkt_repeat; }; -int hdmi4_read_edid(struct hdmi_core_data *core, u8 *edid, int len); +int hdmi4_core_ddc_init(struct hdmi_core_data *core); +int hdmi4_core_ddc_read(void *data, u8 *buf, unsigned int block, size_t len); + void hdmi4_configure(struct hdmi_core_data *core, struct hdmi_wp_data *wp, struct hdmi_config *cfg); void hdmi4_core_dump(struct hdmi_core_data *core, struct seq_file *s); int hdmi4_core_init(struct platform_device *pdev, struct hdmi_core_data *core); +int hdmi4_core_enable(struct hdmi_core_data *core); +void hdmi4_core_disable(struct hdmi_core_data *core); +void hdmi4_core_powerdown_disable(struct hdmi_core_data *core); + int hdmi4_audio_start(struct hdmi_core_data *core, struct hdmi_wp_data *wp); void hdmi4_audio_stop(struct hdmi_core_data *core, struct hdmi_wp_data *wp); int hdmi4_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp, diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5.c b/drivers/gpu/drm/omapdrm/dss/hdmi5.c index 441e1999d86a..5636b3dfec1c 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi5.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi5.c @@ -1,25 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * HDMI driver for OMAP5 * - * Copyright (C) 2014 Texas Instruments Incorporated + * Copyright (C) 2014 Texas Instruments Incorporated - https://www.ti.com/ * * Authors: * Yong Zhi * Mythri pk * Archit Taneja <archit@ti.com> * Tomi Valkeinen <tomi.valkeinen@ti.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. */ #define DSS_SUBSYS_NAME "HDMI" @@ -35,47 +24,48 @@ #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/clk.h> -#include <linux/gpio.h> #include <linux/regulator/consumer.h> #include <linux/component.h> #include <linux/of.h> #include <linux/of_graph.h> #include <sound/omap-hdmi-audio.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_state_helper.h> +#include <drm/drm_edid.h> + #include "omapdss.h" #include "hdmi5_core.h" #include "dss.h" -#include "dss_features.h" - -static struct omap_hdmi hdmi; -static int hdmi_runtime_get(void) +static int hdmi_runtime_get(struct omap_hdmi *hdmi) { int r; DSSDBG("hdmi_runtime_get\n"); - r = pm_runtime_get_sync(&hdmi.pdev->dev); - WARN_ON(r < 0); - if (r < 0) + r = pm_runtime_get_sync(&hdmi->pdev->dev); + if (WARN_ON(r < 0)) { + pm_runtime_put_noidle(&hdmi->pdev->dev); return r; - + } return 0; } -static void hdmi_runtime_put(void) +static void hdmi_runtime_put(struct omap_hdmi *hdmi) { int r; DSSDBG("hdmi_runtime_put\n"); - r = pm_runtime_put_sync(&hdmi.pdev->dev); + r = pm_runtime_put_sync(&hdmi->pdev->dev); WARN_ON(r < 0 && r != -ENOSYS); } static irqreturn_t hdmi_irq_handler(int irq, void *data) { - struct hdmi_wp_data *wp = data; + struct omap_hdmi *hdmi = data; + struct hdmi_wp_data *wp = &hdmi->wp; u32 irqstatus; irqstatus = hdmi_wp_get_irqstatus(wp); @@ -98,17 +88,17 @@ static irqreturn_t hdmi_irq_handler(int irq, void *data) * setting the PHY to LDOON. To ignore those, we force the RXDET * line to 0 until the PHY power state has been changed. */ - v = hdmi_read_reg(hdmi.phy.base, HDMI_TXPHY_PAD_CFG_CTRL); + v = hdmi_read_reg(hdmi->phy.base, HDMI_TXPHY_PAD_CFG_CTRL); v = FLD_MOD(v, 1, 15, 15); /* FORCE_RXDET_HIGH */ v = FLD_MOD(v, 0, 14, 7); /* RXDET_LINE */ - hdmi_write_reg(hdmi.phy.base, HDMI_TXPHY_PAD_CFG_CTRL, v); + hdmi_write_reg(hdmi->phy.base, HDMI_TXPHY_PAD_CFG_CTRL, v); hdmi_wp_set_irqstatus(wp, HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT); hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON); - REG_FLD_MOD(hdmi.phy.base, HDMI_TXPHY_PAD_CFG_CTRL, 0, 15, 15); + REG_FLD_MOD(hdmi->phy.base, HDMI_TXPHY_PAD_CFG_CTRL, 0, 15, 15); } else if (irqstatus & HDMI_IRQ_LINK_CONNECT) { hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_TXON); @@ -119,70 +109,51 @@ static irqreturn_t hdmi_irq_handler(int irq, void *data) return IRQ_HANDLED; } -static int hdmi_init_regulator(void) -{ - struct regulator *reg; - - if (hdmi.vdda_reg != NULL) - return 0; - - reg = devm_regulator_get(&hdmi.pdev->dev, "vdda"); - if (IS_ERR(reg)) { - DSSERR("can't get VDDA regulator\n"); - return PTR_ERR(reg); - } - - hdmi.vdda_reg = reg; - - return 0; -} - -static int hdmi_power_on_core(struct omap_dss_device *dssdev) +static int hdmi_power_on_core(struct omap_hdmi *hdmi) { int r; - r = regulator_enable(hdmi.vdda_reg); + r = regulator_enable(hdmi->vdda_reg); if (r) return r; - r = hdmi_runtime_get(); + r = hdmi_runtime_get(hdmi); if (r) goto err_runtime_get; /* Make selection of HDMI in DSS */ - dss_select_hdmi_venc_clk_source(DSS_HDMI_M_PCLK); + dss_select_hdmi_venc_clk_source(hdmi->dss, DSS_HDMI_M_PCLK); - hdmi.core_enabled = true; + hdmi->core_enabled = true; return 0; err_runtime_get: - regulator_disable(hdmi.vdda_reg); + regulator_disable(hdmi->vdda_reg); return r; } -static void hdmi_power_off_core(struct omap_dss_device *dssdev) +static void hdmi_power_off_core(struct omap_hdmi *hdmi) { - hdmi.core_enabled = false; + hdmi->core_enabled = false; - hdmi_runtime_put(); - regulator_disable(hdmi.vdda_reg); + hdmi_runtime_put(hdmi); + regulator_disable(hdmi->vdda_reg); } -static int hdmi_power_on_full(struct omap_dss_device *dssdev) +static int hdmi_power_on_full(struct omap_hdmi *hdmi) { int r; - struct videomode *vm; - enum omap_channel channel = dssdev->dispc_channel; + const struct videomode *vm; struct dss_pll_clock_info hdmi_cinfo = { 0 }; - unsigned pc; + unsigned int pc; - r = hdmi_power_on_core(dssdev); + r = hdmi_power_on_core(hdmi); if (r) return r; - vm = &hdmi.cfg.vm; + vm = &hdmi->cfg.vm; DSSDBG("hdmi_power_on hactive= %d vactive = %d\n", vm->hactive, vm->vactive); @@ -194,157 +165,104 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev) /* DSS_HDMI_TCLK is bitclk / 10 */ pc *= 10; - dss_pll_calc_b(&hdmi.pll.pll, clk_get_rate(hdmi.pll.pll.clkin), + dss_pll_calc_b(&hdmi->pll.pll, clk_get_rate(hdmi->pll.pll.clkin), pc, &hdmi_cinfo); /* disable and clear irqs */ - hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff); - hdmi_wp_set_irqstatus(&hdmi.wp, - hdmi_wp_get_irqstatus(&hdmi.wp)); + hdmi_wp_clear_irqenable(&hdmi->wp, 0xffffffff); + hdmi_wp_set_irqstatus(&hdmi->wp, + hdmi_wp_get_irqstatus(&hdmi->wp)); - r = dss_pll_enable(&hdmi.pll.pll); + r = dss_pll_enable(&hdmi->pll.pll); if (r) { DSSERR("Failed to enable PLL\n"); goto err_pll_enable; } - r = dss_pll_set_config(&hdmi.pll.pll, &hdmi_cinfo); + r = dss_pll_set_config(&hdmi->pll.pll, &hdmi_cinfo); if (r) { DSSERR("Failed to configure PLL\n"); goto err_pll_cfg; } - r = hdmi_phy_configure(&hdmi.phy, hdmi_cinfo.clkdco, + r = hdmi_phy_configure(&hdmi->phy, hdmi_cinfo.clkdco, hdmi_cinfo.clkout[0]); if (r) { DSSDBG("Failed to start PHY\n"); goto err_phy_cfg; } - r = hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_LDOON); + r = hdmi_wp_set_phy_pwr(&hdmi->wp, HDMI_PHYPWRCMD_LDOON); if (r) goto err_phy_pwr; - hdmi5_configure(&hdmi.core, &hdmi.wp, &hdmi.cfg); + hdmi5_configure(&hdmi->core, &hdmi->wp, &hdmi->cfg); - /* tv size */ - dss_mgr_set_timings(channel, vm); - - r = dss_mgr_enable(channel); + r = dss_mgr_enable(&hdmi->output); if (r) goto err_mgr_enable; - r = hdmi_wp_video_start(&hdmi.wp); + r = hdmi_wp_video_start(&hdmi->wp); if (r) goto err_vid_enable; - hdmi_wp_set_irqenable(&hdmi.wp, + hdmi_wp_set_irqenable(&hdmi->wp, HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT); return 0; err_vid_enable: - dss_mgr_disable(channel); + dss_mgr_disable(&hdmi->output); err_mgr_enable: - hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF); + hdmi_wp_set_phy_pwr(&hdmi->wp, HDMI_PHYPWRCMD_OFF); err_phy_pwr: err_phy_cfg: err_pll_cfg: - dss_pll_disable(&hdmi.pll.pll); + dss_pll_disable(&hdmi->pll.pll); err_pll_enable: - hdmi_power_off_core(dssdev); + hdmi_power_off_core(hdmi); return -EIO; } -static void hdmi_power_off_full(struct omap_dss_device *dssdev) +static void hdmi_power_off_full(struct omap_hdmi *hdmi) { - enum omap_channel channel = dssdev->dispc_channel; - - hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff); + hdmi_wp_clear_irqenable(&hdmi->wp, 0xffffffff); - hdmi_wp_video_stop(&hdmi.wp); + hdmi_wp_video_stop(&hdmi->wp); - dss_mgr_disable(channel); + dss_mgr_disable(&hdmi->output); - hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF); + hdmi_wp_set_phy_pwr(&hdmi->wp, HDMI_PHYPWRCMD_OFF); - dss_pll_disable(&hdmi.pll.pll); + dss_pll_disable(&hdmi->pll.pll); - hdmi_power_off_core(dssdev); + hdmi_power_off_core(hdmi); } -static int hdmi_display_check_timing(struct omap_dss_device *dssdev, - struct videomode *vm) +static int hdmi_dump_regs(struct seq_file *s, void *p) { - if (!dispc_mgr_timings_ok(dssdev->dispc_channel, vm)) - return -EINVAL; - - return 0; -} - -static void hdmi_display_set_timing(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - mutex_lock(&hdmi.lock); - - hdmi.cfg.vm = *vm; + struct omap_hdmi *hdmi = s->private; - dispc_set_tv_pclk(vm->pixelclock); + mutex_lock(&hdmi->lock); - mutex_unlock(&hdmi.lock); -} - -static void hdmi_display_get_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - *vm = hdmi.cfg.vm; -} - -static void hdmi_dump_regs(struct seq_file *s) -{ - mutex_lock(&hdmi.lock); - - if (hdmi_runtime_get()) { - mutex_unlock(&hdmi.lock); - return; + if (hdmi_runtime_get(hdmi)) { + mutex_unlock(&hdmi->lock); + return 0; } - hdmi_wp_dump(&hdmi.wp, s); - hdmi_pll_dump(&hdmi.pll, s); - hdmi_phy_dump(&hdmi.phy, s); - hdmi5_core_dump(&hdmi.core, s); - - hdmi_runtime_put(); - mutex_unlock(&hdmi.lock); -} + hdmi_wp_dump(&hdmi->wp, s); + hdmi_pll_dump(&hdmi->pll, s); + hdmi_phy_dump(&hdmi->phy, s); + hdmi5_core_dump(&hdmi->core, s); -static int read_edid(u8 *buf, int len) -{ - int r; - int idlemode; - - mutex_lock(&hdmi.lock); - - r = hdmi_runtime_get(); - BUG_ON(r); - - idlemode = REG_GET(hdmi.wp.base, HDMI_WP_SYSCONFIG, 3, 2); - /* No-idle mode */ - REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2); - - r = hdmi5_read_edid(&hdmi.core, buf, len); - - REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, idlemode, 3, 2); - - hdmi_runtime_put(); - mutex_unlock(&hdmi.lock); - - return r; + hdmi_runtime_put(hdmi); + mutex_unlock(&hdmi->lock); + return 0; } static void hdmi_start_audio_stream(struct omap_hdmi *hd) { - REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2); + REG_FLD_MOD(hd->wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2); hdmi_wp_audio_enable(&hd->wp, true); hdmi_wp_audio_core_req_enable(&hd->wp, true); } @@ -356,259 +274,242 @@ static void hdmi_stop_audio_stream(struct omap_hdmi *hd) REG_FLD_MOD(hd->wp.base, HDMI_WP_SYSCONFIG, hd->wp_idlemode, 3, 2); } -static int hdmi_display_enable(struct omap_dss_device *dssdev) +static int hdmi_core_enable(struct omap_hdmi *hdmi) { - struct omap_dss_device *out = &hdmi.output; - unsigned long flags; int r = 0; - DSSDBG("ENTER hdmi_display_enable\n"); - - mutex_lock(&hdmi.lock); + DSSDBG("ENTER omapdss_hdmi_core_enable\n"); - if (!out->dispc_channel_connected) { - DSSERR("failed to enable display: no output/manager\n"); - r = -ENODEV; - goto err0; - } + mutex_lock(&hdmi->lock); - r = hdmi_power_on_full(dssdev); + r = hdmi_power_on_core(hdmi); if (r) { DSSERR("failed to power on device\n"); goto err0; } - if (hdmi.audio_configured) { - r = hdmi5_audio_config(&hdmi.core, &hdmi.wp, &hdmi.audio_config, - hdmi.cfg.vm.pixelclock); - if (r) { - DSSERR("Error restoring audio configuration: %d", r); - hdmi.audio_abort_cb(&hdmi.pdev->dev); - hdmi.audio_configured = false; - } - } - - spin_lock_irqsave(&hdmi.audio_playing_lock, flags); - if (hdmi.audio_configured && hdmi.audio_playing) - hdmi_start_audio_stream(&hdmi); - hdmi.display_enabled = true; - spin_unlock_irqrestore(&hdmi.audio_playing_lock, flags); - - mutex_unlock(&hdmi.lock); + mutex_unlock(&hdmi->lock); return 0; err0: - mutex_unlock(&hdmi.lock); + mutex_unlock(&hdmi->lock); return r; } -static void hdmi_display_disable(struct omap_dss_device *dssdev) +static void hdmi_core_disable(struct omap_hdmi *hdmi) { - unsigned long flags; + DSSDBG("Enter omapdss_hdmi_core_disable\n"); + + mutex_lock(&hdmi->lock); - DSSDBG("Enter hdmi_display_disable\n"); + hdmi_power_off_core(hdmi); - mutex_lock(&hdmi.lock); + mutex_unlock(&hdmi->lock); +} - spin_lock_irqsave(&hdmi.audio_playing_lock, flags); - hdmi_stop_audio_stream(&hdmi); - hdmi.display_enabled = false; - spin_unlock_irqrestore(&hdmi.audio_playing_lock, flags); +/* ----------------------------------------------------------------------------- + * DRM Bridge Operations + */ - hdmi_power_off_full(dssdev); +static int hdmi5_bridge_attach(struct drm_bridge *bridge, + struct drm_encoder *encoder, + enum drm_bridge_attach_flags flags) +{ + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); - mutex_unlock(&hdmi.lock); + if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) + return -EINVAL; + + return drm_bridge_attach(encoder, hdmi->output.next_bridge, + bridge, flags); } -static int hdmi_core_enable(struct omap_dss_device *dssdev) +static void hdmi5_bridge_mode_set(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + const struct drm_display_mode *adjusted_mode) { - int r = 0; + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); - DSSDBG("ENTER omapdss_hdmi_core_enable\n"); + mutex_lock(&hdmi->lock); - mutex_lock(&hdmi.lock); + drm_display_mode_to_videomode(adjusted_mode, &hdmi->cfg.vm); - r = hdmi_power_on_core(dssdev); - if (r) { - DSSERR("failed to power on device\n"); - goto err0; - } - - mutex_unlock(&hdmi.lock); - return 0; + dispc_set_tv_pclk(hdmi->dss->dispc, adjusted_mode->clock * 1000); -err0: - mutex_unlock(&hdmi.lock); - return r; + mutex_unlock(&hdmi->lock); } -static void hdmi_core_disable(struct omap_dss_device *dssdev) +static void hdmi5_bridge_enable(struct drm_bridge *bridge, + struct drm_atomic_state *state) { - DSSDBG("Enter omapdss_hdmi_core_disable\n"); + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); + struct drm_connector_state *conn_state; + struct drm_connector *connector; + struct drm_crtc_state *crtc_state; + unsigned long flags; + int ret; - mutex_lock(&hdmi.lock); + /* + * None of these should fail, as the bridge can't be enabled without a + * valid CRTC to connector path with fully populated new states. + */ + connector = drm_atomic_get_new_connector_for_encoder(state, + bridge->encoder); + if (WARN_ON(!connector)) + return; + conn_state = drm_atomic_get_new_connector_state(state, connector); + if (WARN_ON(!conn_state)) + return; + crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); + if (WARN_ON(!crtc_state)) + return; - hdmi_power_off_core(dssdev); + hdmi->cfg.hdmi_dvi_mode = connector->display_info.is_hdmi + ? HDMI_HDMI : HDMI_DVI; - mutex_unlock(&hdmi.lock); -} + if (connector->display_info.is_hdmi) { + const struct drm_display_mode *mode; + struct hdmi_avi_infoframe avi; -static int hdmi_connect(struct omap_dss_device *dssdev, - struct omap_dss_device *dst) -{ - enum omap_channel channel = dssdev->dispc_channel; - int r; + mode = &crtc_state->adjusted_mode; + ret = drm_hdmi_avi_infoframe_from_display_mode(&avi, connector, + mode); + if (ret == 0) + hdmi->cfg.infoframe = avi; + } - r = hdmi_init_regulator(); - if (r) - return r; + mutex_lock(&hdmi->lock); - r = dss_mgr_connect(channel, dssdev); - if (r) - return r; + ret = hdmi_power_on_full(hdmi); + if (ret) { + DSSERR("failed to power on device\n"); + goto done; + } - r = omapdss_output_set_device(dssdev, dst); - if (r) { - DSSERR("failed to connect output to new device: %s\n", - dst->name); - dss_mgr_disconnect(channel, dssdev); - return r; + if (hdmi->audio_configured) { + ret = hdmi5_audio_config(&hdmi->core, &hdmi->wp, + &hdmi->audio_config, + hdmi->cfg.vm.pixelclock); + if (ret) { + DSSERR("Error restoring audio configuration: %d", ret); + hdmi->audio_abort_cb(&hdmi->pdev->dev); + hdmi->audio_configured = false; + } } - return 0; + spin_lock_irqsave(&hdmi->audio_playing_lock, flags); + if (hdmi->audio_configured && hdmi->audio_playing) + hdmi_start_audio_stream(hdmi); + hdmi->display_enabled = true; + spin_unlock_irqrestore(&hdmi->audio_playing_lock, flags); + +done: + mutex_unlock(&hdmi->lock); } -static void hdmi_disconnect(struct omap_dss_device *dssdev, - struct omap_dss_device *dst) +static void hdmi5_bridge_disable(struct drm_bridge *bridge, + struct drm_atomic_state *state) { - enum omap_channel channel = dssdev->dispc_channel; + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); + unsigned long flags; - WARN_ON(dst != dssdev->dst); + mutex_lock(&hdmi->lock); - if (dst != dssdev->dst) - return; + spin_lock_irqsave(&hdmi->audio_playing_lock, flags); + hdmi_stop_audio_stream(hdmi); + hdmi->display_enabled = false; + spin_unlock_irqrestore(&hdmi->audio_playing_lock, flags); - omapdss_output_unset_device(dssdev); + hdmi_power_off_full(hdmi); - dss_mgr_disconnect(channel, dssdev); + mutex_unlock(&hdmi->lock); } -static int hdmi_read_edid(struct omap_dss_device *dssdev, - u8 *edid, int len) +static const struct drm_edid *hdmi5_bridge_edid_read(struct drm_bridge *bridge, + struct drm_connector *connector) { + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); + const struct drm_edid *drm_edid; bool need_enable; + int idlemode; int r; - need_enable = hdmi.core_enabled == false; + need_enable = hdmi->core_enabled == false; if (need_enable) { - r = hdmi_core_enable(dssdev); + r = hdmi_core_enable(hdmi); if (r) - return r; + return NULL; } - r = read_edid(edid, len); + mutex_lock(&hdmi->lock); + r = hdmi_runtime_get(hdmi); + BUG_ON(r); - if (need_enable) - hdmi_core_disable(dssdev); + idlemode = REG_GET(hdmi->wp.base, HDMI_WP_SYSCONFIG, 3, 2); + /* No-idle mode */ + REG_FLD_MOD(hdmi->wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2); - return r; -} + hdmi5_core_ddc_init(&hdmi->core); -static int hdmi_set_infoframe(struct omap_dss_device *dssdev, - const struct hdmi_avi_infoframe *avi) -{ - hdmi.cfg.infoframe = *avi; - return 0; -} + drm_edid = drm_edid_read_custom(connector, hdmi5_core_ddc_read, &hdmi->core); -static int hdmi_set_hdmi_mode(struct omap_dss_device *dssdev, - bool hdmi_mode) -{ - hdmi.cfg.hdmi_dvi_mode = hdmi_mode ? HDMI_HDMI : HDMI_DVI; - return 0; -} + hdmi5_core_ddc_uninit(&hdmi->core); -static const struct omapdss_hdmi_ops hdmi_ops = { - .connect = hdmi_connect, - .disconnect = hdmi_disconnect, + REG_FLD_MOD(hdmi->wp.base, HDMI_WP_SYSCONFIG, idlemode, 3, 2); - .enable = hdmi_display_enable, - .disable = hdmi_display_disable, + hdmi_runtime_put(hdmi); + mutex_unlock(&hdmi->lock); - .check_timings = hdmi_display_check_timing, - .set_timings = hdmi_display_set_timing, - .get_timings = hdmi_display_get_timings, + if (need_enable) + hdmi_core_disable(hdmi); + + return drm_edid; +} - .read_edid = hdmi_read_edid, - .set_infoframe = hdmi_set_infoframe, - .set_hdmi_mode = hdmi_set_hdmi_mode, +static const struct drm_bridge_funcs hdmi5_bridge_funcs = { + .attach = hdmi5_bridge_attach, + .mode_set = hdmi5_bridge_mode_set, + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_enable = hdmi5_bridge_enable, + .atomic_disable = hdmi5_bridge_disable, + .edid_read = hdmi5_bridge_edid_read, }; -static void hdmi_init_output(struct platform_device *pdev) +static void hdmi5_bridge_init(struct omap_hdmi *hdmi) { - struct omap_dss_device *out = &hdmi.output; + hdmi->bridge.of_node = hdmi->pdev->dev.of_node; + hdmi->bridge.ops = DRM_BRIDGE_OP_EDID; + hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA; - out->dev = &pdev->dev; - out->id = OMAP_DSS_OUTPUT_HDMI; - out->output_type = OMAP_DISPLAY_TYPE_HDMI; - out->name = "hdmi.0"; - out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT; - out->ops.hdmi = &hdmi_ops; - out->owner = THIS_MODULE; - - omapdss_register_output(out); + drm_bridge_add(&hdmi->bridge); } -static void hdmi_uninit_output(struct platform_device *pdev) +static void hdmi5_bridge_cleanup(struct omap_hdmi *hdmi) { - struct omap_dss_device *out = &hdmi.output; - - omapdss_unregister_output(out); + drm_bridge_remove(&hdmi->bridge); } -static int hdmi_probe_of(struct platform_device *pdev) -{ - struct device_node *node = pdev->dev.of_node; - struct device_node *ep; - int r; - - ep = of_graph_get_endpoint_by_regs(node, 0, 0); - if (!ep) - return 0; - - r = hdmi_parse_lanes_of(pdev, ep, &hdmi.phy); - if (r) - goto err; - - of_node_put(ep); - return 0; - -err: - of_node_put(ep); - return r; -} +/* ----------------------------------------------------------------------------- + * Audio Callbacks + */ -/* Audio callbacks */ static int hdmi_audio_startup(struct device *dev, void (*abort_cb)(struct device *dev)) { struct omap_hdmi *hd = dev_get_drvdata(dev); - int ret = 0; mutex_lock(&hd->lock); - if (!hdmi_mode_has_audio(&hd->cfg) || !hd->display_enabled) { - ret = -EPERM; - goto out; - } + WARN_ON(hd->audio_abort_cb != NULL); hd->audio_abort_cb = abort_cb; -out: mutex_unlock(&hd->lock); - return ret; + return 0; } static int hdmi_audio_shutdown(struct device *dev) @@ -629,12 +530,14 @@ static int hdmi_audio_start(struct device *dev) struct omap_hdmi *hd = dev_get_drvdata(dev); unsigned long flags; - WARN_ON(!hdmi_mode_has_audio(&hd->cfg)); - spin_lock_irqsave(&hd->audio_playing_lock, flags); - if (hd->display_enabled) + if (hd->display_enabled) { + if (!hdmi_mode_has_audio(&hd->cfg)) + DSSERR("%s: Video mode does not support audio\n", + __func__); hdmi_start_audio_stream(hd); + } hd->audio_playing = true; spin_unlock_irqrestore(&hd->audio_playing_lock, flags); @@ -646,7 +549,8 @@ static void hdmi_audio_stop(struct device *dev) struct omap_hdmi *hd = dev_get_drvdata(dev); unsigned long flags; - WARN_ON(!hdmi_mode_has_audio(&hd->cfg)); + if (!hdmi_mode_has_audio(&hd->cfg)) + DSSERR("%s: Video mode does not support audio\n", __func__); spin_lock_irqsave(&hd->audio_playing_lock, flags); @@ -661,22 +565,19 @@ static int hdmi_audio_config(struct device *dev, struct omap_dss_audio *dss_audio) { struct omap_hdmi *hd = dev_get_drvdata(dev); - int ret; + int ret = 0; mutex_lock(&hd->lock); - if (!hdmi_mode_has_audio(&hd->cfg) || !hd->display_enabled) { - ret = -EPERM; - goto out; + if (hd->display_enabled) { + ret = hdmi5_audio_config(&hd->core, &hd->wp, dss_audio, + hd->cfg.vm.pixelclock); + if (ret) + goto out; } - ret = hdmi5_audio_config(&hd->core, &hd->wp, dss_audio, - hd->cfg.vm.pixelclock); - - if (!ret) { - hd->audio_configured = true; - hd->audio_config = *dss_audio; - } + hd->audio_configured = true; + hd->audio_config = *dss_audio; out: mutex_unlock(&hd->lock); @@ -691,110 +592,72 @@ static const struct omap_hdmi_audio_ops hdmi_audio_ops = { .audio_config = hdmi_audio_config, }; -static int hdmi_audio_register(struct device *dev) +static int hdmi_audio_register(struct omap_hdmi *hdmi) { struct omap_hdmi_audio_pdata pdata = { - .dev = dev, - .dss_version = omapdss_get_version(), - .audio_dma_addr = hdmi_wp_get_audio_dma_addr(&hdmi.wp), + .dev = &hdmi->pdev->dev, + .version = 5, + .audio_dma_addr = hdmi_wp_get_audio_dma_addr(&hdmi->wp), .ops = &hdmi_audio_ops, }; - hdmi.audio_pdev = platform_device_register_data( - dev, "omap-hdmi-audio", PLATFORM_DEVID_AUTO, + hdmi->audio_pdev = platform_device_register_data( + &hdmi->pdev->dev, "omap-hdmi-audio", PLATFORM_DEVID_AUTO, &pdata, sizeof(pdata)); - if (IS_ERR(hdmi.audio_pdev)) - return PTR_ERR(hdmi.audio_pdev); + if (IS_ERR(hdmi->audio_pdev)) + return PTR_ERR(hdmi->audio_pdev); - hdmi_runtime_get(); - hdmi.wp_idlemode = - REG_GET(hdmi.wp.base, HDMI_WP_SYSCONFIG, 3, 2); - hdmi_runtime_put(); + hdmi_runtime_get(hdmi); + hdmi->wp_idlemode = + REG_GET(hdmi->wp.base, HDMI_WP_SYSCONFIG, 3, 2); + hdmi_runtime_put(hdmi); return 0; } -/* HDMI HW IP initialisation */ +/* ----------------------------------------------------------------------------- + * Component Bind & Unbind + */ + static int hdmi5_bind(struct device *dev, struct device *master, void *data) { - struct platform_device *pdev = to_platform_device(dev); + struct dss_device *dss = dss_get_device(master); + struct omap_hdmi *hdmi = dev_get_drvdata(dev); int r; - int irq; - - hdmi.pdev = pdev; - dev_set_drvdata(&pdev->dev, &hdmi); - mutex_init(&hdmi.lock); - spin_lock_init(&hdmi.audio_playing_lock); + hdmi->dss = dss; - r = hdmi_probe_of(pdev); + r = hdmi_pll_init(dss, hdmi->pdev, &hdmi->pll, &hdmi->wp); if (r) return r; - r = hdmi_wp_init(pdev, &hdmi.wp); - if (r) - return r; - - r = hdmi_pll_init(pdev, &hdmi.pll, &hdmi.wp); - if (r) - return r; - - r = hdmi_phy_init(pdev, &hdmi.phy); - if (r) - goto err; - - r = hdmi5_core_init(pdev, &hdmi.core); - if (r) - goto err; - - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - DSSERR("platform_get_irq failed\n"); - r = -ENODEV; - goto err; - } - - r = devm_request_threaded_irq(&pdev->dev, irq, - NULL, hdmi_irq_handler, - IRQF_ONESHOT, "OMAP HDMI", &hdmi.wp); - if (r) { - DSSERR("HDMI IRQ request failed\n"); - goto err; - } - - pm_runtime_enable(&pdev->dev); - - hdmi_init_output(pdev); - - r = hdmi_audio_register(&pdev->dev); + r = hdmi_audio_register(hdmi); if (r) { DSSERR("Registering HDMI audio failed %d\n", r); - hdmi_uninit_output(pdev); - pm_runtime_disable(&pdev->dev); - return r; + goto err_pll_uninit; } - dss_debugfs_create_file("hdmi", hdmi_dump_regs); + hdmi->debugfs = dss_debugfs_create_file(dss, "hdmi", hdmi_dump_regs, + hdmi); return 0; -err: - hdmi_pll_uninit(&hdmi.pll); + +err_pll_uninit: + hdmi_pll_uninit(&hdmi->pll); return r; } static void hdmi5_unbind(struct device *dev, struct device *master, void *data) { - struct platform_device *pdev = to_platform_device(dev); - - if (hdmi.audio_pdev) - platform_device_unregister(hdmi.audio_pdev); + struct omap_hdmi *hdmi = dev_get_drvdata(dev); - hdmi_uninit_output(pdev); + dss_debugfs_remove_file(hdmi->debugfs); - hdmi_pll_uninit(&hdmi.pll); + if (hdmi->audio_pdev) + platform_device_unregister(hdmi->audio_pdev); - pm_runtime_disable(&pdev->dev); + hdmi_pll_uninit(&hdmi->pll); } static const struct component_ops hdmi5_component_ops = { @@ -802,39 +665,145 @@ static const struct component_ops hdmi5_component_ops = { .unbind = hdmi5_unbind, }; -static int hdmi5_probe(struct platform_device *pdev) +/* ----------------------------------------------------------------------------- + * Probe & Remove, Suspend & Resume + */ + +static int hdmi5_init_output(struct omap_hdmi *hdmi) { - return component_add(&pdev->dev, &hdmi5_component_ops); + struct omap_dss_device *out = &hdmi->output; + int r; + + hdmi5_bridge_init(hdmi); + + out->dev = &hdmi->pdev->dev; + out->id = OMAP_DSS_OUTPUT_HDMI; + out->type = OMAP_DISPLAY_TYPE_HDMI; + out->name = "hdmi.0"; + out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT; + out->of_port = 0; + + r = omapdss_device_init_output(out, &hdmi->bridge); + if (r < 0) { + hdmi5_bridge_cleanup(hdmi); + return r; + } + + omapdss_device_register(out); + + return 0; } -static int hdmi5_remove(struct platform_device *pdev) +static void hdmi5_uninit_output(struct omap_hdmi *hdmi) { - component_del(&pdev->dev, &hdmi5_component_ops); - return 0; + struct omap_dss_device *out = &hdmi->output; + + omapdss_device_unregister(out); + omapdss_device_cleanup_output(out); + + hdmi5_bridge_cleanup(hdmi); } -static int hdmi_runtime_suspend(struct device *dev) +static int hdmi5_probe_of(struct omap_hdmi *hdmi) { - dispc_runtime_put(); + struct platform_device *pdev = hdmi->pdev; + struct device_node *node = pdev->dev.of_node; + struct device_node *ep; + int r; - return 0; + ep = of_graph_get_endpoint_by_regs(node, 0, 0); + if (!ep) + return 0; + + r = hdmi_parse_lanes_of(pdev, ep, &hdmi->phy); + of_node_put(ep); + return r; } -static int hdmi_runtime_resume(struct device *dev) +static int hdmi5_probe(struct platform_device *pdev) { + struct omap_hdmi *hdmi; + int irq; int r; - r = dispc_runtime_get(); - if (r < 0) + hdmi = devm_drm_bridge_alloc(&pdev->dev, struct omap_hdmi, bridge, &hdmi5_bridge_funcs); + if (IS_ERR(hdmi)) + return PTR_ERR(hdmi); + + hdmi->pdev = pdev; + + dev_set_drvdata(&pdev->dev, hdmi); + + mutex_init(&hdmi->lock); + spin_lock_init(&hdmi->audio_playing_lock); + + r = hdmi5_probe_of(hdmi); + if (r) + return r; + + r = hdmi_wp_init(pdev, &hdmi->wp, 5); + if (r) + return r; + + r = hdmi_phy_init(pdev, &hdmi->phy, 5); + if (r) + return r; + + r = hdmi5_core_init(pdev, &hdmi->core); + if (r) + return r; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + DSSERR("platform_get_irq failed\n"); + return -ENODEV; + } + + r = devm_request_threaded_irq(&pdev->dev, irq, + NULL, hdmi_irq_handler, + IRQF_ONESHOT, "OMAP HDMI", hdmi); + if (r) { + DSSERR("HDMI IRQ request failed\n"); return r; + } + + hdmi->vdda_reg = devm_regulator_get(&pdev->dev, "vdda"); + if (IS_ERR(hdmi->vdda_reg)) { + r = PTR_ERR(hdmi->vdda_reg); + if (r != -EPROBE_DEFER) + DSSERR("can't get VDDA regulator\n"); + return r; + } + + pm_runtime_enable(&pdev->dev); + + r = hdmi5_init_output(hdmi); + if (r) + goto err_pm_disable; + + r = component_add(&pdev->dev, &hdmi5_component_ops); + if (r) + goto err_uninit_output; return 0; + +err_uninit_output: + hdmi5_uninit_output(hdmi); +err_pm_disable: + pm_runtime_disable(&pdev->dev); + return r; } -static const struct dev_pm_ops hdmi_pm_ops = { - .runtime_suspend = hdmi_runtime_suspend, - .runtime_resume = hdmi_runtime_resume, -}; +static void hdmi5_remove(struct platform_device *pdev) +{ + struct omap_hdmi *hdmi = platform_get_drvdata(pdev); + + component_del(&pdev->dev, &hdmi5_component_ops); + + hdmi5_uninit_output(hdmi); + + pm_runtime_disable(&pdev->dev); +} static const struct of_device_id hdmi_of_match[] = { { .compatible = "ti,omap5-hdmi", }, @@ -842,23 +811,12 @@ static const struct of_device_id hdmi_of_match[] = { {}, }; -static struct platform_driver omapdss_hdmihw_driver = { +struct platform_driver omapdss_hdmi5hw_driver = { .probe = hdmi5_probe, .remove = hdmi5_remove, .driver = { .name = "omapdss_hdmi5", - .pm = &hdmi_pm_ops, .of_match_table = hdmi_of_match, .suppress_bind_attrs = true, }, }; - -int __init hdmi5_init_platform_driver(void) -{ - return platform_driver_register(&omapdss_hdmihw_driver); -} - -void hdmi5_uninit_platform_driver(void) -{ - platform_driver_unregister(&omapdss_hdmihw_driver); -} diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5_core.c b/drivers/gpu/drm/omapdrm/dss/hdmi5_core.c index ab179ec133c0..12ef47cd232b 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi5_core.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi5_core.c @@ -1,25 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * OMAP5 HDMI CORE IP driver library * - * Copyright (C) 2014 Texas Instruments Incorporated - * + * Copyright (C) 2014 Texas Instruments Incorporated - https://www.ti.com/ * Authors: * Yong Zhi * Mythri pk * Archit Taneja <archit@ti.com> * Tomi Valkeinen <tomi.valkeinen@ti.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. */ #include <linux/kernel.h> @@ -35,30 +23,18 @@ #include "hdmi5_core.h" -/* only 24 bit color depth used for now */ -static const struct csc_table csc_table_deepcolor[] = { - /* HDMI_DEEP_COLOR_24BIT */ - [0] = { 7036, 0, 0, 32, 0, 7036, 0, 32, 0, 0, 7036, 32, }, - /* HDMI_DEEP_COLOR_30BIT */ - [1] = { 7015, 0, 0, 128, 0, 7015, 0, 128, 0, 0, 7015, 128, }, - /* HDMI_DEEP_COLOR_36BIT */ - [2] = { 7010, 0, 0, 512, 0, 7010, 0, 512, 0, 0, 7010, 512, }, - /* FULL RANGE */ - [3] = { 8192, 0, 0, 0, 0, 8192, 0, 0, 0, 0, 8192, 0, }, -}; - -static void hdmi_core_ddc_init(struct hdmi_core_data *core) +void hdmi5_core_ddc_init(struct hdmi_core_data *core) { void __iomem *base = core->base; const unsigned long long iclk = 266000000; /* DSS L3 ICLK */ - const unsigned ss_scl_high = 4600; /* ns */ - const unsigned ss_scl_low = 5400; /* ns */ - const unsigned fs_scl_high = 600; /* ns */ - const unsigned fs_scl_low = 1300; /* ns */ - const unsigned sda_hold = 1000; /* ns */ - const unsigned sfr_div = 10; + const unsigned int ss_scl_high = 4700; /* ns */ + const unsigned int ss_scl_low = 5500; /* ns */ + const unsigned int fs_scl_high = 600; /* ns */ + const unsigned int fs_scl_low = 1300; /* ns */ + const unsigned int sda_hold = 1000; /* ns */ + const unsigned int sfr_div = 10; unsigned long long sfr; - unsigned v; + unsigned int v; sfr = iclk / sfr_div; /* SFR_DIV */ sfr /= 1000; /* SFR clock in kHz */ @@ -126,7 +102,7 @@ static void hdmi_core_ddc_init(struct hdmi_core_data *core) REG_FLD_MOD(base, HDMI_CORE_I2CM_INT, 0x0, 2, 2); } -static void hdmi_core_ddc_uninit(struct hdmi_core_data *core) +void hdmi5_core_ddc_uninit(struct hdmi_core_data *core) { void __iomem *base = core->base; @@ -136,14 +112,14 @@ static void hdmi_core_ddc_uninit(struct hdmi_core_data *core) REG_FLD_MOD(base, HDMI_CORE_I2CM_INT, 0x1, 2, 2); } -static int hdmi_core_ddc_edid(struct hdmi_core_data *core, u8 *pedid, u8 ext) +int hdmi5_core_ddc_read(void *data, u8 *buf, unsigned int block, size_t len) { + struct hdmi_core_data *core = data; void __iomem *base = core->base; u8 cur_addr; - char checksum = 0; const int retries = 1000; - u8 seg_ptr = ext / 2; - u8 edidbase = ((ext % 2) * 0x80); + u8 seg_ptr = block / 2; + u8 edidbase = ((block % 2) * EDID_LENGTH); REG_FLD_MOD(base, HDMI_CORE_I2CM_SEGPTR, seg_ptr, 7, 0); @@ -151,7 +127,7 @@ static int hdmi_core_ddc_edid(struct hdmi_core_data *core, u8 *pedid, u8 ext) * TODO: We use polling here, although we probably should use proper * interrupts. */ - for (cur_addr = 0; cur_addr < 128; ++cur_addr) { + for (cur_addr = 0; cur_addr < len; ++cur_addr) { int i; /* clear ERROR and DONE */ @@ -188,45 +164,13 @@ static int hdmi_core_ddc_edid(struct hdmi_core_data *core, u8 *pedid, u8 ext) return -EIO; } - pedid[cur_addr] = REG_GET(base, HDMI_CORE_I2CM_DATAI, 7, 0); - checksum += pedid[cur_addr]; + buf[cur_addr] = REG_GET(base, HDMI_CORE_I2CM_DATAI, 7, 0); } return 0; } -int hdmi5_read_edid(struct hdmi_core_data *core, u8 *edid, int len) -{ - int r, n, i; - int max_ext_blocks = (len / 128) - 1; - - if (len < 128) - return -EINVAL; - - hdmi_core_ddc_init(core); - - r = hdmi_core_ddc_edid(core, edid, 0); - if (r) - goto out; - - n = edid[0x7e]; - - if (n > max_ext_blocks) - n = max_ext_blocks; - - for (i = 1; i <= n; i++) { - r = hdmi_core_ddc_edid(core, edid + i * EDID_LENGTH, i); - if (r) - goto out; - } - -out: - hdmi_core_ddc_uninit(core); - - return r ? r : len; -} - void hdmi5_core_dump(struct hdmi_core_data *core, struct seq_file *s) { @@ -288,7 +232,7 @@ void hdmi5_core_dump(struct hdmi_core_data *core, struct seq_file *s) } static void hdmi_core_init(struct hdmi_core_vid_config *video_cfg, - struct hdmi_config *cfg) + const struct hdmi_config *cfg) { DSSDBG("hdmi_core_init\n"); @@ -326,10 +270,10 @@ static void hdmi_core_init(struct hdmi_core_vid_config *video_cfg, /* DSS_HDMI_CORE_VIDEO_CONFIG */ static void hdmi_core_video_config(struct hdmi_core_data *core, - struct hdmi_core_vid_config *cfg) + const struct hdmi_core_vid_config *cfg) { void __iomem *base = core->base; - struct videomode *vm = &cfg->v_fc_config.vm; + const struct videomode *vm = &cfg->v_fc_config.vm; unsigned char r = 0; bool vsync_pol, hsync_pol; @@ -409,14 +353,6 @@ static void hdmi_core_config_video_packetizer(struct hdmi_core_data *core) REG_FLD_MOD(base, HDMI_CORE_VP_CONF, clr_depth ? 0 : 2, 1, 0); } -static void hdmi_core_config_csc(struct hdmi_core_data *core) -{ - int clr_depth = 0; /* 24 bit color depth */ - - /* CSC_COLORDEPTH */ - REG_FLD_MOD(core->base, HDMI_CORE_CSC_SCALE, clr_depth, 7, 4); -} - static void hdmi_core_config_video_sampler(struct hdmi_core_data *core) { int video_mapping = 1; /* for 24 bit color depth */ @@ -431,11 +367,11 @@ static void hdmi_core_write_avi_infoframe(struct hdmi_core_data *core, void __iomem *base = core->base; u8 data[HDMI_INFOFRAME_SIZE(AVI)]; u8 *ptr; - unsigned y, a, b, s; - unsigned c, m, r; - unsigned itc, ec, q, sc; - unsigned vic; - unsigned yq, cn, pr; + unsigned int y, a, b, s; + unsigned int c, m, r; + unsigned int itc, ec, q, sc; + unsigned int vic; + unsigned int yq, cn, pr; hdmi_avi_infoframe_pack(frame, data, sizeof(data)); @@ -481,47 +417,67 @@ static void hdmi_core_write_avi_infoframe(struct hdmi_core_data *core, REG_FLD_MOD(base, HDMI_CORE_FC_PRCONF, pr, 3, 0); } -static void hdmi_core_csc_config(struct hdmi_core_data *core, - struct csc_table csc_coeff) +static void hdmi_core_write_csc(struct hdmi_core_data *core, + const struct csc_table *csc_coeff) { void __iomem *base = core->base; - REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A1_MSB, csc_coeff.a1 >> 8 , 6, 0); - REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A1_LSB, csc_coeff.a1, 7, 0); - REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A2_MSB, csc_coeff.a2 >> 8, 6, 0); - REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A2_LSB, csc_coeff.a2, 7, 0); - REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A3_MSB, csc_coeff.a3 >> 8, 6, 0); - REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A3_LSB, csc_coeff.a3, 7, 0); - REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A4_MSB, csc_coeff.a4 >> 8, 6, 0); - REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A4_LSB, csc_coeff.a4, 7, 0); - REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B1_MSB, csc_coeff.b1 >> 8, 6, 0); - REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B1_LSB, csc_coeff.b1, 7, 0); - REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B2_MSB, csc_coeff.b2 >> 8, 6, 0); - REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B2_LSB, csc_coeff.b2, 7, 0); - REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B3_MSB, csc_coeff.b3 >> 8, 6, 0); - REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B3_LSB, csc_coeff.b3, 7, 0); - REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B4_MSB, csc_coeff.b4 >> 8, 6, 0); - REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B4_LSB, csc_coeff.b4, 7, 0); - REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C1_MSB, csc_coeff.c1 >> 8, 6, 0); - REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C1_LSB, csc_coeff.c1, 7, 0); - REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C2_MSB, csc_coeff.c2 >> 8, 6, 0); - REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C2_LSB, csc_coeff.c2, 7, 0); - REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C3_MSB, csc_coeff.c3 >> 8, 6, 0); - REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C3_LSB, csc_coeff.c3, 7, 0); - REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C4_MSB, csc_coeff.c4 >> 8, 6, 0); - REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C4_LSB, csc_coeff.c4, 7, 0); - + REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A1_MSB, csc_coeff->a1 >> 8, 6, 0); + REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A1_LSB, csc_coeff->a1, 7, 0); + REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A2_MSB, csc_coeff->a2 >> 8, 6, 0); + REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A2_LSB, csc_coeff->a2, 7, 0); + REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A3_MSB, csc_coeff->a3 >> 8, 6, 0); + REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A3_LSB, csc_coeff->a3, 7, 0); + REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A4_MSB, csc_coeff->a4 >> 8, 6, 0); + REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A4_LSB, csc_coeff->a4, 7, 0); + REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B1_MSB, csc_coeff->b1 >> 8, 6, 0); + REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B1_LSB, csc_coeff->b1, 7, 0); + REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B2_MSB, csc_coeff->b2 >> 8, 6, 0); + REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B2_LSB, csc_coeff->b2, 7, 0); + REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B3_MSB, csc_coeff->b3 >> 8, 6, 0); + REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B3_LSB, csc_coeff->b3, 7, 0); + REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B4_MSB, csc_coeff->b4 >> 8, 6, 0); + REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B4_LSB, csc_coeff->b4, 7, 0); + REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C1_MSB, csc_coeff->c1 >> 8, 6, 0); + REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C1_LSB, csc_coeff->c1, 7, 0); + REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C2_MSB, csc_coeff->c2 >> 8, 6, 0); + REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C2_LSB, csc_coeff->c2, 7, 0); + REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C3_MSB, csc_coeff->c3 >> 8, 6, 0); + REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C3_LSB, csc_coeff->c3, 7, 0); + REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C4_MSB, csc_coeff->c4 >> 8, 6, 0); + REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C4_LSB, csc_coeff->c4, 7, 0); + + /* enable CSC */ REG_FLD_MOD(base, HDMI_CORE_MC_FLOWCTRL, 0x1, 0, 0); } -static void hdmi_core_configure_range(struct hdmi_core_data *core) +static void hdmi_core_configure_range(struct hdmi_core_data *core, + enum hdmi_quantization_range range) { - struct csc_table csc_coeff = { 0 }; + static const struct csc_table csc_limited_range = { + 7036, 0, 0, 32, 0, 7036, 0, 32, 0, 0, 7036, 32 + }; + static const struct csc_table csc_full_range = { + 8192, 0, 0, 0, 0, 8192, 0, 0, 0, 0, 8192, 0 + }; + const struct csc_table *csc_coeff; + + /* CSC_COLORDEPTH = 24 bits*/ + REG_FLD_MOD(core->base, HDMI_CORE_CSC_SCALE, 0, 7, 4); + + switch (range) { + case HDMI_QUANTIZATION_RANGE_FULL: + csc_coeff = &csc_full_range; + break; - /* support limited range with 24 bit color depth for now */ - csc_coeff = csc_table_deepcolor[0]; + case HDMI_QUANTIZATION_RANGE_DEFAULT: + case HDMI_QUANTIZATION_RANGE_LIMITED: + default: + csc_coeff = &csc_limited_range; + break; + } - hdmi_core_csc_config(core, csc_coeff); + hdmi_core_write_csc(core, csc_coeff); } static void hdmi_core_enable_video_path(struct hdmi_core_data *core) @@ -589,32 +545,26 @@ static void hdmi_core_enable_interrupts(struct hdmi_core_data *core) REG_FLD_MOD(core->base, HDMI_CORE_IH_MUTE, 0x0, 1, 0); } -int hdmi5_core_handle_irqs(struct hdmi_core_data *core) -{ - void __iomem *base = core->base; - - REG_FLD_MOD(base, HDMI_CORE_IH_FC_STAT0, 0xff, 7, 0); - REG_FLD_MOD(base, HDMI_CORE_IH_FC_STAT1, 0xff, 7, 0); - REG_FLD_MOD(base, HDMI_CORE_IH_FC_STAT2, 0xff, 7, 0); - REG_FLD_MOD(base, HDMI_CORE_IH_AS_STAT0, 0xff, 7, 0); - REG_FLD_MOD(base, HDMI_CORE_IH_PHY_STAT0, 0xff, 7, 0); - REG_FLD_MOD(base, HDMI_CORE_IH_I2CM_STAT0, 0xff, 7, 0); - REG_FLD_MOD(base, HDMI_CORE_IH_CEC_STAT0, 0xff, 7, 0); - REG_FLD_MOD(base, HDMI_CORE_IH_VP_STAT0, 0xff, 7, 0); - REG_FLD_MOD(base, HDMI_CORE_IH_I2CMPHY_STAT0, 0xff, 7, 0); - - return 0; -} - void hdmi5_configure(struct hdmi_core_data *core, struct hdmi_wp_data *wp, struct hdmi_config *cfg) { struct videomode vm; struct hdmi_video_format video_format; struct hdmi_core_vid_config v_core_cfg; + enum hdmi_quantization_range range; hdmi_core_mask_interrupts(core); + if (cfg->hdmi_dvi_mode == HDMI_HDMI) { + char vic = cfg->infoframe.video_code; + + /* All CEA modes other than VIC 1 use limited quantization range. */ + range = vic > 1 ? HDMI_QUANTIZATION_RANGE_LIMITED : + HDMI_QUANTIZATION_RANGE_FULL; + } else { + range = HDMI_QUANTIZATION_RANGE_FULL; + } + hdmi_core_init(&v_core_cfg, cfg); hdmi_wp_init_vid_fmt_timings(&video_format, &vm, cfg); @@ -628,9 +578,8 @@ void hdmi5_configure(struct hdmi_core_data *core, struct hdmi_wp_data *wp, hdmi_wp_video_config_interface(wp, &vm); - /* support limited range with 24 bit color depth for now */ - hdmi_core_configure_range(core); - cfg->infoframe.quantization_range = HDMI_QUANTIZATION_RANGE_LIMITED; + hdmi_core_configure_range(core, range); + cfg->infoframe.quantization_range = range; /* * configure core video part, set software reset in the core @@ -640,7 +589,6 @@ void hdmi5_configure(struct hdmi_core_data *core, struct hdmi_wp_data *wp, hdmi_core_video_config(core, &v_core_cfg); hdmi_core_config_video_packetizer(core); - hdmi_core_config_csc(core); hdmi_core_config_video_sampler(core); if (cfg->hdmi_dvi_mode == HDMI_HDMI) @@ -810,7 +758,7 @@ int hdmi5_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp, struct hdmi_audio_format audio_format; struct hdmi_audio_dma audio_dma; struct hdmi_core_audio_config core_cfg; - int err, n, cts, channel_count; + int n, cts, channel_count; unsigned int fs_nr; bool word_length_16b = false; @@ -853,7 +801,7 @@ int hdmi5_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp, return -EINVAL; } - err = hdmi_compute_acr(pclk, fs_nr, &n, &cts); + hdmi_compute_acr(pclk, fs_nr, &n, &cts); core_cfg.n = n; core_cfg.cts = cts; @@ -907,10 +855,7 @@ int hdmi5_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp, int hdmi5_core_init(struct platform_device *pdev, struct hdmi_core_data *core) { - struct resource *res; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core"); - core->base = devm_ioremap_resource(&pdev->dev, res); + core->base = devm_platform_ioremap_resource_byname(pdev, "core"); if (IS_ERR(core->base)) return PTR_ERR(core->base); diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5_core.h b/drivers/gpu/drm/omapdrm/dss/hdmi5_core.h index f2f1022c5516..b8ed21156e8c 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi5_core.h +++ b/drivers/gpu/drm/omapdrm/dss/hdmi5_core.h @@ -1,19 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * HDMI driver definition for TI OMAP5 processors. * - * Copyright (C) 2011-2012 Texas Instruments Incorporated - http://www.ti.com/ - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. + * Copyright (C) 2011-2012 Texas Instruments Incorporated - https://www.ti.com/ */ #ifndef _HDMI5_CORE_H_ @@ -292,9 +281,11 @@ struct csc_table { u16 c1, c2, c3, c4; }; -int hdmi5_read_edid(struct hdmi_core_data *core, u8 *edid, int len); +void hdmi5_core_ddc_init(struct hdmi_core_data *core); +int hdmi5_core_ddc_read(void *data, u8 *buf, unsigned int block, size_t len); +void hdmi5_core_ddc_uninit(struct hdmi_core_data *core); + void hdmi5_core_dump(struct hdmi_core_data *core, struct seq_file *s); -int hdmi5_core_handle_irqs(struct hdmi_core_data *core); void hdmi5_configure(struct hdmi_core_data *core, struct hdmi_wp_data *wp, struct hdmi_config *cfg); int hdmi5_core_init(struct platform_device *pdev, struct hdmi_core_data *core); diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi_common.c b/drivers/gpu/drm/omapdrm/dss/hdmi_common.c index 4dfb67fe5f6d..3ecde23ac604 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi_common.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi_common.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 #define DSS_SUBSYS_NAME "HDMI" diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi_phy.c b/drivers/gpu/drm/omapdrm/dss/hdmi_phy.c index fb5e4c724b4b..060e8f76f2be 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi_phy.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi_phy.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * HDMI PHY * - * Copyright (C) 2013 Texas Instruments Incorporated - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. + * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com/ */ #include <linux/kernel.h> @@ -19,14 +16,6 @@ #include "dss.h" #include "hdmi.h" -struct hdmi_phy_features { - bool bist_ctrl; - bool ldo_voltage; - unsigned long max_phy; -}; - -static const struct hdmi_phy_features *phy_feat; - void hdmi_phy_dump(struct hdmi_phy_data *phy, struct seq_file *s) { #define DUMPPHY(r) seq_printf(s, "%-35s %08x\n", #r,\ @@ -36,7 +25,7 @@ void hdmi_phy_dump(struct hdmi_phy_data *phy, struct seq_file *s) DUMPPHY(HDMI_TXPHY_DIGITAL_CTRL); DUMPPHY(HDMI_TXPHY_POWER_CTRL); DUMPPHY(HDMI_TXPHY_PAD_CFG_CTRL); - if (phy_feat->bist_ctrl) + if (phy->features->bist_ctrl) DUMPPHY(HDMI_TXPHY_BIST_CONTROL); } @@ -107,7 +96,7 @@ static void hdmi_phy_configure_lanes(struct hdmi_phy_data *phy) u16 lane_cfg = 0; int i; - unsigned lane_cfg_val; + unsigned int lane_cfg_val; u16 pol_val = 0; for (i = 0; i < 4; ++i) @@ -146,7 +135,7 @@ int hdmi_phy_configure(struct hdmi_phy_data *phy, unsigned long hfbitclk, * In OMAP5+, the HFBITCLK must be divided by 2 before issuing the * HDMI_PHYPWRCMD_LDOON command. */ - if (phy_feat->bist_ctrl) + if (phy->features->bist_ctrl) REG_FLD_MOD(phy->base, HDMI_TXPHY_BIST_CONTROL, 1, 11, 11); /* @@ -155,7 +144,7 @@ int hdmi_phy_configure(struct hdmi_phy_data *phy, unsigned long hfbitclk, */ if (hfbitclk != lfbitclk) freqout = 0; - else if (hfbitclk / 10 < phy_feat->max_phy) + else if (hfbitclk / 10 < phy->features->max_phy) freqout = 1; else freqout = 2; @@ -170,7 +159,7 @@ int hdmi_phy_configure(struct hdmi_phy_data *phy, unsigned long hfbitclk, hdmi_write_reg(phy->base, HDMI_TXPHY_DIGITAL_CTRL, 0xF0000000); /* Setup max LDO voltage */ - if (phy_feat->ldo_voltage) + if (phy->features->ldo_voltage) REG_FLD_MOD(phy->base, HDMI_TXPHY_POWER_CTRL, 0xB, 3, 0); hdmi_phy_configure_lanes(phy); @@ -190,50 +179,15 @@ static const struct hdmi_phy_features omap54xx_phy_feats = { .max_phy = 186000000, }; -static int hdmi_phy_init_features(struct platform_device *pdev) -{ - struct hdmi_phy_features *dst; - const struct hdmi_phy_features *src; - - dst = devm_kzalloc(&pdev->dev, sizeof(*dst), GFP_KERNEL); - if (!dst) { - dev_err(&pdev->dev, "Failed to allocate HDMI PHY Features\n"); - return -ENOMEM; - } - - switch (omapdss_get_version()) { - case OMAPDSS_VER_OMAP4430_ES1: - case OMAPDSS_VER_OMAP4430_ES2: - case OMAPDSS_VER_OMAP4: - src = &omap44xx_phy_feats; - break; - - case OMAPDSS_VER_OMAP5: - case OMAPDSS_VER_DRA7xx: - src = &omap54xx_phy_feats; - break; - - default: - return -ENODEV; - } - - memcpy(dst, src, sizeof(*dst)); - phy_feat = dst; - - return 0; -} - -int hdmi_phy_init(struct platform_device *pdev, struct hdmi_phy_data *phy) +int hdmi_phy_init(struct platform_device *pdev, struct hdmi_phy_data *phy, + unsigned int version) { - int r; - struct resource *res; - - r = hdmi_phy_init_features(pdev); - if (r) - return r; + if (version == 4) + phy->features = &omap44xx_phy_feats; + else + phy->features = &omap54xx_phy_feats; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy"); - phy->base = devm_ioremap_resource(&pdev->dev, res); + phy->base = devm_platform_ioremap_resource_byname(pdev, "phy"); if (IS_ERR(phy->base)) return PTR_ERR(phy->base); diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi_pll.c b/drivers/gpu/drm/omapdrm/dss/hdmi_pll.c index 46239358655a..eea719243eaf 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi_pll.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi_pll.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * HDMI PLL * - * Copyright (C) 2013 Texas Instruments Incorporated - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. + * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com/ */ #define DSS_SUBSYS_NAME "HDMIPLL" @@ -48,7 +45,7 @@ static int hdmi_pll_enable(struct dss_pll *dsspll) r = pm_runtime_get_sync(&pll->pdev->dev); WARN_ON(r < 0); - dss_ctrl_pll_enable(DSS_PLL_HDMI, true); + dss_ctrl_pll_enable(dsspll, true); r = hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_BOTHON_ALLCLKS); if (r) @@ -65,13 +62,13 @@ static void hdmi_pll_disable(struct dss_pll *dsspll) hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_ALLOFF); - dss_ctrl_pll_enable(DSS_PLL_HDMI, false); + dss_ctrl_pll_enable(dsspll, false); r = pm_runtime_put_sync(&pll->pdev->dev); WARN_ON(r < 0 && r != -ENOSYS); } -static const struct dss_pll_ops dsi_pll_ops = { +static const struct dss_pll_ops hdmi_pll_ops = { .enable = hdmi_pll_enable, .disable = hdmi_pll_disable, .set_config = dss_pll_write_config_type_b, @@ -128,7 +125,9 @@ static const struct dss_pll_hw dss_omap5_hdmi_pll_hw = { .has_refsel = true, }; -static int dsi_init_pll_data(struct platform_device *pdev, struct hdmi_pll_data *hpll) +static int hdmi_init_pll_data(struct dss_device *dss, + struct platform_device *pdev, + struct hdmi_pll_data *hpll) { struct dss_pll *pll = &hpll->pll; struct clk *clk; @@ -145,46 +144,33 @@ static int dsi_init_pll_data(struct platform_device *pdev, struct hdmi_pll_data pll->base = hpll->base; pll->clkin = clk; - switch (omapdss_get_version()) { - case OMAPDSS_VER_OMAP4430_ES1: - case OMAPDSS_VER_OMAP4430_ES2: - case OMAPDSS_VER_OMAP4: + if (hpll->wp->version == 4) pll->hw = &dss_omap4_hdmi_pll_hw; - break; - - case OMAPDSS_VER_OMAP5: - case OMAPDSS_VER_DRA7xx: + else pll->hw = &dss_omap5_hdmi_pll_hw; - break; - - default: - return -ENODEV; - } - pll->ops = &dsi_pll_ops; + pll->ops = &hdmi_pll_ops; - r = dss_pll_register(pll); + r = dss_pll_register(dss, pll); if (r) return r; return 0; } -int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll, - struct hdmi_wp_data *wp) +int hdmi_pll_init(struct dss_device *dss, struct platform_device *pdev, + struct hdmi_pll_data *pll, struct hdmi_wp_data *wp) { int r; - struct resource *res; pll->pdev = pdev; pll->wp = wp; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pll"); - pll->base = devm_ioremap_resource(&pdev->dev, res); + pll->base = devm_platform_ioremap_resource_byname(pdev, "pll"); if (IS_ERR(pll->base)) return PTR_ERR(pll->base); - r = dsi_init_pll_data(pdev, pll); + r = hdmi_init_pll_data(dss, pdev, pll); if (r) { DSSERR("failed to init HDMI PLL\n"); return r; diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi_wp.c b/drivers/gpu/drm/omapdrm/dss/hdmi_wp.c index ab129df2e310..9d830584a762 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi_wp.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi_wp.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * HDMI wrapper * - * Copyright (C) 2013 Texas Instruments Incorporated - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. + * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com/ */ #define DSS_SUBSYS_NAME "HDMIWP" @@ -131,7 +128,7 @@ void hdmi_wp_video_stop(struct hdmi_wp_data *wp) } void hdmi_wp_video_config_format(struct hdmi_wp_data *wp, - struct hdmi_video_format *video_fmt) + const struct hdmi_video_format *video_fmt) { u32 l = 0; @@ -144,7 +141,7 @@ void hdmi_wp_video_config_format(struct hdmi_wp_data *wp, } void hdmi_wp_video_config_interface(struct hdmi_wp_data *wp, - struct videomode *vm) + const struct videomode *vm) { u32 r; bool vsync_inv, hsync_inv; @@ -164,11 +161,11 @@ void hdmi_wp_video_config_interface(struct hdmi_wp_data *wp, } void hdmi_wp_video_config_timing(struct hdmi_wp_data *wp, - struct videomode *vm) + const struct videomode *vm) { u32 timing_h = 0; u32 timing_v = 0; - unsigned hsync_len_offset = 1; + unsigned int hsync_len_offset = 1; DSSDBG("Enter hdmi_wp_video_config_timing\n"); @@ -178,9 +175,7 @@ void hdmi_wp_video_config_timing(struct hdmi_wp_data *wp, * However, we don't support OMAP5 ES1 at all, so we can just check for * OMAP4 here. */ - if (omapdss_get_version() == OMAPDSS_VER_OMAP4430_ES1 || - omapdss_get_version() == OMAPDSS_VER_OMAP4430_ES2 || - omapdss_get_version() == OMAPDSS_VER_OMAP4) + if (wp->version == 4) hsync_len_offset = 0; timing_h |= FLD_VAL(vm->hback_porch, 31, 20); @@ -195,7 +190,7 @@ void hdmi_wp_video_config_timing(struct hdmi_wp_data *wp, } void hdmi_wp_init_vid_fmt_timings(struct hdmi_video_format *video_fmt, - struct videomode *vm, struct hdmi_config *param) + struct videomode *vm, const struct hdmi_config *param) { DSSDBG("Enter hdmi_wp_video_init_format\n"); @@ -235,9 +230,7 @@ void hdmi_wp_audio_config_format(struct hdmi_wp_data *wp, DSSDBG("Enter hdmi_wp_audio_config_format\n"); r = hdmi_read_reg(wp->base, HDMI_WP_AUDIO_CFG); - if (omapdss_get_version() == OMAPDSS_VER_OMAP4430_ES1 || - omapdss_get_version() == OMAPDSS_VER_OMAP4430_ES2 || - omapdss_get_version() == OMAPDSS_VER_OMAP4) { + if (wp->version == 4) { r = FLD_MOD(r, aud_fmt->stereo_channels, 26, 24); r = FLD_MOD(r, aud_fmt->active_chnnls_msk, 23, 16); } @@ -282,7 +275,8 @@ int hdmi_wp_audio_core_req_enable(struct hdmi_wp_data *wp, bool enable) return 0; } -int hdmi_wp_init(struct platform_device *pdev, struct hdmi_wp_data *wp) +int hdmi_wp_init(struct platform_device *pdev, struct hdmi_wp_data *wp, + unsigned int version) { struct resource *res; @@ -292,6 +286,7 @@ int hdmi_wp_init(struct platform_device *pdev, struct hdmi_wp_data *wp) return PTR_ERR(wp->base); wp->phys_base = res->start; + wp->version = version; return 0; } diff --git a/drivers/gpu/drm/omapdrm/dss/omapdss-boot-init.c b/drivers/gpu/drm/omapdrm/dss/omapdss-boot-init.c deleted file mode 100644 index bf626acae271..000000000000 --- a/drivers/gpu/drm/omapdrm/dss/omapdss-boot-init.c +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright (C) 2014 Texas Instruments - * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * As omapdss panel drivers are omapdss specific, but we want to define the - * DT-data in generic manner, we convert the compatible strings of the panel and - * encoder nodes from "panel-foo" to "omapdss,panel-foo". This way we can have - * both correct DT data and omapdss specific drivers. - * - * When we get generic panel drivers to the kernel, this file will be removed. - */ - -#include <linux/kernel.h> -#include <linux/of.h> -#include <linux/of_graph.h> -#include <linux/slab.h> -#include <linux/list.h> - -static struct list_head dss_conv_list __initdata; - -static const char prefix[] __initconst = "omapdss,"; - -struct dss_conv_node { - struct list_head list; - struct device_node *node; - bool root; -}; - -static int __init omapdss_count_strings(const struct property *prop) -{ - const char *p = prop->value; - int l = 0, total = 0; - int i; - - for (i = 0; total < prop->length; total += l, p += l, i++) - l = strlen(p) + 1; - - return i; -} - -static void __init omapdss_update_prop(struct device_node *node, char *compat, - int len) -{ - struct property *prop; - - prop = kzalloc(sizeof(*prop), GFP_KERNEL); - if (!prop) - return; - - prop->name = "compatible"; - prop->value = compat; - prop->length = len; - - of_update_property(node, prop); -} - -static void __init omapdss_prefix_strcpy(char *dst, int dst_len, - const char *src, int src_len) -{ - size_t total = 0; - - while (total < src_len) { - size_t l = strlen(src) + 1; - - strcpy(dst, prefix); - dst += strlen(prefix); - - strcpy(dst, src); - dst += l; - - src += l; - total += l; - } -} - -/* prepend compatible property strings with "omapdss," */ -static void __init omapdss_omapify_node(struct device_node *node) -{ - struct property *prop; - char *new_compat; - int num_strs; - int new_len; - - prop = of_find_property(node, "compatible", NULL); - - if (!prop || !prop->value) - return; - - if (strnlen(prop->value, prop->length) >= prop->length) - return; - - /* is it already prefixed? */ - if (strncmp(prefix, prop->value, strlen(prefix)) == 0) - return; - - num_strs = omapdss_count_strings(prop); - - new_len = prop->length + strlen(prefix) * num_strs; - new_compat = kmalloc(new_len, GFP_KERNEL); - - omapdss_prefix_strcpy(new_compat, new_len, prop->value, prop->length); - - omapdss_update_prop(node, new_compat, new_len); -} - -static void __init omapdss_add_to_list(struct device_node *node, bool root) -{ - struct dss_conv_node *n = kmalloc(sizeof(*n), GFP_KERNEL); - if (n) { - n->node = node; - n->root = root; - list_add(&n->list, &dss_conv_list); - } -} - -static bool __init omapdss_list_contains(const struct device_node *node) -{ - struct dss_conv_node *n; - - list_for_each_entry(n, &dss_conv_list, list) { - if (n->node == node) - return true; - } - - return false; -} - -static void __init omapdss_walk_device(struct device_node *node, bool root) -{ - struct device_node *n; - - omapdss_add_to_list(node, root); - - /* - * of_graph_get_remote_port_parent() prints an error if there is no - * port/ports node. To avoid that, check first that there's the node. - */ - n = of_get_child_by_name(node, "ports"); - if (!n) - n = of_get_child_by_name(node, "port"); - if (!n) - return; - - of_node_put(n); - - n = NULL; - while ((n = of_graph_get_next_endpoint(node, n)) != NULL) { - struct device_node *pn; - - pn = of_graph_get_remote_port_parent(n); - - if (!pn) - continue; - - if (!of_device_is_available(pn) || omapdss_list_contains(pn)) { - of_node_put(pn); - continue; - } - - omapdss_walk_device(pn, false); - } -} - -static const struct of_device_id omapdss_of_match[] __initconst = { - { .compatible = "ti,omap2-dss", }, - { .compatible = "ti,omap3-dss", }, - { .compatible = "ti,omap4-dss", }, - { .compatible = "ti,omap5-dss", }, - { .compatible = "ti,dra7-dss", }, - {}, -}; - -static int __init omapdss_boot_init(void) -{ - struct device_node *dss, *child; - - INIT_LIST_HEAD(&dss_conv_list); - - dss = of_find_matching_node(NULL, omapdss_of_match); - - if (dss == NULL || !of_device_is_available(dss)) - return 0; - - omapdss_walk_device(dss, true); - - for_each_available_child_of_node(dss, child) { - if (!of_find_property(child, "compatible", NULL)) - continue; - - omapdss_walk_device(child, true); - } - - while (!list_empty(&dss_conv_list)) { - struct dss_conv_node *n; - - n = list_first_entry(&dss_conv_list, struct dss_conv_node, - list); - - if (!n->root) - omapdss_omapify_node(n->node); - - list_del(&n->list); - of_node_put(n->node); - kfree(n); - } - - return 0; -} - -subsys_initcall(omapdss_boot_init); diff --git a/drivers/gpu/drm/omapdrm/dss/omapdss.h b/drivers/gpu/drm/omapdrm/dss/omapdss.h index 85953a0bc7c2..4c22c09c93d5 100644 --- a/drivers/gpu/drm/omapdrm/dss/omapdss.h +++ b/drivers/gpu/drm/omapdrm/dss/omapdss.h @@ -1,30 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (C) 2016 Texas Instruments + * Copyright (C) 2016 Texas Instruments Incorporated - https://www.ti.com/ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __OMAP_DRM_DSS_H #define __OMAP_DRM_DSS_H -#include <linux/list.h> -#include <linux/kobject.h> +#include <drm/drm_color_mgmt.h> +#include <drm/drm_crtc.h> +#include <drm/drm_mode.h> #include <linux/device.h> #include <linux/interrupt.h> -#include <video/videomode.h> +#include <linux/list.h> #include <linux/platform_data/omapdss.h> -#include <uapi/drm/drm_mode.h> +#include <video/videomode.h> #define DISPC_IRQ_FRAMEDONE (1 << 0) #define DISPC_IRQ_VSYNC (1 << 1) @@ -58,11 +48,15 @@ #define DISPC_IRQ_ACBIAS_COUNT_STAT3 (1 << 29) #define DISPC_IRQ_FRAMEDONE3 (1 << 30) -struct omap_dss_device; +struct dispc_device; +struct drm_connector; +struct dss_device; struct dss_lcd_mgr_config; +struct hdmi_avi_infoframe; +struct omap_drm_private; +struct omap_dss_device; struct snd_aes_iec958; struct snd_cea_861_aud_if; -struct hdmi_avi_infoframe; enum omap_display_type { OMAP_DISPLAY_TYPE_NONE = 0, @@ -122,28 +116,6 @@ enum omap_dss_venc_type { OMAP_DSS_VENC_TYPE_SVIDEO, }; -enum omap_dss_dsi_pixel_format { - OMAP_DSS_DSI_FMT_RGB888, - OMAP_DSS_DSI_FMT_RGB666, - OMAP_DSS_DSI_FMT_RGB666_PACKED, - OMAP_DSS_DSI_FMT_RGB565, -}; - -enum omap_dss_dsi_mode { - OMAP_DSS_DSI_CMD_MODE = 0, - OMAP_DSS_DSI_VIDEO_MODE, -}; - -enum omap_display_caps { - OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE = 1 << 0, - OMAP_DSS_DISPLAY_CAP_TEAR_ELIM = 1 << 1, -}; - -enum omap_dss_display_state { - OMAP_DSS_DISPLAY_DISABLED = 0, - OMAP_DSS_DISPLAY_ACTIVE, -}; - enum omap_dss_rotation_type { OMAP_DSS_ROT_NONE = 0, OMAP_DSS_ROT_TILER = 1 << 0, @@ -158,21 +130,6 @@ enum omap_overlay_caps { OMAP_DSS_OVL_CAP_REPLICATION = 1 << 5, }; -enum omap_dss_clk_source { - OMAP_DSS_CLK_SRC_FCK = 0, /* OMAP2/3: DSS1_ALWON_FCLK - * OMAP4: DSS_FCLK */ - OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC, /* OMAP3: DSI1_PLL_FCLK - * OMAP4: PLL1_CLK1 */ - OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI, /* OMAP3: DSI2_PLL_FCLK - * OMAP4: PLL1_CLK2 */ - OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC, /* OMAP4: PLL2_CLK1 */ - OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DSI, /* OMAP4: PLL2_CLK2 */ -}; - -enum omap_hdmi_flags { - OMAP_HDMI_SDA_SCL_EXTERNAL_PULLUP = 1 << 0, -}; - enum omap_dss_output_id { OMAP_DSS_OUTPUT_DPI = 1 << 0, OMAP_DSS_OUTPUT_DBI = 1 << 1, @@ -183,71 +140,6 @@ enum omap_dss_output_id { OMAP_DSS_OUTPUT_HDMI = 1 << 6, }; -/* DSI */ - -enum omap_dss_dsi_trans_mode { - /* Sync Pulses: both sync start and end packets sent */ - OMAP_DSS_DSI_PULSE_MODE, - /* Sync Events: only sync start packets sent */ - OMAP_DSS_DSI_EVENT_MODE, - /* Burst: only sync start packets sent, pixels are time compressed */ - OMAP_DSS_DSI_BURST_MODE, -}; - -struct omap_dss_dsi_videomode_timings { - unsigned long hsclk; - - unsigned ndl; - unsigned bitspp; - - /* pixels */ - u16 hact; - /* lines */ - u16 vact; - - /* DSI video mode blanking data */ - /* Unit: byte clock cycles */ - u16 hss; - u16 hsa; - u16 hse; - u16 hfp; - u16 hbp; - /* Unit: line clocks */ - u16 vsa; - u16 vfp; - u16 vbp; - - /* DSI blanking modes */ - int blanking_mode; - int hsa_blanking_mode; - int hbp_blanking_mode; - int hfp_blanking_mode; - - enum omap_dss_dsi_trans_mode trans_mode; - - bool ddr_clk_always_on; - int window_sync; -}; - -struct omap_dss_dsi_config { - enum omap_dss_dsi_mode mode; - enum omap_dss_dsi_pixel_format pixel_format; - const struct videomode *vm; - - unsigned long hs_clk_min, hs_clk_max; - unsigned long lp_clk_min, lp_clk_max; - - bool ddr_clk_always_on; - enum omap_dss_dsi_trans_mode trans_mode; -}; - -/* Hardcoded videomodes for tv. Venc only uses these to - * identify the mode, and does not actually use the configs - * itself. However, the configs should be something that - * a normal monitor can also show */ -extern const struct videomode omap_dss_pal_vm; -extern const struct videomode omap_dss_ntsc_vm; - struct omap_dss_cpr_coefs { s16 rr, rg, rb; s16 gr, gg, gb; @@ -271,6 +163,9 @@ struct omap_overlay_info { u8 global_alpha; u8 pre_mult_alpha; u8 zorder; + + enum drm_color_encoding color_encoding; + enum drm_color_range color_range; }; struct omap_overlay_manager_info { @@ -286,21 +181,6 @@ struct omap_overlay_manager_info { struct omap_dss_cpr_coefs cpr_coefs; }; -/* 22 pins means 1 clk lane and 10 data lanes */ -#define OMAP_DSS_MAX_DSI_PINS 22 - -struct omap_dsi_pin_config { - int num_pins; - /* - * pin numbers in the following order: - * clk+, clk- - * data1+, data1- - * data2+, data2- - * ... - */ - int pins[OMAP_DSS_MAX_DSI_PINS]; -}; - struct omap_dss_writeback_info { u32 paddr; u32 p_uv_addr; @@ -313,298 +193,69 @@ struct omap_dss_writeback_info { u8 pre_mult_alpha; }; -struct omapdss_dpi_ops { - int (*connect)(struct omap_dss_device *dssdev, - struct omap_dss_device *dst); - void (*disconnect)(struct omap_dss_device *dssdev, - struct omap_dss_device *dst); - - int (*enable)(struct omap_dss_device *dssdev); - void (*disable)(struct omap_dss_device *dssdev); - - int (*check_timings)(struct omap_dss_device *dssdev, - struct videomode *vm); - void (*set_timings)(struct omap_dss_device *dssdev, - struct videomode *vm); - void (*get_timings)(struct omap_dss_device *dssdev, - struct videomode *vm); -}; - -struct omapdss_sdi_ops { - int (*connect)(struct omap_dss_device *dssdev, - struct omap_dss_device *dst); - void (*disconnect)(struct omap_dss_device *dssdev, - struct omap_dss_device *dst); - - int (*enable)(struct omap_dss_device *dssdev); - void (*disable)(struct omap_dss_device *dssdev); - - int (*check_timings)(struct omap_dss_device *dssdev, - struct videomode *vm); - void (*set_timings)(struct omap_dss_device *dssdev, - struct videomode *vm); - void (*get_timings)(struct omap_dss_device *dssdev, - struct videomode *vm); -}; - -struct omapdss_dvi_ops { - int (*connect)(struct omap_dss_device *dssdev, - struct omap_dss_device *dst); - void (*disconnect)(struct omap_dss_device *dssdev, - struct omap_dss_device *dst); - - int (*enable)(struct omap_dss_device *dssdev); - void (*disable)(struct omap_dss_device *dssdev); - - int (*check_timings)(struct omap_dss_device *dssdev, - struct videomode *vm); - void (*set_timings)(struct omap_dss_device *dssdev, - struct videomode *vm); - void (*get_timings)(struct omap_dss_device *dssdev, - struct videomode *vm); -}; - -struct omapdss_atv_ops { - int (*connect)(struct omap_dss_device *dssdev, - struct omap_dss_device *dst); - void (*disconnect)(struct omap_dss_device *dssdev, - struct omap_dss_device *dst); - - int (*enable)(struct omap_dss_device *dssdev); - void (*disable)(struct omap_dss_device *dssdev); - - int (*check_timings)(struct omap_dss_device *dssdev, - struct videomode *vm); - void (*set_timings)(struct omap_dss_device *dssdev, - struct videomode *vm); - void (*get_timings)(struct omap_dss_device *dssdev, - struct videomode *vm); - - int (*set_wss)(struct omap_dss_device *dssdev, u32 wss); - u32 (*get_wss)(struct omap_dss_device *dssdev); -}; - -struct omapdss_hdmi_ops { - int (*connect)(struct omap_dss_device *dssdev, - struct omap_dss_device *dst); - void (*disconnect)(struct omap_dss_device *dssdev, - struct omap_dss_device *dst); - - int (*enable)(struct omap_dss_device *dssdev); - void (*disable)(struct omap_dss_device *dssdev); - - int (*check_timings)(struct omap_dss_device *dssdev, - struct videomode *vm); - void (*set_timings)(struct omap_dss_device *dssdev, - struct videomode *vm); - void (*get_timings)(struct omap_dss_device *dssdev, - struct videomode *vm); - - int (*read_edid)(struct omap_dss_device *dssdev, u8 *buf, int len); - bool (*detect)(struct omap_dss_device *dssdev); - - int (*set_hdmi_mode)(struct omap_dss_device *dssdev, bool hdmi_mode); - int (*set_infoframe)(struct omap_dss_device *dssdev, - const struct hdmi_avi_infoframe *avi); -}; - struct omapdss_dsi_ops { - int (*connect)(struct omap_dss_device *dssdev, - struct omap_dss_device *dst); - void (*disconnect)(struct omap_dss_device *dssdev, - struct omap_dss_device *dst); - - int (*enable)(struct omap_dss_device *dssdev); - void (*disable)(struct omap_dss_device *dssdev, bool disconnect_lanes, - bool enter_ulps); - - /* bus configuration */ - int (*set_config)(struct omap_dss_device *dssdev, - const struct omap_dss_dsi_config *cfg); - int (*configure_pins)(struct omap_dss_device *dssdev, - const struct omap_dsi_pin_config *pin_cfg); - - void (*enable_hs)(struct omap_dss_device *dssdev, int channel, - bool enable); - int (*enable_te)(struct omap_dss_device *dssdev, bool enable); - - int (*update)(struct omap_dss_device *dssdev, int channel, - void (*callback)(int, void *), void *data); - - void (*bus_lock)(struct omap_dss_device *dssdev); - void (*bus_unlock)(struct omap_dss_device *dssdev); - - int (*enable_video_output)(struct omap_dss_device *dssdev, int channel); - void (*disable_video_output)(struct omap_dss_device *dssdev, - int channel); - - int (*request_vc)(struct omap_dss_device *dssdev, int *channel); - int (*set_vc_id)(struct omap_dss_device *dssdev, int channel, - int vc_id); - void (*release_vc)(struct omap_dss_device *dssdev, int channel); - - /* data transfer */ - int (*dcs_write)(struct omap_dss_device *dssdev, int channel, - u8 *data, int len); - int (*dcs_write_nosync)(struct omap_dss_device *dssdev, int channel, - u8 *data, int len); - int (*dcs_read)(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd, - u8 *data, int len); - - int (*gen_write)(struct omap_dss_device *dssdev, int channel, - u8 *data, int len); - int (*gen_write_nosync)(struct omap_dss_device *dssdev, int channel, - u8 *data, int len); - int (*gen_read)(struct omap_dss_device *dssdev, int channel, - u8 *reqdata, int reqlen, - u8 *data, int len); - - int (*bta_sync)(struct omap_dss_device *dssdev, int channel); - - int (*set_max_rx_packet_size)(struct omap_dss_device *dssdev, - int channel, u16 plen); + int (*update)(struct omap_dss_device *dssdev); + bool (*is_video_mode)(struct omap_dss_device *dssdev); }; struct omap_dss_device { - struct kobject kobj; struct device *dev; - struct module *owner; + struct dss_device *dss; + struct drm_bridge *bridge; + struct drm_bridge *next_bridge; + struct drm_panel *panel; - struct list_head panel_list; - - /* alias in the form of "display%d" */ - char alias[16]; + struct list_head list; + /* + * DSS type that this device generates (for DSS internal devices) or + * requires (for external encoders, connectors and panels). Must be a + * non-zero (different than OMAP_DISPLAY_TYPE_NONE) value. + */ enum omap_display_type type; - enum omap_display_type output_type; - - struct { - struct videomode vm; - - enum omap_dss_dsi_pixel_format dsi_pix_fmt; - enum omap_dss_dsi_mode dsi_mode; - } panel; const char *name; - struct omap_dss_driver *driver; - - union { - const struct omapdss_dpi_ops *dpi; - const struct omapdss_sdi_ops *sdi; - const struct omapdss_dvi_ops *dvi; - const struct omapdss_hdmi_ops *hdmi; - const struct omapdss_atv_ops *atv; - const struct omapdss_dsi_ops *dsi; - } ops; - - /* helper variable for driver suspend/resume */ - bool activate_after_resume; - - enum omap_display_caps caps; - - struct omap_dss_device *src; - - enum omap_dss_display_state state; + const struct omapdss_dsi_ops *dsi_ops; + u32 bus_flags; /* OMAP DSS output specific fields */ - struct list_head list; - /* DISPC channel for this output */ enum omap_channel dispc_channel; - bool dispc_channel_connected; /* output instance */ enum omap_dss_output_id id; - /* the port number in the DT node */ - int port_num; - - /* dynamic fields */ - struct omap_dss_device *dst; + /* port number in DT */ + unsigned int of_port; }; -struct omap_dss_driver { - int (*probe)(struct omap_dss_device *); - void (*remove)(struct omap_dss_device *); - - int (*connect)(struct omap_dss_device *dssdev); - void (*disconnect)(struct omap_dss_device *dssdev); - - int (*enable)(struct omap_dss_device *display); - void (*disable)(struct omap_dss_device *display); - int (*run_test)(struct omap_dss_device *display, int test); - - int (*update)(struct omap_dss_device *dssdev, - u16 x, u16 y, u16 w, u16 h); - int (*sync)(struct omap_dss_device *dssdev); - - int (*enable_te)(struct omap_dss_device *dssdev, bool enable); - int (*get_te)(struct omap_dss_device *dssdev); - - u8 (*get_rotate)(struct omap_dss_device *dssdev); - int (*set_rotate)(struct omap_dss_device *dssdev, u8 rotate); - - bool (*get_mirror)(struct omap_dss_device *dssdev); - int (*set_mirror)(struct omap_dss_device *dssdev, bool enable); - - int (*memory_read)(struct omap_dss_device *dssdev, - void *buf, size_t size, - u16 x, u16 y, u16 w, u16 h); - - int (*check_timings)(struct omap_dss_device *dssdev, - struct videomode *vm); - void (*set_timings)(struct omap_dss_device *dssdev, - struct videomode *vm); - void (*get_timings)(struct omap_dss_device *dssdev, - struct videomode *vm); - - int (*set_wss)(struct omap_dss_device *dssdev, u32 wss); - u32 (*get_wss)(struct omap_dss_device *dssdev); - - int (*read_edid)(struct omap_dss_device *dssdev, u8 *buf, int len); - bool (*detect)(struct omap_dss_device *dssdev); - - int (*set_hdmi_mode)(struct omap_dss_device *dssdev, bool hdmi_mode); - int (*set_hdmi_infoframe)(struct omap_dss_device *dssdev, - const struct hdmi_avi_infoframe *avi); +struct dss_pdata { + struct dss_device *dss; }; -enum omapdss_version omapdss_get_version(void); -bool omapdss_is_initialized(void); - -int omap_dss_register_driver(struct omap_dss_driver *); -void omap_dss_unregister_driver(struct omap_dss_driver *); - -int omapdss_register_display(struct omap_dss_device *dssdev); -void omapdss_unregister_display(struct omap_dss_device *dssdev); - -struct omap_dss_device *omap_dss_get_device(struct omap_dss_device *dssdev); -void omap_dss_put_device(struct omap_dss_device *dssdev); -#define for_each_dss_dev(d) while ((d = omap_dss_get_next_device(d)) != NULL) -struct omap_dss_device *omap_dss_get_next_device(struct omap_dss_device *from); -struct omap_dss_device *omap_dss_find_device(void *data, - int (*match)(struct omap_dss_device *dssdev, void *data)); - +void omapdss_device_register(struct omap_dss_device *dssdev); +void omapdss_device_unregister(struct omap_dss_device *dssdev); +struct omap_dss_device *omapdss_device_get(struct omap_dss_device *dssdev); +void omapdss_device_put(struct omap_dss_device *dssdev); +struct omap_dss_device *omapdss_find_device_by_node(struct device_node *node); +int omapdss_device_connect(struct dss_device *dss, + struct omap_dss_device *dst); +void omapdss_device_disconnect(struct dss_device *dss, + struct omap_dss_device *dst); int omap_dss_get_num_overlay_managers(void); int omap_dss_get_num_overlays(void); -int omapdss_register_output(struct omap_dss_device *output); -void omapdss_unregister_output(struct omap_dss_device *output); -struct omap_dss_device *omap_dss_get_output(enum omap_dss_output_id id); -struct omap_dss_device *omap_dss_find_output_by_port_node(struct device_node *port); -int omapdss_output_set_device(struct omap_dss_device *out, - struct omap_dss_device *dssdev); -int omapdss_output_unset_device(struct omap_dss_device *out); - -struct omap_dss_device *omapdss_find_output_from_display(struct omap_dss_device *dssdev); - -void omapdss_default_get_timings(struct omap_dss_device *dssdev, - struct videomode *vm); +#define for_each_dss_output(d) \ + while ((d = omapdss_device_next_output(d)) != NULL) +struct omap_dss_device *omapdss_device_next_output(struct omap_dss_device *from); +int omapdss_device_init_output(struct omap_dss_device *out, + struct drm_bridge *local_bridge); +void omapdss_device_cleanup_output(struct omap_dss_device *out); typedef void (*omap_dispc_isr_t) (void *arg, u32 mask); int omap_dispc_register_isr(omap_dispc_isr_t isr, void *arg, u32 mask); @@ -613,113 +264,53 @@ int omap_dispc_unregister_isr(omap_dispc_isr_t isr, void *arg, u32 mask); int omapdss_compat_init(void); void omapdss_compat_uninit(void); -static inline bool omapdss_device_is_connected(struct omap_dss_device *dssdev) -{ - return dssdev->src; -} - -static inline bool omapdss_device_is_enabled(struct omap_dss_device *dssdev) -{ - return dssdev->state == OMAP_DSS_DISPLAY_ACTIVE; -} - -struct omap_dss_device * -omapdss_of_find_source_for_first_ep(struct device_node *node); - -void omapdss_set_is_initialized(bool set); - -struct device_node *dss_of_port_get_parent_device(struct device_node *port); -u32 dss_of_port_get_port_number(struct device_node *port); - -struct dss_mgr_ops { - int (*connect)(enum omap_channel channel, - struct omap_dss_device *dst); - void (*disconnect)(enum omap_channel channel, - struct omap_dss_device *dst); - - void (*start_update)(enum omap_channel channel); - int (*enable)(enum omap_channel channel); - void (*disable)(enum omap_channel channel); - void (*set_timings)(enum omap_channel channel, - const struct videomode *vm); - void (*set_lcd_config)(enum omap_channel channel, - const struct dss_lcd_mgr_config *config); - int (*register_framedone_handler)(enum omap_channel channel, - void (*handler)(void *), void *data); - void (*unregister_framedone_handler)(enum omap_channel channel, - void (*handler)(void *), void *data); -}; - -int dss_install_mgr_ops(const struct dss_mgr_ops *mgr_ops); -void dss_uninstall_mgr_ops(void); - -int dss_mgr_connect(enum omap_channel channel, - struct omap_dss_device *dst); -void dss_mgr_disconnect(enum omap_channel channel, - struct omap_dss_device *dst); -void dss_mgr_set_timings(enum omap_channel channel, +enum dss_writeback_channel { + DSS_WB_LCD1_MGR = 0, + DSS_WB_LCD2_MGR = 1, + DSS_WB_TV_MGR = 2, + DSS_WB_OVL0 = 3, + DSS_WB_OVL1 = 4, + DSS_WB_OVL2 = 5, + DSS_WB_OVL3 = 6, + DSS_WB_LCD3_MGR = 7, +}; + +void omap_crtc_dss_start_update(struct omap_drm_private *priv, + enum omap_channel channel); +void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable); +int omap_crtc_dss_enable(struct omap_drm_private *priv, enum omap_channel channel); +void omap_crtc_dss_disable(struct omap_drm_private *priv, enum omap_channel channel); +void omap_crtc_dss_set_timings(struct omap_drm_private *priv, + enum omap_channel channel, const struct videomode *vm); -void dss_mgr_set_lcd_config(enum omap_channel channel, +void omap_crtc_dss_set_lcd_config(struct omap_drm_private *priv, + enum omap_channel channel, const struct dss_lcd_mgr_config *config); -int dss_mgr_enable(enum omap_channel channel); -void dss_mgr_disable(enum omap_channel channel); -void dss_mgr_start_update(enum omap_channel channel); -int dss_mgr_register_framedone_handler(enum omap_channel channel, +int omap_crtc_dss_register_framedone( + struct omap_drm_private *priv, enum omap_channel channel, void (*handler)(void *), void *data); -void dss_mgr_unregister_framedone_handler(enum omap_channel channel, +void omap_crtc_dss_unregister_framedone( + struct omap_drm_private *priv, enum omap_channel channel, void (*handler)(void *), void *data); -/* dispc ops */ - -struct dispc_ops { - u32 (*read_irqstatus)(void); - void (*clear_irqstatus)(u32 mask); - void (*write_irqenable)(u32 mask); - - int (*request_irq)(irq_handler_t handler, void *dev_id); - void (*free_irq)(void *dev_id); - - int (*runtime_get)(void); - void (*runtime_put)(void); - - int (*get_num_ovls)(void); - int (*get_num_mgrs)(void); - - void (*mgr_enable)(enum omap_channel channel, bool enable); - bool (*mgr_is_enabled)(enum omap_channel channel); - u32 (*mgr_get_vsync_irq)(enum omap_channel channel); - u32 (*mgr_get_framedone_irq)(enum omap_channel channel); - u32 (*mgr_get_sync_lost_irq)(enum omap_channel channel); - bool (*mgr_go_busy)(enum omap_channel channel); - void (*mgr_go)(enum omap_channel channel); - void (*mgr_set_lcd_config)(enum omap_channel channel, - const struct dss_lcd_mgr_config *config); - void (*mgr_set_timings)(enum omap_channel channel, - const struct videomode *vm); - void (*mgr_setup)(enum omap_channel channel, - const struct omap_overlay_manager_info *info); - enum omap_dss_output_id (*mgr_get_supported_outputs)(enum omap_channel channel); - u32 (*mgr_gamma_size)(enum omap_channel channel); - void (*mgr_set_gamma)(enum omap_channel channel, - const struct drm_color_lut *lut, - unsigned int length); - - int (*ovl_enable)(enum omap_plane_id plane, bool enable); - int (*ovl_setup)(enum omap_plane_id plane, - const struct omap_overlay_info *oi, - const struct videomode *vm, bool mem_to_mem, - enum omap_channel channel); - - const u32 *(*ovl_get_color_modes)(enum omap_plane_id plane); -}; - -void dispc_set_ops(const struct dispc_ops *o); -const struct dispc_ops *dispc_get_ops(void); +void dss_mgr_set_timings(struct omap_dss_device *dssdev, + const struct videomode *vm); +void dss_mgr_set_lcd_config(struct omap_dss_device *dssdev, + const struct dss_lcd_mgr_config *config); +int dss_mgr_enable(struct omap_dss_device *dssdev); +void dss_mgr_disable(struct omap_dss_device *dssdev); +void dss_mgr_start_update(struct omap_dss_device *dssdev); +int dss_mgr_register_framedone_handler(struct omap_dss_device *dssdev, + void (*handler)(void *), void *data); +void dss_mgr_unregister_framedone_handler(struct omap_dss_device *dssdev, + void (*handler)(void *), void *data); -bool omapdss_component_is_display(struct device_node *node); -bool omapdss_component_is_output(struct device_node *node); +struct dispc_device *dispc_get_dispc(struct dss_device *dss); bool omapdss_stack_is_ready(void); void omapdss_gather_components(struct device *dev); +int omap_dss_init(void); +void omap_dss_exit(void); + #endif /* __OMAP_DRM_DSS_H */ diff --git a/drivers/gpu/drm/omapdrm/dss/output.c b/drivers/gpu/drm/omapdrm/dss/output.c index 3c572b699ed3..7378e855c278 100644 --- a/drivers/gpu/drm/omapdrm/dss/output.c +++ b/drivers/gpu/drm/omapdrm/dss/output.c @@ -1,18 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2012 Texas Instruments Ltd + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ * Author: Archit Taneja <archit@ti.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. */ #include <linux/kernel.h> @@ -20,230 +9,126 @@ #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/of.h> +#include <linux/of_graph.h> -#include "omapdss.h" - -static LIST_HEAD(output_list); -static DEFINE_MUTEX(output_lock); - -int omapdss_output_set_device(struct omap_dss_device *out, - struct omap_dss_device *dssdev) -{ - int r; +#include <drm/drm_bridge.h> +#include <drm/drm_panel.h> - mutex_lock(&output_lock); - - if (out->dst) { - dev_err(out->dev, - "output already has device %s connected to it\n", - out->dst->name); - r = -EINVAL; - goto err; - } - - if (out->output_type != dssdev->type) { - dev_err(out->dev, "output type and display type don't match\n"); - r = -EINVAL; - goto err; - } - - out->dst = dssdev; - dssdev->src = out; - - mutex_unlock(&output_lock); - - return 0; -err: - mutex_unlock(&output_lock); - - return r; -} -EXPORT_SYMBOL(omapdss_output_set_device); +#include "dss.h" +#include "omapdss.h" -int omapdss_output_unset_device(struct omap_dss_device *out) +int omapdss_device_init_output(struct omap_dss_device *out, + struct drm_bridge *local_bridge) { - int r; + struct device_node *remote_node; + int ret; - mutex_lock(&output_lock); - - if (!out->dst) { - dev_err(out->dev, - "output doesn't have a device connected to it\n"); - r = -EINVAL; - goto err; - } - - if (out->dst->state != OMAP_DSS_DISPLAY_DISABLED) { - dev_err(out->dev, - "device %s is not disabled, cannot unset device\n", - out->dst->name); - r = -EINVAL; - goto err; + remote_node = of_graph_get_remote_node(out->dev->of_node, + out->of_port, 0); + if (!remote_node) { + dev_dbg(out->dev, "failed to find video sink\n"); + return 0; } - out->dst->src = NULL; - out->dst = NULL; - - mutex_unlock(&output_lock); + out->bridge = of_drm_find_bridge(remote_node); + out->panel = of_drm_find_panel(remote_node); + if (IS_ERR(out->panel)) + out->panel = NULL; - return 0; -err: - mutex_unlock(&output_lock); + of_node_put(remote_node); - return r; -} -EXPORT_SYMBOL(omapdss_output_unset_device); + if (out->panel) { + struct drm_bridge *bridge; -int omapdss_register_output(struct omap_dss_device *out) -{ - list_add_tail(&out->list, &output_list); - return 0; -} -EXPORT_SYMBOL(omapdss_register_output); - -void omapdss_unregister_output(struct omap_dss_device *out) -{ - list_del(&out->list); -} -EXPORT_SYMBOL(omapdss_unregister_output); - -bool omapdss_component_is_output(struct device_node *node) -{ - struct omap_dss_device *out; + bridge = drm_panel_bridge_add(out->panel); + if (IS_ERR(bridge)) { + dev_err(out->dev, + "unable to create panel bridge (%ld)\n", + PTR_ERR(bridge)); + ret = PTR_ERR(bridge); + goto error; + } - list_for_each_entry(out, &output_list, list) { - if (out->dev->of_node == node) - return true; + out->bridge = bridge; } - return false; -} -EXPORT_SYMBOL(omapdss_component_is_output); - -struct omap_dss_device *omap_dss_get_output(enum omap_dss_output_id id) -{ - struct omap_dss_device *out; + if (local_bridge) { + if (!out->bridge) { + ret = -EPROBE_DEFER; + goto error; + } - list_for_each_entry(out, &output_list, list) { - if (out->id == id) - return out; + out->next_bridge = out->bridge; + out->bridge = local_bridge; } - return NULL; -} -EXPORT_SYMBOL(omap_dss_get_output); - -struct omap_dss_device *omap_dss_find_output_by_port_node(struct device_node *port) -{ - struct device_node *src_node; - struct omap_dss_device *out; - u32 reg; - - src_node = dss_of_port_get_parent_device(port); - if (!src_node) - return NULL; - - reg = dss_of_port_get_port_number(port); - - list_for_each_entry(out, &output_list, list) { - if (out->dev->of_node == src_node && out->port_num == reg) { - of_node_put(src_node); - return omap_dss_get_device(out); - } + if (!out->bridge) { + ret = -EPROBE_DEFER; + goto error; } - of_node_put(src_node); - - return NULL; -} -EXPORT_SYMBOL(omap_dss_find_output_by_port_node); - -struct omap_dss_device *omapdss_find_output_from_display(struct omap_dss_device *dssdev) -{ - while (dssdev->src) - dssdev = dssdev->src; - - if (dssdev->id != 0) - return omap_dss_get_device(dssdev); - - return NULL; -} -EXPORT_SYMBOL(omapdss_find_output_from_display); - -static const struct dss_mgr_ops *dss_mgr_ops; - -int dss_install_mgr_ops(const struct dss_mgr_ops *mgr_ops) -{ - if (dss_mgr_ops) - return -EBUSY; - - dss_mgr_ops = mgr_ops; - return 0; -} -EXPORT_SYMBOL(dss_install_mgr_ops); -void dss_uninstall_mgr_ops(void) -{ - dss_mgr_ops = NULL; +error: + omapdss_device_cleanup_output(out); + return ret; } -EXPORT_SYMBOL(dss_uninstall_mgr_ops); -int dss_mgr_connect(enum omap_channel channel, - struct omap_dss_device *dst) +void omapdss_device_cleanup_output(struct omap_dss_device *out) { - return dss_mgr_ops->connect(channel, dst); + if (out->bridge && out->panel) + drm_panel_bridge_remove(out->next_bridge ? + out->next_bridge : out->bridge); } -EXPORT_SYMBOL(dss_mgr_connect); -void dss_mgr_disconnect(enum omap_channel channel, - struct omap_dss_device *dst) +void dss_mgr_set_timings(struct omap_dss_device *dssdev, + const struct videomode *vm) { - dss_mgr_ops->disconnect(channel, dst); + omap_crtc_dss_set_timings(dssdev->dss->mgr_ops_priv, + dssdev->dispc_channel, vm); } -EXPORT_SYMBOL(dss_mgr_disconnect); -void dss_mgr_set_timings(enum omap_channel channel, const struct videomode *vm) -{ - dss_mgr_ops->set_timings(channel, vm); -} -EXPORT_SYMBOL(dss_mgr_set_timings); - -void dss_mgr_set_lcd_config(enum omap_channel channel, +void dss_mgr_set_lcd_config(struct omap_dss_device *dssdev, const struct dss_lcd_mgr_config *config) { - dss_mgr_ops->set_lcd_config(channel, config); + omap_crtc_dss_set_lcd_config(dssdev->dss->mgr_ops_priv, + dssdev->dispc_channel, config); } -EXPORT_SYMBOL(dss_mgr_set_lcd_config); -int dss_mgr_enable(enum omap_channel channel) +int dss_mgr_enable(struct omap_dss_device *dssdev) { - return dss_mgr_ops->enable(channel); + return omap_crtc_dss_enable(dssdev->dss->mgr_ops_priv, + dssdev->dispc_channel); } -EXPORT_SYMBOL(dss_mgr_enable); -void dss_mgr_disable(enum omap_channel channel) +void dss_mgr_disable(struct omap_dss_device *dssdev) { - dss_mgr_ops->disable(channel); + omap_crtc_dss_disable(dssdev->dss->mgr_ops_priv, + dssdev->dispc_channel); } -EXPORT_SYMBOL(dss_mgr_disable); -void dss_mgr_start_update(enum omap_channel channel) +void dss_mgr_start_update(struct omap_dss_device *dssdev) { - dss_mgr_ops->start_update(channel); + omap_crtc_dss_start_update(dssdev->dss->mgr_ops_priv, + dssdev->dispc_channel); } -EXPORT_SYMBOL(dss_mgr_start_update); -int dss_mgr_register_framedone_handler(enum omap_channel channel, +int dss_mgr_register_framedone_handler(struct omap_dss_device *dssdev, void (*handler)(void *), void *data) { - return dss_mgr_ops->register_framedone_handler(channel, handler, data); + struct dss_device *dss = dssdev->dss; + + return omap_crtc_dss_register_framedone(dss->mgr_ops_priv, + dssdev->dispc_channel, + handler, data); } -EXPORT_SYMBOL(dss_mgr_register_framedone_handler); -void dss_mgr_unregister_framedone_handler(enum omap_channel channel, +void dss_mgr_unregister_framedone_handler(struct omap_dss_device *dssdev, void (*handler)(void *), void *data) { - dss_mgr_ops->unregister_framedone_handler(channel, handler, data); + struct dss_device *dss = dssdev->dss; + + omap_crtc_dss_unregister_framedone(dss->mgr_ops_priv, + dssdev->dispc_channel, + handler, data); } -EXPORT_SYMBOL(dss_mgr_unregister_framedone_handler); diff --git a/drivers/gpu/drm/omapdrm/dss/pll.c b/drivers/gpu/drm/omapdrm/dss/pll.c index 5e221302768b..4c8246a3ded9 100644 --- a/drivers/gpu/drm/omapdrm/dss/pll.c +++ b/drivers/gpu/drm/omapdrm/dss/pll.c @@ -1,21 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2014 Texas Instruments Incorporated - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. + * Copyright (C) 2014 Texas Instruments Incorporated - https://www.ti.com/ */ #define DSS_SUBSYS_NAME "PLL" +#include <linux/delay.h> #include <linux/clk.h> #include <linux/io.h> #include <linux/kernel.h> @@ -35,15 +25,14 @@ #define PLL_SSC_CONFIGURATION2 0x001C #define PLL_CONFIGURATION4 0x0020 -static struct dss_pll *dss_plls[4]; - -int dss_pll_register(struct dss_pll *pll) +int dss_pll_register(struct dss_device *dss, struct dss_pll *pll) { int i; - for (i = 0; i < ARRAY_SIZE(dss_plls); ++i) { - if (!dss_plls[i]) { - dss_plls[i] = pll; + for (i = 0; i < ARRAY_SIZE(dss->plls); ++i) { + if (!dss->plls[i]) { + dss->plls[i] = pll; + pll->dss = dss; return 0; } } @@ -53,29 +42,32 @@ int dss_pll_register(struct dss_pll *pll) void dss_pll_unregister(struct dss_pll *pll) { + struct dss_device *dss = pll->dss; int i; - for (i = 0; i < ARRAY_SIZE(dss_plls); ++i) { - if (dss_plls[i] == pll) { - dss_plls[i] = NULL; + for (i = 0; i < ARRAY_SIZE(dss->plls); ++i) { + if (dss->plls[i] == pll) { + dss->plls[i] = NULL; + pll->dss = NULL; return; } } } -struct dss_pll *dss_pll_find(const char *name) +struct dss_pll *dss_pll_find(struct dss_device *dss, const char *name) { int i; - for (i = 0; i < ARRAY_SIZE(dss_plls); ++i) { - if (dss_plls[i] && strcmp(dss_plls[i]->name, name) == 0) - return dss_plls[i]; + for (i = 0; i < ARRAY_SIZE(dss->plls); ++i) { + if (dss->plls[i] && strcmp(dss->plls[i]->name, name) == 0) + return dss->plls[i]; } return NULL; } -struct dss_pll *dss_pll_find_by_src(enum dss_clk_source src) +struct dss_pll *dss_pll_find_by_src(struct dss_device *dss, + enum dss_clk_source src) { struct dss_pll *pll; @@ -85,27 +77,27 @@ struct dss_pll *dss_pll_find_by_src(enum dss_clk_source src) return NULL; case DSS_CLK_SRC_HDMI_PLL: - return dss_pll_find("hdmi"); + return dss_pll_find(dss, "hdmi"); case DSS_CLK_SRC_PLL1_1: case DSS_CLK_SRC_PLL1_2: case DSS_CLK_SRC_PLL1_3: - pll = dss_pll_find("dsi0"); + pll = dss_pll_find(dss, "dsi0"); if (!pll) - pll = dss_pll_find("video0"); + pll = dss_pll_find(dss, "video0"); return pll; case DSS_CLK_SRC_PLL2_1: case DSS_CLK_SRC_PLL2_2: case DSS_CLK_SRC_PLL2_3: - pll = dss_pll_find("dsi1"); + pll = dss_pll_find(dss, "dsi1"); if (!pll) - pll = dss_pll_find("video1"); + pll = dss_pll_find(dss, "video1"); return pll; } } -unsigned dss_pll_get_clkout_idx_for_src(enum dss_clk_source src) +unsigned int dss_pll_get_clkout_idx_for_src(enum dss_clk_source src) { switch (src) { case DSS_CLK_SRC_HDMI_PLL: @@ -215,8 +207,8 @@ bool dss_pll_calc_a(const struct dss_pll *pll, unsigned long clkin, dss_pll_calc_func func, void *data) { const struct dss_pll_hw *hw = pll->hw; - int n, n_min, n_max; - int m, m_min, m_max; + int n, n_start, n_stop, n_inc; + int m, m_start, m_stop, m_inc; unsigned long fint, clkdco; unsigned long pll_hw_max; unsigned long fint_hw_min, fint_hw_max; @@ -226,22 +218,39 @@ bool dss_pll_calc_a(const struct dss_pll *pll, unsigned long clkin, fint_hw_min = hw->fint_min; fint_hw_max = hw->fint_max; - n_min = max(DIV_ROUND_UP(clkin, fint_hw_max), 1ul); - n_max = min((unsigned)(clkin / fint_hw_min), hw->n_max); + n_start = max(DIV_ROUND_UP(clkin, fint_hw_max), 1ul); + n_stop = min((unsigned)(clkin / fint_hw_min), hw->n_max); + n_inc = 1; + + if (n_start > n_stop) + return false; + + if (hw->errata_i886) { + swap(n_start, n_stop); + n_inc = -1; + } pll_max = pll_max ? pll_max : ULONG_MAX; - /* Try to find high N & M to avoid jitter (DRA7 errata i886) */ - for (n = n_max; n >= n_min; --n) { + for (n = n_start; n != n_stop; n += n_inc) { fint = clkin / n; - m_min = max(DIV_ROUND_UP(DIV_ROUND_UP(pll_min, fint), 2), + m_start = max(DIV_ROUND_UP(DIV_ROUND_UP(pll_min, fint), 2), 1ul); - m_max = min3((unsigned)(pll_max / fint / 2), + m_stop = min3((unsigned)(pll_max / fint / 2), (unsigned)(pll_hw_max / fint / 2), hw->m_max); + m_inc = 1; + + if (m_start > m_stop) + continue; - for (m = m_max; m >= m_min; --m) { + if (hw->errata_i886) { + swap(m_start, m_stop); + m_inc = -1; + } + + for (m = m_start; m != m_stop; m += m_inc) { clkdco = 2 * m * fint; if (func(n, m, fint, clkdco, data)) @@ -266,7 +275,7 @@ bool dss_pll_calc_b(const struct dss_pll *pll, unsigned long clkin, unsigned long fint, clkdco, clkout; unsigned long target_clkdco; unsigned long min_dco; - unsigned n, m, mf, m2, sd; + unsigned int n, m, mf, m2, sd; const struct dss_pll_hw *hw = pll->hw; DSSDBG("clkin %lu, target clkout %lu\n", clkin, target_clkout); @@ -368,6 +377,22 @@ static int dss_wait_hsdiv_ack(struct dss_pll *pll, u32 hsdiv_ack_mask) return -ETIMEDOUT; } +static bool pll_is_locked(u32 stat) +{ + /* + * Required value for each bitfield listed below + * + * PLL_STATUS[6] = 0 PLL_BYPASS + * PLL_STATUS[5] = 0 PLL_HIGHJITTER + * + * PLL_STATUS[3] = 0 PLL_LOSSREF + * PLL_STATUS[2] = 0 PLL_RECAL + * PLL_STATUS[1] = 1 PLL_LOCK + * PLL_STATUS[0] = 1 PLL_CTRL_RESET_DONE + */ + return ((stat & 0x6f) == 0x3); +} + int dss_pll_write_config_type_a(struct dss_pll *pll, const struct dss_pll_clock_info *cinfo) { @@ -423,18 +448,54 @@ int dss_pll_write_config_type_a(struct dss_pll *pll, l = FLD_MOD(l, 0, 25, 25); /* M7_CLOCK_EN */ writel_relaxed(l, base + PLL_CONFIGURATION2); - writel_relaxed(1, base + PLL_GO); /* PLL_GO */ + if (hw->errata_i932) { + int cnt = 0; + u32 sleep_time; + const u32 max_lock_retries = 20; - if (wait_for_bit_change(base + PLL_GO, 0, 0) != 0) { - DSSERR("DSS DPLL GO bit not going down.\n"); - r = -EIO; - goto err; - } + /* + * Calculate wait time for PLL LOCK + * 1000 REFCLK cycles in us. + */ + sleep_time = DIV_ROUND_UP(1000*1000*1000, cinfo->fint); - if (wait_for_bit_change(base + PLL_STATUS, 1, 1) != 1) { - DSSERR("cannot lock DSS DPLL\n"); - r = -EIO; - goto err; + for (cnt = 0; cnt < max_lock_retries; cnt++) { + writel_relaxed(1, base + PLL_GO); /* PLL_GO */ + + /** + * read the register back to ensure the write is + * flushed + */ + readl_relaxed(base + PLL_GO); + + usleep_range(sleep_time, sleep_time + 5); + l = readl_relaxed(base + PLL_STATUS); + + if (pll_is_locked(l) && + !(readl_relaxed(base + PLL_GO) & 0x1)) + break; + + } + + if (cnt == max_lock_retries) { + DSSERR("cannot lock PLL\n"); + r = -EIO; + goto err; + } + } else { + writel_relaxed(1, base + PLL_GO); /* PLL_GO */ + + if (wait_for_bit_change(base + PLL_GO, 0, 0) != 0) { + DSSERR("DSS DPLL GO bit not going down.\n"); + r = -EIO; + goto err; + } + + if (wait_for_bit_change(base + PLL_STATUS, 1, 1) != 1) { + DSSERR("cannot lock DSS DPLL\n"); + r = -EIO; + goto err; + } } l = readl_relaxed(base + PLL_CONFIGURATION2); diff --git a/drivers/gpu/drm/omapdrm/dss/sdi.c b/drivers/gpu/drm/omapdrm/dss/sdi.c index d18ad58c5a19..df4cbc683e2c 100644 --- a/drivers/gpu/drm/omapdrm/dss/sdi.c +++ b/drivers/gpu/drm/omapdrm/dss/sdi.c @@ -1,52 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0-only /* - * linux/drivers/video/omap2/dss/sdi.c - * * Copyright (C) 2009 Nokia Corporation - * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> */ #define DSS_SUBSYS_NAME "SDI" -#include <linux/kernel.h> #include <linux/delay.h> #include <linux/err.h> -#include <linux/regulator/consumer.h> #include <linux/export.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/of_graph.h> #include <linux/platform_device.h> +#include <linux/regulator/consumer.h> #include <linux/string.h> -#include <linux/of.h> -#include "omapdss.h" +#include <drm/drm_bridge.h> + #include "dss.h" +#include "omapdss.h" -static struct { +struct sdi_device { struct platform_device *pdev; + struct dss_device *dss; bool update_enabled; struct regulator *vdds_sdi_reg; struct dss_lcd_mgr_config mgr_config; - struct videomode vm; + unsigned long pixelclock; int datapairs; struct omap_dss_device output; + struct drm_bridge bridge; +}; - bool port_initialized; -} sdi; +#define drm_bridge_to_sdi(bridge) \ + container_of(bridge, struct sdi_device, bridge) struct sdi_clk_calc_ctx { + struct sdi_device *sdi; unsigned long pck_min, pck_max; unsigned long fck; @@ -72,13 +66,14 @@ static bool dpi_calc_dss_cb(unsigned long fck, void *data) ctx->fck = fck; - return dispc_div_calc(fck, ctx->pck_min, ctx->pck_max, - dpi_calc_dispc_cb, ctx); + return dispc_div_calc(ctx->sdi->dss->dispc, fck, + ctx->pck_min, ctx->pck_max, + dpi_calc_dispc_cb, ctx); } -static int sdi_calc_clock_div(unsigned long pclk, - unsigned long *fck, - struct dispc_clock_info *dispc_cinfo) +static int sdi_calc_clock_div(struct sdi_device *sdi, unsigned long pclk, + unsigned long *fck, + struct dispc_clock_info *dispc_cinfo) { int i; struct sdi_clk_calc_ctx ctx; @@ -94,13 +89,17 @@ static int sdi_calc_clock_div(unsigned long pclk, bool ok; memset(&ctx, 0, sizeof(ctx)); + + ctx.sdi = sdi; + if (pclk > 1000 * i * i * i) ctx.pck_min = max(pclk - 1000 * i * i * i, 0lu); else ctx.pck_min = 0; ctx.pck_max = pclk + 1000 * i * i * i; - ok = dss_div_calc(pclk, ctx.pck_min, dpi_calc_dss_cb, &ctx); + ok = dss_div_calc(sdi->dss, pclk, ctx.pck_min, + dpi_calc_dss_cb, &ctx); if (ok) { *fck = ctx.fck; *dispc_cinfo = ctx.dispc_cinfo; @@ -111,70 +110,119 @@ static int sdi_calc_clock_div(unsigned long pclk, return -EINVAL; } -static void sdi_config_lcd_manager(struct omap_dss_device *dssdev) +static void sdi_config_lcd_manager(struct sdi_device *sdi) { - enum omap_channel channel = dssdev->dispc_channel; + sdi->mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS; + + sdi->mgr_config.stallmode = false; + sdi->mgr_config.fifohandcheck = false; + + sdi->mgr_config.video_port_width = 24; + sdi->mgr_config.lcden_sig_polarity = 1; + + dss_mgr_set_lcd_config(&sdi->output, &sdi->mgr_config); +} - sdi.mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS; +/* ----------------------------------------------------------------------------- + * DRM Bridge Operations + */ - sdi.mgr_config.stallmode = false; - sdi.mgr_config.fifohandcheck = false; +static int sdi_bridge_attach(struct drm_bridge *bridge, + struct drm_encoder *encoder, + enum drm_bridge_attach_flags flags) +{ + struct sdi_device *sdi = drm_bridge_to_sdi(bridge); - sdi.mgr_config.video_port_width = 24; - sdi.mgr_config.lcden_sig_polarity = 1; + if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) + return -EINVAL; - dss_mgr_set_lcd_config(channel, &sdi.mgr_config); + return drm_bridge_attach(encoder, sdi->output.next_bridge, + bridge, flags); } -static int sdi_display_enable(struct omap_dss_device *dssdev) +static enum drm_mode_status +sdi_bridge_mode_valid(struct drm_bridge *bridge, + const struct drm_display_info *info, + const struct drm_display_mode *mode) { - struct omap_dss_device *out = &sdi.output; - enum omap_channel channel = dssdev->dispc_channel; - struct videomode *vm = &sdi.vm; + struct sdi_device *sdi = drm_bridge_to_sdi(bridge); + unsigned long pixelclock = mode->clock * 1000; + struct dispc_clock_info dispc_cinfo; unsigned long fck; + int ret; + + if (pixelclock == 0) + return MODE_NOCLOCK; + + ret = sdi_calc_clock_div(sdi, pixelclock, &fck, &dispc_cinfo); + if (ret < 0) + return MODE_CLOCK_RANGE; + + return MODE_OK; +} + +static bool sdi_bridge_mode_fixup(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct sdi_device *sdi = drm_bridge_to_sdi(bridge); + unsigned long pixelclock = mode->clock * 1000; struct dispc_clock_info dispc_cinfo; + unsigned long fck; unsigned long pck; - int r; + int ret; - if (!out->dispc_channel_connected) { - DSSERR("failed to enable display: no output/manager\n"); - return -ENODEV; - } + ret = sdi_calc_clock_div(sdi, pixelclock, &fck, &dispc_cinfo); + if (ret < 0) + return false; - r = regulator_enable(sdi.vdds_sdi_reg); - if (r) - goto err_reg_enable; + pck = fck / dispc_cinfo.lck_div / dispc_cinfo.pck_div; - r = dispc_runtime_get(); - if (r) - goto err_get_dispc; + if (pck != pixelclock) + dev_dbg(&sdi->pdev->dev, + "pixel clock adjusted from %lu Hz to %lu Hz\n", + pixelclock, pck); - /* 15.5.9.1.2 */ - vm->flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE | DISPLAY_FLAGS_SYNC_POSEDGE; + adjusted_mode->clock = pck / 1000; - r = sdi_calc_clock_div(vm->pixelclock, &fck, &dispc_cinfo); - if (r) - goto err_calc_clock_div; + return true; +} - sdi.mgr_config.clock_info = dispc_cinfo; +static void sdi_bridge_mode_set(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + const struct drm_display_mode *adjusted_mode) +{ + struct sdi_device *sdi = drm_bridge_to_sdi(bridge); - pck = fck / dispc_cinfo.lck_div / dispc_cinfo.pck_div; + sdi->pixelclock = adjusted_mode->clock * 1000; +} - if (pck != vm->pixelclock) { - DSSWARN("Could not find exact pixel clock. Requested %lu Hz, got %lu Hz\n", - vm->pixelclock, pck); +static void sdi_bridge_enable(struct drm_bridge *bridge) +{ + struct sdi_device *sdi = drm_bridge_to_sdi(bridge); + struct dispc_clock_info dispc_cinfo; + unsigned long fck; + int r; - vm->pixelclock = pck; - } + r = regulator_enable(sdi->vdds_sdi_reg); + if (r) + return; + + r = dispc_runtime_get(sdi->dss->dispc); + if (r) + goto err_get_dispc; + r = sdi_calc_clock_div(sdi, sdi->pixelclock, &fck, &dispc_cinfo); + if (r) + goto err_calc_clock_div; - dss_mgr_set_timings(channel, vm); + sdi->mgr_config.clock_info = dispc_cinfo; - r = dss_set_fck_rate(fck); + r = dss_set_fck_rate(sdi->dss, fck); if (r) goto err_set_dss_clock_div; - sdi_config_lcd_manager(dssdev); + sdi_config_lcd_manager(sdi); /* * LCLK and PCLK divisors are located in shadow registers, and we @@ -187,204 +235,156 @@ static int sdi_display_enable(struct omap_dss_device *dssdev) * need to care about the shadow register mechanism for pck-free. The * exact reason for this is unknown. */ - dispc_mgr_set_clock_div(channel, &sdi.mgr_config.clock_info); + dispc_mgr_set_clock_div(sdi->dss->dispc, sdi->output.dispc_channel, + &sdi->mgr_config.clock_info); - dss_sdi_init(sdi.datapairs); - r = dss_sdi_enable(); + dss_sdi_init(sdi->dss, sdi->datapairs); + r = dss_sdi_enable(sdi->dss); if (r) goto err_sdi_enable; mdelay(2); - r = dss_mgr_enable(channel); + r = dss_mgr_enable(&sdi->output); if (r) goto err_mgr_enable; - return 0; + return; err_mgr_enable: - dss_sdi_disable(); + dss_sdi_disable(sdi->dss); err_sdi_enable: err_set_dss_clock_div: err_calc_clock_div: - dispc_runtime_put(); + dispc_runtime_put(sdi->dss->dispc); err_get_dispc: - regulator_disable(sdi.vdds_sdi_reg); -err_reg_enable: - return r; + regulator_disable(sdi->vdds_sdi_reg); } -static void sdi_display_disable(struct omap_dss_device *dssdev) +static void sdi_bridge_disable(struct drm_bridge *bridge) { - enum omap_channel channel = dssdev->dispc_channel; + struct sdi_device *sdi = drm_bridge_to_sdi(bridge); - dss_mgr_disable(channel); + dss_mgr_disable(&sdi->output); - dss_sdi_disable(); + dss_sdi_disable(sdi->dss); - dispc_runtime_put(); + dispc_runtime_put(sdi->dss->dispc); - regulator_disable(sdi.vdds_sdi_reg); + regulator_disable(sdi->vdds_sdi_reg); } -static void sdi_set_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - sdi.vm = *vm; -} - -static void sdi_get_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - *vm = sdi.vm; -} +static const struct drm_bridge_funcs sdi_bridge_funcs = { + .attach = sdi_bridge_attach, + .mode_valid = sdi_bridge_mode_valid, + .mode_fixup = sdi_bridge_mode_fixup, + .mode_set = sdi_bridge_mode_set, + .enable = sdi_bridge_enable, + .disable = sdi_bridge_disable, +}; -static int sdi_check_timings(struct omap_dss_device *dssdev, - struct videomode *vm) +static void sdi_bridge_init(struct sdi_device *sdi) { - enum omap_channel channel = dssdev->dispc_channel; + sdi->bridge.of_node = sdi->pdev->dev.of_node; + sdi->bridge.type = DRM_MODE_CONNECTOR_LVDS; - if (!dispc_mgr_timings_ok(channel, vm)) - return -EINVAL; - - if (vm->pixelclock == 0) - return -EINVAL; - - return 0; + drm_bridge_add(&sdi->bridge); } -static int sdi_init_regulator(void) +static void sdi_bridge_cleanup(struct sdi_device *sdi) { - struct regulator *vdds_sdi; - - if (sdi.vdds_sdi_reg) - return 0; - - vdds_sdi = devm_regulator_get(&sdi.pdev->dev, "vdds_sdi"); - if (IS_ERR(vdds_sdi)) { - if (PTR_ERR(vdds_sdi) != -EPROBE_DEFER) - DSSERR("can't get VDDS_SDI regulator\n"); - return PTR_ERR(vdds_sdi); - } - - sdi.vdds_sdi_reg = vdds_sdi; - - return 0; + drm_bridge_remove(&sdi->bridge); } -static int sdi_connect(struct omap_dss_device *dssdev, - struct omap_dss_device *dst) -{ - enum omap_channel channel = dssdev->dispc_channel; - int r; - - r = sdi_init_regulator(); - if (r) - return r; - - r = dss_mgr_connect(channel, dssdev); - if (r) - return r; - - r = omapdss_output_set_device(dssdev, dst); - if (r) { - DSSERR("failed to connect output to new device: %s\n", - dst->name); - dss_mgr_disconnect(channel, dssdev); - return r; - } - - return 0; -} +/* ----------------------------------------------------------------------------- + * Initialisation and Cleanup + */ -static void sdi_disconnect(struct omap_dss_device *dssdev, - struct omap_dss_device *dst) +static int sdi_init_output(struct sdi_device *sdi) { - enum omap_channel channel = dssdev->dispc_channel; - - WARN_ON(dst != dssdev->dst); - - if (dst != dssdev->dst) - return; - - omapdss_output_unset_device(dssdev); - - dss_mgr_disconnect(channel, dssdev); -} - -static const struct omapdss_sdi_ops sdi_ops = { - .connect = sdi_connect, - .disconnect = sdi_disconnect, - - .enable = sdi_display_enable, - .disable = sdi_display_disable, - - .check_timings = sdi_check_timings, - .set_timings = sdi_set_timings, - .get_timings = sdi_get_timings, -}; + struct omap_dss_device *out = &sdi->output; + int r; -static void sdi_init_output(struct platform_device *pdev) -{ - struct omap_dss_device *out = &sdi.output; + sdi_bridge_init(sdi); - out->dev = &pdev->dev; + out->dev = &sdi->pdev->dev; out->id = OMAP_DSS_OUTPUT_SDI; - out->output_type = OMAP_DISPLAY_TYPE_SDI; + out->type = OMAP_DISPLAY_TYPE_SDI; out->name = "sdi.0"; out->dispc_channel = OMAP_DSS_CHANNEL_LCD; /* We have SDI only on OMAP3, where it's on port 1 */ - out->port_num = 1; - out->ops.sdi = &sdi_ops; - out->owner = THIS_MODULE; + out->of_port = 1; + out->bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE /* 15.5.9.1.2 */ + | DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE; - omapdss_register_output(out); + r = omapdss_device_init_output(out, &sdi->bridge); + if (r < 0) { + sdi_bridge_cleanup(sdi); + return r; + } + + omapdss_device_register(out); + + return 0; } -static void sdi_uninit_output(struct platform_device *pdev) +static void sdi_uninit_output(struct sdi_device *sdi) { - struct omap_dss_device *out = &sdi.output; + omapdss_device_unregister(&sdi->output); + omapdss_device_cleanup_output(&sdi->output); - omapdss_unregister_output(out); + sdi_bridge_cleanup(sdi); } -int sdi_init_port(struct platform_device *pdev, struct device_node *port) +int sdi_init_port(struct dss_device *dss, struct platform_device *pdev, + struct device_node *port) { + struct sdi_device *sdi; struct device_node *ep; u32 datapairs; int r; - ep = of_get_next_child(port, NULL); + sdi = devm_drm_bridge_alloc(&pdev->dev, struct sdi_device, bridge, &sdi_bridge_funcs); + if (IS_ERR(sdi)) + return PTR_ERR(sdi); + + ep = of_graph_get_next_port_endpoint(port, NULL); if (!ep) return 0; r = of_property_read_u32(ep, "datapairs", &datapairs); + of_node_put(ep); if (r) { DSSERR("failed to parse datapairs\n"); - goto err_datapairs; + return r; } - sdi.datapairs = datapairs; + sdi->datapairs = datapairs; + sdi->dss = dss; - of_node_put(ep); + sdi->pdev = pdev; + port->data = sdi; - sdi.pdev = pdev; - - sdi_init_output(pdev); + sdi->vdds_sdi_reg = devm_regulator_get(&pdev->dev, "vdds_sdi"); + if (IS_ERR(sdi->vdds_sdi_reg)) { + r = PTR_ERR(sdi->vdds_sdi_reg); + if (r != -EPROBE_DEFER) + DSSERR("can't get VDDS_SDI regulator\n"); + return r; + } - sdi.port_initialized = true; + r = sdi_init_output(sdi); + if (r) + return r; return 0; - -err_datapairs: - of_node_put(ep); - - return r; } void sdi_uninit_port(struct device_node *port) { - if (!sdi.port_initialized) + struct sdi_device *sdi = port->data; + + if (!sdi) return; - sdi_uninit_output(sdi.pdev); + sdi_uninit_output(sdi); } diff --git a/drivers/gpu/drm/omapdrm/dss/venc.c b/drivers/gpu/drm/omapdrm/dss/venc.c index a6bfb3918b8d..9b5d53dc361e 100644 --- a/drivers/gpu/drm/omapdrm/dss/venc.c +++ b/drivers/gpu/drm/omapdrm/dss/venc.c @@ -1,22 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* - * linux/drivers/video/omap2/dss/venc.c - * * Copyright (C) 2009 Nokia Corporation - * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> * * VENC settings from TI's DSS driver - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. */ #define DSS_SUBSYS_NAME "VENC" @@ -26,7 +13,6 @@ #include <linux/clk.h> #include <linux/err.h> #include <linux/io.h> -#include <linux/mutex.h> #include <linux/completion.h> #include <linux/delay.h> #include <linux/string.h> @@ -37,10 +23,12 @@ #include <linux/of.h> #include <linux/of_graph.h> #include <linux/component.h> +#include <linux/sys_soc.h> + +#include <drm/drm_bridge.h> #include "omapdss.h" #include "dss.h" -#include "dss_features.h" /* Venc registers */ #define VENC_REV_ID 0x00 @@ -220,174 +208,139 @@ static const struct venc_config venc_config_ntsc_trm = { .gen_ctrl = 0x00F90000, }; -static const struct venc_config venc_config_pal_bdghi = { - .f_control = 0, - .vidout_ctrl = 0, - .sync_ctrl = 0, - .hfltr_ctrl = 0, - .x_color = 0, - .line21 = 0, - .ln_sel = 21, - .htrigger_vtrigger = 0, - .tvdetgp_int_start_stop_x = 0x00140001, - .tvdetgp_int_start_stop_y = 0x00010001, - .gen_ctrl = 0x00FB0000, - - .llen = 864-1, - .flens = 625-1, - .cc_carr_wss_carr = 0x2F7625ED, - .c_phase = 0xDF, - .gain_u = 0x111, - .gain_v = 0x181, - .gain_y = 0x140, - .black_level = 0x3e, - .blank_level = 0x3e, - .m_control = 0<<2 | 1<<1, - .bstamp_wss_data = 0x42, - .s_carr = 0x2a098acb, - .l21__wc_ctl = 0<<13 | 0x16<<8 | 0<<0, - .savid__eavid = 0x06A70108, - .flen__fal = 23<<16 | 624<<0, - .lal__phase_reset = 2<<17 | 310<<0, - .hs_int_start_stop_x = 0x00920358, - .hs_ext_start_stop_x = 0x000F035F, - .vs_int_start_x = 0x1a7<<16, - .vs_int_stop_x__vs_int_start_y = 0x000601A7, - .vs_int_stop_y__vs_ext_start_x = 0x01AF0036, - .vs_ext_stop_x__vs_ext_start_y = 0x27101af, - .vs_ext_stop_y = 0x05, - .avid_start_stop_x = 0x03530082, - .avid_start_stop_y = 0x0270002E, - .fid_int_start_x__fid_int_start_y = 0x0005008A, - .fid_int_offset_y__fid_ext_start_x = 0x002E0138, - .fid_ext_start_y__fid_ext_offset_y = 0x01380005, +enum venc_videomode { + VENC_MODE_UNKNOWN, + VENC_MODE_PAL, + VENC_MODE_NTSC, }; -const struct videomode omap_dss_pal_vm = { - .hactive = 720, - .vactive = 574, - .pixelclock = 13500000, - .hsync_len = 64, - .hfront_porch = 12, - .hback_porch = 68, - .vsync_len = 5, - .vfront_porch = 5, - .vback_porch = 41, - - .flags = DISPLAY_FLAGS_INTERLACED | DISPLAY_FLAGS_HSYNC_LOW | - DISPLAY_FLAGS_VSYNC_LOW | DISPLAY_FLAGS_DE_HIGH | - DISPLAY_FLAGS_PIXDATA_POSEDGE | - DISPLAY_FLAGS_SYNC_NEGEDGE, +static const struct drm_display_mode omap_dss_pal_mode = { + .hdisplay = 720, + .hsync_start = 732, + .hsync_end = 796, + .htotal = 864, + .vdisplay = 574, + .vsync_start = 579, + .vsync_end = 584, + .vtotal = 625, + .clock = 13500, + + .flags = DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_NHSYNC | + DRM_MODE_FLAG_NVSYNC, }; -EXPORT_SYMBOL(omap_dss_pal_vm); - -const struct videomode omap_dss_ntsc_vm = { - .hactive = 720, - .vactive = 482, - .pixelclock = 13500000, - .hsync_len = 64, - .hfront_porch = 16, - .hback_porch = 58, - .vsync_len = 6, - .vfront_porch = 6, - .vback_porch = 31, - - .flags = DISPLAY_FLAGS_INTERLACED | DISPLAY_FLAGS_HSYNC_LOW | - DISPLAY_FLAGS_VSYNC_LOW | DISPLAY_FLAGS_DE_HIGH | - DISPLAY_FLAGS_PIXDATA_POSEDGE | - DISPLAY_FLAGS_SYNC_NEGEDGE, + +static const struct drm_display_mode omap_dss_ntsc_mode = { + .hdisplay = 720, + .hsync_start = 736, + .hsync_end = 800, + .htotal = 858, + .vdisplay = 482, + .vsync_start = 488, + .vsync_end = 494, + .vtotal = 525, + .clock = 13500, + + .flags = DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_NHSYNC | + DRM_MODE_FLAG_NVSYNC, }; -EXPORT_SYMBOL(omap_dss_ntsc_vm); -static struct { +struct venc_device { struct platform_device *pdev; void __iomem *base; - struct mutex venc_lock; - u32 wss_data; struct regulator *vdda_dac_reg; + struct dss_device *dss; + + struct dss_debugfs_entry *debugfs; struct clk *tv_dac_clk; - struct videomode vm; + const struct venc_config *config; enum omap_dss_venc_type type; bool invert_polarity; + bool requires_tv_dac_clk; struct omap_dss_device output; -} venc; + struct drm_bridge bridge; +}; + +#define drm_bridge_to_venc(b) container_of(b, struct venc_device, bridge) -static inline void venc_write_reg(int idx, u32 val) +static inline void venc_write_reg(struct venc_device *venc, int idx, u32 val) { - __raw_writel(val, venc.base + idx); + __raw_writel(val, venc->base + idx); } -static inline u32 venc_read_reg(int idx) +static inline u32 venc_read_reg(struct venc_device *venc, int idx) { - u32 l = __raw_readl(venc.base + idx); + u32 l = __raw_readl(venc->base + idx); return l; } -static void venc_write_config(const struct venc_config *config) +static void venc_write_config(struct venc_device *venc, + const struct venc_config *config) { DSSDBG("write venc conf\n"); - venc_write_reg(VENC_LLEN, config->llen); - venc_write_reg(VENC_FLENS, config->flens); - venc_write_reg(VENC_CC_CARR_WSS_CARR, config->cc_carr_wss_carr); - venc_write_reg(VENC_C_PHASE, config->c_phase); - venc_write_reg(VENC_GAIN_U, config->gain_u); - venc_write_reg(VENC_GAIN_V, config->gain_v); - venc_write_reg(VENC_GAIN_Y, config->gain_y); - venc_write_reg(VENC_BLACK_LEVEL, config->black_level); - venc_write_reg(VENC_BLANK_LEVEL, config->blank_level); - venc_write_reg(VENC_M_CONTROL, config->m_control); - venc_write_reg(VENC_BSTAMP_WSS_DATA, config->bstamp_wss_data | - venc.wss_data); - venc_write_reg(VENC_S_CARR, config->s_carr); - venc_write_reg(VENC_L21__WC_CTL, config->l21__wc_ctl); - venc_write_reg(VENC_SAVID__EAVID, config->savid__eavid); - venc_write_reg(VENC_FLEN__FAL, config->flen__fal); - venc_write_reg(VENC_LAL__PHASE_RESET, config->lal__phase_reset); - venc_write_reg(VENC_HS_INT_START_STOP_X, config->hs_int_start_stop_x); - venc_write_reg(VENC_HS_EXT_START_STOP_X, config->hs_ext_start_stop_x); - venc_write_reg(VENC_VS_INT_START_X, config->vs_int_start_x); - venc_write_reg(VENC_VS_INT_STOP_X__VS_INT_START_Y, + venc_write_reg(venc, VENC_LLEN, config->llen); + venc_write_reg(venc, VENC_FLENS, config->flens); + venc_write_reg(venc, VENC_CC_CARR_WSS_CARR, config->cc_carr_wss_carr); + venc_write_reg(venc, VENC_C_PHASE, config->c_phase); + venc_write_reg(venc, VENC_GAIN_U, config->gain_u); + venc_write_reg(venc, VENC_GAIN_V, config->gain_v); + venc_write_reg(venc, VENC_GAIN_Y, config->gain_y); + venc_write_reg(venc, VENC_BLACK_LEVEL, config->black_level); + venc_write_reg(venc, VENC_BLANK_LEVEL, config->blank_level); + venc_write_reg(venc, VENC_M_CONTROL, config->m_control); + venc_write_reg(venc, VENC_BSTAMP_WSS_DATA, config->bstamp_wss_data); + venc_write_reg(venc, VENC_S_CARR, config->s_carr); + venc_write_reg(venc, VENC_L21__WC_CTL, config->l21__wc_ctl); + venc_write_reg(venc, VENC_SAVID__EAVID, config->savid__eavid); + venc_write_reg(venc, VENC_FLEN__FAL, config->flen__fal); + venc_write_reg(venc, VENC_LAL__PHASE_RESET, config->lal__phase_reset); + venc_write_reg(venc, VENC_HS_INT_START_STOP_X, + config->hs_int_start_stop_x); + venc_write_reg(venc, VENC_HS_EXT_START_STOP_X, + config->hs_ext_start_stop_x); + venc_write_reg(venc, VENC_VS_INT_START_X, config->vs_int_start_x); + venc_write_reg(venc, VENC_VS_INT_STOP_X__VS_INT_START_Y, config->vs_int_stop_x__vs_int_start_y); - venc_write_reg(VENC_VS_INT_STOP_Y__VS_EXT_START_X, + venc_write_reg(venc, VENC_VS_INT_STOP_Y__VS_EXT_START_X, config->vs_int_stop_y__vs_ext_start_x); - venc_write_reg(VENC_VS_EXT_STOP_X__VS_EXT_START_Y, + venc_write_reg(venc, VENC_VS_EXT_STOP_X__VS_EXT_START_Y, config->vs_ext_stop_x__vs_ext_start_y); - venc_write_reg(VENC_VS_EXT_STOP_Y, config->vs_ext_stop_y); - venc_write_reg(VENC_AVID_START_STOP_X, config->avid_start_stop_x); - venc_write_reg(VENC_AVID_START_STOP_Y, config->avid_start_stop_y); - venc_write_reg(VENC_FID_INT_START_X__FID_INT_START_Y, + venc_write_reg(venc, VENC_VS_EXT_STOP_Y, config->vs_ext_stop_y); + venc_write_reg(venc, VENC_AVID_START_STOP_X, config->avid_start_stop_x); + venc_write_reg(venc, VENC_AVID_START_STOP_Y, config->avid_start_stop_y); + venc_write_reg(venc, VENC_FID_INT_START_X__FID_INT_START_Y, config->fid_int_start_x__fid_int_start_y); - venc_write_reg(VENC_FID_INT_OFFSET_Y__FID_EXT_START_X, + venc_write_reg(venc, VENC_FID_INT_OFFSET_Y__FID_EXT_START_X, config->fid_int_offset_y__fid_ext_start_x); - venc_write_reg(VENC_FID_EXT_START_Y__FID_EXT_OFFSET_Y, + venc_write_reg(venc, VENC_FID_EXT_START_Y__FID_EXT_OFFSET_Y, config->fid_ext_start_y__fid_ext_offset_y); - venc_write_reg(VENC_DAC_B__DAC_C, venc_read_reg(VENC_DAC_B__DAC_C)); - venc_write_reg(VENC_VIDOUT_CTRL, config->vidout_ctrl); - venc_write_reg(VENC_HFLTR_CTRL, config->hfltr_ctrl); - venc_write_reg(VENC_X_COLOR, config->x_color); - venc_write_reg(VENC_LINE21, config->line21); - venc_write_reg(VENC_LN_SEL, config->ln_sel); - venc_write_reg(VENC_HTRIGGER_VTRIGGER, config->htrigger_vtrigger); - venc_write_reg(VENC_TVDETGP_INT_START_STOP_X, + venc_write_reg(venc, VENC_DAC_B__DAC_C, + venc_read_reg(venc, VENC_DAC_B__DAC_C)); + venc_write_reg(venc, VENC_VIDOUT_CTRL, config->vidout_ctrl); + venc_write_reg(venc, VENC_HFLTR_CTRL, config->hfltr_ctrl); + venc_write_reg(venc, VENC_X_COLOR, config->x_color); + venc_write_reg(venc, VENC_LINE21, config->line21); + venc_write_reg(venc, VENC_LN_SEL, config->ln_sel); + venc_write_reg(venc, VENC_HTRIGGER_VTRIGGER, config->htrigger_vtrigger); + venc_write_reg(venc, VENC_TVDETGP_INT_START_STOP_X, config->tvdetgp_int_start_stop_x); - venc_write_reg(VENC_TVDETGP_INT_START_STOP_Y, + venc_write_reg(venc, VENC_TVDETGP_INT_START_STOP_Y, config->tvdetgp_int_start_stop_y); - venc_write_reg(VENC_GEN_CTRL, config->gen_ctrl); - venc_write_reg(VENC_F_CONTROL, config->f_control); - venc_write_reg(VENC_SYNC_CTRL, config->sync_ctrl); + venc_write_reg(venc, VENC_GEN_CTRL, config->gen_ctrl); + venc_write_reg(venc, VENC_F_CONTROL, config->f_control); + venc_write_reg(venc, VENC_SYNC_CTRL, config->sync_ctrl); } -static void venc_reset(void) +static void venc_reset(struct venc_device *venc) { int t = 1000; - venc_write_reg(VENC_F_CONTROL, 1<<8); - while (venc_read_reg(VENC_F_CONTROL) & (1<<8)) { + venc_write_reg(venc, VENC_F_CONTROL, 1<<8); + while (venc_read_reg(venc, VENC_F_CONTROL) & (1<<8)) { if (--t == 0) { DSSERR("Failed to reset venc\n"); return; @@ -401,393 +354,409 @@ static void venc_reset(void) #endif } -static int venc_runtime_get(void) +static int venc_runtime_get(struct venc_device *venc) { int r; DSSDBG("venc_runtime_get\n"); - r = pm_runtime_get_sync(&venc.pdev->dev); - WARN_ON(r < 0); - return r < 0 ? r : 0; + r = pm_runtime_get_sync(&venc->pdev->dev); + if (WARN_ON(r < 0)) { + pm_runtime_put_noidle(&venc->pdev->dev); + return r; + } + return 0; } -static void venc_runtime_put(void) +static void venc_runtime_put(struct venc_device *venc) { int r; DSSDBG("venc_runtime_put\n"); - r = pm_runtime_put_sync(&venc.pdev->dev); + r = pm_runtime_put_sync(&venc->pdev->dev); WARN_ON(r < 0 && r != -ENOSYS); } -static const struct venc_config *venc_timings_to_config(struct videomode *vm) -{ - if (memcmp(&omap_dss_pal_vm, vm, sizeof(*vm)) == 0) - return &venc_config_pal_trm; - - if (memcmp(&omap_dss_ntsc_vm, vm, sizeof(*vm)) == 0) - return &venc_config_ntsc_trm; - - BUG(); - return NULL; -} - -static int venc_power_on(struct omap_dss_device *dssdev) +static int venc_power_on(struct venc_device *venc) { - enum omap_channel channel = dssdev->dispc_channel; u32 l; int r; - r = venc_runtime_get(); + r = venc_runtime_get(venc); if (r) goto err0; - venc_reset(); - venc_write_config(venc_timings_to_config(&venc.vm)); + venc_reset(venc); + venc_write_config(venc, venc->config); - dss_set_venc_output(venc.type); - dss_set_dac_pwrdn_bgz(1); + dss_set_venc_output(venc->dss, venc->type); + dss_set_dac_pwrdn_bgz(venc->dss, 1); l = 0; - if (venc.type == OMAP_DSS_VENC_TYPE_COMPOSITE) + if (venc->type == OMAP_DSS_VENC_TYPE_COMPOSITE) l |= 1 << 1; else /* S-Video */ l |= (1 << 0) | (1 << 2); - if (venc.invert_polarity == false) + if (venc->invert_polarity == false) l |= 1 << 3; - venc_write_reg(VENC_OUTPUT_CONTROL, l); - - dss_mgr_set_timings(channel, &venc.vm); + venc_write_reg(venc, VENC_OUTPUT_CONTROL, l); - r = regulator_enable(venc.vdda_dac_reg); + r = regulator_enable(venc->vdda_dac_reg); if (r) goto err1; - r = dss_mgr_enable(channel); + r = dss_mgr_enable(&venc->output); if (r) goto err2; return 0; err2: - regulator_disable(venc.vdda_dac_reg); + regulator_disable(venc->vdda_dac_reg); err1: - venc_write_reg(VENC_OUTPUT_CONTROL, 0); - dss_set_dac_pwrdn_bgz(0); + venc_write_reg(venc, VENC_OUTPUT_CONTROL, 0); + dss_set_dac_pwrdn_bgz(venc->dss, 0); - venc_runtime_put(); + venc_runtime_put(venc); err0: return r; } -static void venc_power_off(struct omap_dss_device *dssdev) +static void venc_power_off(struct venc_device *venc) { - enum omap_channel channel = dssdev->dispc_channel; - - venc_write_reg(VENC_OUTPUT_CONTROL, 0); - dss_set_dac_pwrdn_bgz(0); + venc_write_reg(venc, VENC_OUTPUT_CONTROL, 0); + dss_set_dac_pwrdn_bgz(venc->dss, 0); - dss_mgr_disable(channel); + dss_mgr_disable(&venc->output); - regulator_disable(venc.vdda_dac_reg); + regulator_disable(venc->vdda_dac_reg); - venc_runtime_put(); + venc_runtime_put(venc); } -static int venc_display_enable(struct omap_dss_device *dssdev) +static enum venc_videomode venc_get_videomode(const struct drm_display_mode *mode) { - struct omap_dss_device *out = &venc.output; - int r; + if (!(mode->flags & DRM_MODE_FLAG_INTERLACE)) + return VENC_MODE_UNKNOWN; - DSSDBG("venc_display_enable\n"); + if (mode->clock == omap_dss_pal_mode.clock && + mode->hdisplay == omap_dss_pal_mode.hdisplay && + mode->vdisplay == omap_dss_pal_mode.vdisplay) + return VENC_MODE_PAL; - mutex_lock(&venc.venc_lock); + if (mode->clock == omap_dss_ntsc_mode.clock && + mode->hdisplay == omap_dss_ntsc_mode.hdisplay && + mode->vdisplay == omap_dss_ntsc_mode.vdisplay) + return VENC_MODE_NTSC; - if (!out->dispc_channel_connected) { - DSSERR("Failed to enable display: no output/manager\n"); - r = -ENODEV; - goto err0; - } + return VENC_MODE_UNKNOWN; +} - r = venc_power_on(dssdev); - if (r) - goto err0; +static int venc_dump_regs(struct seq_file *s, void *p) +{ + struct venc_device *venc = s->private; + +#define DUMPREG(venc, r) \ + seq_printf(s, "%-35s %08x\n", #r, venc_read_reg(venc, r)) - venc.wss_data = 0; + if (venc_runtime_get(venc)) + return 0; - mutex_unlock(&venc.venc_lock); + DUMPREG(venc, VENC_F_CONTROL); + DUMPREG(venc, VENC_VIDOUT_CTRL); + DUMPREG(venc, VENC_SYNC_CTRL); + DUMPREG(venc, VENC_LLEN); + DUMPREG(venc, VENC_FLENS); + DUMPREG(venc, VENC_HFLTR_CTRL); + DUMPREG(venc, VENC_CC_CARR_WSS_CARR); + DUMPREG(venc, VENC_C_PHASE); + DUMPREG(venc, VENC_GAIN_U); + DUMPREG(venc, VENC_GAIN_V); + DUMPREG(venc, VENC_GAIN_Y); + DUMPREG(venc, VENC_BLACK_LEVEL); + DUMPREG(venc, VENC_BLANK_LEVEL); + DUMPREG(venc, VENC_X_COLOR); + DUMPREG(venc, VENC_M_CONTROL); + DUMPREG(venc, VENC_BSTAMP_WSS_DATA); + DUMPREG(venc, VENC_S_CARR); + DUMPREG(venc, VENC_LINE21); + DUMPREG(venc, VENC_LN_SEL); + DUMPREG(venc, VENC_L21__WC_CTL); + DUMPREG(venc, VENC_HTRIGGER_VTRIGGER); + DUMPREG(venc, VENC_SAVID__EAVID); + DUMPREG(venc, VENC_FLEN__FAL); + DUMPREG(venc, VENC_LAL__PHASE_RESET); + DUMPREG(venc, VENC_HS_INT_START_STOP_X); + DUMPREG(venc, VENC_HS_EXT_START_STOP_X); + DUMPREG(venc, VENC_VS_INT_START_X); + DUMPREG(venc, VENC_VS_INT_STOP_X__VS_INT_START_Y); + DUMPREG(venc, VENC_VS_INT_STOP_Y__VS_EXT_START_X); + DUMPREG(venc, VENC_VS_EXT_STOP_X__VS_EXT_START_Y); + DUMPREG(venc, VENC_VS_EXT_STOP_Y); + DUMPREG(venc, VENC_AVID_START_STOP_X); + DUMPREG(venc, VENC_AVID_START_STOP_Y); + DUMPREG(venc, VENC_FID_INT_START_X__FID_INT_START_Y); + DUMPREG(venc, VENC_FID_INT_OFFSET_Y__FID_EXT_START_X); + DUMPREG(venc, VENC_FID_EXT_START_Y__FID_EXT_OFFSET_Y); + DUMPREG(venc, VENC_TVDETGP_INT_START_STOP_X); + DUMPREG(venc, VENC_TVDETGP_INT_START_STOP_Y); + DUMPREG(venc, VENC_GEN_CTRL); + DUMPREG(venc, VENC_OUTPUT_CONTROL); + DUMPREG(venc, VENC_OUTPUT_TEST); + + venc_runtime_put(venc); +#undef DUMPREG return 0; -err0: - mutex_unlock(&venc.venc_lock); - return r; } -static void venc_display_disable(struct omap_dss_device *dssdev) +static int venc_get_clocks(struct venc_device *venc) { - DSSDBG("venc_display_disable\n"); + struct clk *clk; - mutex_lock(&venc.venc_lock); + if (venc->requires_tv_dac_clk) { + clk = devm_clk_get(&venc->pdev->dev, "tv_dac_clk"); + if (IS_ERR(clk)) { + DSSERR("can't get tv_dac_clk\n"); + return PTR_ERR(clk); + } + } else { + clk = NULL; + } - venc_power_off(dssdev); + venc->tv_dac_clk = clk; - mutex_unlock(&venc.venc_lock); + return 0; } -static void venc_set_timings(struct omap_dss_device *dssdev, - struct videomode *vm) -{ - DSSDBG("venc_set_timings\n"); - - mutex_lock(&venc.venc_lock); - - /* Reset WSS data when the TV standard changes. */ - if (memcmp(&venc.vm, vm, sizeof(*vm))) - venc.wss_data = 0; +/* ----------------------------------------------------------------------------- + * DRM Bridge Operations + */ - venc.vm = *vm; +static int venc_bridge_attach(struct drm_bridge *bridge, + struct drm_encoder *encoder, + enum drm_bridge_attach_flags flags) +{ + struct venc_device *venc = drm_bridge_to_venc(bridge); - dispc_set_tv_pclk(13500000); + if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) + return -EINVAL; - mutex_unlock(&venc.venc_lock); + return drm_bridge_attach(encoder, venc->output.next_bridge, + bridge, flags); } -static int venc_check_timings(struct omap_dss_device *dssdev, - struct videomode *vm) +static enum drm_mode_status +venc_bridge_mode_valid(struct drm_bridge *bridge, + const struct drm_display_info *info, + const struct drm_display_mode *mode) { - DSSDBG("venc_check_timings\n"); - - if (memcmp(&omap_dss_pal_vm, vm, sizeof(*vm)) == 0) - return 0; - - if (memcmp(&omap_dss_ntsc_vm, vm, sizeof(*vm)) == 0) - return 0; + switch (venc_get_videomode(mode)) { + case VENC_MODE_PAL: + case VENC_MODE_NTSC: + return MODE_OK; - return -EINVAL; + default: + return MODE_BAD; + } } -static void venc_get_timings(struct omap_dss_device *dssdev, - struct videomode *vm) +static bool venc_bridge_mode_fixup(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) { - mutex_lock(&venc.venc_lock); + const struct drm_display_mode *venc_mode; - *vm = venc.vm; + switch (venc_get_videomode(adjusted_mode)) { + case VENC_MODE_PAL: + venc_mode = &omap_dss_pal_mode; + break; - mutex_unlock(&venc.venc_lock); -} + case VENC_MODE_NTSC: + venc_mode = &omap_dss_ntsc_mode; + break; -static u32 venc_get_wss(struct omap_dss_device *dssdev) -{ - /* Invert due to VENC_L21_WC_CTL:INV=1 */ - return (venc.wss_data >> 8) ^ 0xfffff; -} + default: + return false; + } -static int venc_set_wss(struct omap_dss_device *dssdev, u32 wss) -{ - const struct venc_config *config; - int r; + drm_mode_copy(adjusted_mode, venc_mode); + drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V); + drm_mode_set_name(adjusted_mode); - DSSDBG("venc_set_wss\n"); + return true; +} - mutex_lock(&venc.venc_lock); +static void venc_bridge_mode_set(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + const struct drm_display_mode *adjusted_mode) +{ + struct venc_device *venc = drm_bridge_to_venc(bridge); + enum venc_videomode venc_mode = venc_get_videomode(adjusted_mode); - config = venc_timings_to_config(&venc.vm); + switch (venc_mode) { + default: + WARN_ON_ONCE(1); + fallthrough; + case VENC_MODE_PAL: + venc->config = &venc_config_pal_trm; + break; - /* Invert due to VENC_L21_WC_CTL:INV=1 */ - venc.wss_data = (wss ^ 0xfffff) << 8; + case VENC_MODE_NTSC: + venc->config = &venc_config_ntsc_trm; + break; + } - r = venc_runtime_get(); - if (r) - goto err; + dispc_set_tv_pclk(venc->dss->dispc, 13500000); +} - venc_write_reg(VENC_BSTAMP_WSS_DATA, config->bstamp_wss_data | - venc.wss_data); +static void venc_bridge_enable(struct drm_bridge *bridge) +{ + struct venc_device *venc = drm_bridge_to_venc(bridge); - venc_runtime_put(); + venc_power_on(venc); +} -err: - mutex_unlock(&venc.venc_lock); +static void venc_bridge_disable(struct drm_bridge *bridge) +{ + struct venc_device *venc = drm_bridge_to_venc(bridge); - return r; + venc_power_off(venc); } -static int venc_init_regulator(void) +static int venc_bridge_get_modes(struct drm_bridge *bridge, + struct drm_connector *connector) { - struct regulator *vdda_dac; + static const struct drm_display_mode *modes[] = { + &omap_dss_pal_mode, + &omap_dss_ntsc_mode, + }; + unsigned int i; - if (venc.vdda_dac_reg != NULL) - return 0; + for (i = 0; i < ARRAY_SIZE(modes); ++i) { + struct drm_display_mode *mode; - vdda_dac = devm_regulator_get(&venc.pdev->dev, "vdda"); - if (IS_ERR(vdda_dac)) { - if (PTR_ERR(vdda_dac) != -EPROBE_DEFER) - DSSERR("can't get VDDA_DAC regulator\n"); - return PTR_ERR(vdda_dac); - } + mode = drm_mode_duplicate(connector->dev, modes[i]); + if (!mode) + return i; - venc.vdda_dac_reg = vdda_dac; + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; + drm_mode_set_name(mode); + drm_mode_probed_add(connector, mode); + } - return 0; + return ARRAY_SIZE(modes); } -static void venc_dump_regs(struct seq_file *s) +static const struct drm_bridge_funcs venc_bridge_funcs = { + .attach = venc_bridge_attach, + .mode_valid = venc_bridge_mode_valid, + .mode_fixup = venc_bridge_mode_fixup, + .mode_set = venc_bridge_mode_set, + .enable = venc_bridge_enable, + .disable = venc_bridge_disable, + .get_modes = venc_bridge_get_modes, +}; + +static void venc_bridge_init(struct venc_device *venc) { -#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, venc_read_reg(r)) - - if (venc_runtime_get()) - return; - - DUMPREG(VENC_F_CONTROL); - DUMPREG(VENC_VIDOUT_CTRL); - DUMPREG(VENC_SYNC_CTRL); - DUMPREG(VENC_LLEN); - DUMPREG(VENC_FLENS); - DUMPREG(VENC_HFLTR_CTRL); - DUMPREG(VENC_CC_CARR_WSS_CARR); - DUMPREG(VENC_C_PHASE); - DUMPREG(VENC_GAIN_U); - DUMPREG(VENC_GAIN_V); - DUMPREG(VENC_GAIN_Y); - DUMPREG(VENC_BLACK_LEVEL); - DUMPREG(VENC_BLANK_LEVEL); - DUMPREG(VENC_X_COLOR); - DUMPREG(VENC_M_CONTROL); - DUMPREG(VENC_BSTAMP_WSS_DATA); - DUMPREG(VENC_S_CARR); - DUMPREG(VENC_LINE21); - DUMPREG(VENC_LN_SEL); - DUMPREG(VENC_L21__WC_CTL); - DUMPREG(VENC_HTRIGGER_VTRIGGER); - DUMPREG(VENC_SAVID__EAVID); - DUMPREG(VENC_FLEN__FAL); - DUMPREG(VENC_LAL__PHASE_RESET); - DUMPREG(VENC_HS_INT_START_STOP_X); - DUMPREG(VENC_HS_EXT_START_STOP_X); - DUMPREG(VENC_VS_INT_START_X); - DUMPREG(VENC_VS_INT_STOP_X__VS_INT_START_Y); - DUMPREG(VENC_VS_INT_STOP_Y__VS_EXT_START_X); - DUMPREG(VENC_VS_EXT_STOP_X__VS_EXT_START_Y); - DUMPREG(VENC_VS_EXT_STOP_Y); - DUMPREG(VENC_AVID_START_STOP_X); - DUMPREG(VENC_AVID_START_STOP_Y); - DUMPREG(VENC_FID_INT_START_X__FID_INT_START_Y); - DUMPREG(VENC_FID_INT_OFFSET_Y__FID_EXT_START_X); - DUMPREG(VENC_FID_EXT_START_Y__FID_EXT_OFFSET_Y); - DUMPREG(VENC_TVDETGP_INT_START_STOP_X); - DUMPREG(VENC_TVDETGP_INT_START_STOP_Y); - DUMPREG(VENC_GEN_CTRL); - DUMPREG(VENC_OUTPUT_CONTROL); - DUMPREG(VENC_OUTPUT_TEST); - - venc_runtime_put(); + venc->bridge.of_node = venc->pdev->dev.of_node; + venc->bridge.ops = DRM_BRIDGE_OP_MODES; + venc->bridge.type = DRM_MODE_CONNECTOR_SVIDEO; + venc->bridge.interlace_allowed = true; -#undef DUMPREG + drm_bridge_add(&venc->bridge); } -static int venc_get_clocks(struct platform_device *pdev) +static void venc_bridge_cleanup(struct venc_device *venc) { - struct clk *clk; - - if (dss_has_feature(FEAT_VENC_REQUIRES_TV_DAC_CLK)) { - clk = devm_clk_get(&pdev->dev, "tv_dac_clk"); - if (IS_ERR(clk)) { - DSSERR("can't get tv_dac_clk\n"); - return PTR_ERR(clk); - } - } else { - clk = NULL; - } - - venc.tv_dac_clk = clk; - - return 0; + drm_bridge_remove(&venc->bridge); } -static int venc_connect(struct omap_dss_device *dssdev, - struct omap_dss_device *dst) +/* ----------------------------------------------------------------------------- + * Component Bind & Unbind + */ + +static int venc_bind(struct device *dev, struct device *master, void *data) { - enum omap_channel channel = dssdev->dispc_channel; + struct dss_device *dss = dss_get_device(master); + struct venc_device *venc = dev_get_drvdata(dev); + u8 rev_id; int r; - r = venc_init_regulator(); - if (r) - return r; + venc->dss = dss; - r = dss_mgr_connect(channel, dssdev); + r = venc_runtime_get(venc); if (r) return r; - r = omapdss_output_set_device(dssdev, dst); - if (r) { - DSSERR("failed to connect output to new device: %s\n", - dst->name); - dss_mgr_disconnect(channel, dssdev); - return r; - } + rev_id = (u8)(venc_read_reg(venc, VENC_REV_ID) & 0xff); + dev_dbg(dev, "OMAP VENC rev %d\n", rev_id); + + venc_runtime_put(venc); + + venc->debugfs = dss_debugfs_create_file(dss, "venc", venc_dump_regs, + venc); return 0; } -static void venc_disconnect(struct omap_dss_device *dssdev, - struct omap_dss_device *dst) +static void venc_unbind(struct device *dev, struct device *master, void *data) { - enum omap_channel channel = dssdev->dispc_channel; - - WARN_ON(dst != dssdev->dst); + struct venc_device *venc = dev_get_drvdata(dev); - if (dst != dssdev->dst) - return; - - omapdss_output_unset_device(dssdev); - - dss_mgr_disconnect(channel, dssdev); + dss_debugfs_remove_file(venc->debugfs); } -static const struct omapdss_atv_ops venc_ops = { - .connect = venc_connect, - .disconnect = venc_disconnect, - - .enable = venc_display_enable, - .disable = venc_display_disable, - - .check_timings = venc_check_timings, - .set_timings = venc_set_timings, - .get_timings = venc_get_timings, - - .set_wss = venc_set_wss, - .get_wss = venc_get_wss, +static const struct component_ops venc_component_ops = { + .bind = venc_bind, + .unbind = venc_unbind, }; -static void venc_init_output(struct platform_device *pdev) +/* ----------------------------------------------------------------------------- + * Probe & Remove, Suspend & Resume + */ + +static int venc_init_output(struct venc_device *venc) { - struct omap_dss_device *out = &venc.output; + struct omap_dss_device *out = &venc->output; + int r; + + venc_bridge_init(venc); - out->dev = &pdev->dev; + out->dev = &venc->pdev->dev; out->id = OMAP_DSS_OUTPUT_VENC; - out->output_type = OMAP_DISPLAY_TYPE_VENC; + out->type = OMAP_DISPLAY_TYPE_VENC; out->name = "venc.0"; out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT; - out->ops.atv = &venc_ops; - out->owner = THIS_MODULE; + out->of_port = 0; + + r = omapdss_device_init_output(out, &venc->bridge); + if (r < 0) { + venc_bridge_cleanup(venc); + return r; + } - omapdss_register_output(out); + omapdss_device_register(out); + + return 0; } -static void venc_uninit_output(struct platform_device *pdev) +static void venc_uninit_output(struct venc_device *venc) { - struct omap_dss_device *out = &venc.output; + omapdss_device_unregister(&venc->output); + omapdss_device_cleanup_output(&venc->output); - omapdss_unregister_output(out); + venc_bridge_cleanup(venc); } -static int venc_probe_of(struct platform_device *pdev) +static int venc_probe_of(struct venc_device *venc) { - struct device_node *node = pdev->dev.of_node; + struct device_node *node = venc->pdev->dev.of_node; struct device_node *ep; u32 channels; int r; @@ -796,24 +765,25 @@ static int venc_probe_of(struct platform_device *pdev) if (!ep) return 0; - venc.invert_polarity = of_property_read_bool(ep, "ti,invert-polarity"); + venc->invert_polarity = of_property_read_bool(ep, "ti,invert-polarity"); r = of_property_read_u32(ep, "ti,channels", &channels); if (r) { - dev_err(&pdev->dev, + dev_err(&venc->pdev->dev, "failed to read property 'ti,channels': %d\n", r); goto err; } switch (channels) { case 1: - venc.type = OMAP_DSS_VENC_TYPE_COMPOSITE; + venc->type = OMAP_DSS_VENC_TYPE_COMPOSITE; break; case 2: - venc.type = OMAP_DSS_VENC_TYPE_SVIDEO; + venc->type = OMAP_DSS_VENC_TYPE_SVIDEO; break; default: - dev_err(&pdev->dev, "bad channel propert '%d'\n", channels); + dev_err(&venc->pdev->dev, "bad channel property '%d'\n", + channels); r = -EINVAL; goto err; } @@ -821,116 +791,110 @@ static int venc_probe_of(struct platform_device *pdev) of_node_put(ep); return 0; + err: of_node_put(ep); - - return 0; + return r; } -/* VENC HW IP initialisation */ -static int venc_bind(struct device *dev, struct device *master, void *data) +static const struct soc_device_attribute venc_soc_devices[] = { + { .machine = "OMAP3[45]*" }, + { .machine = "AM35*" }, + { /* sentinel */ } +}; + +static int venc_probe(struct platform_device *pdev) { - struct platform_device *pdev = to_platform_device(dev); - u8 rev_id; - struct resource *venc_mem; + struct venc_device *venc; int r; - venc.pdev = pdev; + venc = devm_drm_bridge_alloc(&pdev->dev, struct venc_device, bridge, &venc_bridge_funcs); + if (IS_ERR(venc)) + return PTR_ERR(venc); - mutex_init(&venc.venc_lock); + venc->pdev = pdev; - venc.wss_data = 0; + platform_set_drvdata(pdev, venc); - venc_mem = platform_get_resource(venc.pdev, IORESOURCE_MEM, 0); - venc.base = devm_ioremap_resource(&pdev->dev, venc_mem); - if (IS_ERR(venc.base)) - return PTR_ERR(venc.base); + /* The OMAP34xx, OMAP35xx and AM35xx VENC require the TV DAC clock. */ + if (soc_device_match(venc_soc_devices)) + venc->requires_tv_dac_clk = true; - r = venc_get_clocks(pdev); - if (r) - return r; + venc->config = &venc_config_pal_trm; - pm_runtime_enable(&pdev->dev); + venc->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(venc->base)) + return PTR_ERR(venc->base); - r = venc_runtime_get(); - if (r) - goto err_runtime_get; + venc->vdda_dac_reg = devm_regulator_get(&pdev->dev, "vdda"); + if (IS_ERR(venc->vdda_dac_reg)) { + r = PTR_ERR(venc->vdda_dac_reg); + if (r != -EPROBE_DEFER) + DSSERR("can't get VDDA_DAC regulator\n"); + return r; + } - rev_id = (u8)(venc_read_reg(VENC_REV_ID) & 0xff); - dev_dbg(&pdev->dev, "OMAP VENC rev %d\n", rev_id); + r = venc_get_clocks(venc); + if (r) + return r; - venc_runtime_put(); + r = venc_probe_of(venc); + if (r) + return r; - r = venc_probe_of(pdev); - if (r) { - DSSERR("Invalid DT data\n"); - goto err_probe_of; - } + pm_runtime_enable(&pdev->dev); - dss_debugfs_create_file("venc", venc_dump_regs); + r = venc_init_output(venc); + if (r) + goto err_pm_disable; - venc_init_output(pdev); + r = component_add(&pdev->dev, &venc_component_ops); + if (r) + goto err_uninit_output; return 0; -err_probe_of: -err_runtime_get: +err_uninit_output: + venc_uninit_output(venc); +err_pm_disable: pm_runtime_disable(&pdev->dev); return r; } -static void venc_unbind(struct device *dev, struct device *master, void *data) +static void venc_remove(struct platform_device *pdev) { - struct platform_device *pdev = to_platform_device(dev); - - venc_uninit_output(pdev); + struct venc_device *venc = platform_get_drvdata(pdev); - pm_runtime_disable(&pdev->dev); -} - -static const struct component_ops venc_component_ops = { - .bind = venc_bind, - .unbind = venc_unbind, -}; + component_del(&pdev->dev, &venc_component_ops); -static int venc_probe(struct platform_device *pdev) -{ - return component_add(&pdev->dev, &venc_component_ops); -} + venc_uninit_output(venc); -static int venc_remove(struct platform_device *pdev) -{ - component_del(&pdev->dev, &venc_component_ops); - return 0; + pm_runtime_disable(&pdev->dev); } -static int venc_runtime_suspend(struct device *dev) +static __maybe_unused int venc_runtime_suspend(struct device *dev) { - if (venc.tv_dac_clk) - clk_disable_unprepare(venc.tv_dac_clk); + struct venc_device *venc = dev_get_drvdata(dev); - dispc_runtime_put(); + if (venc->tv_dac_clk) + clk_disable_unprepare(venc->tv_dac_clk); return 0; } -static int venc_runtime_resume(struct device *dev) +static __maybe_unused int venc_runtime_resume(struct device *dev) { - int r; + struct venc_device *venc = dev_get_drvdata(dev); - r = dispc_runtime_get(); - if (r < 0) - return r; - - if (venc.tv_dac_clk) - clk_prepare_enable(venc.tv_dac_clk); + if (venc->tv_dac_clk) + clk_prepare_enable(venc->tv_dac_clk); return 0; } static const struct dev_pm_ops venc_pm_ops = { - .runtime_suspend = venc_runtime_suspend, - .runtime_resume = venc_runtime_resume, + SET_RUNTIME_PM_OPS(venc_runtime_suspend, venc_runtime_resume, NULL) + SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; static const struct of_device_id venc_of_match[] = { @@ -940,7 +904,7 @@ static const struct of_device_id venc_of_match[] = { {}, }; -static struct platform_driver omap_venchw_driver = { +struct platform_driver omap_venchw_driver = { .probe = venc_probe, .remove = venc_remove, .driver = { @@ -950,13 +914,3 @@ static struct platform_driver omap_venchw_driver = { .suppress_bind_attrs = true, }, }; - -int __init venc_init_platform_driver(void) -{ - return platform_driver_register(&omap_venchw_driver); -} - -void venc_uninit_platform_driver(void) -{ - platform_driver_unregister(&omap_venchw_driver); -} diff --git a/drivers/gpu/drm/omapdrm/dss/video-pll.c b/drivers/gpu/drm/omapdrm/dss/video-pll.c index fbd1263a29a4..b6b52049f753 100644 --- a/drivers/gpu/drm/omapdrm/dss/video-pll.c +++ b/drivers/gpu/drm/omapdrm/dss/video-pll.c @@ -1,13 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* -* Copyright (C) 2014 Texas Instruments Ltd -* -* This program is free software; you can redistribute it and/or modify it -* under the terms of the GNU General Public License version 2 as published by -* the Free Software Foundation. -* -* You should have received a copy of the GNU General Public License along with -* this program. If not, see <http://www.gnu.org/licenses/>. -*/ + * Copyright (C) 2014 Texas Instruments Incorporated - https://www.ti.com/ + */ #include <linux/clk.h> #include <linux/delay.h> @@ -19,7 +13,6 @@ #include "omapdss.h" #include "dss.h" -#include "dss_features.h" struct dss_video_pll { struct dss_pll pll; @@ -63,11 +56,11 @@ static int dss_video_pll_enable(struct dss_pll *pll) struct dss_video_pll *vpll = container_of(pll, struct dss_video_pll, pll); int r; - r = dss_runtime_get(); + r = dss_runtime_get(pll->dss); if (r) return r; - dss_ctrl_pll_enable(pll->id, true); + dss_ctrl_pll_enable(pll, true); dss_dpll_enable_scp_clk(vpll); @@ -81,8 +74,8 @@ static int dss_video_pll_enable(struct dss_pll *pll) err_reset: dss_dpll_disable_scp_clk(vpll); - dss_ctrl_pll_enable(pll->id, false); - dss_runtime_put(); + dss_ctrl_pll_enable(pll, false); + dss_runtime_put(pll->dss); return r; } @@ -95,9 +88,9 @@ static void dss_video_pll_disable(struct dss_pll *pll) dss_dpll_disable_scp_clk(vpll); - dss_ctrl_pll_enable(pll->id, false); + dss_ctrl_pll_enable(pll, false); - dss_runtime_put(); + dss_runtime_put(pll->dss); } static const struct dss_pll_ops dss_pll_ops = { @@ -131,16 +124,19 @@ static const struct dss_pll_hw dss_dra7_video_pll_hw = { .mX_lsb[3] = 5, .has_refsel = true, + + .errata_i886 = true, + .errata_i932 = true, }; -struct dss_pll *dss_video_pll_init(struct platform_device *pdev, int id, - struct regulator *regulator) +struct dss_pll *dss_video_pll_init(struct dss_device *dss, + struct platform_device *pdev, int id, + struct regulator *regulator) { const char * const reg_name[] = { "pll1", "pll2" }; const char * const clkctrl_name[] = { "pll1_clkctrl", "pll2_clkctrl" }; const char * const clkin_name[] = { "video1_clk", "video2_clk" }; - struct resource *res; struct dss_video_pll *vpll; void __iomem *pll_base, *clkctrl_base; struct clk *clk; @@ -149,16 +145,13 @@ struct dss_pll *dss_video_pll_init(struct platform_device *pdev, int id, /* PLL CONTROL */ - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, reg_name[id]); - pll_base = devm_ioremap_resource(&pdev->dev, res); + pll_base = devm_platform_ioremap_resource_byname(pdev, reg_name[id]); if (IS_ERR(pll_base)) return ERR_CAST(pll_base); /* CLOCK CONTROL */ - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - clkctrl_name[id]); - clkctrl_base = devm_ioremap_resource(&pdev->dev, res); + clkctrl_base = devm_platform_ioremap_resource_byname(pdev, clkctrl_name[id]); if (IS_ERR(clkctrl_base)) return ERR_CAST(clkctrl_base); @@ -187,7 +180,7 @@ struct dss_pll *dss_video_pll_init(struct platform_device *pdev, int id, pll->hw = &dss_dra7_video_pll_hw; pll->ops = &dss_pll_ops; - r = dss_pll_register(pll); + r = dss_pll_register(dss, pll); if (r) return ERR_PTR(r); diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c b/drivers/gpu/drm/omapdrm/omap_connector.c deleted file mode 100644 index c24b6b783e9a..000000000000 --- a/drivers/gpu/drm/omapdrm/omap_connector.c +++ /dev/null @@ -1,252 +0,0 @@ -/* - * drivers/gpu/drm/omapdrm/omap_connector.c - * - * Copyright (C) 2011 Texas Instruments - * Author: Rob Clark <rob@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 <drm/drm_atomic_helper.h> -#include <drm/drm_crtc.h> -#include <drm/drm_crtc_helper.h> - -#include "omap_drv.h" - -/* - * connector funcs - */ - -#define to_omap_connector(x) container_of(x, struct omap_connector, base) - -struct omap_connector { - struct drm_connector base; - struct omap_dss_device *dssdev; - bool hdmi_mode; -}; - -bool omap_connector_get_hdmi_mode(struct drm_connector *connector) -{ - struct omap_connector *omap_connector = to_omap_connector(connector); - - return omap_connector->hdmi_mode; -} - -static enum drm_connector_status omap_connector_detect( - struct drm_connector *connector, bool force) -{ - struct omap_connector *omap_connector = to_omap_connector(connector); - struct omap_dss_device *dssdev = omap_connector->dssdev; - struct omap_dss_driver *dssdrv = dssdev->driver; - enum drm_connector_status ret; - - if (dssdrv->detect) { - if (dssdrv->detect(dssdev)) - ret = connector_status_connected; - else - ret = connector_status_disconnected; - } else if (dssdev->type == OMAP_DISPLAY_TYPE_DPI || - dssdev->type == OMAP_DISPLAY_TYPE_DBI || - dssdev->type == OMAP_DISPLAY_TYPE_SDI || - dssdev->type == OMAP_DISPLAY_TYPE_DSI) { - ret = connector_status_connected; - } else { - ret = connector_status_unknown; - } - - VERB("%s: %d (force=%d)", omap_connector->dssdev->name, ret, force); - - return ret; -} - -static void omap_connector_destroy(struct drm_connector *connector) -{ - struct omap_connector *omap_connector = to_omap_connector(connector); - struct omap_dss_device *dssdev = omap_connector->dssdev; - - DBG("%s", omap_connector->dssdev->name); - drm_connector_unregister(connector); - drm_connector_cleanup(connector); - kfree(omap_connector); - - omap_dss_put_device(dssdev); -} - -#define MAX_EDID 512 - -static int omap_connector_get_modes(struct drm_connector *connector) -{ - struct omap_connector *omap_connector = to_omap_connector(connector); - struct omap_dss_device *dssdev = omap_connector->dssdev; - struct omap_dss_driver *dssdrv = dssdev->driver; - struct drm_device *dev = connector->dev; - int n = 0; - - DBG("%s", omap_connector->dssdev->name); - - /* if display exposes EDID, then we parse that in the normal way to - * build table of supported modes.. otherwise (ie. fixed resolution - * LCD panels) we just return a single mode corresponding to the - * currently configured timings: - */ - if (dssdrv->read_edid) { - void *edid = kzalloc(MAX_EDID, GFP_KERNEL); - - if ((dssdrv->read_edid(dssdev, edid, MAX_EDID) > 0) && - drm_edid_is_valid(edid)) { - drm_mode_connector_update_edid_property( - connector, edid); - n = drm_add_edid_modes(connector, edid); - - omap_connector->hdmi_mode = - drm_detect_hdmi_monitor(edid); - } else { - drm_mode_connector_update_edid_property( - connector, NULL); - } - - kfree(edid); - } else { - struct drm_display_mode *mode = drm_mode_create(dev); - struct videomode vm = {0}; - - dssdrv->get_timings(dssdev, &vm); - - drm_display_mode_from_videomode(&vm, mode); - - mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; - drm_mode_set_name(mode); - drm_mode_probed_add(connector, mode); - - n = 1; - } - - return n; -} - -static int omap_connector_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - struct omap_connector *omap_connector = to_omap_connector(connector); - struct omap_dss_device *dssdev = omap_connector->dssdev; - struct omap_dss_driver *dssdrv = dssdev->driver; - struct videomode vm = {0}; - struct drm_device *dev = connector->dev; - struct drm_display_mode *new_mode; - int r, ret = MODE_BAD; - - drm_display_mode_to_videomode(mode, &vm); - mode->vrefresh = drm_mode_vrefresh(mode); - - /* - * if the panel driver doesn't have a check_timings, it's most likely - * a fixed resolution panel, check if the timings match with the - * panel's timings - */ - if (dssdrv->check_timings) { - r = dssdrv->check_timings(dssdev, &vm); - } else { - struct videomode t = {0}; - - dssdrv->get_timings(dssdev, &t); - - /* - * Ignore the flags, as we don't get them from - * drm_display_mode_to_videomode. - */ - t.flags = 0; - - if (memcmp(&vm, &t, sizeof(vm))) - r = -EINVAL; - else - r = 0; - } - - if (!r) { - /* check if vrefresh is still valid */ - new_mode = drm_mode_duplicate(dev, mode); - new_mode->clock = vm.pixelclock / 1000; - new_mode->vrefresh = 0; - if (mode->vrefresh == drm_mode_vrefresh(new_mode)) - ret = MODE_OK; - drm_mode_destroy(dev, new_mode); - } - - DBG("connector: mode %s: " - "%d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", - (ret == MODE_OK) ? "valid" : "invalid", - mode->base.id, mode->name, mode->vrefresh, mode->clock, - mode->hdisplay, mode->hsync_start, - mode->hsync_end, mode->htotal, - mode->vdisplay, mode->vsync_start, - mode->vsync_end, mode->vtotal, mode->type, mode->flags); - - return ret; -} - -static const struct drm_connector_funcs omap_connector_funcs = { - .dpms = drm_atomic_helper_connector_dpms, - .reset = drm_atomic_helper_connector_reset, - .detect = omap_connector_detect, - .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = omap_connector_destroy, - .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, -}; - -static const struct drm_connector_helper_funcs omap_connector_helper_funcs = { - .get_modes = omap_connector_get_modes, - .mode_valid = omap_connector_mode_valid, -}; - -/* initialize connector */ -struct drm_connector *omap_connector_init(struct drm_device *dev, - int connector_type, struct omap_dss_device *dssdev, - struct drm_encoder *encoder) -{ - struct drm_connector *connector = NULL; - struct omap_connector *omap_connector; - - DBG("%s", dssdev->name); - - omap_dss_get_device(dssdev); - - omap_connector = kzalloc(sizeof(*omap_connector), GFP_KERNEL); - if (!omap_connector) - goto fail; - - omap_connector->dssdev = dssdev; - - connector = &omap_connector->base; - - drm_connector_init(dev, connector, &omap_connector_funcs, - connector_type); - drm_connector_helper_add(connector, &omap_connector_helper_funcs); - - if (dssdev->driver->detect) - connector->polled = DRM_CONNECTOR_POLL_CONNECT | - DRM_CONNECTOR_POLL_DISCONNECT; - else - connector->polled = 0; - - connector->interlace_allowed = 1; - connector->doublescan_allowed = 0; - - return connector; - -fail: - if (connector) - omap_connector_destroy(connector); - - return NULL; -} diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c index dd0ef40ca469..1c2a1920c0a6 100644 --- a/drivers/gpu/drm/omapdrm/omap_crtc.c +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c @@ -1,37 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0-only /* - * drivers/gpu/drm/omapdrm/omap_crtc.c - * - * Copyright (C) 2011 Texas Instruments + * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/ * Author: Rob Clark <rob@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/math64.h> + #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc.h> -#include <drm/drm_crtc_helper.h> #include <drm/drm_mode.h> -#include <drm/drm_plane_helper.h> +#include <drm/drm_print.h> +#include <drm/drm_vblank.h> #include "omap_drv.h" +#define to_omap_crtc_state(x) container_of(x, struct omap_crtc_state, base) + +struct omap_crtc_state { + /* Must be first. */ + struct drm_crtc_state base; + /* Shadow values for legacy userspace support. */ + unsigned int rotation; + unsigned int zpos; + bool manually_updated; +}; + #define to_omap_crtc(x) container_of(x, struct omap_crtc, base) struct omap_crtc { struct drm_crtc base; const char *name; + struct omap_drm_pipeline *pipe; enum omap_channel channel; struct videomode vm; @@ -42,6 +43,10 @@ struct omap_crtc { bool pending; wait_queue_head_t pending_wait; struct drm_pending_vblank_event *event; + struct delayed_work update_work; + + void (*framedone_handler)(void *); + void *framedone_handler_data; }; /* ----------------------------------------------------------------------------- @@ -93,48 +98,18 @@ int omap_crtc_wait_pending(struct drm_crtc *crtc) /* * Manager-ops, callbacks from output when they need to configure * the upstream part of the video pipe. - * - * Most of these we can ignore until we add support for command-mode - * panels.. for video-mode the crtc-helpers already do an adequate - * job of sequencing the setup of the video pipe in the proper order */ -/* ovl-mgr-id -> crtc */ -static struct omap_crtc *omap_crtcs[8]; -static struct omap_dss_device *omap_crtc_output[8]; - -/* we can probably ignore these until we support command-mode panels: */ -static int omap_crtc_dss_connect(enum omap_channel channel, - struct omap_dss_device *dst) -{ - const struct dispc_ops *dispc_ops = dispc_get_ops(); - - if (omap_crtc_output[channel]) - return -EINVAL; - - if ((dispc_ops->mgr_get_supported_outputs(channel) & dst->id) == 0) - return -EINVAL; - - omap_crtc_output[channel] = dst; - dst->dispc_channel_connected = true; - - return 0; -} - -static void omap_crtc_dss_disconnect(enum omap_channel channel, - struct omap_dss_device *dst) -{ - omap_crtc_output[channel] = NULL; - dst->dispc_channel_connected = false; -} - -static void omap_crtc_dss_start_update(enum omap_channel channel) +void omap_crtc_dss_start_update(struct omap_drm_private *priv, + enum omap_channel channel) { + dispc_mgr_enable(priv->dispc, channel, true); } /* Called only from the encoder enable/disable and suspend/resume handlers. */ -static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable) +void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable) { + struct omap_crtc_state *omap_state = to_omap_crtc_state(crtc->state); struct drm_device *dev = crtc->dev; struct omap_drm_private *priv = dev->dev_private; struct omap_crtc *omap_crtc = to_omap_crtc(crtc); @@ -146,8 +121,14 @@ static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable) if (WARN_ON(omap_crtc->enabled == enable)) return; - if (omap_crtc_output[channel]->output_type == OMAP_DISPLAY_TYPE_HDMI) { - priv->dispc_ops->mgr_enable(channel, enable); + if (omap_state->manually_updated) { + omap_irq_enable_framedone(crtc, enable); + omap_crtc->enabled = enable; + return; + } + + if (omap_crtc->pipe->output->type == OMAP_DISPLAY_TYPE_HDMI) { + dispc_mgr_enable(priv->dispc, channel, enable); omap_crtc->enabled = enable; return; } @@ -160,8 +141,9 @@ static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable) omap_crtc->ignore_digit_sync_lost = true; } - framedone_irq = priv->dispc_ops->mgr_get_framedone_irq(channel); - vsync_irq = priv->dispc_ops->mgr_get_vsync_irq(channel); + framedone_irq = dispc_mgr_get_framedone_irq(priv->dispc, + channel); + vsync_irq = dispc_mgr_get_vsync_irq(priv->dispc, channel); if (enable) { wait = omap_irq_wait_init(dev, vsync_irq, 1); @@ -181,7 +163,7 @@ static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable) wait = omap_irq_wait_init(dev, vsync_irq, 2); } - priv->dispc_ops->mgr_enable(channel, enable); + dispc_mgr_enable(priv->dispc, channel, enable); omap_crtc->enabled = enable; ret = omap_irq_wait(dev, wait, msecs_to_jiffies(100)); @@ -198,72 +180,90 @@ static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable) } -static int omap_crtc_dss_enable(enum omap_channel channel) +int omap_crtc_dss_enable(struct omap_drm_private *priv, enum omap_channel channel) { - struct omap_crtc *omap_crtc = omap_crtcs[channel]; - struct omap_drm_private *priv = omap_crtc->base.dev->dev_private; + struct drm_crtc *crtc = priv->channels[channel]->crtc; + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - priv->dispc_ops->mgr_set_timings(omap_crtc->channel, &omap_crtc->vm); + dispc_mgr_set_timings(priv->dispc, omap_crtc->channel, + &omap_crtc->vm); omap_crtc_set_enabled(&omap_crtc->base, true); return 0; } -static void omap_crtc_dss_disable(enum omap_channel channel) +void omap_crtc_dss_disable(struct omap_drm_private *priv, enum omap_channel channel) { - struct omap_crtc *omap_crtc = omap_crtcs[channel]; + struct drm_crtc *crtc = priv->channels[channel]->crtc; + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); omap_crtc_set_enabled(&omap_crtc->base, false); } -static void omap_crtc_dss_set_timings(enum omap_channel channel, +void omap_crtc_dss_set_timings(struct omap_drm_private *priv, + enum omap_channel channel, const struct videomode *vm) { - struct omap_crtc *omap_crtc = omap_crtcs[channel]; + struct drm_crtc *crtc = priv->channels[channel]->crtc; + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + DBG("%s", omap_crtc->name); omap_crtc->vm = *vm; } -static void omap_crtc_dss_set_lcd_config(enum omap_channel channel, +void omap_crtc_dss_set_lcd_config(struct omap_drm_private *priv, + enum omap_channel channel, const struct dss_lcd_mgr_config *config) { - struct omap_crtc *omap_crtc = omap_crtcs[channel]; - struct omap_drm_private *priv = omap_crtc->base.dev->dev_private; + struct drm_crtc *crtc = priv->channels[channel]->crtc; + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); DBG("%s", omap_crtc->name); - priv->dispc_ops->mgr_set_lcd_config(omap_crtc->channel, config); + dispc_mgr_set_lcd_config(priv->dispc, omap_crtc->channel, + config); } -static int omap_crtc_dss_register_framedone( - enum omap_channel channel, +int omap_crtc_dss_register_framedone( + struct omap_drm_private *priv, enum omap_channel channel, void (*handler)(void *), void *data) { + struct drm_crtc *crtc = priv->channels[channel]->crtc; + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + struct drm_device *dev = omap_crtc->base.dev; + + if (omap_crtc->framedone_handler) + return -EBUSY; + + dev_dbg(dev->dev, "register framedone %s", omap_crtc->name); + + omap_crtc->framedone_handler = handler; + omap_crtc->framedone_handler_data = data; + return 0; } -static void omap_crtc_dss_unregister_framedone( - enum omap_channel channel, +void omap_crtc_dss_unregister_framedone( + struct omap_drm_private *priv, enum omap_channel channel, void (*handler)(void *), void *data) { -} + struct drm_crtc *crtc = priv->channels[channel]->crtc; + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + struct drm_device *dev = omap_crtc->base.dev; -static const struct dss_mgr_ops mgr_ops = { - .connect = omap_crtc_dss_connect, - .disconnect = omap_crtc_dss_disconnect, - .start_update = omap_crtc_dss_start_update, - .enable = omap_crtc_dss_enable, - .disable = omap_crtc_dss_disable, - .set_timings = omap_crtc_dss_set_timings, - .set_lcd_config = omap_crtc_dss_set_lcd_config, - .register_framedone_handler = omap_crtc_dss_register_framedone, - .unregister_framedone_handler = omap_crtc_dss_unregister_framedone, -}; + dev_dbg(dev->dev, "unregister framedone %s", omap_crtc->name); + + WARN_ON(omap_crtc->framedone_handler != handler); + WARN_ON(omap_crtc->framedone_handler_data != data); + + omap_crtc->framedone_handler = NULL; + omap_crtc->framedone_handler_data = NULL; +} /* ----------------------------------------------------------------------------- * Setup, Flush and Page Flip */ -void omap_crtc_error_irq(struct drm_crtc *crtc, uint32_t irqstatus) +void omap_crtc_error_irq(struct drm_crtc *crtc, u32 irqstatus) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); @@ -288,7 +288,7 @@ void omap_crtc_vblank_irq(struct drm_crtc *crtc) * If the dispc is busy we're racing the flush operation. Try again on * the next vblank interrupt. */ - if (priv->dispc_ops->mgr_go_busy(omap_crtc->channel)) { + if (dispc_mgr_go_busy(priv->dispc, omap_crtc->channel)) { spin_unlock(&crtc->dev->event_lock); return; } @@ -312,6 +312,87 @@ void omap_crtc_vblank_irq(struct drm_crtc *crtc) DBG("%s: apply done", omap_crtc->name); } +void omap_crtc_framedone_irq(struct drm_crtc *crtc, uint32_t irqstatus) +{ + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + + if (!omap_crtc->framedone_handler) + return; + + omap_crtc->framedone_handler(omap_crtc->framedone_handler_data); + + spin_lock(&crtc->dev->event_lock); + /* Send the vblank event if one has been requested. */ + if (omap_crtc->event) { + drm_crtc_send_vblank_event(crtc, omap_crtc->event); + omap_crtc->event = NULL; + } + omap_crtc->pending = false; + spin_unlock(&crtc->dev->event_lock); + + /* Wake up omap_atomic_complete. */ + wake_up(&omap_crtc->pending_wait); +} + +void omap_crtc_flush(struct drm_crtc *crtc) +{ + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + struct omap_crtc_state *omap_state = to_omap_crtc_state(crtc->state); + + if (!omap_state->manually_updated) + return; + + if (!delayed_work_pending(&omap_crtc->update_work)) + schedule_delayed_work(&omap_crtc->update_work, 0); +} + +static void omap_crtc_manual_display_update(struct work_struct *data) +{ + struct omap_crtc *omap_crtc = + container_of(data, struct omap_crtc, update_work.work); + struct omap_dss_device *dssdev = omap_crtc->pipe->output; + struct drm_device *dev = omap_crtc->base.dev; + int ret; + + if (!dssdev || !dssdev->dsi_ops || !dssdev->dsi_ops->update) + return; + + ret = dssdev->dsi_ops->update(dssdev); + if (ret < 0) { + spin_lock_irq(&dev->event_lock); + omap_crtc->pending = false; + spin_unlock_irq(&dev->event_lock); + wake_up(&omap_crtc->pending_wait); + } +} + +static s16 omap_crtc_s31_32_to_s2_8(s64 coef) +{ + u64 sign_bit = 1ULL << 63; + u64 cbits = (u64)coef; + + s16 ret = clamp_val(((cbits & ~sign_bit) >> 24), 0, 0x1ff); + + if (cbits & sign_bit) + ret = -ret; + + return ret; +} + +static void omap_crtc_cpr_coefs_from_ctm(const struct drm_color_ctm *ctm, + struct omap_dss_cpr_coefs *cpr) +{ + cpr->rr = omap_crtc_s31_32_to_s2_8(ctm->matrix[0]); + cpr->rg = omap_crtc_s31_32_to_s2_8(ctm->matrix[1]); + cpr->rb = omap_crtc_s31_32_to_s2_8(ctm->matrix[2]); + cpr->gr = omap_crtc_s31_32_to_s2_8(ctm->matrix[3]); + cpr->gg = omap_crtc_s31_32_to_s2_8(ctm->matrix[4]); + cpr->gb = omap_crtc_s31_32_to_s2_8(ctm->matrix[5]); + cpr->br = omap_crtc_s31_32_to_s2_8(ctm->matrix[6]); + cpr->bg = omap_crtc_s31_32_to_s2_8(ctm->matrix[7]); + cpr->bb = omap_crtc_s31_32_to_s2_8(ctm->matrix[8]); +} + static void omap_crtc_write_crtc_properties(struct drm_crtc *crtc) { struct omap_drm_private *priv = crtc->dev->dev_private; @@ -323,9 +404,17 @@ static void omap_crtc_write_crtc_properties(struct drm_crtc *crtc) info.default_color = 0x000000; info.trans_enabled = false; info.partial_alpha_enabled = false; - info.cpr_enable = false; - priv->dispc_ops->mgr_setup(omap_crtc->channel, &info); + if (crtc->state->ctm) { + struct drm_color_ctm *ctm = crtc->state->ctm->data; + + info.cpr_enable = true; + omap_crtc_cpr_coefs_from_ctm(ctm, &info.cpr_coefs); + } else { + info.cpr_enable = false; + } + + dispc_mgr_setup(priv->dispc, omap_crtc->channel, &info); } /* ----------------------------------------------------------------------------- @@ -356,25 +445,38 @@ static void omap_crtc_arm_event(struct drm_crtc *crtc) } } -static void omap_crtc_enable(struct drm_crtc *crtc) +static void omap_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_atomic_state *state) { + struct omap_drm_private *priv = crtc->dev->dev_private; struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + struct omap_crtc_state *omap_state = to_omap_crtc_state(crtc->state); int ret; DBG("%s", omap_crtc->name); - spin_lock_irq(&crtc->dev->event_lock); + dispc_runtime_get(priv->dispc); + + /* manual updated display will not trigger vsync irq */ + if (omap_state->manually_updated) + return; + drm_crtc_vblank_on(crtc); + ret = drm_crtc_vblank_get(crtc); WARN_ON(ret != 0); + spin_lock_irq(&crtc->dev->event_lock); omap_crtc_arm_event(crtc); spin_unlock_irq(&crtc->dev->event_lock); } -static void omap_crtc_disable(struct drm_crtc *crtc) +static void omap_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_atomic_state *state) { + struct omap_drm_private *priv = crtc->dev->dev_private; struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + struct drm_device *dev = crtc->dev; DBG("%s", omap_crtc->name); @@ -385,98 +487,152 @@ static void omap_crtc_disable(struct drm_crtc *crtc) } spin_unlock_irq(&crtc->dev->event_lock); + cancel_delayed_work(&omap_crtc->update_work); + + if (!omap_crtc_wait_pending(crtc)) + dev_warn(dev->dev, "manual display update did not finish!"); + drm_crtc_vblank_off(crtc); + + dispc_runtime_put(priv->dispc); } -static void omap_crtc_mode_set_nofb(struct drm_crtc *crtc) +static enum drm_mode_status omap_crtc_mode_valid(struct drm_crtc *crtc, + const struct drm_display_mode *mode) { - struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - struct drm_display_mode *mode = &crtc->state->adjusted_mode; struct omap_drm_private *priv = crtc->dev->dev_private; - const u32 flags_mask = DISPLAY_FLAGS_DE_HIGH | DISPLAY_FLAGS_DE_LOW | - DISPLAY_FLAGS_PIXDATA_POSEDGE | DISPLAY_FLAGS_PIXDATA_NEGEDGE | - DISPLAY_FLAGS_SYNC_POSEDGE | DISPLAY_FLAGS_SYNC_NEGEDGE; - unsigned int i; - - DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", - omap_crtc->name, mode->base.id, mode->name, - mode->vrefresh, mode->clock, - mode->hdisplay, mode->hsync_start, mode->hsync_end, mode->htotal, - mode->vdisplay, mode->vsync_start, mode->vsync_end, mode->vtotal, - mode->type, mode->flags); + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + struct videomode vm = {0}; + int r; - drm_display_mode_to_videomode(mode, &omap_crtc->vm); + drm_display_mode_to_videomode(mode, &vm); /* - * HACK: This fixes the vm flags. - * struct drm_display_mode does not contain the VSYNC/HSYNC/DE flags - * and they get lost when converting back and forth between - * struct drm_display_mode and struct videomode. The hack below - * goes and fetches the missing flags from the panel drivers. - * - * Correct solution would be to use DRM's bus-flags, but that's not - * easily possible before the omapdrm's panel/encoder driver model - * has been changed to the DRM model. + * DSI might not call this, since the supplied mode is not a + * valid DISPC mode. DSI will calculate and configure the + * proper DISPC mode later. */ + if (omap_crtc->pipe->output->type != OMAP_DISPLAY_TYPE_DSI) { + r = dispc_mgr_check_timings(priv->dispc, + omap_crtc->channel, + &vm); + if (r) + return r; + } + + /* Check for bandwidth limit */ + if (priv->max_bandwidth) { + /* + * Estimation for the bandwidth need of a given mode with one + * full screen plane: + * bandwidth = resolution * 32bpp * (pclk / (vtotal * htotal)) + * ^^ Refresh rate ^^ + * + * The interlaced mode is taken into account by using the + * pixelclock in the calculation. + * + * The equation is rearranged for 64bit arithmetic. + */ + uint64_t bandwidth = mode->clock * 1000; + unsigned int bpp = 4; + + bandwidth = bandwidth * mode->hdisplay * mode->vdisplay * bpp; + bandwidth = div_u64(bandwidth, mode->htotal * mode->vtotal); + + /* + * Reject modes which would need more bandwidth if used with one + * full resolution plane (most common use case). + */ + if (priv->max_bandwidth < bandwidth) + return MODE_BAD; + } - for (i = 0; i < priv->num_encoders; ++i) { - struct drm_encoder *encoder = priv->encoders[i]; + return MODE_OK; +} + +static void omap_crtc_mode_set_nofb(struct drm_crtc *crtc) +{ + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + struct drm_display_mode *mode = &crtc->state->adjusted_mode; - if (encoder->crtc == crtc) { - struct omap_dss_device *dssdev; + DBG("%s: set mode: " DRM_MODE_FMT, + omap_crtc->name, DRM_MODE_ARG(mode)); - dssdev = omap_encoder_get_dssdev(encoder); + drm_display_mode_to_videomode(mode, &omap_crtc->vm); +} - if (dssdev) { - struct videomode vm = {0}; +static bool omap_crtc_is_manually_updated(struct drm_crtc *crtc) +{ + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + struct omap_dss_device *dssdev = omap_crtc->pipe->output; - dssdev->driver->get_timings(dssdev, &vm); + if (!dssdev || !dssdev->dsi_ops || !dssdev->dsi_ops->is_video_mode) + return false; - omap_crtc->vm.flags |= vm.flags & flags_mask; - } + if (dssdev->dsi_ops->is_video_mode(dssdev)) + return false; - break; - } - } + DBG("detected manually updated display!"); + return true; } static int omap_crtc_atomic_check(struct drm_crtc *crtc, - struct drm_crtc_state *state) + struct drm_atomic_state *state) { - if (state->color_mgmt_changed && state->gamma_lut) { - uint length = state->gamma_lut->length / + struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, + crtc); + struct drm_plane_state *pri_state; + + if (crtc_state->color_mgmt_changed && crtc_state->degamma_lut) { + unsigned int length = crtc_state->degamma_lut->length / sizeof(struct drm_color_lut); if (length < 2) return -EINVAL; } + pri_state = drm_atomic_get_new_plane_state(state, + crtc->primary); + if (pri_state) { + struct omap_crtc_state *omap_crtc_state = + to_omap_crtc_state(crtc_state); + + /* Mirror new values for zpos and rotation in omap_crtc_state */ + omap_crtc_state->zpos = pri_state->zpos; + omap_crtc_state->rotation = pri_state->rotation; + + /* Check if this CRTC is for a manually updated display */ + omap_crtc_state->manually_updated = omap_crtc_is_manually_updated(crtc); + } + return 0; } static void omap_crtc_atomic_begin(struct drm_crtc *crtc, - struct drm_crtc_state *old_crtc_state) + struct drm_atomic_state *state) { } static void omap_crtc_atomic_flush(struct drm_crtc *crtc, - struct drm_crtc_state *old_crtc_state) + struct drm_atomic_state *state) { struct omap_drm_private *priv = crtc->dev->dev_private; struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + struct omap_crtc_state *omap_crtc_state = to_omap_crtc_state(crtc->state); int ret; if (crtc->state->color_mgmt_changed) { struct drm_color_lut *lut = NULL; - uint length = 0; + unsigned int length = 0; - if (crtc->state->gamma_lut) { + if (crtc->state->degamma_lut) { lut = (struct drm_color_lut *) - crtc->state->gamma_lut->data; - length = crtc->state->gamma_lut->length / + crtc->state->degamma_lut->data; + length = crtc->state->degamma_lut->length / sizeof(*lut); } - priv->dispc_ops->mgr_set_gamma(omap_crtc->channel, lut, length); + dispc_mgr_set_gamma(priv->dispc, omap_crtc->channel, + lut, length); } omap_crtc_write_crtc_properties(crtc); @@ -487,77 +643,113 @@ static void omap_crtc_atomic_flush(struct drm_crtc *crtc, DBG("%s: GO", omap_crtc->name); + if (omap_crtc_state->manually_updated) { + /* send new image for page flips and modeset changes */ + spin_lock_irq(&crtc->dev->event_lock); + omap_crtc_flush(crtc); + omap_crtc_arm_event(crtc); + spin_unlock_irq(&crtc->dev->event_lock); + return; + } + ret = drm_crtc_vblank_get(crtc); WARN_ON(ret != 0); spin_lock_irq(&crtc->dev->event_lock); - priv->dispc_ops->mgr_go(omap_crtc->channel); + dispc_mgr_go(priv->dispc, omap_crtc->channel); omap_crtc_arm_event(crtc); spin_unlock_irq(&crtc->dev->event_lock); } -static bool omap_crtc_is_plane_prop(struct drm_crtc *crtc, - struct drm_property *property) +static int omap_crtc_atomic_set_property(struct drm_crtc *crtc, + struct drm_crtc_state *state, + struct drm_property *property, + u64 val) { - struct drm_device *dev = crtc->dev; - struct omap_drm_private *priv = dev->dev_private; + struct omap_drm_private *priv = crtc->dev->dev_private; + struct drm_plane_state *plane_state; + + /* + * Delegate property set to the primary plane. Get the plane state and + * set the property directly, the shadow copy will be assigned in the + * omap_crtc_atomic_check callback. This way updates to plane state will + * always be mirrored in the crtc state correctly. + */ + plane_state = drm_atomic_get_plane_state(state->state, crtc->primary); + if (IS_ERR(plane_state)) + return PTR_ERR(plane_state); + + if (property == crtc->primary->rotation_property) + plane_state->rotation = val; + else if (property == priv->zorder_prop) + plane_state->zpos = val; + else + return -EINVAL; - return property == priv->zorder_prop || - property == crtc->primary->rotation_property; + return 0; } -static int omap_crtc_atomic_set_property(struct drm_crtc *crtc, - struct drm_crtc_state *state, +static int omap_crtc_atomic_get_property(struct drm_crtc *crtc, + const struct drm_crtc_state *state, struct drm_property *property, - uint64_t val) + u64 *val) { - if (omap_crtc_is_plane_prop(crtc, property)) { - struct drm_plane_state *plane_state; - struct drm_plane *plane = crtc->primary; + struct omap_drm_private *priv = crtc->dev->dev_private; + struct omap_crtc_state *omap_state = to_omap_crtc_state(state); - /* - * Delegate property set to the primary plane. Get the plane - * state and set the property directly. - */ + if (property == crtc->primary->rotation_property) + *val = omap_state->rotation; + else if (property == priv->zorder_prop) + *val = omap_state->zpos; + else + return -EINVAL; - plane_state = drm_atomic_get_plane_state(state->state, plane); - if (IS_ERR(plane_state)) - return PTR_ERR(plane_state); + return 0; +} - return drm_atomic_plane_set_property(plane, plane_state, - property, val); - } +static void omap_crtc_reset(struct drm_crtc *crtc) +{ + struct omap_crtc_state *state; + + if (crtc->state) + __drm_atomic_helper_crtc_destroy_state(crtc->state); + + kfree(crtc->state); - return -EINVAL; + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (state) + __drm_atomic_helper_crtc_reset(crtc, &state->base); } -static int omap_crtc_atomic_get_property(struct drm_crtc *crtc, - const struct drm_crtc_state *state, - struct drm_property *property, - uint64_t *val) +static struct drm_crtc_state * +omap_crtc_duplicate_state(struct drm_crtc *crtc) { - if (omap_crtc_is_plane_prop(crtc, property)) { - /* - * Delegate property get to the primary plane. The - * drm_atomic_plane_get_property() function isn't exported, but - * can be called through drm_object_property_get_value() as that - * will call drm_atomic_get_property() for atomic drivers. - */ - return drm_object_property_get_value(&crtc->primary->base, - property, val); - } + struct omap_crtc_state *state, *current_state; - return -EINVAL; + if (WARN_ON(!crtc->state)) + return NULL; + + current_state = to_omap_crtc_state(crtc->state); + + state = kmalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return NULL; + + __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base); + + state->zpos = current_state->zpos; + state->rotation = current_state->rotation; + state->manually_updated = current_state->manually_updated; + + return &state->base; } static const struct drm_crtc_funcs omap_crtc_funcs = { - .reset = drm_atomic_helper_crtc_reset, + .reset = omap_crtc_reset, .set_config = drm_atomic_helper_set_config, .destroy = omap_crtc_destroy, .page_flip = drm_atomic_helper_page_flip, - .gamma_set = drm_atomic_helper_legacy_gamma_set, - .set_property = drm_atomic_helper_crtc_set_property, - .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_duplicate_state = omap_crtc_duplicate_state, .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, .atomic_set_property = omap_crtc_atomic_set_property, .atomic_get_property = omap_crtc_atomic_get_property, @@ -567,11 +759,12 @@ static const struct drm_crtc_funcs omap_crtc_funcs = { static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = { .mode_set_nofb = omap_crtc_mode_set_nofb, - .disable = omap_crtc_disable, - .enable = omap_crtc_enable, .atomic_check = omap_crtc_atomic_check, .atomic_begin = omap_crtc_atomic_begin, .atomic_flush = omap_crtc_atomic_flush, + .atomic_enable = omap_crtc_atomic_enable, + .atomic_disable = omap_crtc_atomic_disable, + .mode_valid = omap_crtc_mode_valid, }; /* ----------------------------------------------------------------------------- @@ -585,39 +778,21 @@ static const char *channel_names[] = { [OMAP_DSS_CHANNEL_LCD3] = "lcd3", }; -void omap_crtc_pre_init(void) -{ - memset(omap_crtcs, 0, sizeof(omap_crtcs)); - - dss_install_mgr_ops(&mgr_ops); -} - -void omap_crtc_pre_uninit(void) -{ - dss_uninstall_mgr_ops(); -} - /* initialize crtc */ struct drm_crtc *omap_crtc_init(struct drm_device *dev, - struct drm_plane *plane, struct omap_dss_device *dssdev) + struct omap_drm_pipeline *pipe, + struct drm_plane *plane) { struct omap_drm_private *priv = dev->dev_private; struct drm_crtc *crtc = NULL; struct omap_crtc *omap_crtc; enum omap_channel channel; - struct omap_dss_device *out; int ret; - out = omapdss_find_output_from_display(dssdev); - channel = out->dispc_channel; - omap_dss_put_device(out); + channel = pipe->output->dispc_channel; DBG("%s", channel_names[channel]); - /* Multiple displays on same channel is not allowed */ - if (WARN_ON(omap_crtcs[channel] != NULL)) - return ERR_PTR(-EINVAL); - omap_crtc = kzalloc(sizeof(*omap_crtc), GFP_KERNEL); if (!omap_crtc) return ERR_PTR(-ENOMEM); @@ -626,14 +801,28 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev, init_waitqueue_head(&omap_crtc->pending_wait); + omap_crtc->pipe = pipe; omap_crtc->channel = channel; omap_crtc->name = channel_names[channel]; + /* + * We want to refresh manually updated displays from dirty callback, + * which is called quite often (e.g. for each drawn line). This will + * be used to do the display update asynchronously to avoid blocking + * the rendering process and merges multiple dirty calls into one + * update if they arrive very fast. We also call this function for + * atomic display updates (e.g. for page flips), which means we do + * not need extra locking. Atomic updates should be synchronous, but + * need to wait for the framedone interrupt anyways. + */ + INIT_DELAYED_WORK(&omap_crtc->update_work, + omap_crtc_manual_display_update); + ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL, &omap_crtc_funcs, NULL); if (ret < 0) { dev_err(dev->dev, "%s(): could not init crtc for: %s\n", - __func__, dssdev->name); + __func__, pipe->output->name); kfree(omap_crtc); return ERR_PTR(ret); } @@ -645,18 +834,16 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev, * OMAP_DSS_CHANNEL_DIGIT. X server assumes 256 element gamma * tables so lets use that. Size of HW gamma table can be * extracted with dispc_mgr_gamma_size(). If it returns 0 - * gamma table is not supprted. + * gamma table is not supported. */ - if (priv->dispc_ops->mgr_gamma_size(channel)) { - uint gamma_lut_size = 256; + if (dispc_mgr_gamma_size(priv->dispc, channel)) { + unsigned int gamma_lut_size = 256; - drm_crtc_enable_color_mgmt(crtc, 0, false, gamma_lut_size); + drm_crtc_enable_color_mgmt(crtc, gamma_lut_size, true, 0); drm_mode_crtc_set_gamma_size(crtc, gamma_lut_size); } omap_plane_install_properties(crtc->primary, &crtc->base); - omap_crtcs[channel] = omap_crtc; - return crtc; } diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.h b/drivers/gpu/drm/omapdrm/omap_crtc.h new file mode 100644 index 000000000000..a8b9cbee86e0 --- /dev/null +++ b/drivers/gpu/drm/omapdrm/omap_crtc.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * omap_crtc.h -- OMAP DRM CRTC + * + * Copyright (C) 2011 Texas Instruments + * Author: Rob Clark <rob@ti.com> + */ + +#ifndef __OMAPDRM_CRTC_H__ +#define __OMAPDRM_CRTC_H__ + +#include <linux/types.h> + +enum omap_channel; + +struct drm_crtc; +struct drm_device; +struct drm_plane; +struct omap_drm_pipeline; +struct omap_dss_device; +struct videomode; + +struct videomode *omap_crtc_timings(struct drm_crtc *crtc); +enum omap_channel omap_crtc_channel(struct drm_crtc *crtc); +struct drm_crtc *omap_crtc_init(struct drm_device *dev, + struct omap_drm_pipeline *pipe, + struct drm_plane *plane); +int omap_crtc_wait_pending(struct drm_crtc *crtc); +void omap_crtc_error_irq(struct drm_crtc *crtc, u32 irqstatus); +void omap_crtc_vblank_irq(struct drm_crtc *crtc); +void omap_crtc_framedone_irq(struct drm_crtc *crtc, uint32_t irqstatus); +void omap_crtc_flush(struct drm_crtc *crtc); + +#endif /* __OMAPDRM_CRTC_H__ */ diff --git a/drivers/gpu/drm/omapdrm/omap_debugfs.c b/drivers/gpu/drm/omapdrm/omap_debugfs.c index 19b716745623..9edc1b3f9f95 100644 --- a/drivers/gpu/drm/omapdrm/omap_debugfs.c +++ b/drivers/gpu/drm/omapdrm/omap_debugfs.c @@ -1,26 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0-only /* - * drivers/gpu/drm/omapdrm/omap_debugfs.c - * - * Copyright (C) 2011 Texas Instruments + * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/ * Author: Rob Clark <rob.clark@linaro.org> - * - * 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/seq_file.h> #include <drm/drm_crtc.h> +#include <drm/drm_debugfs.h> +#include <drm/drm_file.h> #include <drm/drm_fb_helper.h> +#include <drm/drm_framebuffer.h> +#include <drm/drm_print.h> #include "omap_drv.h" #include "omap_dmm_tiler.h" @@ -32,16 +23,11 @@ static int gem_show(struct seq_file *m, void *arg) struct drm_info_node *node = (struct drm_info_node *) m->private; struct drm_device *dev = node->minor->dev; struct omap_drm_private *priv = dev->dev_private; - int ret; - - ret = mutex_lock_interruptible(&dev->struct_mutex); - if (ret) - return ret; seq_printf(m, "All Objects:\n"); + mutex_lock(&priv->list_lock); omap_gem_describe_objects(&priv->obj_list, m); - - mutex_unlock(&dev->struct_mutex); + mutex_unlock(&priv->list_lock); return 0; } @@ -62,15 +48,15 @@ static int fb_show(struct seq_file *m, void *arg) { struct drm_info_node *node = (struct drm_info_node *) m->private; struct drm_device *dev = node->minor->dev; - struct omap_drm_private *priv = dev->dev_private; + struct drm_fb_helper *helper = dev->fb_helper; struct drm_framebuffer *fb; seq_printf(m, "fbcon "); - omap_framebuffer_describe(priv->fbdev->fb, m); + omap_framebuffer_describe(helper->fb, m); mutex_lock(&dev->mode_config.fb_lock); list_for_each_entry(fb, &dev->mode_config.fb_list, head) { - if (fb == priv->fbdev->fb) + if (fb == helper->fb) continue; seq_printf(m, "user "); @@ -96,31 +82,16 @@ static struct drm_info_list omap_dmm_debugfs_list[] = { {"tiler_map", tiler_map_show, 0}, }; -int omap_debugfs_init(struct drm_minor *minor) +void omap_debugfs_init(struct drm_minor *minor) { - struct drm_device *dev = minor->dev; - int ret; - - ret = drm_debugfs_create_files(omap_debugfs_list, - ARRAY_SIZE(omap_debugfs_list), - minor->debugfs_root, minor); - - if (ret) { - dev_err(dev->dev, "could not install omap_debugfs_list\n"); - return ret; - } + drm_debugfs_create_files(omap_debugfs_list, + ARRAY_SIZE(omap_debugfs_list), + minor->debugfs_root, minor); if (dmm_is_available()) - ret = drm_debugfs_create_files(omap_dmm_debugfs_list, - ARRAY_SIZE(omap_dmm_debugfs_list), - minor->debugfs_root, minor); - - if (ret) { - dev_err(dev->dev, "could not install omap_dmm_debugfs_list\n"); - return ret; - } - - return ret; + drm_debugfs_create_files(omap_dmm_debugfs_list, + ARRAY_SIZE(omap_dmm_debugfs_list), + minor->debugfs_root, minor); } #endif diff --git a/drivers/gpu/drm/omapdrm/omap_dmm_priv.h b/drivers/gpu/drm/omapdrm/omap_dmm_priv.h index 9f32a83ca507..2288ed56f23d 100644 --- a/drivers/gpu/drm/omapdrm/omap_dmm_priv.h +++ b/drivers/gpu/drm/omapdrm/omap_dmm_priv.h @@ -1,18 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* - * - * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/ * Author: Rob Clark <rob@ti.com> * Andy Gross <andy.gross@ti.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ + #ifndef OMAP_DMM_PRIV_H #define OMAP_DMM_PRIV_H @@ -59,12 +51,12 @@ #define DMM_IRQSTAT_ERR_UPD_DATA (1<<6) #define DMM_IRQSTAT_ERR_LUT_MISS (1<<7) -#define DMM_IRQSTAT_ERR_MASK (DMM_IRQ_STAT_ERR_INV_DSC | \ - DMM_IRQ_STAT_ERR_INV_DATA | \ - DMM_IRQ_STAT_ERR_UPD_AREA | \ - DMM_IRQ_STAT_ERR_UPD_CTRL | \ - DMM_IRQ_STAT_ERR_UPD_DATA | \ - DMM_IRQ_STAT_ERR_LUT_MISS) +#define DMM_IRQSTAT_ERR_MASK (DMM_IRQSTAT_ERR_INV_DSC | \ + DMM_IRQSTAT_ERR_INV_DATA | \ + DMM_IRQSTAT_ERR_UPD_AREA | \ + DMM_IRQSTAT_ERR_UPD_CTRL | \ + DMM_IRQSTAT_ERR_UPD_DATA | \ + DMM_IRQSTAT_ERR_LUT_MISS) #define DMM_PATSTATUS_READY (1<<0) #define DMM_PATSTATUS_VALID (1<<1) @@ -102,10 +94,10 @@ struct pat_ctrl { }; struct pat { - uint32_t next_pa; + u32 next_pa; struct pat_area area; struct pat_ctrl ctrl; - uint32_t data_pa; + u32 data_pa; }; #define DMM_FIXED_RETRY_COUNT 1000 @@ -129,7 +121,7 @@ struct dmm_txn { void *engine_handle; struct tcm *tcm; - uint8_t *current_va; + u8 *current_va; dma_addr_t current_pa; struct pat *last_pat; @@ -140,7 +132,7 @@ struct refill_engine { struct dmm *dmm; struct tcm *tcm; - uint8_t *refill_va; + u8 *refill_va; dma_addr_t refill_pa; /* only one trans per engine for now */ @@ -154,11 +146,12 @@ struct refill_engine { }; struct dmm_platform_data { - uint32_t cpu_cache_flags; + u32 cpu_cache_flags; }; struct dmm { struct device *dev; + dma_addr_t phys_base; void __iomem *base; int irq; @@ -189,6 +182,12 @@ struct dmm { struct list_head alloc_head; const struct dmm_platform_data *plat_data; + + bool dmm_workaround; + spinlock_t wa_lock; + u32 *wa_dma_data; + dma_addr_t wa_dma_handle; + struct dma_chan *wa_dma_chan; }; #endif diff --git a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c index 1dd3dafc59af..bbe427ab43c1 100644 --- a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c +++ b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c @@ -1,30 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * DMM IOMMU driver support functions for TI OMAP processors. * + * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/ * Author: Rob Clark <rob@ti.com> * Andy Gross <andy.gross@ti.com> - * - * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/completion.h> #include <linux/delay.h> #include <linux/dma-mapping.h> +#include <linux/dmaengine.h> #include <linux/errno.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/list.h> #include <linux/mm.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/platform_device.h> /* platform_device() */ #include <linux/sched.h> #include <linux/seq_file.h> @@ -33,6 +26,8 @@ #include <linux/vmalloc.h> #include <linux/wait.h> +#include <drm/drm_print.h> + #include "omap_dmm_tiler.h" #include "omap_dmm_priv.h" @@ -59,11 +54,11 @@ static DEFINE_SPINLOCK(list_lock); } static const struct { - uint32_t x_shft; /* unused X-bits (as part of bpp) */ - uint32_t y_shft; /* unused Y-bits (as part of bpp) */ - uint32_t cpp; /* bytes/chars per pixel */ - uint32_t slot_w; /* width of each slot (in pixels) */ - uint32_t slot_h; /* height of each slot (in pixels) */ + u32 x_shft; /* unused X-bits (as part of bpp) */ + u32 y_shft; /* unused Y-bits (as part of bpp) */ + u32 cpp; /* bytes/chars per pixel */ + u32 slot_w; /* width of each slot (in pixels) */ + u32 slot_h; /* height of each slot (in pixels) */ } geom[TILFMT_NFORMATS] = { [TILFMT_8BIT] = GEOM(0, 0, 1), [TILFMT_16BIT] = GEOM(0, 1, 2), @@ -73,21 +68,143 @@ static const struct { /* lookup table for registers w/ per-engine instances */ -static const uint32_t reg[][4] = { +static const u32 reg[][4] = { [PAT_STATUS] = {DMM_PAT_STATUS__0, DMM_PAT_STATUS__1, DMM_PAT_STATUS__2, DMM_PAT_STATUS__3}, [PAT_DESCR] = {DMM_PAT_DESCR__0, DMM_PAT_DESCR__1, DMM_PAT_DESCR__2, DMM_PAT_DESCR__3}, }; +static int dmm_dma_copy(struct dmm *dmm, dma_addr_t src, dma_addr_t dst) +{ + struct dma_async_tx_descriptor *tx; + enum dma_status status; + dma_cookie_t cookie; + + tx = dmaengine_prep_dma_memcpy(dmm->wa_dma_chan, dst, src, 4, 0); + if (!tx) { + dev_err(dmm->dev, "Failed to prepare DMA memcpy\n"); + return -EIO; + } + + cookie = tx->tx_submit(tx); + if (dma_submit_error(cookie)) { + dev_err(dmm->dev, "Failed to do DMA tx_submit\n"); + return -EIO; + } + + status = dma_sync_wait(dmm->wa_dma_chan, cookie); + if (status != DMA_COMPLETE) + dev_err(dmm->dev, "i878 wa DMA copy failure\n"); + + dmaengine_terminate_all(dmm->wa_dma_chan); + return 0; +} + +static u32 dmm_read_wa(struct dmm *dmm, u32 reg) +{ + dma_addr_t src, dst; + int r; + + src = dmm->phys_base + reg; + dst = dmm->wa_dma_handle; + + r = dmm_dma_copy(dmm, src, dst); + if (r) { + dev_err(dmm->dev, "sDMA read transfer timeout\n"); + return readl(dmm->base + reg); + } + + /* + * As per i878 workaround, the DMA is used to access the DMM registers. + * Make sure that the readl is not moved by the compiler or the CPU + * earlier than the DMA finished writing the value to memory. + */ + rmb(); + return readl((__iomem void *)dmm->wa_dma_data); +} + +static void dmm_write_wa(struct dmm *dmm, u32 val, u32 reg) +{ + dma_addr_t src, dst; + int r; + + writel(val, (__iomem void *)dmm->wa_dma_data); + /* + * As per i878 workaround, the DMA is used to access the DMM registers. + * Make sure that the writel is not moved by the compiler or the CPU, so + * the data will be in place before we start the DMA to do the actual + * register write. + */ + wmb(); + + src = dmm->wa_dma_handle; + dst = dmm->phys_base + reg; + + r = dmm_dma_copy(dmm, src, dst); + if (r) { + dev_err(dmm->dev, "sDMA write transfer timeout\n"); + writel(val, dmm->base + reg); + } +} + static u32 dmm_read(struct dmm *dmm, u32 reg) { - return readl(dmm->base + reg); + if (dmm->dmm_workaround) { + u32 v; + unsigned long flags; + + spin_lock_irqsave(&dmm->wa_lock, flags); + v = dmm_read_wa(dmm, reg); + spin_unlock_irqrestore(&dmm->wa_lock, flags); + + return v; + } else { + return readl(dmm->base + reg); + } } static void dmm_write(struct dmm *dmm, u32 val, u32 reg) { - writel(val, dmm->base + reg); + if (dmm->dmm_workaround) { + unsigned long flags; + + spin_lock_irqsave(&dmm->wa_lock, flags); + dmm_write_wa(dmm, val, reg); + spin_unlock_irqrestore(&dmm->wa_lock, flags); + } else { + writel(val, dmm->base + reg); + } +} + +static int dmm_workaround_init(struct dmm *dmm) +{ + dma_cap_mask_t mask; + + spin_lock_init(&dmm->wa_lock); + + dmm->wa_dma_data = dma_alloc_coherent(dmm->dev, sizeof(u32), + &dmm->wa_dma_handle, GFP_KERNEL); + if (!dmm->wa_dma_data) + return -ENOMEM; + + dma_cap_zero(mask); + dma_cap_set(DMA_MEMCPY, mask); + + dmm->wa_dma_chan = dma_request_channel(mask, NULL, NULL); + if (!dmm->wa_dma_chan) { + dma_free_coherent(dmm->dev, 4, dmm->wa_dma_data, dmm->wa_dma_handle); + return -ENODEV; + } + + return 0; +} + +static void dmm_workaround_uninit(struct dmm *dmm) +{ + dma_release_channel(dmm->wa_dma_chan); + + dma_free_coherent(dmm->dev, 4, dmm->wa_dma_data, dmm->wa_dma_handle); } /* simple allocator to grab next 16 byte aligned memory from txn */ @@ -112,23 +229,31 @@ static void *alloc_dma(struct dmm_txn *txn, size_t sz, dma_addr_t *pa) } /* check status and spin until wait_mask comes true */ -static int wait_status(struct refill_engine *engine, uint32_t wait_mask) +static int wait_status(struct refill_engine *engine, u32 wait_mask) { struct dmm *dmm = engine->dmm; - uint32_t r = 0, err, i; + u32 r = 0, err, i; i = DMM_FIXED_RETRY_COUNT; while (true) { r = dmm_read(dmm, reg[PAT_STATUS][engine->id]); err = r & DMM_PATSTATUS_ERR; - if (err) + if (err) { + dev_err(dmm->dev, + "%s: error (engine%d). PAT_STATUS: 0x%08x\n", + __func__, engine->id, r); return -EFAULT; + } if ((r & wait_mask) == wait_mask) break; - if (--i == 0) + if (--i == 0) { + dev_err(dmm->dev, + "%s: timeout (engine%d). PAT_STATUS: 0x%08x\n", + __func__, engine->id, r); return -ETIMEDOUT; + } udelay(1); } @@ -151,13 +276,18 @@ static void release_engine(struct refill_engine *engine) static irqreturn_t omap_dmm_irq_handler(int irq, void *arg) { struct dmm *dmm = arg; - uint32_t status = dmm_read(dmm, DMM_PAT_IRQSTATUS); + u32 status = dmm_read(dmm, DMM_PAT_IRQSTATUS); int i; /* ack IRQ */ dmm_write(dmm, status, DMM_PAT_IRQSTATUS); for (i = 0; i < dmm->num_engines; i++) { + if (status & DMM_IRQSTAT_ERR_MASK) + dev_err(dmm->dev, + "irq error(engine%d): IRQSTAT 0x%02x\n", + i, status & 0xff); + if (status & DMM_IRQSTAT_LST) { if (dmm->engines[i].async) release_engine(&dmm->engines[i]); @@ -171,7 +301,7 @@ static irqreturn_t omap_dmm_irq_handler(int irq, void *arg) return IRQ_HANDLED; } -/** +/* * Get a handle for a DMM transaction */ static struct dmm_txn *dmm_txn_init(struct dmm *dmm, struct tcm *tcm) @@ -209,15 +339,15 @@ static struct dmm_txn *dmm_txn_init(struct dmm *dmm, struct tcm *tcm) return txn; } -/** +/* * Add region to DMM transaction. If pages or pages[i] is NULL, then the * corresponding slot is cleared (ie. dummy_pa is programmed) */ static void dmm_txn_append(struct dmm_txn *txn, struct pat_area *area, - struct page **pages, uint32_t npages, uint32_t roll) + struct page **pages, u32 npages, u32 roll) { dma_addr_t pat_pa = 0, data_pa = 0; - uint32_t *data; + u32 *data; struct pat *pat; struct refill_engine *engine = txn->engine_handle; int columns = (1 + area->x1 - area->x0); @@ -227,7 +357,7 @@ static void dmm_txn_append(struct dmm_txn *txn, struct pat_area *area, pat = alloc_dma(txn, sizeof(*pat), &pat_pa); if (txn->last_pat) - txn->last_pat->next_pa = (uint32_t)pat_pa; + txn->last_pat->next_pa = (u32)pat_pa; pat->area = *area; @@ -257,7 +387,7 @@ static void dmm_txn_append(struct dmm_txn *txn, struct pat_area *area, return; } -/** +/* * Commit the DMM transaction. */ static int dmm_txn_commit(struct dmm_txn *txn, bool wait) @@ -273,6 +403,17 @@ static int dmm_txn_commit(struct dmm_txn *txn, bool wait) } txn->last_pat->next_pa = 0; + /* ensure that the written descriptors are visible to DMM */ + wmb(); + + /* + * NOTE: the wmb() above should be enough, but there seems to be a bug + * in OMAP's memory barrier implementation, which in some rare cases may + * cause the writes not to be observable after wmb(). + */ + + /* read back to ensure the data is in RAM */ + readl((__iomem void *)&txn->last_pat->next_pa); /* write to PAT_DESCR to clear out any pending transaction */ dmm_write(dmm, 0x0, reg[PAT_DESCR][engine->id]); @@ -298,7 +439,12 @@ static int dmm_txn_commit(struct dmm_txn *txn, bool wait) msecs_to_jiffies(100))) { dev_err(dmm->dev, "timed out waiting for done\n"); ret = -ETIMEDOUT; + goto cleanup; } + + /* Check the engine status before continue */ + ret = wait_status(engine, DMM_PATSTATUS_READY | + DMM_PATSTATUS_VALID | DMM_PATSTATUS_DONE); } cleanup: @@ -313,7 +459,7 @@ cleanup: * DMM programming */ static int fill(struct tcm_area *area, struct page **pages, - uint32_t npages, uint32_t roll, bool wait) + u32 npages, u32 roll, bool wait) { int ret = 0; struct tcm_area slice, area_s; @@ -361,7 +507,7 @@ static int fill(struct tcm_area *area, struct page **pages, /* note: slots for which pages[i] == NULL are filled w/ dummy page */ int tiler_pin(struct tiler_block *block, struct page **pages, - uint32_t npages, uint32_t roll, bool wait) + u32 npages, u32 roll, bool wait) { int ret; @@ -381,15 +527,19 @@ int tiler_unpin(struct tiler_block *block) /* * Reserve/release */ -struct tiler_block *tiler_reserve_2d(enum tiler_fmt fmt, uint16_t w, - uint16_t h, uint16_t align) +struct tiler_block *tiler_reserve_2d(enum tiler_fmt fmt, u16 w, + u16 h, u16 align) { - struct tiler_block *block = kzalloc(sizeof(*block), GFP_KERNEL); + struct tiler_block *block; u32 min_align = 128; int ret; unsigned long flags; u32 slot_bytes; + block = kzalloc(sizeof(*block), GFP_KERNEL); + if (!block) + return ERR_PTR(-ENOMEM); + BUG_ON(!validfmt(fmt)); /* convert width/height to slots */ @@ -525,8 +675,8 @@ dma_addr_t tiler_ssptr(struct tiler_block *block) block->area.p0.y * geom[block->fmt].slot_h); } -dma_addr_t tiler_tsptr(struct tiler_block *block, uint32_t orient, - uint32_t x, uint32_t y) +dma_addr_t tiler_tsptr(struct tiler_block *block, u32 orient, + u32 x, u32 y) { struct tcm_pt *p = &block->area.p0; BUG_ON(!validfmt(block->fmt)); @@ -536,14 +686,14 @@ dma_addr_t tiler_tsptr(struct tiler_block *block, uint32_t orient, (p->y * geom[block->fmt].slot_h) + y); } -void tiler_align(enum tiler_fmt fmt, uint16_t *w, uint16_t *h) +void tiler_align(enum tiler_fmt fmt, u16 *w, u16 *h) { BUG_ON(!validfmt(fmt)); *w = round_up(*w, geom[fmt].slot_w); *h = round_up(*h, geom[fmt].slot_h); } -uint32_t tiler_stride(enum tiler_fmt fmt, uint32_t orient) +u32 tiler_stride(enum tiler_fmt fmt, u32 orient) { BUG_ON(!validfmt(fmt)); @@ -553,19 +703,19 @@ uint32_t tiler_stride(enum tiler_fmt fmt, uint32_t orient) return 1 << (CONT_WIDTH_BITS + geom[fmt].y_shft); } -size_t tiler_size(enum tiler_fmt fmt, uint16_t w, uint16_t h) +size_t tiler_size(enum tiler_fmt fmt, u16 w, u16 h) { tiler_align(fmt, &w, &h); return geom[fmt].cpp * w * h; } -size_t tiler_vsize(enum tiler_fmt fmt, uint16_t w, uint16_t h) +size_t tiler_vsize(enum tiler_fmt fmt, u16 w, u16 h) { BUG_ON(!validfmt(fmt)); return round_up(geom[fmt].cpp * w, PAGE_SIZE) * h; } -uint32_t tiler_get_cpu_cache_flags(void) +u32 tiler_get_cpu_cache_flags(void) { return omap_dmm->plat_data->cpu_cache_flags; } @@ -575,13 +725,17 @@ bool dmm_is_available(void) return omap_dmm ? true : false; } -static int omap_dmm_remove(struct platform_device *dev) +static void omap_dmm_remove(struct platform_device *dev) { struct tiler_block *block, *_block; int i; unsigned long flags; if (omap_dmm) { + /* Disable all enabled interrupts */ + dmm_write(omap_dmm, 0x7e7e7e7e, DMM_PAT_IRQENABLE_CLR); + free_irq(omap_dmm->irq, omap_dmm); + /* free all area regions */ spin_lock_irqsave(&list_lock, flags); list_for_each_entry_safe(block, _block, &omap_dmm->alloc_head, @@ -604,15 +758,13 @@ static int omap_dmm_remove(struct platform_device *dev) if (omap_dmm->dummy_page) __free_page(omap_dmm->dummy_page); - if (omap_dmm->irq > 0) - free_irq(omap_dmm->irq, omap_dmm); + if (omap_dmm->dmm_workaround) + dmm_workaround_uninit(omap_dmm); iounmap(omap_dmm->base); kfree(omap_dmm); omap_dmm = NULL; } - - return 0; } static int omap_dmm_probe(struct platform_device *dev) @@ -638,7 +790,8 @@ static int omap_dmm_probe(struct platform_device *dev) match = of_match_node(dmm_of_match, dev->dev.of_node); if (!match) { dev_err(&dev->dev, "failed to find matching device node\n"); - return -ENODEV; + ret = -ENODEV; + goto fail; } omap_dmm->plat_data = match->data; @@ -651,6 +804,7 @@ static int omap_dmm_probe(struct platform_device *dev) goto fail; } + omap_dmm->phys_base = mem->start; omap_dmm->base = ioremap(mem->start, SZ_2K); if (!omap_dmm->base) { @@ -659,13 +813,27 @@ static int omap_dmm_probe(struct platform_device *dev) } omap_dmm->irq = platform_get_irq(dev, 0); - if (omap_dmm->irq < 0) { - dev_err(&dev->dev, "failed to get IRQ resource\n"); + if (omap_dmm->irq < 0) goto fail; - } omap_dmm->dev = &dev->dev; + if (of_machine_is_compatible("ti,dra7")) { + /* + * DRA7 Errata i878 says that MPU should not be used to access + * RAM and DMM at the same time. As it's not possible to prevent + * MPU accessing RAM, we need to access DMM via a proxy. + */ + if (!dmm_workaround_init(omap_dmm)) { + omap_dmm->dmm_workaround = true; + dev_info(&dev->dev, + "workaround for errata i878 in use\n"); + } else { + dev_warn(&dev->dev, + "failed to initialize work-around for i878\n"); + } + } + hwinfo = dmm_read(omap_dmm, DMM_PAT_HWINFO); omap_dmm->num_engines = (hwinfo >> 24) & 0x1F; omap_dmm->num_lut = (hwinfo >> 16) & 0x1F; @@ -692,24 +860,6 @@ static int omap_dmm_probe(struct platform_device *dev) dmm_write(omap_dmm, 0x88888888, DMM_TILER_OR__0); dmm_write(omap_dmm, 0x88888888, DMM_TILER_OR__1); - ret = request_irq(omap_dmm->irq, omap_dmm_irq_handler, IRQF_SHARED, - "omap_dmm_irq_handler", omap_dmm); - - if (ret) { - dev_err(&dev->dev, "couldn't register IRQ %d, error %d\n", - omap_dmm->irq, ret); - omap_dmm->irq = -1; - goto fail; - } - - /* Enable all interrupts for each refill engine except - * ERR_LUT_MISS<n> (which is just advisory, and we don't care - * about because we want to be able to refill live scanout - * buffers for accelerated pan/scroll) and FILL_DSC<n> which - * we just generally don't care about. - */ - dmm_write(omap_dmm, 0x7e7e7e7e, DMM_PAT_IRQENABLE_SET); - omap_dmm->dummy_page = alloc_page(GFP_KERNEL | __GFP_DMA32); if (!omap_dmm->dummy_page) { dev_err(&dev->dev, "could not allocate dummy page\n"); @@ -730,6 +880,7 @@ static int omap_dmm_probe(struct platform_device *dev) &omap_dmm->refill_pa, GFP_KERNEL); if (!omap_dmm->refill_va) { dev_err(&dev->dev, "could not allocate refill memory\n"); + ret = -ENOMEM; goto fail; } @@ -801,6 +952,24 @@ static int omap_dmm_probe(struct platform_device *dev) .p1.y = omap_dmm->container_height - 1, }; + ret = request_irq(omap_dmm->irq, omap_dmm_irq_handler, IRQF_SHARED, + "omap_dmm_irq_handler", omap_dmm); + + if (ret) { + dev_err(&dev->dev, "couldn't register IRQ %d, error %d\n", + omap_dmm->irq, ret); + omap_dmm->irq = -1; + goto fail; + } + + /* Enable all interrupts for each refill engine except + * ERR_LUT_MISS<n> (which is just advisory, and we don't care + * about because we want to be able to refill live scanout + * buffers for accelerated pan/scroll) and FILL_DSC<n> which + * we just generally don't care about. + */ + dmm_write(omap_dmm, 0x7e7e7e7e, DMM_PAT_IRQENABLE_SET); + /* initialize all LUTs to dummy page entries */ for (i = 0; i < omap_dmm->num_lut; i++) { area.tcm = omap_dmm->tcm[i]; @@ -813,8 +982,7 @@ static int omap_dmm_probe(struct platform_device *dev) return 0; fail: - if (omap_dmm_remove(dev)) - dev_err(&dev->dev, "cleanup failed\n"); + omap_dmm_remove(dev); return ret; } @@ -918,8 +1086,8 @@ int tiler_map_show(struct seq_file *s, void *arg) h_adj = omap_dmm->container_height / ydiv; w_adj = omap_dmm->container_width / xdiv; - map = kmalloc(h_adj * sizeof(*map), GFP_KERNEL); - global_map = kmalloc((w_adj + 1) * h_adj, GFP_KERNEL); + map = kmalloc_array(h_adj, sizeof(*map), GFP_KERNEL); + global_map = kmalloc_array(w_adj + 1, h_adj, GFP_KERNEL); if (!map || !global_map) goto error; @@ -1046,7 +1214,6 @@ struct platform_driver omap_dmm_driver = { .probe = omap_dmm_probe, .remove = omap_dmm_remove, .driver = { - .owner = THIS_MODULE, .name = DMM_DRIVER_NAME, .of_match_table = of_match_ptr(dmm_of_match), .pm = &omap_dmm_pm_ops, diff --git a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.h b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.h index e83c78372db8..87a32b3cd3b0 100644 --- a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.h +++ b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.h @@ -1,17 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* - * - * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/ * Author: Rob Clark <rob@ti.com> * Andy Gross <andy.gross@ti.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef OMAP_DMM_TILER_H #define OMAP_DMM_TILER_H @@ -89,32 +80,32 @@ int tiler_map_show(struct seq_file *s, void *arg); /* pin/unpin */ int tiler_pin(struct tiler_block *block, struct page **pages, - uint32_t npages, uint32_t roll, bool wait); + u32 npages, u32 roll, bool wait); int tiler_unpin(struct tiler_block *block); /* reserve/release */ -struct tiler_block *tiler_reserve_2d(enum tiler_fmt fmt, uint16_t w, uint16_t h, - uint16_t align); +struct tiler_block *tiler_reserve_2d(enum tiler_fmt fmt, u16 w, u16 h, + u16 align); struct tiler_block *tiler_reserve_1d(size_t size); int tiler_release(struct tiler_block *block); /* utilities */ dma_addr_t tiler_ssptr(struct tiler_block *block); -dma_addr_t tiler_tsptr(struct tiler_block *block, uint32_t orient, - uint32_t x, uint32_t y); -uint32_t tiler_stride(enum tiler_fmt fmt, uint32_t orient); -size_t tiler_size(enum tiler_fmt fmt, uint16_t w, uint16_t h); -size_t tiler_vsize(enum tiler_fmt fmt, uint16_t w, uint16_t h); -void tiler_align(enum tiler_fmt fmt, uint16_t *w, uint16_t *h); -uint32_t tiler_get_cpu_cache_flags(void); +dma_addr_t tiler_tsptr(struct tiler_block *block, u32 orient, + u32 x, u32 y); +u32 tiler_stride(enum tiler_fmt fmt, u32 orient); +size_t tiler_size(enum tiler_fmt fmt, u16 w, u16 h); +size_t tiler_vsize(enum tiler_fmt fmt, u16 w, u16 h); +void tiler_align(enum tiler_fmt fmt, u16 *w, u16 *h); +u32 tiler_get_cpu_cache_flags(void); bool dmm_is_available(void); extern struct platform_driver omap_dmm_driver; /* GEM bo flags -> tiler fmt */ -static inline enum tiler_fmt gem2fmt(uint32_t flags) +static inline enum tiler_fmt gem2fmt(u32 flags) { - switch (flags & OMAP_BO_TILED) { + switch (flags & OMAP_BO_TILED_MASK) { case OMAP_BO_TILED_8: return TILFMT_8BIT; case OMAP_BO_TILED_16: diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 022029ea6972..1b96343226a5 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -1,35 +1,34 @@ +// SPDX-License-Identifier: GPL-2.0-only /* - * drivers/gpu/drm/omapdrm/omap_drv.c - * - * Copyright (C) 2011 Texas Instruments + * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/ * Author: Rob Clark <rob@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/dma-mapping.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/sort.h> #include <linux/sys_soc.h> #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> -#include <drm/drm_crtc_helper.h> -#include <drm/drm_fb_helper.h> +#include <drm/drm_bridge.h> +#include <drm/drm_bridge_connector.h> +#include <drm/drm_drv.h> +#include <drm/drm_file.h> +#include <drm/drm_ioctl.h> +#include <drm/drm_panel.h> +#include <drm/drm_prime.h> +#include <drm/drm_print.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_vblank.h> #include "omap_dmm_tiler.h" #include "omap_drv.h" +#include "omap_fbdev.h" #define DRIVER_NAME MODULE_NAME #define DRIVER_DESC "OMAP DRM" -#define DRIVER_DATE "20110917" #define DRIVER_MAJOR 1 #define DRIVER_MINOR 0 #define DRIVER_PATCHLEVEL 0 @@ -46,24 +45,16 @@ * devices */ -static void omap_fb_output_poll_changed(struct drm_device *dev) -{ - struct omap_drm_private *priv = dev->dev_private; - DBG("dev=%p", dev); - if (priv->fbdev) - drm_fb_helper_hotplug_event(priv->fbdev); -} - static void omap_atomic_wait_for_completion(struct drm_device *dev, struct drm_atomic_state *old_state) { - struct drm_crtc_state *old_crtc_state; + struct drm_crtc_state *new_crtc_state; struct drm_crtc *crtc; unsigned int i; int ret; - for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { - if (!crtc->state->enable) + for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) { + if (!new_crtc_state->active) continue; ret = omap_crtc_wait_pending(crtc); @@ -79,28 +70,41 @@ static void omap_atomic_commit_tail(struct drm_atomic_state *old_state) struct drm_device *dev = old_state->dev; struct omap_drm_private *priv = dev->dev_private; - priv->dispc_ops->runtime_get(); + dispc_runtime_get(priv->dispc); /* Apply the atomic update. */ drm_atomic_helper_commit_modeset_disables(dev, old_state); - /* With the current dss dispc implementation we have to enable - * the new modeset before we can commit planes. The dispc ovl - * configuration relies on the video mode configuration been - * written into the HW when the ovl configuration is - * calculated. - * - * This approach is not ideal because after a mode change the - * plane update is executed only after the first vblank - * interrupt. The dispc implementation should be fixed so that - * it is able use uncommitted drm state information. - */ - drm_atomic_helper_commit_modeset_enables(dev, old_state); - omap_atomic_wait_for_completion(dev, old_state); - - drm_atomic_helper_commit_planes(dev, old_state, 0); - - drm_atomic_helper_commit_hw_done(old_state); + if (priv->omaprev != 0x3430) { + /* With the current dss dispc implementation we have to enable + * the new modeset before we can commit planes. The dispc ovl + * configuration relies on the video mode configuration been + * written into the HW when the ovl configuration is + * calculated. + * + * This approach is not ideal because after a mode change the + * plane update is executed only after the first vblank + * interrupt. The dispc implementation should be fixed so that + * it is able use uncommitted drm state information. + */ + drm_atomic_helper_commit_modeset_enables(dev, old_state); + omap_atomic_wait_for_completion(dev, old_state); + + drm_atomic_helper_commit_planes(dev, old_state, 0); + + drm_atomic_helper_commit_hw_done(old_state); + } else { + /* + * OMAP3 DSS seems to have issues with the work-around above, + * resulting in endless sync losts if a crtc is enabled without + * a plane. For now, skip the WA for OMAP3. + */ + drm_atomic_helper_commit_planes(dev, old_state, 0); + + drm_atomic_helper_commit_modeset_enables(dev, old_state); + + drm_atomic_helper_commit_hw_done(old_state); + } /* * Wait for completion of the page flips to ensure that old buffers @@ -110,7 +114,103 @@ static void omap_atomic_commit_tail(struct drm_atomic_state *old_state) drm_atomic_helper_cleanup_planes(dev, old_state); - priv->dispc_ops->runtime_put(); + dispc_runtime_put(priv->dispc); +} + +static int drm_atomic_state_normalized_zpos_cmp(const void *a, const void *b) +{ + const struct drm_plane_state *sa = *(struct drm_plane_state **)a; + const struct drm_plane_state *sb = *(struct drm_plane_state **)b; + + if (sa->normalized_zpos != sb->normalized_zpos) + return sa->normalized_zpos - sb->normalized_zpos; + else + return sa->plane->base.id - sb->plane->base.id; +} + +/* + * This replaces the drm_atomic_normalize_zpos to handle the dual overlay case. + * + * Since both halves need to be 'appear' side by side the zpos is + * recalculated when dealing with dual overlay cases so that the other + * planes zpos is consistent. + */ +static int omap_atomic_update_normalize_zpos(struct drm_device *dev, + struct drm_atomic_state *state) +{ + struct drm_crtc *crtc; + struct drm_crtc_state *old_state, *new_state; + struct drm_plane *plane; + int c, i, n, inc; + int total_planes = dev->mode_config.num_total_plane; + struct drm_plane_state **states; + int ret = 0; + + states = kmalloc_array(total_planes, sizeof(*states), GFP_KERNEL); + if (!states) + return -ENOMEM; + + for_each_oldnew_crtc_in_state(state, crtc, old_state, new_state, c) { + if (old_state->plane_mask == new_state->plane_mask && + !new_state->zpos_changed) + continue; + + /* Reset plane increment and index value for every crtc */ + n = 0; + + /* + * Normalization process might create new states for planes + * which normalized_zpos has to be recalculated. + */ + drm_for_each_plane_mask(plane, dev, new_state->plane_mask) { + struct drm_plane_state *plane_state = + drm_atomic_get_plane_state(new_state->state, + plane); + if (IS_ERR(plane_state)) { + ret = PTR_ERR(plane_state); + goto done; + } + states[n++] = plane_state; + } + + sort(states, n, sizeof(*states), + drm_atomic_state_normalized_zpos_cmp, NULL); + + for (i = 0, inc = 0; i < n; i++) { + plane = states[i]->plane; + + states[i]->normalized_zpos = i + inc; + DRM_DEBUG_ATOMIC("[PLANE:%d:%s] updated normalized zpos value %d\n", + plane->base.id, plane->name, + states[i]->normalized_zpos); + + if (is_omap_plane_dual_overlay(states[i])) + inc++; + } + new_state->zpos_changed = true; + } + +done: + kfree(states); + return ret; +} + +static int omap_atomic_check(struct drm_device *dev, + struct drm_atomic_state *state) +{ + int ret; + + ret = drm_atomic_helper_check(dev, state); + if (ret) + return ret; + + if (dev->mode_config.normalize_zpos) { + ret = omap_atomic_update_normalize_zpos(dev, state); + if (ret) + return ret; + } + + return 0; } static const struct drm_mode_config_helper_funcs omap_mode_config_helper_funcs = { @@ -119,76 +219,152 @@ static const struct drm_mode_config_helper_funcs omap_mode_config_helper_funcs = static const struct drm_mode_config_funcs omap_mode_config_funcs = { .fb_create = omap_framebuffer_create, - .output_poll_changed = omap_fb_output_poll_changed, - .atomic_check = drm_atomic_helper_check, + .atomic_check = omap_atomic_check, .atomic_commit = drm_atomic_helper_commit, }; -static int get_connector_type(struct omap_dss_device *dssdev) +/* Global/shared object state funcs */ + +/* + * This is a helper that returns the private state currently in operation. + * Note that this would return the "old_state" if called in the atomic check + * path, and the "new_state" after the atomic swap has been done. + */ +struct omap_global_state * +omap_get_existing_global_state(struct omap_drm_private *priv) { - switch (dssdev->type) { - case OMAP_DISPLAY_TYPE_HDMI: - return DRM_MODE_CONNECTOR_HDMIA; - case OMAP_DISPLAY_TYPE_DVI: - return DRM_MODE_CONNECTOR_DVID; - case OMAP_DISPLAY_TYPE_DSI: - return DRM_MODE_CONNECTOR_DSI; - case OMAP_DISPLAY_TYPE_DPI: - case OMAP_DISPLAY_TYPE_DBI: - return DRM_MODE_CONNECTOR_DPI; - case OMAP_DISPLAY_TYPE_VENC: - /* TODO: This could also be composite */ - return DRM_MODE_CONNECTOR_SVIDEO; - case OMAP_DISPLAY_TYPE_SDI: - return DRM_MODE_CONNECTOR_LVDS; - default: - return DRM_MODE_CONNECTOR_Unknown; - } + return to_omap_global_state(priv->glob_obj.state); } -static void omap_disconnect_dssdevs(void) +/* + * This acquires the modeset lock set aside for global state, creates + * a new duplicated private object state. + */ +struct omap_global_state *__must_check +omap_get_global_state(struct drm_atomic_state *s) { - struct omap_dss_device *dssdev = NULL; + struct omap_drm_private *priv = s->dev->dev_private; + struct drm_private_state *priv_state; - for_each_dss_dev(dssdev) - dssdev->driver->disconnect(dssdev); + priv_state = drm_atomic_get_private_obj_state(s, &priv->glob_obj); + if (IS_ERR(priv_state)) + return ERR_CAST(priv_state); + + return to_omap_global_state(priv_state); } -static int omap_connect_dssdevs(void) +static struct drm_private_state * +omap_global_duplicate_state(struct drm_private_obj *obj) { - int r; - struct omap_dss_device *dssdev = NULL; + struct omap_global_state *state; - if (!omapdss_stack_is_ready()) - return -EPROBE_DEFER; + state = kmemdup(obj->state, sizeof(*state), GFP_KERNEL); + if (!state) + return NULL; + + __drm_atomic_helper_private_obj_duplicate_state(obj, &state->base); + + return &state->base; +} + +static void omap_global_destroy_state(struct drm_private_obj *obj, + struct drm_private_state *state) +{ + struct omap_global_state *omap_state = to_omap_global_state(state); + + kfree(omap_state); +} + +static const struct drm_private_state_funcs omap_global_state_funcs = { + .atomic_duplicate_state = omap_global_duplicate_state, + .atomic_destroy_state = omap_global_destroy_state, +}; + +static int omap_global_obj_init(struct drm_device *dev) +{ + struct omap_drm_private *priv = dev->dev_private; + struct omap_global_state *state; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + drm_atomic_private_obj_init(dev, &priv->glob_obj, &state->base, + &omap_global_state_funcs); + return 0; +} + +static void omap_global_obj_fini(struct omap_drm_private *priv) +{ + drm_atomic_private_obj_fini(&priv->glob_obj); +} + +static void omap_disconnect_pipelines(struct drm_device *ddev) +{ + struct omap_drm_private *priv = ddev->dev_private; + unsigned int i; + + for (i = 0; i < priv->num_pipes; i++) { + struct omap_drm_pipeline *pipe = &priv->pipes[i]; + + omapdss_device_disconnect(priv->dss, pipe->output); + + omapdss_device_put(pipe->output); + pipe->output = NULL; + } - for_each_dss_dev(dssdev) { - r = dssdev->driver->connect(dssdev); + memset(&priv->channels, 0, sizeof(priv->channels)); + + priv->num_pipes = 0; +} + +static int omap_connect_pipelines(struct drm_device *ddev) +{ + struct omap_drm_private *priv = ddev->dev_private; + struct omap_dss_device *output = NULL; + int r; + + for_each_dss_output(output) { + r = omapdss_device_connect(priv->dss, output); if (r == -EPROBE_DEFER) { - omap_dss_put_device(dssdev); - goto cleanup; + omapdss_device_put(output); + return r; } else if (r) { - dev_warn(dssdev->dev, "could not connect display: %s\n", - dssdev->name); + dev_warn(output->dev, "could not connect output %s\n", + output->name); + } else { + struct omap_drm_pipeline *pipe; + + pipe = &priv->pipes[priv->num_pipes++]; + pipe->output = omapdss_device_get(output); + + if (priv->num_pipes == ARRAY_SIZE(priv->pipes)) { + /* To balance the 'for_each_dss_output' loop */ + omapdss_device_put(output); + break; + } } } return 0; +} -cleanup: - /* - * if we are deferring probe, we disconnect the devices we previously - * connected - */ - omap_disconnect_dssdevs(); +static int omap_compare_pipelines(const void *a, const void *b) +{ + const struct omap_drm_pipeline *pipe1 = a; + const struct omap_drm_pipeline *pipe2 = b; - return r; + if (pipe1->alias_id > pipe2->alias_id) + return 1; + else if (pipe1->alias_id < pipe2->alias_id) + return -1; + return 0; } static int omap_modeset_init_properties(struct drm_device *dev) { struct omap_drm_private *priv = dev->dev_private; - unsigned int num_planes = priv->dispc_ops->get_num_ovls(); + unsigned int num_planes = dispc_get_num_ovls(priv->dispc); priv->zorder_prop = drm_property_create_range(dev, 0, "zorder", 0, num_planes - 1); @@ -198,17 +374,31 @@ static int omap_modeset_init_properties(struct drm_device *dev) return 0; } +static int omap_display_id(struct omap_dss_device *output) +{ + struct device_node *node = NULL; + + if (output->bridge) { + struct drm_bridge *bridge __free(drm_bridge_put) = + drm_bridge_chain_get_last_bridge(output->bridge->encoder); + + node = bridge->of_node; + } + + return node ? of_alias_get_id(node, "display") : -ENODEV; +} + static int omap_modeset_init(struct drm_device *dev) { struct omap_drm_private *priv = dev->dev_private; - struct omap_dss_device *dssdev = NULL; - int num_ovls = priv->dispc_ops->get_num_ovls(); - int num_mgrs = priv->dispc_ops->get_num_mgrs(); - int num_crtcs, crtc_idx, plane_idx; + int num_ovls = dispc_get_num_ovls(priv->dispc); + int num_mgrs = dispc_get_num_mgrs(priv->dispc); + unsigned int i; int ret; u32 plane_crtc_mask; - drm_mode_config_init(dev); + if (!omapdss_stack_is_ready()) + return -EPROBE_DEFER; ret = omap_modeset_init_properties(dev); if (ret < 0) @@ -222,96 +412,118 @@ static int omap_modeset_init(struct drm_device *dev) * configuration does not match the expectations or exceeds * the available resources, the configuration is rejected. */ - num_crtcs = 0; - for_each_dss_dev(dssdev) - if (omapdss_device_is_connected(dssdev)) - num_crtcs++; - - if (num_crtcs > num_mgrs || num_crtcs > num_ovls || - num_crtcs > ARRAY_SIZE(priv->crtcs) || - num_crtcs > ARRAY_SIZE(priv->planes) || - num_crtcs > ARRAY_SIZE(priv->encoders) || - num_crtcs > ARRAY_SIZE(priv->connectors)) { + ret = omap_connect_pipelines(dev); + if (ret < 0) + return ret; + + if (priv->num_pipes > num_mgrs || priv->num_pipes > num_ovls) { dev_err(dev->dev, "%s(): Too many connected displays\n", __func__); return -EINVAL; } - /* All planes can be put to any CRTC */ - plane_crtc_mask = (1 << num_crtcs) - 1; - - dssdev = NULL; + /* Create all planes first. They can all be put to any CRTC. */ + plane_crtc_mask = (1 << priv->num_pipes) - 1; - crtc_idx = 0; - plane_idx = 0; - for_each_dss_dev(dssdev) { - struct drm_connector *connector; - struct drm_encoder *encoder; + for (i = 0; i < num_ovls; i++) { + enum drm_plane_type type = i < priv->num_pipes + ? DRM_PLANE_TYPE_PRIMARY + : DRM_PLANE_TYPE_OVERLAY; struct drm_plane *plane; - struct drm_crtc *crtc; - - if (!omapdss_device_is_connected(dssdev)) - continue; - - encoder = omap_encoder_init(dev, dssdev); - if (!encoder) - return -ENOMEM; - connector = omap_connector_init(dev, - get_connector_type(dssdev), dssdev, encoder); - if (!connector) - return -ENOMEM; + if (WARN_ON(priv->num_planes >= ARRAY_SIZE(priv->planes))) + return -EINVAL; - plane = omap_plane_init(dev, plane_idx, DRM_PLANE_TYPE_PRIMARY, - plane_crtc_mask); + plane = omap_plane_init(dev, i, type, plane_crtc_mask); if (IS_ERR(plane)) return PTR_ERR(plane); - crtc = omap_crtc_init(dev, plane, dssdev); - if (IS_ERR(crtc)) - return PTR_ERR(crtc); + priv->planes[priv->num_planes++] = plane; + } + + /* + * Create the encoders, attach the bridges and get the pipeline alias + * IDs. + */ + for (i = 0; i < priv->num_pipes; i++) { + struct omap_drm_pipeline *pipe = &priv->pipes[i]; + int id; - drm_mode_connector_attach_encoder(connector, encoder); - encoder->possible_crtcs = (1 << crtc_idx); + pipe->encoder = omap_encoder_init(dev, pipe->output); + if (!pipe->encoder) + return -ENOMEM; - priv->crtcs[priv->num_crtcs++] = crtc; - priv->planes[priv->num_planes++] = plane; - priv->encoders[priv->num_encoders++] = encoder; - priv->connectors[priv->num_connectors++] = connector; + if (pipe->output->bridge) { + ret = drm_bridge_attach(pipe->encoder, + pipe->output->bridge, NULL, + DRM_BRIDGE_ATTACH_NO_CONNECTOR); + if (ret < 0) + return ret; + } - plane_idx++; - crtc_idx++; + id = omap_display_id(pipe->output); + pipe->alias_id = id >= 0 ? id : i; } + /* Sort the pipelines by DT aliases. */ + sort(priv->pipes, priv->num_pipes, sizeof(priv->pipes[0]), + omap_compare_pipelines, NULL); + /* - * Create normal planes for the remaining overlays: + * Populate the pipeline lookup table by DISPC channel. Only one display + * is allowed per channel. */ - for (; plane_idx < num_ovls; plane_idx++) { - struct drm_plane *plane; + for (i = 0; i < priv->num_pipes; ++i) { + struct omap_drm_pipeline *pipe = &priv->pipes[i]; + enum omap_channel channel = pipe->output->dispc_channel; - if (WARN_ON(priv->num_planes >= ARRAY_SIZE(priv->planes))) + if (WARN_ON(priv->channels[channel] != NULL)) return -EINVAL; - plane = omap_plane_init(dev, plane_idx, DRM_PLANE_TYPE_OVERLAY, - plane_crtc_mask); - if (IS_ERR(plane)) - return PTR_ERR(plane); + priv->channels[channel] = pipe; + } - priv->planes[priv->num_planes++] = plane; + /* Create the connectors and CRTCs. */ + for (i = 0; i < priv->num_pipes; i++) { + struct omap_drm_pipeline *pipe = &priv->pipes[i]; + struct drm_encoder *encoder = pipe->encoder; + struct drm_crtc *crtc; + + pipe->connector = drm_bridge_connector_init(dev, encoder); + if (IS_ERR(pipe->connector)) { + dev_err(priv->dev, + "unable to create bridge connector for %s\n", + pipe->output->name); + return PTR_ERR(pipe->connector); + } + + drm_connector_attach_encoder(pipe->connector, encoder); + + crtc = omap_crtc_init(dev, pipe, priv->planes[i]); + if (IS_ERR(crtc)) + return PTR_ERR(crtc); + + encoder->possible_crtcs = 1 << i; + pipe->crtc = crtc; } - DBG("registered %d planes, %d crtcs, %d encoders and %d connectors\n", - priv->num_planes, priv->num_crtcs, priv->num_encoders, - priv->num_connectors); + DBG("registered %u planes, %u crtcs/encoders/connectors\n", + priv->num_planes, priv->num_pipes); dev->mode_config.min_width = 8; dev->mode_config.min_height = 2; - /* note: eventually will need some cpu_is_omapXYZ() type stuff here - * to fill in these limits properly on different OMAP generations.. + /* + * Note: these values are used for multiple independent things: + * connector mode filtering, buffer sizes, crtc sizes... + * Use big enough values here to cover all use cases, and do more + * specific checking in the respective code paths. */ - dev->mode_config.max_width = 2048; - dev->mode_config.max_height = 2048; + dev->mode_config.max_width = 8192; + dev->mode_config.max_height = 8192; + + /* We want the zpos to be normalized */ + dev->mode_config.normalize_zpos = true; dev->mode_config.funcs = &omap_mode_config_funcs; dev->mode_config.helper_private = &omap_mode_config_helper_funcs; @@ -323,6 +535,13 @@ static int omap_modeset_init(struct drm_device *dev) return 0; } +static void omap_modeset_fini(struct drm_device *ddev) +{ + omap_drm_irq_uninstall(ddev); + + drm_mode_config_cleanup(ddev); +} + /* * drm ioctl funcs */ @@ -348,20 +567,6 @@ static int ioctl_get_param(struct drm_device *dev, void *data, return 0; } -static int ioctl_set_param(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_omap_param *args = data; - - switch (args->param) { - default: - DBG("unknown parameter %lld", args->param); - return -EINVAL; - } - - return 0; -} - #define OMAP_BO_USER_MASK 0x00ffffff /* flags settable by userspace */ static int ioctl_gem_new(struct drm_device *dev, void *data, @@ -393,26 +598,26 @@ static int ioctl_gem_info(struct drm_device *dev, void *data, args->size = omap_gem_mmap_size(obj); args->offset = omap_gem_mmap_offset(obj); - drm_gem_object_unreference_unlocked(obj); + drm_gem_object_put(obj); return ret; } static const struct drm_ioctl_desc ioctls[DRM_COMMAND_END - DRM_COMMAND_BASE] = { DRM_IOCTL_DEF_DRV(OMAP_GET_PARAM, ioctl_get_param, - DRM_AUTH | DRM_RENDER_ALLOW), - DRM_IOCTL_DEF_DRV(OMAP_SET_PARAM, ioctl_set_param, + DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(OMAP_SET_PARAM, drm_invalid_op, DRM_AUTH | DRM_MASTER | DRM_ROOT_ONLY), DRM_IOCTL_DEF_DRV(OMAP_GEM_NEW, ioctl_gem_new, - DRM_AUTH | DRM_RENDER_ALLOW), + DRM_RENDER_ALLOW), /* Deprecated, to be removed. */ DRM_IOCTL_DEF_DRV(OMAP_GEM_CPU_PREP, drm_noop, - DRM_AUTH | DRM_RENDER_ALLOW), + DRM_RENDER_ALLOW), /* Deprecated, to be removed. */ DRM_IOCTL_DEF_DRV(OMAP_GEM_CPU_FINI, drm_noop, - DRM_AUTH | DRM_RENDER_ALLOW), + DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(OMAP_GEM_INFO, ioctl_gem_info, - DRM_AUTH | DRM_RENDER_ALLOW), + DRM_RENDER_ALLOW), }; /* @@ -428,102 +633,24 @@ static int dev_open(struct drm_device *dev, struct drm_file *file) return 0; } -/** - * lastclose - clean up after all DRM clients have exited - * @dev: DRM device - * - * Take care of cleaning up after all DRM clients have exited. In the - * mode setting case, we want to restore the kernel's initial mode (just - * in case the last client left us in a bad state). - */ -static void dev_lastclose(struct drm_device *dev) -{ - int i; - - /* we don't support vga_switcheroo.. so just make sure the fbdev - * mode is active - */ - struct omap_drm_private *priv = dev->dev_private; - int ret; - - DBG("lastclose: dev=%p", dev); - - /* need to restore default rotation state.. not sure - * if there is a cleaner way to restore properties to - * default state? Maybe a flag that properties should - * automatically be restored to default state on - * lastclose? - */ - for (i = 0; i < priv->num_crtcs; i++) { - struct drm_crtc *crtc = priv->crtcs[i]; - - if (!crtc->primary->rotation_property) - continue; - - drm_object_property_set_value(&crtc->base, - crtc->primary->rotation_property, - DRM_MODE_ROTATE_0); - } - - for (i = 0; i < priv->num_planes; i++) { - struct drm_plane *plane = priv->planes[i]; - - if (!plane->rotation_property) - continue; - - drm_object_property_set_value(&plane->base, - plane->rotation_property, - DRM_MODE_ROTATE_0); - } - - if (priv->fbdev) { - ret = drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev); - if (ret) - DBG("failed to restore crtc mode"); - } -} - -static const struct vm_operations_struct omap_gem_vm_ops = { - .fault = omap_gem_fault, - .open = drm_gem_vm_open, - .close = drm_gem_vm_close, -}; - -static const struct file_operations omapdriver_fops = { - .owner = THIS_MODULE, - .open = drm_open, - .unlocked_ioctl = drm_ioctl, - .compat_ioctl = drm_compat_ioctl, - .release = drm_release, - .mmap = omap_gem_mmap, - .poll = drm_poll, - .read = drm_read, - .llseek = noop_llseek, -}; +DEFINE_DRM_GEM_FOPS(omapdriver_fops); -static struct drm_driver omap_drm_driver = { - .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | +static const struct drm_driver omap_drm_driver = { + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC | DRIVER_RENDER, .open = dev_open, - .lastclose = dev_lastclose, #ifdef CONFIG_DEBUG_FS .debugfs_init = omap_debugfs_init, #endif - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, - .prime_fd_to_handle = drm_gem_prime_fd_to_handle, - .gem_prime_export = omap_gem_prime_export, .gem_prime_import = omap_gem_prime_import, - .gem_free_object = omap_gem_free_object, - .gem_vm_ops = &omap_gem_vm_ops, .dumb_create = omap_gem_dumb_create, .dumb_map_offset = omap_gem_dumb_map_offset, - .dumb_destroy = drm_gem_dumb_destroy, + OMAP_FBDEV_DRIVER_OPS, .ioctls = ioctls, .num_ioctls = DRM_OMAP_NUM_IOCTLS, .fops = &omapdriver_fops, .name = DRIVER_NAME, .desc = DRIVER_DESC, - .date = DRIVER_DATE, .major = DRIVER_MAJOR, .minor = DRIVER_MINOR, .patchlevel = DRIVER_PATCHLEVEL, @@ -537,71 +664,71 @@ static const struct soc_device_attribute omapdrm_soc_devices[] = { { /* sentinel */ } }; -static int pdev_probe(struct platform_device *pdev) +static int omapdrm_init(struct omap_drm_private *priv, struct device *dev) { const struct soc_device_attribute *soc; - struct omap_drm_private *priv; + struct dss_pdata *pdata = dev->platform_data; struct drm_device *ddev; - unsigned int i; int ret; - DBG("%s", pdev->name); + DBG("%s", dev_name(dev)); - if (omapdss_is_initialized() == false) - return -EPROBE_DEFER; + if (drm_firmware_drivers_only()) + return -ENODEV; - omap_crtc_pre_init(); + /* Allocate and initialize the DRM device. */ + ddev = drm_dev_alloc(&omap_drm_driver, dev); + if (IS_ERR(ddev)) + return PTR_ERR(ddev); - ret = omap_connect_dssdevs(); - if (ret) - goto err_crtc_uninit; + priv->ddev = ddev; + ddev->dev_private = priv; - /* Allocate and initialize the driver private structure. */ - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) { - ret = -ENOMEM; - goto err_disconnect_dssdevs; - } + priv->dev = dev; + priv->dss = pdata->dss; + priv->dispc = dispc_get_dispc(priv->dss); - priv->dispc_ops = dispc_get_ops(); + priv->dss->mgr_ops_priv = priv; soc = soc_device_match(omapdrm_soc_devices); - priv->omaprev = soc ? (unsigned int)soc->data : 0; + priv->omaprev = soc ? (uintptr_t)soc->data : 0; priv->wq = alloc_ordered_workqueue("omapdrm", 0); + if (!priv->wq) { + ret = -ENOMEM; + goto err_alloc_workqueue; + } - spin_lock_init(&priv->list_lock); + mutex_init(&priv->list_lock); INIT_LIST_HEAD(&priv->obj_list); - /* Allocate and initialize the DRM device. */ - ddev = drm_dev_alloc(&omap_drm_driver, &pdev->dev); - if (IS_ERR(ddev)) { - ret = PTR_ERR(ddev); - goto err_free_priv; - } - - ddev->dev_private = priv; - platform_set_drvdata(pdev, ddev); + /* Get memory bandwidth limits */ + priv->max_bandwidth = dispc_get_memory_bandwidth_limit(priv->dispc); omap_gem_init(ddev); + drm_mode_config_init(ddev); + + ret = omap_global_obj_init(ddev); + if (ret) + goto err_gem_deinit; + + ret = omap_hwoverlays_init(priv); + if (ret) + goto err_free_priv_obj; + ret = omap_modeset_init(ddev); if (ret) { - dev_err(&pdev->dev, "omap_modeset_init failed: ret=%d\n", ret); - goto err_free_drm_dev; + dev_err(priv->dev, "omap_modeset_init failed: ret=%d\n", ret); + goto err_free_overlays; } /* Initialize vblank handling, start with all CRTCs disabled. */ - ret = drm_vblank_init(ddev, priv->num_crtcs); + ret = drm_vblank_init(ddev, priv->num_pipes); if (ret) { - dev_err(&pdev->dev, "could not init vblank\n"); + dev_err(priv->dev, "could not init vblank\n"); goto err_cleanup_modeset; } - for (i = 0; i < priv->num_crtcs; i++) - drm_crtc_vblank_off(priv->crtcs[i]); - - priv->fbdev = omap_fbdev_init(ddev); - drm_kms_helper_poll_init(ddev); /* @@ -612,32 +739,31 @@ static int pdev_probe(struct platform_device *pdev) if (ret) goto err_cleanup_helpers; + omap_fbdev_setup(ddev); + return 0; err_cleanup_helpers: drm_kms_helper_poll_fini(ddev); - if (priv->fbdev) - omap_fbdev_free(ddev); err_cleanup_modeset: + omap_modeset_fini(ddev); +err_free_overlays: + omap_hwoverlays_destroy(priv); +err_free_priv_obj: + omap_global_obj_fini(priv); +err_gem_deinit: drm_mode_config_cleanup(ddev); - omap_drm_irq_uninstall(ddev); -err_free_drm_dev: omap_gem_deinit(ddev); - drm_dev_unref(ddev); -err_free_priv: destroy_workqueue(priv->wq); - kfree(priv); -err_disconnect_dssdevs: - omap_disconnect_dssdevs(); -err_crtc_uninit: - omap_crtc_pre_uninit(); +err_alloc_workqueue: + omap_disconnect_pipelines(ddev); + drm_dev_put(ddev); return ret; } -static int pdev_remove(struct platform_device *pdev) +static void omapdrm_cleanup(struct omap_drm_private *priv) { - struct drm_device *ddev = platform_get_drvdata(pdev); - struct omap_drm_private *priv = ddev->dev_private; + struct drm_device *ddev = priv->ddev; DBG(""); @@ -645,88 +771,78 @@ static int pdev_remove(struct platform_device *pdev) drm_kms_helper_poll_fini(ddev); - if (priv->fbdev) - omap_fbdev_free(ddev); - drm_atomic_helper_shutdown(ddev); + omap_modeset_fini(ddev); + omap_hwoverlays_destroy(priv); + omap_global_obj_fini(priv); drm_mode_config_cleanup(ddev); - - omap_drm_irq_uninstall(ddev); omap_gem_deinit(ddev); - drm_dev_unref(ddev); - destroy_workqueue(priv->wq); - kfree(priv); - omap_disconnect_dssdevs(); - omap_crtc_pre_uninit(); + omap_disconnect_pipelines(ddev); - return 0; + drm_dev_put(ddev); } -#ifdef CONFIG_PM_SLEEP -static int omap_drm_suspend_all_displays(void) +static int pdev_probe(struct platform_device *pdev) { - struct omap_dss_device *dssdev = NULL; - - for_each_dss_dev(dssdev) { - if (!dssdev->driver) - continue; + struct omap_drm_private *priv; + int ret; - if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { - dssdev->driver->disable(dssdev); - dssdev->activate_after_resume = true; - } else { - dssdev->activate_after_resume = false; - } + ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pdev->dev, "Failed to set the DMA mask\n"); + return ret; } - return 0; + /* Allocate and initialize the driver private structure. */ + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + + ret = omapdrm_init(priv, &pdev->dev); + if (ret < 0) + kfree(priv); + + return ret; } -static int omap_drm_resume_all_displays(void) +static void pdev_remove(struct platform_device *pdev) { - struct omap_dss_device *dssdev = NULL; + struct omap_drm_private *priv = platform_get_drvdata(pdev); - for_each_dss_dev(dssdev) { - if (!dssdev->driver) - continue; + omapdrm_cleanup(priv); + kfree(priv); +} - if (dssdev->activate_after_resume) { - dssdev->driver->enable(dssdev); - dssdev->activate_after_resume = false; - } - } +static void pdev_shutdown(struct platform_device *pdev) +{ + struct omap_drm_private *priv = platform_get_drvdata(pdev); - return 0; + drm_atomic_helper_shutdown(priv->ddev); } +#ifdef CONFIG_PM_SLEEP static int omap_drm_suspend(struct device *dev) { - struct drm_device *drm_dev = dev_get_drvdata(dev); - - drm_kms_helper_poll_disable(drm_dev); + struct omap_drm_private *priv = dev_get_drvdata(dev); + struct drm_device *drm_dev = priv->ddev; - drm_modeset_lock_all(drm_dev); - omap_drm_suspend_all_displays(); - drm_modeset_unlock_all(drm_dev); - - return 0; + return drm_mode_config_helper_suspend(drm_dev); } static int omap_drm_resume(struct device *dev) { - struct drm_device *drm_dev = dev_get_drvdata(dev); + struct omap_drm_private *priv = dev_get_drvdata(dev); + struct drm_device *drm_dev = priv->ddev; - drm_modeset_lock_all(drm_dev); - omap_drm_resume_all_displays(); - drm_modeset_unlock_all(drm_dev); + drm_mode_config_helper_resume(drm_dev); - drm_kms_helper_poll_enable(drm_dev); - - return omap_gem_resume(dev); + return omap_gem_resume(drm_dev); } #endif @@ -734,11 +850,12 @@ static SIMPLE_DEV_PM_OPS(omapdrm_pm_ops, omap_drm_suspend, omap_drm_resume); static struct platform_driver pdev = { .driver = { - .name = DRIVER_NAME, + .name = "omapdrm", .pm = &omapdrm_pm_ops, }, .probe = pdev_probe, .remove = pdev_remove, + .shutdown = pdev_shutdown, }; static struct platform_driver * const drivers[] = { @@ -748,9 +865,21 @@ static struct platform_driver * const drivers[] = { static int __init omap_drm_init(void) { + int r; + DBG("init"); - return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); + r = omap_dss_init(); + if (r) + return r; + + r = platform_register_drivers(drivers, ARRAY_SIZE(drivers)); + if (r) { + omap_dss_exit(); + return r; + } + + return 0; } static void __exit omap_drm_fini(void) @@ -758,13 +887,15 @@ static void __exit omap_drm_fini(void) DBG("fini"); platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); + + omap_dss_exit(); } -/* need late_initcall() so we load after dss_driver's are loaded */ -late_initcall(omap_drm_init); +module_init(omap_drm_init); module_exit(omap_drm_fini); MODULE_AUTHOR("Rob Clark <rob@ti.com>"); +MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); MODULE_DESCRIPTION("OMAP DRM Display Driver"); MODULE_ALIAS("platform:" DRIVER_NAME); MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h index 4bd1e9070b31..d903568fd8cc 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.h +++ b/drivers/gpu/drm/omapdrm/omap_drv.h @@ -1,74 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* - * drivers/gpu/drm/omapdrm/omap_drv.h - * - * Copyright (C) 2011 Texas Instruments + * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/ * Author: Rob Clark <rob@ti.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef __OMAP_DRV_H__ -#define __OMAP_DRV_H__ +#ifndef __OMAPDRM_DRV_H__ +#define __OMAPDRM_DRV_H__ #include <linux/module.h> #include <linux/types.h> #include <linux/workqueue.h> -#include <drm/drmP.h> -#include <drm/drm_crtc_helper.h> +#include "dss/omapdss.h" +#include "dss/dss.h" + +#include <drm/drm_atomic.h> #include <drm/drm_gem.h> #include <drm/omap_drm.h> -#include "dss/omapdss.h" +#include "omap_crtc.h" +#include "omap_encoder.h" +#include "omap_fb.h" +#include "omap_gem.h" +#include "omap_irq.h" +#include "omap_plane.h" +#include "omap_overlay.h" -#define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__) -#define VERB(fmt, ...) if (0) DRM_DEBUG(fmt, ##__VA_ARGS__) /* verbose debug */ +#define DBG(fmt, ...) DRM_DEBUG_DRIVER(fmt"\n", ##__VA_ARGS__) +#define VERB(fmt, ...) if (0) DRM_DEBUG_DRIVER(fmt, ##__VA_ARGS__) /* verbose debug */ #define MODULE_NAME "omapdrm" struct omap_drm_usergart; +struct omap_fbdev; + +struct omap_drm_pipeline { + struct drm_crtc *crtc; + struct drm_encoder *encoder; + struct drm_connector *connector; + struct omap_dss_device *output; + unsigned int alias_id; +}; -/* For KMS code that needs to wait for a certain # of IRQs: +/* + * Global private object state for tracking resources that are shared across + * multiple kms objects (planes/crtcs/etc). */ -struct omap_irq_wait; -struct omap_irq_wait * omap_irq_wait_init(struct drm_device *dev, - uint32_t irqmask, int count); -int omap_irq_wait(struct drm_device *dev, struct omap_irq_wait *wait, - unsigned long timeout); +#define to_omap_global_state(x) container_of(x, struct omap_global_state, base) + +struct omap_global_state { + struct drm_private_state base; + + /* global atomic state of assignment between overlays and planes */ + struct drm_plane *hwoverlay_to_plane[8]; +}; struct omap_drm_private { - uint32_t omaprev; + struct drm_device *ddev; + struct device *dev; + u32 omaprev; - const struct dispc_ops *dispc_ops; + struct dss_device *dss; + struct dispc_device *dispc; - unsigned int num_crtcs; - struct drm_crtc *crtcs[8]; + bool irq_enabled; + + unsigned int num_pipes; + struct omap_drm_pipeline pipes[8]; + struct omap_drm_pipeline *channels[8]; unsigned int num_planes; struct drm_plane *planes[8]; - unsigned int num_encoders; - struct drm_encoder *encoders[8]; - - unsigned int num_connectors; - struct drm_connector *connectors[8]; + unsigned int num_ovls; + struct omap_hw_overlay *overlays[8]; - struct drm_fb_helper *fbdev; + struct drm_private_obj glob_obj; struct workqueue_struct *wq; /* lock for obj_list below */ - spinlock_t list_lock; + struct mutex list_lock; /* list of GEM objects: */ struct list_head obj_list; @@ -82,118 +94,19 @@ struct omap_drm_private { /* irq handling: */ spinlock_t wait_lock; /* protects the wait_list */ struct list_head wait_list; /* list of omap_irq_wait */ - uint32_t irq_mask; /* enabled irqs in addition to wait_list */ + u32 irq_mask; /* enabled irqs in addition to wait_list */ + + /* memory bandwidth limit if it is needed on the platform */ + unsigned int max_bandwidth; + + struct omap_fbdev *fbdev; }; -#ifdef CONFIG_DEBUG_FS -int omap_debugfs_init(struct drm_minor *minor); -void omap_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m); -void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m); -void omap_gem_describe_objects(struct list_head *list, struct seq_file *m); -#endif - -#ifdef CONFIG_PM -int omap_gem_resume(struct device *dev); -#endif - -int omap_irq_enable_vblank(struct drm_crtc *crtc); -void omap_irq_disable_vblank(struct drm_crtc *crtc); -void omap_drm_irq_uninstall(struct drm_device *dev); -int omap_drm_irq_install(struct drm_device *dev); - -#ifdef CONFIG_DRM_FBDEV_EMULATION -struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev); -void omap_fbdev_free(struct drm_device *dev); -#else -static inline struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev) -{ - return NULL; -} -static inline void omap_fbdev_free(struct drm_device *dev) -{ -} -#endif - -struct videomode *omap_crtc_timings(struct drm_crtc *crtc); -enum omap_channel omap_crtc_channel(struct drm_crtc *crtc); -void omap_crtc_pre_init(void); -void omap_crtc_pre_uninit(void); -struct drm_crtc *omap_crtc_init(struct drm_device *dev, - struct drm_plane *plane, struct omap_dss_device *dssdev); -int omap_crtc_wait_pending(struct drm_crtc *crtc); -void omap_crtc_error_irq(struct drm_crtc *crtc, uint32_t irqstatus); -void omap_crtc_vblank_irq(struct drm_crtc *crtc); - -struct drm_plane *omap_plane_init(struct drm_device *dev, - int idx, enum drm_plane_type type, - u32 possible_crtcs); -void omap_plane_install_properties(struct drm_plane *plane, - struct drm_mode_object *obj); - -struct drm_encoder *omap_encoder_init(struct drm_device *dev, - struct omap_dss_device *dssdev); - -struct drm_connector *omap_connector_init(struct drm_device *dev, - int connector_type, struct omap_dss_device *dssdev, - struct drm_encoder *encoder); -struct drm_encoder *omap_connector_attached_encoder( - struct drm_connector *connector); -bool omap_connector_get_hdmi_mode(struct drm_connector *connector); - -struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev, - struct drm_file *file, const struct drm_mode_fb_cmd2 *mode_cmd); -struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, - const struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos); -int omap_framebuffer_pin(struct drm_framebuffer *fb); -void omap_framebuffer_unpin(struct drm_framebuffer *fb); -void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, - struct drm_plane_state *state, struct omap_overlay_info *info); -struct drm_connector *omap_framebuffer_get_next_connector( - struct drm_framebuffer *fb, struct drm_connector *from); -bool omap_framebuffer_supports_rotation(struct drm_framebuffer *fb); - -void omap_gem_init(struct drm_device *dev); -void omap_gem_deinit(struct drm_device *dev); - -struct drm_gem_object *omap_gem_new(struct drm_device *dev, - union omap_gem_size gsize, uint32_t flags); -struct drm_gem_object *omap_gem_new_dmabuf(struct drm_device *dev, size_t size, - struct sg_table *sgt); -int omap_gem_new_handle(struct drm_device *dev, struct drm_file *file, - union omap_gem_size gsize, uint32_t flags, uint32_t *handle); -void omap_gem_free_object(struct drm_gem_object *obj); -void *omap_gem_vaddr(struct drm_gem_object *obj); -int omap_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev, - uint32_t handle, uint64_t *offset); -int omap_gem_dumb_create(struct drm_file *file, struct drm_device *dev, - struct drm_mode_create_dumb *args); -int omap_gem_mmap(struct file *filp, struct vm_area_struct *vma); -int omap_gem_mmap_obj(struct drm_gem_object *obj, - struct vm_area_struct *vma); -int omap_gem_fault(struct vm_fault *vmf); -int omap_gem_roll(struct drm_gem_object *obj, uint32_t roll); -void omap_gem_cpu_sync_page(struct drm_gem_object *obj, int pgoff); -void omap_gem_dma_sync_buffer(struct drm_gem_object *obj, - enum dma_data_direction dir); -int omap_gem_pin(struct drm_gem_object *obj, dma_addr_t *dma_addr); -void omap_gem_unpin(struct drm_gem_object *obj); -int omap_gem_get_pages(struct drm_gem_object *obj, struct page ***pages, - bool remap); -int omap_gem_put_pages(struct drm_gem_object *obj); -uint32_t omap_gem_flags(struct drm_gem_object *obj); -int omap_gem_rotated_dma_addr(struct drm_gem_object *obj, uint32_t orient, - int x, int y, dma_addr_t *dma_addr); -uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj); -size_t omap_gem_mmap_size(struct drm_gem_object *obj); -int omap_gem_tiled_stride(struct drm_gem_object *obj, uint32_t orient); - -struct dma_buf *omap_gem_prime_export(struct drm_device *dev, - struct drm_gem_object *obj, int flags); -struct drm_gem_object *omap_gem_prime_import(struct drm_device *dev, - struct dma_buf *buffer); - -/* map crtc to vblank mask */ -struct omap_dss_device *omap_encoder_get_dssdev(struct drm_encoder *encoder); - -#endif /* __OMAP_DRV_H__ */ +void omap_debugfs_init(struct drm_minor *minor); + +struct omap_global_state * __must_check omap_get_global_state(struct drm_atomic_state *s); + +struct omap_global_state *omap_get_existing_global_state(struct omap_drm_private *priv); + +#endif /* __OMAPDRM_DRV_H__ */ diff --git a/drivers/gpu/drm/omapdrm/omap_encoder.c b/drivers/gpu/drm/omapdrm/omap_encoder.c index 86c977b7189a..195715b162e3 100644 --- a/drivers/gpu/drm/omapdrm/omap_encoder.c +++ b/drivers/gpu/drm/omapdrm/omap_encoder.c @@ -1,26 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0-only /* - * drivers/gpu/drm/omapdrm/omap_encoder.c - * - * Copyright (C) 2011 Texas Instruments + * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/ * Author: Rob Clark <rob@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/list.h> +#include <drm/drm_bridge.h> #include <drm/drm_crtc.h> -#include <drm/drm_crtc_helper.h> +#include <drm/drm_modeset_helper_vtables.h> #include <drm/drm_edid.h> #include "omap_drv.h" @@ -38,16 +26,9 @@ */ struct omap_encoder { struct drm_encoder base; - struct omap_dss_device *dssdev; + struct omap_dss_device *output; }; -struct omap_dss_device *omap_encoder_get_dssdev(struct drm_encoder *encoder) -{ - struct omap_encoder *omap_encoder = to_omap_encoder(encoder); - - return omap_encoder->dssdev; -} - static void omap_encoder_destroy(struct drm_encoder *encoder) { struct omap_encoder *omap_encoder = to_omap_encoder(encoder); @@ -60,114 +41,83 @@ static const struct drm_encoder_funcs omap_encoder_funcs = { .destroy = omap_encoder_destroy, }; -static void omap_encoder_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) +static void omap_encoder_update_videomode_flags(struct videomode *vm, + u32 bus_flags) { - struct drm_device *dev = encoder->dev; - struct omap_encoder *omap_encoder = to_omap_encoder(encoder); - struct omap_dss_device *dssdev = omap_encoder->dssdev; - struct drm_connector *connector; - bool hdmi_mode; - int r; - - hdmi_mode = false; - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - if (connector->encoder == encoder) { - hdmi_mode = omap_connector_get_hdmi_mode(connector); - break; - } + if (!(vm->flags & (DISPLAY_FLAGS_DE_LOW | + DISPLAY_FLAGS_DE_HIGH))) { + if (bus_flags & DRM_BUS_FLAG_DE_LOW) + vm->flags |= DISPLAY_FLAGS_DE_LOW; + else if (bus_flags & DRM_BUS_FLAG_DE_HIGH) + vm->flags |= DISPLAY_FLAGS_DE_HIGH; } - if (dssdev->driver->set_hdmi_mode) - dssdev->driver->set_hdmi_mode(dssdev, hdmi_mode); - - if (hdmi_mode && dssdev->driver->set_hdmi_infoframe) { - struct hdmi_avi_infoframe avi; + if (!(vm->flags & (DISPLAY_FLAGS_PIXDATA_POSEDGE | + DISPLAY_FLAGS_PIXDATA_NEGEDGE))) { + if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE) + vm->flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE; + else if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE) + vm->flags |= DISPLAY_FLAGS_PIXDATA_NEGEDGE; + } - r = drm_hdmi_avi_infoframe_from_display_mode(&avi, adjusted_mode); - if (r == 0) - dssdev->driver->set_hdmi_infoframe(dssdev, &avi); + if (!(vm->flags & (DISPLAY_FLAGS_SYNC_POSEDGE | + DISPLAY_FLAGS_SYNC_NEGEDGE))) { + if (bus_flags & DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE) + vm->flags |= DISPLAY_FLAGS_SYNC_POSEDGE; + else if (bus_flags & DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE) + vm->flags |= DISPLAY_FLAGS_SYNC_NEGEDGE; } } -static void omap_encoder_disable(struct drm_encoder *encoder) +static void omap_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) { struct omap_encoder *omap_encoder = to_omap_encoder(encoder); - struct omap_dss_device *dssdev = omap_encoder->dssdev; - struct omap_dss_driver *dssdrv = dssdev->driver; - - dssdrv->disable(dssdev); -} - -static int omap_encoder_update(struct drm_encoder *encoder, - enum omap_channel channel, - struct videomode *vm) -{ + struct omap_dss_device *output = omap_encoder->output; struct drm_device *dev = encoder->dev; - struct omap_encoder *omap_encoder = to_omap_encoder(encoder); - struct omap_dss_device *dssdev = omap_encoder->dssdev; - struct omap_dss_driver *dssdrv = dssdev->driver; - int ret; - - if (dssdrv->check_timings) { - ret = dssdrv->check_timings(dssdev, vm); - } else { - struct videomode t = {0}; - - dssdrv->get_timings(dssdev, &t); + struct drm_connector *connector; + struct videomode vm = { 0 }; + u32 bus_flags; - if (memcmp(vm, &t, sizeof(*vm))) - ret = -EINVAL; - else - ret = 0; + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (connector->encoder == encoder) + break; } - if (ret) { - dev_err(dev->dev, "could not set timings: %d\n", ret); - return ret; + drm_display_mode_to_videomode(adjusted_mode, &vm); + + /* + * HACK: This fixes the vm flags. + * struct drm_display_mode does not contain the VSYNC/HSYNC/DE flags and + * they get lost when converting back and forth between struct + * drm_display_mode and struct videomode. The hack below goes and + * fetches the missing flags. + * + * A better solution is to use DRM's bus-flags through the whole driver. + */ + drm_for_each_bridge_in_chain_from(output->bridge, bridge) { + if (!bridge->timings) + continue; + + bus_flags = bridge->timings->input_bus_flags; + omap_encoder_update_videomode_flags(&vm, bus_flags); } - if (dssdrv->set_timings) - dssdrv->set_timings(dssdev, vm); - - return 0; -} - -static void omap_encoder_enable(struct drm_encoder *encoder) -{ - struct omap_encoder *omap_encoder = to_omap_encoder(encoder); - struct omap_dss_device *dssdev = omap_encoder->dssdev; - struct omap_dss_driver *dssdrv = dssdev->driver; - int r; - - omap_encoder_update(encoder, omap_crtc_channel(encoder->crtc), - omap_crtc_timings(encoder->crtc)); - - r = dssdrv->enable(dssdev); - if (r) - dev_err(encoder->dev->dev, - "Failed to enable display '%s': %d\n", - dssdev->name, r); -} + bus_flags = connector->display_info.bus_flags; + omap_encoder_update_videomode_flags(&vm, bus_flags); -static int omap_encoder_atomic_check(struct drm_encoder *encoder, - struct drm_crtc_state *crtc_state, - struct drm_connector_state *conn_state) -{ - return 0; + /* Set timings for all devices in the display pipeline. */ + dss_mgr_set_timings(output, &vm); } static const struct drm_encoder_helper_funcs omap_encoder_helper_funcs = { .mode_set = omap_encoder_mode_set, - .disable = omap_encoder_disable, - .enable = omap_encoder_enable, - .atomic_check = omap_encoder_atomic_check, }; /* initialize encoder */ struct drm_encoder *omap_encoder_init(struct drm_device *dev, - struct omap_dss_device *dssdev) + struct omap_dss_device *output) { struct drm_encoder *encoder = NULL; struct omap_encoder *omap_encoder; @@ -176,7 +126,7 @@ struct drm_encoder *omap_encoder_init(struct drm_device *dev, if (!omap_encoder) goto fail; - omap_encoder->dssdev = dssdev; + omap_encoder->output = output; encoder = &omap_encoder->base; diff --git a/drivers/gpu/drm/omapdrm/omap_encoder.h b/drivers/gpu/drm/omapdrm/omap_encoder.h new file mode 100644 index 000000000000..051e590cd3ea --- /dev/null +++ b/drivers/gpu/drm/omapdrm/omap_encoder.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * omap_encoder.h -- OMAP DRM Encoder + * + * Copyright (C) 2011 Texas Instruments + * Author: Rob Clark <rob@ti.com> + */ + +#ifndef __OMAPDRM_ENCODER_H__ +#define __OMAPDRM_ENCODER_H__ + +struct drm_device; +struct drm_encoder; +struct omap_dss_device; + +struct drm_encoder *omap_encoder_init(struct drm_device *dev, + struct omap_dss_device *output); + +#endif /* __OMAPDRM_ENCODER_H__ */ diff --git a/drivers/gpu/drm/omapdrm/omap_fb.c b/drivers/gpu/drm/omapdrm/omap_fb.c index ddf7a457951b..b8c249ec1891 100644 --- a/drivers/gpu/drm/omapdrm/omap_fb.c +++ b/drivers/gpu/drm/omapdrm/omap_fb.c @@ -1,26 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-only /* - * drivers/gpu/drm/omapdrm/omap_fb.c - * - * Copyright (C) 2011 Texas Instruments + * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/ * Author: Rob Clark <rob@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/dma-mapping.h> #include <linux/seq_file.h> -#include <drm/drm_crtc.h> -#include <drm/drm_crtc_helper.h> +#include <drm/drm_blend.h> +#include <drm/drm_modeset_helper.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_framebuffer.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_print.h> #include "omap_dmm_tiler.h" #include "omap_drv.h" @@ -53,9 +45,6 @@ static const u32 formats[] = { /* per-plane info for the fb: */ struct plane { - struct drm_gem_object *bo; - uint32_t pitch; - uint32_t offset; dma_addr_t dma_addr; }; @@ -70,62 +59,53 @@ struct omap_framebuffer { struct mutex lock; }; -static int omap_framebuffer_create_handle(struct drm_framebuffer *fb, - struct drm_file *file_priv, - unsigned int *handle) +static int omap_framebuffer_dirty(struct drm_framebuffer *fb, + struct drm_file *file_priv, + unsigned flags, unsigned color, + struct drm_clip_rect *clips, + unsigned num_clips) { - struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); - return drm_gem_handle_create(file_priv, - omap_fb->planes[0].bo, handle); -} - -static void omap_framebuffer_destroy(struct drm_framebuffer *fb) -{ - struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); - int i, n = fb->format->num_planes; + struct drm_crtc *crtc; - DBG("destroy: FB ID: %d (%p)", fb->base.id, fb); + drm_modeset_lock_all(fb->dev); - drm_framebuffer_cleanup(fb); + drm_for_each_crtc(crtc, fb->dev) + omap_crtc_flush(crtc); - for (i = 0; i < n; i++) { - struct plane *plane = &omap_fb->planes[i]; - - drm_gem_object_unreference_unlocked(plane->bo); - } + drm_modeset_unlock_all(fb->dev); - kfree(omap_fb); + return 0; } static const struct drm_framebuffer_funcs omap_framebuffer_funcs = { - .create_handle = omap_framebuffer_create_handle, - .destroy = omap_framebuffer_destroy, + .create_handle = drm_gem_fb_create_handle, + .dirty = omap_framebuffer_dirty, + .destroy = drm_gem_fb_destroy, }; -static uint32_t get_linear_addr(struct plane *plane, +static u32 get_linear_addr(struct drm_framebuffer *fb, const struct drm_format_info *format, int n, int x, int y) { - uint32_t offset; + struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); + struct plane *plane = &omap_fb->planes[n]; + u32 offset; - offset = plane->offset + offset = fb->offsets[n] + (x * format->cpp[n] / (n == 0 ? 1 : format->hsub)) - + (y * plane->pitch / (n == 0 ? 1 : format->vsub)); + + (y * fb->pitches[n] / (n == 0 ? 1 : format->vsub)); return plane->dma_addr + offset; } bool omap_framebuffer_supports_rotation(struct drm_framebuffer *fb) { - struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); - struct plane *plane = &omap_fb->planes[0]; - - return omap_gem_flags(plane->bo) & OMAP_BO_TILED; + return omap_gem_flags(fb->obj[0]) & OMAP_BO_TILED_MASK; } /* Note: DRM rotates counter-clockwise, TILER & DSS rotates clockwise */ -static uint32_t drm_rotation_to_tiler(unsigned int drm_rot) +static u32 drm_rotation_to_tiler(unsigned int drm_rot) { - uint32_t orient; + u32 orient; switch (drm_rot & DRM_MODE_ROTATE_MASK) { default: @@ -155,12 +135,13 @@ static uint32_t drm_rotation_to_tiler(unsigned int drm_rot) /* update ovl info for scanout, handles cases of multi-planar fb's, etc. */ void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, - struct drm_plane_state *state, struct omap_overlay_info *info) + struct drm_plane_state *state, + struct omap_overlay_info *info, + struct omap_overlay_info *r_info) { struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); const struct drm_format_info *format = omap_fb->format; - struct plane *plane = &omap_fb->planes[0]; - uint32_t x, y, orient = 0; + u32 x, y, orient = 0; info->fourcc = fb->format->format; @@ -178,9 +159,9 @@ void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, x = state->src_x >> 16; y = state->src_y >> 16; - if (omap_gem_flags(plane->bo) & OMAP_BO_TILED) { - uint32_t w = state->src_w >> 16; - uint32_t h = state->src_h >> 16; + if (omap_gem_flags(fb->obj[0]) & OMAP_BO_TILED_MASK) { + u32 w = state->src_w >> 16; + u32 h = state->src_h >> 16; orient = drm_rotation_to_tiler(state->rotation); @@ -203,12 +184,12 @@ void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, x += w - 1; /* Note: x and y are in TILER units, not pixels */ - omap_gem_rotated_dma_addr(plane->bo, orient, x, y, + omap_gem_rotated_dma_addr(fb->obj[0], orient, x, y, &info->paddr); info->rotation_type = OMAP_DSS_ROT_TILER; info->rotation = state->rotation ?: DRM_MODE_ROTATE_0; /* Note: stride in TILER units, not pixels */ - info->screen_width = omap_gem_tiled_stride(plane->bo, orient); + info->screen_width = omap_gem_tiled_stride(fb->obj[0], orient); } else { switch (state->rotation & DRM_MODE_ROTATE_MASK) { case 0: @@ -223,28 +204,55 @@ void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, break; } - info->paddr = get_linear_addr(plane, format, 0, x, y); + info->paddr = get_linear_addr(fb, format, 0, x, y); info->rotation_type = OMAP_DSS_ROT_NONE; info->rotation = DRM_MODE_ROTATE_0; - info->screen_width = plane->pitch; + info->screen_width = fb->pitches[0]; } /* convert to pixels: */ info->screen_width /= format->cpp[0]; if (fb->format->format == DRM_FORMAT_NV12) { - plane = &omap_fb->planes[1]; - if (info->rotation_type == OMAP_DSS_ROT_TILER) { - WARN_ON(!(omap_gem_flags(plane->bo) & OMAP_BO_TILED)); - omap_gem_rotated_dma_addr(plane->bo, orient, x/2, y/2, + WARN_ON(!(omap_gem_flags(fb->obj[1]) & OMAP_BO_TILED_MASK)); + omap_gem_rotated_dma_addr(fb->obj[1], orient, x/2, y/2, &info->p_uv_addr); } else { - info->p_uv_addr = get_linear_addr(plane, format, 1, x, y); + info->p_uv_addr = get_linear_addr(fb, format, 1, x, y); } } else { info->p_uv_addr = 0; } + + if (r_info) { + info->width /= 2; + info->out_width /= 2; + + *r_info = *info; + + if (fb->format->is_yuv) { + if (info->width & 1) { + info->width++; + r_info->width--; + } + + if (info->out_width & 1) { + info->out_width++; + r_info->out_width--; + } + } + + r_info->pos_x = info->pos_x + info->out_width; + + r_info->paddr = get_linear_addr(fb, format, 0, + x + info->width, y); + if (fb->format->format == DRM_FORMAT_NV12) { + r_info->p_uv_addr = + get_linear_addr(fb, format, 1, + x + info->width, y); + } + } } /* pin, prepare for scanout: */ @@ -263,10 +271,10 @@ int omap_framebuffer_pin(struct drm_framebuffer *fb) for (i = 0; i < n; i++) { struct plane *plane = &omap_fb->planes[i]; - ret = omap_gem_pin(plane->bo, &plane->dma_addr); + ret = omap_gem_pin(fb->obj[i], &plane->dma_addr); if (ret) goto fail; - omap_gem_dma_sync_buffer(plane->bo, DMA_TO_DEVICE); + omap_gem_dma_sync_buffer(fb->obj[i], DMA_TO_DEVICE); } omap_fb->pin_count++; @@ -278,7 +286,7 @@ int omap_framebuffer_pin(struct drm_framebuffer *fb) fail: for (i--; i >= 0; i--) { struct plane *plane = &omap_fb->planes[i]; - omap_gem_unpin(plane->bo); + omap_gem_unpin(fb->obj[i]); plane->dma_addr = 0; } @@ -304,62 +312,34 @@ void omap_framebuffer_unpin(struct drm_framebuffer *fb) for (i = 0; i < n; i++) { struct plane *plane = &omap_fb->planes[i]; - omap_gem_unpin(plane->bo); + omap_gem_unpin(fb->obj[i]); plane->dma_addr = 0; } mutex_unlock(&omap_fb->lock); } -/* iterate thru all the connectors, returning ones that are attached - * to the same fb.. - */ -struct drm_connector *omap_framebuffer_get_next_connector( - struct drm_framebuffer *fb, struct drm_connector *from) -{ - struct drm_device *dev = fb->dev; - struct list_head *connector_list = &dev->mode_config.connector_list; - struct drm_connector *connector = from; - - if (!from) - return list_first_entry_or_null(connector_list, typeof(*from), - head); - - list_for_each_entry_from(connector, connector_list, head) { - if (connector != from) { - struct drm_encoder *encoder = connector->encoder; - struct drm_crtc *crtc = encoder ? encoder->crtc : NULL; - if (crtc && crtc->primary->fb == fb) - return connector; - - } - } - - return NULL; -} - #ifdef CONFIG_DEBUG_FS void omap_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m) { - struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); int i, n = fb->format->num_planes; seq_printf(m, "fb: %dx%d@%4.4s\n", fb->width, fb->height, (char *)&fb->format->format); for (i = 0; i < n; i++) { - struct plane *plane = &omap_fb->planes[i]; seq_printf(m, " %d: offset=%d pitch=%d, obj: ", - i, plane->offset, plane->pitch); - omap_gem_describe(plane->bo, m); + i, fb->offsets[n], fb->pitches[i]); + omap_gem_describe(fb->obj[i], m); } } #endif struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev, - struct drm_file *file, const struct drm_mode_fb_cmd2 *mode_cmd) + struct drm_file *file, const struct drm_format_info *info, + const struct drm_mode_fb_cmd2 *mode_cmd) { - unsigned int num_planes = drm_format_num_planes(mode_cmd->pixel_format); + unsigned int num_planes = info->num_planes; struct drm_gem_object *bos[4]; struct drm_framebuffer *fb; int i; @@ -372,23 +352,23 @@ struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev, } } - fb = omap_framebuffer_init(dev, mode_cmd, bos); + fb = omap_framebuffer_init(dev, info, mode_cmd, bos); if (IS_ERR(fb)) goto error; return fb; error: - while (--i > 0) - drm_gem_object_unreference_unlocked(bos[i]); + while (--i >= 0) + drm_gem_object_put(bos[i]); return fb; } struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, + const struct drm_format_info *info, const struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos) { - const struct drm_format_info *format = NULL; struct omap_framebuffer *omap_fb = NULL; struct drm_framebuffer *fb = NULL; unsigned int pitch = mode_cmd->pitches[0]; @@ -398,14 +378,12 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, dev, mode_cmd, mode_cmd->width, mode_cmd->height, (char *)&mode_cmd->pixel_format); - format = drm_format_info(mode_cmd->pixel_format); - for (i = 0; i < ARRAY_SIZE(formats); i++) { if (formats[i] == mode_cmd->pixel_format) break; } - if (!format || i == ARRAY_SIZE(formats)) { + if (i == ARRAY_SIZE(formats)) { dev_dbg(dev->dev, "unsupported pixel format: %4.4s\n", (char *)&mode_cmd->pixel_format); ret = -EINVAL; @@ -419,7 +397,7 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, } fb = &omap_fb->base; - omap_fb->format = format; + omap_fb->format = info; mutex_init(&omap_fb->lock); /* @@ -427,23 +405,23 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, * that the two planes of multiplane formats need the same number of * bytes per pixel. */ - if (format->num_planes == 2 && pitch != mode_cmd->pitches[1]) { + if (info->num_planes == 2 && pitch != mode_cmd->pitches[1]) { dev_dbg(dev->dev, "pitches differ between planes 0 and 1\n"); ret = -EINVAL; goto fail; } - if (pitch % format->cpp[0]) { + if (pitch % info->cpp[0]) { dev_dbg(dev->dev, "buffer pitch (%u bytes) is not a multiple of pixel size (%u bytes)\n", - pitch, format->cpp[0]); + pitch, info->cpp[0]); ret = -EINVAL; goto fail; } - for (i = 0; i < format->num_planes; i++) { + for (i = 0; i < info->num_planes; i++) { struct plane *plane = &omap_fb->planes[i]; - unsigned int vsub = i == 0 ? 1 : format->vsub; + unsigned int vsub = i == 0 ? 1 : info->vsub; unsigned int size; size = pitch * mode_cmd->height / vsub; @@ -456,13 +434,11 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, goto fail; } - plane->bo = bos[i]; - plane->offset = mode_cmd->offsets[i]; - plane->pitch = pitch; + fb->obj[i] = bos[i]; plane->dma_addr = 0; } - drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd); + drm_helper_mode_fill_fb_struct(dev, fb, info, mode_cmd); ret = drm_framebuffer_init(dev, fb, &omap_framebuffer_funcs); if (ret) { diff --git a/drivers/gpu/drm/omapdrm/omap_fb.h b/drivers/gpu/drm/omapdrm/omap_fb.h new file mode 100644 index 000000000000..e6010302a22b --- /dev/null +++ b/drivers/gpu/drm/omapdrm/omap_fb.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * omap_fb.h -- OMAP DRM Framebuffer + * + * Copyright (C) 2011 Texas Instruments + * Author: Rob Clark <rob@ti.com> + */ + +#ifndef __OMAPDRM_FB_H__ +#define __OMAPDRM_FB_H__ + +struct drm_connector; +struct drm_device; +struct drm_file; +struct drm_framebuffer; +struct drm_format_info; +struct drm_gem_object; +struct drm_mode_fb_cmd2; +struct drm_plane_state; +struct omap_overlay_info; +struct seq_file; + +struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev, + struct drm_file *file, const struct drm_format_info *info, + const struct drm_mode_fb_cmd2 *mode_cmd); +struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, + const struct drm_format_info *info, + const struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos); +int omap_framebuffer_pin(struct drm_framebuffer *fb); +void omap_framebuffer_unpin(struct drm_framebuffer *fb); +void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, + struct drm_plane_state *state, + struct omap_overlay_info *info, + struct omap_overlay_info *r_info); +bool omap_framebuffer_supports_rotation(struct drm_framebuffer *fb); +void omap_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m); + +#endif /* __OMAPDRM_FB_H__ */ diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c index daf81a0a2899..ca3fb186bf19 100644 --- a/drivers/gpu/drm/omapdrm/omap_fbdev.c +++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c @@ -1,26 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0-only /* - * drivers/gpu/drm/omapdrm/omap_fbdev.c - * - * Copyright (C) 2011 Texas Instruments + * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/ * Author: Rob Clark <rob@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 <drm/drm_crtc.h> +#include <linux/fb.h> + +#include <drm/clients/drm_client_setup.h> +#include <drm/drm_drv.h> +#include <drm/drm_crtc_helper.h> #include <drm/drm_fb_helper.h> +#include <drm/drm_file.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_framebuffer.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_managed.h> +#include <drm/drm_print.h> +#include <drm/drm_util.h> #include "omap_drv.h" +#include "omap_fbdev.h" MODULE_PARM_DESC(ywrap, "Enable ywrap scrolling (omap44xx and later, default 'y')"); static bool ywrap_enabled = true; @@ -30,12 +29,8 @@ module_param_named(ywrap, ywrap_enabled, bool, 0644); * fbdev funcs, to implement legacy fbdev interface on top of drm driver */ -#define to_omap_fbdev(x) container_of(x, struct omap_fbdev, base) - struct omap_fbdev { - struct drm_fb_helper base; - struct drm_framebuffer *fb; - struct drm_gem_object *bo; + struct drm_device *dev; bool ywrap_enabled; /* for deferred dmm roll when getting called in atomic ctx */ @@ -47,32 +42,39 @@ static struct drm_fb_helper *get_fb(struct fb_info *fbi); static void pan_worker(struct work_struct *work) { struct omap_fbdev *fbdev = container_of(work, struct omap_fbdev, work); - struct fb_info *fbi = fbdev->base.fbdev; + struct drm_fb_helper *helper = fbdev->dev->fb_helper; + struct fb_info *fbi = helper->info; + struct drm_gem_object *bo = drm_gem_fb_get_obj(helper->fb, 0); int npages; /* DMM roll shifts in 4K pages: */ npages = fbi->fix.line_length >> PAGE_SHIFT; - omap_gem_roll(fbdev->bo, fbi->var.yoffset * npages); + omap_gem_roll(bo, fbi->var.yoffset * npages); } -static int omap_fbdev_pan_display(struct fb_var_screeninfo *var, - struct fb_info *fbi) +FB_GEN_DEFAULT_DEFERRED_DMAMEM_OPS(omap_fbdev, + drm_fb_helper_damage_range, + drm_fb_helper_damage_area) + +static int omap_fbdev_pan_display(struct fb_var_screeninfo *var, struct fb_info *fbi) { struct drm_fb_helper *helper = get_fb(fbi); - struct omap_fbdev *fbdev = to_omap_fbdev(helper); + struct omap_drm_private *priv; + struct omap_fbdev *fbdev; if (!helper) goto fallback; + priv = helper->dev->dev_private; + fbdev = priv->fbdev; + if (!fbdev->ywrap_enabled) goto fallback; - if (drm_can_sleep()) { + if (drm_can_sleep()) pan_worker(&fbdev->work); - } else { - struct omap_drm_private *priv = helper->dev->dev_private; + else queue_work(priv->wq, &fbdev->work); - } return 0; @@ -80,32 +82,83 @@ fallback: return drm_fb_helper_pan_display(var, fbi); } -static struct fb_ops omap_fb_ops = { - .owner = THIS_MODULE, - DRM_FB_HELPER_DEFAULT_OPS, +static int omap_fbdev_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) +{ + vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags)); - /* Note: to properly handle manual update displays, we wrap the - * basic fbdev ops which write to the framebuffer - */ - .fb_read = drm_fb_helper_sys_read, - .fb_write = drm_fb_helper_sys_write, - .fb_fillrect = drm_fb_helper_sys_fillrect, - .fb_copyarea = drm_fb_helper_sys_copyarea, - .fb_imageblit = drm_fb_helper_sys_imageblit, + return fb_deferred_io_mmap(info, vma); +} + +static void omap_fbdev_fb_destroy(struct fb_info *info) +{ + struct drm_fb_helper *helper = info->par; + struct drm_framebuffer *fb = helper->fb; + struct drm_gem_object *bo = drm_gem_fb_get_obj(fb, 0); + + DBG(); + + fb_deferred_io_cleanup(info); + drm_fb_helper_fini(helper); + + omap_gem_unpin(bo); + drm_framebuffer_remove(fb); + drm_client_release(&helper->client); +} + +/* + * For now, we cannot use FB_DEFAULT_DEFERRED_OPS and fb_deferred_io_mmap() + * because we use write-combine. + */ +static const struct fb_ops omap_fb_ops = { + .owner = THIS_MODULE, + __FB_DEFAULT_DEFERRED_OPS_RDWR(omap_fbdev), + .fb_check_var = drm_fb_helper_check_var, + .fb_set_par = drm_fb_helper_set_par, + .fb_setcmap = drm_fb_helper_setcmap, + .fb_blank = drm_fb_helper_blank, .fb_pan_display = omap_fbdev_pan_display, + __FB_DEFAULT_DEFERRED_OPS_DRAW(omap_fbdev), + .fb_ioctl = drm_fb_helper_ioctl, + .fb_mmap = omap_fbdev_fb_mmap, + .fb_destroy = omap_fbdev_fb_destroy, }; -static int omap_fbdev_create(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes) +static int omap_fbdev_dirty(struct drm_fb_helper *helper, struct drm_clip_rect *clip) +{ + if (!(clip->x1 < clip->x2 && clip->y1 < clip->y2)) + return 0; + + if (helper->fb->funcs->dirty) + return helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, clip, 1); + + return 0; +} + +static const struct drm_fb_helper_funcs omap_fbdev_helper_funcs = { + .fb_dirty = omap_fbdev_dirty, +}; + +static struct drm_fb_helper *get_fb(struct fb_info *fbi) +{ + if (!fbi || strcmp(fbi->fix.id, MODULE_NAME)) { + /* these are not the fb's you're looking for */ + return NULL; + } + return fbi->par; +} + +int omap_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, + struct drm_fb_helper_surface_size *sizes) { - struct omap_fbdev *fbdev = to_omap_fbdev(helper); struct drm_device *dev = helper->dev; struct omap_drm_private *priv = dev->dev_private; + struct omap_fbdev *fbdev = priv->fbdev; + struct fb_info *fbi = helper->info; struct drm_framebuffer *fb = NULL; union omap_gem_size gsize; - struct fb_info *fbi = NULL; struct drm_mode_fb_cmd2 mode_cmd = {0}; + struct drm_gem_object *bo; dma_addr_t dma_addr; int ret; @@ -136,20 +189,23 @@ static int omap_fbdev_create(struct drm_fb_helper *helper, .bytes = PAGE_ALIGN(mode_cmd.pitches[0] * mode_cmd.height), }; DBG("allocating %d bytes for fb %d", gsize.bytes, dev->primary->index); - fbdev->bo = omap_gem_new(dev, gsize, OMAP_BO_SCANOUT | OMAP_BO_WC); - if (!fbdev->bo) { + bo = omap_gem_new(dev, gsize, OMAP_BO_SCANOUT | OMAP_BO_WC); + if (!bo) { dev_err(dev->dev, "failed to allocate buffer object\n"); ret = -ENOMEM; goto fail; } - fb = omap_framebuffer_init(dev, &mode_cmd, &fbdev->bo); + fb = omap_framebuffer_init(dev, + drm_get_format_info(dev, mode_cmd.pixel_format, + mode_cmd.modifier[0]), + &mode_cmd, &bo); if (IS_ERR(fb)) { dev_err(dev->dev, "failed to allocate fb\n"); /* note: if fb creation failed, we can't rely on fb destroy * to unref the bo: */ - drm_gem_object_unreference_unlocked(fbdev->bo); + drm_gem_object_put(bo); ret = PTR_ERR(fb); goto fail; } @@ -162,42 +218,36 @@ static int omap_fbdev_create(struct drm_fb_helper *helper, * to it). Then we just need to be sure that we are able to re- * pin it in case of an opps. */ - ret = omap_gem_pin(fbdev->bo, &dma_addr); + ret = omap_gem_pin(bo, &dma_addr); if (ret) { dev_err(dev->dev, "could not pin framebuffer\n"); ret = -ENOMEM; goto fail; } - mutex_lock(&dev->struct_mutex); - - fbi = drm_fb_helper_alloc_fbi(helper); - if (IS_ERR(fbi)) { - dev_err(dev->dev, "failed to allocate fb info\n"); - ret = PTR_ERR(fbi); - goto fail_unlock; - } - DBG("fbi=%p, dev=%p", fbi, dev); - fbdev->fb = fb; + helper->funcs = &omap_fbdev_helper_funcs; helper->fb = fb; - fbi->par = helper; - fbi->flags = FBINFO_DEFAULT; fbi->fbops = &omap_fb_ops; - strcpy(fbi->fix.id, MODULE_NAME); + drm_fb_helper_fill_info(fbi, helper, sizes); - drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->format->depth); - drm_fb_helper_fill_var(fbi, helper, sizes->fb_width, sizes->fb_height); + fbi->flags |= FBINFO_VIRTFB; + fbi->screen_buffer = omap_gem_vaddr(bo); + fbi->screen_size = bo->size; + fbi->fix.smem_start = dma_addr; + fbi->fix.smem_len = bo->size; - dev->mode_config.fb_base = dma_addr; + /* deferred I/O */ + helper->fbdefio.delay = HZ / 20; + helper->fbdefio.deferred_io = drm_fb_helper_deferred_io; - fbi->screen_base = omap_gem_vaddr(fbdev->bo); - fbi->screen_size = fbdev->bo->size; - fbi->fix.smem_start = dma_addr; - fbi->fix.smem_len = fbdev->bo->size; + fbi->fbdefio = &helper->fbdefio; + ret = fb_deferred_io_init(fbi); + if (ret) + goto fail; /* if we have DMM, then we can use it for scrolling by just * shuffling pages around in DMM rather than doing sw blit. @@ -210,14 +260,10 @@ static int omap_fbdev_create(struct drm_fb_helper *helper, DBG("par=%p, %dx%d", fbi->par, fbi->var.xres, fbi->var.yres); - DBG("allocated %dx%d fb", fbdev->fb->width, fbdev->fb->height); - - mutex_unlock(&dev->struct_mutex); + DBG("allocated %dx%d fb", fb->width, fb->height); return 0; -fail_unlock: - mutex_unlock(&dev->struct_mutex); fail: if (ret) { @@ -228,88 +274,21 @@ fail: return ret; } -static const struct drm_fb_helper_funcs omap_fb_helper_funcs = { - .fb_probe = omap_fbdev_create, -}; - -static struct drm_fb_helper *get_fb(struct fb_info *fbi) -{ - if (!fbi || strcmp(fbi->fix.id, MODULE_NAME)) { - /* these are not the fb's you're looking for */ - return NULL; - } - return fbi->par; -} - -/* initialize fbdev helper */ -struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev) +void omap_fbdev_setup(struct drm_device *dev) { struct omap_drm_private *priv = dev->dev_private; - struct omap_fbdev *fbdev = NULL; - struct drm_fb_helper *helper; - int ret = 0; - - fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL); - if (!fbdev) - goto fail; - - INIT_WORK(&fbdev->work, pan_worker); - - helper = &fbdev->base; - - drm_fb_helper_prepare(dev, helper, &omap_fb_helper_funcs); - - ret = drm_fb_helper_init(dev, helper, priv->num_connectors); - if (ret) { - dev_err(dev->dev, "could not init fbdev: ret=%d\n", ret); - goto fail; - } - - ret = drm_fb_helper_single_add_all_connectors(helper); - if (ret) - goto fini; - - ret = drm_fb_helper_initial_config(helper, 32); - if (ret) - goto fini; - - priv->fbdev = helper; - - return helper; - -fini: - drm_fb_helper_fini(helper); -fail: - kfree(fbdev); - - dev_warn(dev->dev, "omap_fbdev_init failed\n"); - /* well, limp along without an fbdev.. maybe X11 will work? */ - - return NULL; -} - -void omap_fbdev_free(struct drm_device *dev) -{ - struct omap_drm_private *priv = dev->dev_private; - struct drm_fb_helper *helper = priv->fbdev; struct omap_fbdev *fbdev; - DBG(); - - drm_fb_helper_unregister_fbi(helper); + drm_WARN(dev, !dev->registered, "Device has not been registered.\n"); + drm_WARN(dev, dev->fb_helper, "fb_helper is already set!\n"); - drm_fb_helper_fini(helper); - - fbdev = to_omap_fbdev(priv->fbdev); - - /* unpin the GEM object pinned in omap_fbdev_create() */ - omap_gem_unpin(fbdev->bo); - - /* this will free the backing object */ - if (fbdev->fb) - drm_framebuffer_remove(fbdev->fb); + fbdev = drmm_kzalloc(dev, sizeof(*fbdev), GFP_KERNEL); + if (!fbdev) + return; + fbdev->dev = dev; + INIT_WORK(&fbdev->work, pan_worker); - kfree(fbdev); + priv->fbdev = fbdev; - priv->fbdev = NULL; + drm_client_setup(dev, NULL); } diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.h b/drivers/gpu/drm/omapdrm/omap_fbdev.h new file mode 100644 index 000000000000..283e35b42ada --- /dev/null +++ b/drivers/gpu/drm/omapdrm/omap_fbdev.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * omap_fbdev.h -- OMAP DRM FBDEV Compatibility + * + * Copyright (C) 2011 Texas Instruments + * Author: Rob Clark <rob@ti.com> + */ + +#ifndef __OMAPDRM_FBDEV_H__ +#define __OMAPDRM_FBDEV_H__ + +struct drm_device; +struct drm_fb_helper; +struct drm_fb_helper_surface_size; + +#ifdef CONFIG_DRM_FBDEV_EMULATION +int omap_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, + struct drm_fb_helper_surface_size *sizes); +#define OMAP_FBDEV_DRIVER_OPS \ + .fbdev_probe = omap_fbdev_driver_fbdev_probe +void omap_fbdev_setup(struct drm_device *dev); +#else +#define OMAP_FBDEV_DRIVER_OPS \ + .fbdev_probe = NULL +static inline void omap_fbdev_setup(struct drm_device *dev) +{ +} +#endif + +#endif /* __OMAPDRM_FBDEV_H__ */ diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c index 5c5c86ddd6f4..71e79f53489a 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem.c +++ b/drivers/gpu/drm/omapdrm/omap_gem.c @@ -1,27 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-only /* - * drivers/gpu/drm/omapdrm/omap_gem.c - * - * Copyright (C) 2011 Texas Instruments + * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/ * Author: Rob Clark <rob.clark@linaro.org> - * - * 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/dma-mapping.h> #include <linux/seq_file.h> #include <linux/shmem_fs.h> #include <linux/spinlock.h> -#include <linux/pfn_t.h> +#include <linux/vmalloc.h> +#include <drm/drm_dumb_buffers.h> +#include <drm/drm_prime.h> +#include <drm/drm_print.h> #include <drm/drm_vma_manager.h> #include "omap_drv.h" @@ -41,13 +32,16 @@ struct omap_gem_object { struct list_head mm_list; - uint32_t flags; + u32 flags; /** width/height for tiled formats (rounded up to slot boundaries) */ - uint16_t width, height; + u16 width, height; /** roll applied when mapping to DMM */ - uint32_t roll; + u32 roll; + + /** protects pin_cnt, block, pages, dma_addrs and vaddr */ + struct mutex lock; /** * dma_addr contains the buffer DMA address. It is valid for @@ -56,26 +50,26 @@ struct omap_gem_object { * OMAP_BO_MEM_DMA_API flag set) * * - buffers imported from dmabuf (with the OMAP_BO_MEM_DMABUF flag set) - * if they are physically contiguous (when sgt->orig_nents == 1) + * if they are physically contiguous * - * - buffers mapped through the TILER when dma_addr_cnt is not zero, in - * which case the DMA address points to the TILER aperture + * - buffers mapped through the TILER when pin_cnt is not zero, in which + * case the DMA address points to the TILER aperture * * Physically contiguous buffers have their DMA address equal to the * physical address as we don't remap those buffers through the TILER. * * Buffers mapped to the TILER have their DMA address pointing to the - * TILER aperture. As TILER mappings are refcounted (through - * dma_addr_cnt) the DMA address must be accessed through omap_gem_pin() - * to ensure that the mapping won't disappear unexpectedly. References - * must be released with omap_gem_unpin(). + * TILER aperture. As TILER mappings are refcounted (through pin_cnt) + * the DMA address must be accessed through omap_gem_pin() to ensure + * that the mapping won't disappear unexpectedly. References must be + * released with omap_gem_unpin(). */ dma_addr_t dma_addr; /** - * # of users of dma_addr + * # of users */ - uint32_t dma_addr_cnt; + refcount_t pin_cnt; /** * If the buffer has been imported from a dmabuf the OMAP_DB_DMABUF flag @@ -139,14 +133,12 @@ struct omap_drm_usergart { */ /** get mmap offset */ -static uint64_t mmap_offset(struct drm_gem_object *obj) +u64 omap_gem_mmap_offset(struct drm_gem_object *obj) { struct drm_device *dev = obj->dev; int ret; size_t size; - WARN_ON(!mutex_is_locked(&dev->struct_mutex)); - /* Make it mmapable */ size = omap_gem_mmap_size(obj); ret = drm_gem_create_mmap_offset_size(obj, size); @@ -158,12 +150,18 @@ static uint64_t mmap_offset(struct drm_gem_object *obj) return drm_vma_node_offset_addr(&obj->vma_node); } -static bool is_contiguous(struct omap_gem_object *omap_obj) +static bool omap_gem_sgt_is_contiguous(struct sg_table *sgt, size_t size) +{ + return !(drm_prime_get_contiguous_size(sgt) < size); +} + +static bool omap_gem_is_contiguous(struct omap_gem_object *omap_obj) { if (omap_obj->flags & OMAP_BO_MEM_DMA_API) return true; - if ((omap_obj->flags & OMAP_BO_MEM_DMABUF) && omap_obj->sgt->nents == 1) + if ((omap_obj->flags & OMAP_BO_MEM_DMABUF) && + omap_gem_sgt_is_contiguous(omap_obj->sgt, omap_obj->base.size)) return true; return false; @@ -173,14 +171,14 @@ static bool is_contiguous(struct omap_gem_object *omap_obj) * Eviction */ -static void evict_entry(struct drm_gem_object *obj, +static void omap_gem_evict_entry(struct drm_gem_object *obj, enum tiler_fmt fmt, struct omap_drm_usergart_entry *entry) { struct omap_gem_object *omap_obj = to_omap_bo(obj); struct omap_drm_private *priv = obj->dev->dev_private; int n = priv->usergart[fmt].height; size_t size = PAGE_SIZE * n; - loff_t off = mmap_offset(obj) + + loff_t off = omap_gem_mmap_offset(obj) + (entry->obj_pgoff << PAGE_SHIFT); const int m = DIV_ROUND_UP(omap_obj->width << fmt, PAGE_SIZE); @@ -201,12 +199,12 @@ static void evict_entry(struct drm_gem_object *obj, } /* Evict a buffer from usergart, if it is mapped there */ -static void evict(struct drm_gem_object *obj) +static void omap_gem_evict(struct drm_gem_object *obj) { struct omap_gem_object *omap_obj = to_omap_bo(obj); struct omap_drm_private *priv = obj->dev->dev_private; - if (omap_obj->flags & OMAP_BO_TILED) { + if (omap_obj->flags & OMAP_BO_TILED_MASK) { enum tiler_fmt fmt = gem2fmt(omap_obj->flags); int i; @@ -215,7 +213,7 @@ static void evict(struct drm_gem_object *obj) &priv->usergart[fmt].entry[i]; if (entry->obj == obj) - evict_entry(obj, fmt, entry); + omap_gem_evict_entry(obj, fmt, entry); } } } @@ -224,7 +222,10 @@ static void evict(struct drm_gem_object *obj) * Page Management */ -/** ensure backing pages are allocated */ +/* + * Ensure backing pages are allocated. Must be called with the omap_obj.lock + * held. + */ static int omap_gem_attach_pages(struct drm_gem_object *obj) { struct drm_device *dev = obj->dev; @@ -234,7 +235,14 @@ static int omap_gem_attach_pages(struct drm_gem_object *obj) int i, ret; dma_addr_t *addrs; - WARN_ON(omap_obj->pages); + lockdep_assert_held(&omap_obj->lock); + + /* + * If not using shmem (in which case backing pages don't need to be + * allocated) or if pages are already allocated we're done. + */ + if (!(omap_obj->flags & OMAP_BO_MEM_SHMEM) || omap_obj->pages) + return 0; pages = drm_gem_get_pages(obj); if (IS_ERR(pages)) { @@ -246,7 +254,7 @@ static int omap_gem_attach_pages(struct drm_gem_object *obj) * DSS, GPU, etc. are not cache coherent: */ if (omap_obj->flags & (OMAP_BO_WC|OMAP_BO_UNCACHED)) { - addrs = kmalloc(npages * sizeof(*addrs), GFP_KERNEL); + addrs = kmalloc_array(npages, sizeof(*addrs), GFP_KERNEL); if (!addrs) { ret = -ENOMEM; goto free_pages; @@ -270,7 +278,7 @@ static int omap_gem_attach_pages(struct drm_gem_object *obj) } } } else { - addrs = kzalloc(npages * sizeof(*addrs), GFP_KERNEL); + addrs = kcalloc(npages, sizeof(*addrs), GFP_KERNEL); if (!addrs) { ret = -ENOMEM; goto free_pages; @@ -290,35 +298,15 @@ free_pages: return ret; } -/* acquire pages when needed (for example, for DMA where physically - * contiguous buffer is not required - */ -static int get_pages(struct drm_gem_object *obj, struct page ***pages) -{ - struct omap_gem_object *omap_obj = to_omap_bo(obj); - int ret = 0; - - if ((omap_obj->flags & OMAP_BO_MEM_SHMEM) && !omap_obj->pages) { - ret = omap_gem_attach_pages(obj); - if (ret) { - dev_err(obj->dev->dev, "could not attach pages\n"); - return ret; - } - } - - /* TODO: even phys-contig.. we should have a list of pages? */ - *pages = omap_obj->pages; - - return 0; -} - -/** release backing pages */ +/* Release backing pages. Must be called with the omap_obj.lock held. */ static void omap_gem_detach_pages(struct drm_gem_object *obj) { struct omap_gem_object *omap_obj = to_omap_bo(obj); unsigned int npages = obj->size >> PAGE_SHIFT; unsigned int i; + lockdep_assert_held(&omap_obj->lock); + for (i = 0; i < npages; i++) { if (omap_obj->dma_addrs[i]) dma_unmap_page(obj->dev->dev, omap_obj->dma_addrs[i], @@ -333,27 +321,18 @@ static void omap_gem_detach_pages(struct drm_gem_object *obj) } /* get buffer flags */ -uint32_t omap_gem_flags(struct drm_gem_object *obj) +u32 omap_gem_flags(struct drm_gem_object *obj) { return to_omap_bo(obj)->flags; } -uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj) -{ - uint64_t offset; - mutex_lock(&obj->dev->struct_mutex); - offset = mmap_offset(obj); - mutex_unlock(&obj->dev->struct_mutex); - return offset; -} - /** get mmap size */ size_t omap_gem_mmap_size(struct drm_gem_object *obj) { struct omap_gem_object *omap_obj = to_omap_bo(obj); size_t size = obj->size; - if (omap_obj->flags & OMAP_BO_TILED) { + if (omap_obj->flags & OMAP_BO_TILED_MASK) { /* for tiled buffers, the virtual size has stride rounded up * to 4kb.. (to hide the fact that row n+1 might start 16kb or * 32kb later!). But we don't back the entire buffer with @@ -372,7 +351,7 @@ size_t omap_gem_mmap_size(struct drm_gem_object *obj) */ /* Normal handling for the case of faulting in non-tiled buffers */ -static int fault_1d(struct drm_gem_object *obj, +static vm_fault_t omap_gem_fault_1d(struct drm_gem_object *obj, struct vm_area_struct *vma, struct vm_fault *vmf) { struct omap_gem_object *omap_obj = to_omap_bo(obj); @@ -386,18 +365,18 @@ static int fault_1d(struct drm_gem_object *obj, omap_gem_cpu_sync_page(obj, pgoff); pfn = page_to_pfn(omap_obj->pages[pgoff]); } else { - BUG_ON(!is_contiguous(omap_obj)); + BUG_ON(!omap_gem_is_contiguous(omap_obj)); pfn = (omap_obj->dma_addr >> PAGE_SHIFT) + pgoff; } VERB("Inserting %p pfn %lx, pa %lx", (void *)vmf->address, pfn, pfn << PAGE_SHIFT); - return vm_insert_mixed(vma, vmf->address, __pfn_to_pfn_t(pfn, PFN_DEV)); + return vmf_insert_mixed(vma, vmf->address, pfn); } /* Special handling for the case of faulting in 2d tiled buffers */ -static int fault_2d(struct drm_gem_object *obj, +static vm_fault_t omap_gem_fault_2d(struct drm_gem_object *obj, struct vm_area_struct *vma, struct vm_fault *vmf) { struct omap_gem_object *omap_obj = to_omap_bo(obj); @@ -408,7 +387,8 @@ static int fault_2d(struct drm_gem_object *obj, unsigned long pfn; pgoff_t pgoff, base_pgoff; unsigned long vaddr; - int i, ret, slots; + int i, err, slots; + vm_fault_t ret = VM_FAULT_NOPAGE; /* * Note the height of the slot is also equal to the number of pages @@ -444,7 +424,7 @@ static int fault_2d(struct drm_gem_object *obj, /* evict previous buffer using this usergart entry, if any: */ if (entry->obj) - evict_entry(entry->obj, fmt, entry); + omap_gem_evict_entry(entry->obj, fmt, entry); entry->obj = obj; entry->obj_pgoff = base_pgoff; @@ -474,9 +454,10 @@ static int fault_2d(struct drm_gem_object *obj, memset(pages + slots, 0, sizeof(struct page *) * (n - slots)); - ret = tiler_pin(entry->block, pages, ARRAY_SIZE(pages), 0, true); - if (ret) { - dev_err(obj->dev->dev, "failed to pin: %d\n", ret); + err = tiler_pin(entry->block, pages, ARRAY_SIZE(pages), 0, true); + if (err) { + ret = vmf_error(err); + dev_err(obj->dev->dev, "failed to pin: %d\n", err); return ret; } @@ -486,7 +467,9 @@ static int fault_2d(struct drm_gem_object *obj, pfn, pfn << PAGE_SHIFT); for (i = n; i > 0; i--) { - vm_insert_mixed(vma, vaddr, __pfn_to_pfn_t(pfn, PFN_DEV)); + ret = vmf_insert_mixed(vma, vaddr, pfn); + if (ret & VM_FAULT_ERROR) + break; pfn += priv->usergart[fmt].stride_pfn; vaddr += PAGE_SIZE * m; } @@ -495,7 +478,7 @@ static int fault_2d(struct drm_gem_object *obj, priv->usergart[fmt].last = (priv->usergart[fmt].last + 1) % NUM_USERGART_ENTRIES; - return 0; + return ret; } /** @@ -510,24 +493,25 @@ static int fault_2d(struct drm_gem_object *obj, * vma->vm_private_data points to the GEM object that is backing this * mapping. */ -int omap_gem_fault(struct vm_fault *vmf) +static vm_fault_t omap_gem_fault(struct vm_fault *vmf) { struct vm_area_struct *vma = vmf->vma; struct drm_gem_object *obj = vma->vm_private_data; struct omap_gem_object *omap_obj = to_omap_bo(obj); - struct drm_device *dev = obj->dev; - struct page **pages; - int ret; + int err; + vm_fault_t ret; /* Make sure we don't parallel update on a fault, nor move or remove * something from beneath our feet */ - mutex_lock(&dev->struct_mutex); + mutex_lock(&omap_obj->lock); /* if a shmem backed object, make sure we have pages attached now */ - ret = get_pages(obj, &pages); - if (ret) + err = omap_gem_attach_pages(obj); + if (err) { + ret = vmf_error(err); goto fail; + } /* where should we do corresponding put_pages().. we are mapping * the original page, rather than thru a GART, so we can't rely @@ -535,52 +519,22 @@ int omap_gem_fault(struct vm_fault *vmf) * probably trigger put_pages()? */ - if (omap_obj->flags & OMAP_BO_TILED) - ret = fault_2d(obj, vma, vmf); + if (omap_obj->flags & OMAP_BO_TILED_MASK) + ret = omap_gem_fault_2d(obj, vma, vmf); else - ret = fault_1d(obj, vma, vmf); + ret = omap_gem_fault_1d(obj, vma, vmf); fail: - mutex_unlock(&dev->struct_mutex); - switch (ret) { - case 0: - case -ERESTARTSYS: - case -EINTR: - case -EBUSY: - /* - * EBUSY is ok: this just means that another thread - * already did the job. - */ - return VM_FAULT_NOPAGE; - case -ENOMEM: - return VM_FAULT_OOM; - default: - return VM_FAULT_SIGBUS; - } -} - -/** We override mainly to fix up some of the vm mapping flags.. */ -int omap_gem_mmap(struct file *filp, struct vm_area_struct *vma) -{ - int ret; - - ret = drm_gem_mmap(filp, vma); - if (ret) { - DBG("mmap failed: %d", ret); - return ret; - } - - return omap_gem_mmap_obj(vma->vm_private_data, vma); + mutex_unlock(&omap_obj->lock); + return ret; } -int omap_gem_mmap_obj(struct drm_gem_object *obj, - struct vm_area_struct *vma) +static int omap_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) { struct omap_gem_object *omap_obj = to_omap_bo(obj); - vma->vm_flags &= ~VM_PFNMAP; - vma->vm_flags |= VM_MIXEDMAP; + vm_flags_set(vma, VM_DONTEXPAND | VM_DONTDUMP | VM_IO | VM_MIXEDMAP); if (omap_obj->flags & OMAP_BO_WC) { vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags)); @@ -600,13 +554,14 @@ int omap_gem_mmap_obj(struct drm_gem_object *obj, * address_space (so unmap_mapping_range does what we want, * in particular in the case of mmap'd dmabufs) */ - fput(vma->vm_file); - vma->vm_pgoff = 0; - vma->vm_file = get_file(obj->filp); + vma->vm_pgoff -= drm_vma_node_start(&obj->vma_node); + vma_set_file(vma, obj->filp); vma->vm_page_prot = vm_get_page_prot(vma->vm_flags); } + vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot); + return 0; } @@ -616,7 +571,7 @@ int omap_gem_mmap_obj(struct drm_gem_object *obj, /** * omap_gem_dumb_create - create a dumb buffer - * @drm_file: our client file + * @file: our client file * @dev: our device * @args: the requested arguments copied from userspace * @@ -627,31 +582,30 @@ int omap_gem_mmap_obj(struct drm_gem_object *obj, int omap_gem_dumb_create(struct drm_file *file, struct drm_device *dev, struct drm_mode_create_dumb *args) { - union omap_gem_size gsize; - - args->pitch = DIV_ROUND_UP(args->width * args->bpp, 8); - - args->size = PAGE_ALIGN(args->pitch * args->height); + union omap_gem_size gsize = { }; + int ret; - gsize = (union omap_gem_size){ - .bytes = args->size, - }; + ret = drm_mode_size_dumb(dev, args, SZ_8, 0); + if (ret) + return ret; + gsize.bytes = args->size; return omap_gem_new_handle(dev, file, gsize, OMAP_BO_SCANOUT | OMAP_BO_WC, &args->handle); } /** - * omap_gem_dumb_map - buffer mapping for dumb interface + * omap_gem_dumb_map_offset - create an offset for a dumb buffer * @file: our drm client file * @dev: drm device * @handle: GEM handle to the object (from dumb_create) + * @offset: memory map offset placeholder * * Do the necessary setup to allow the mapping of the frame buffer * into user memory. We don't have to do much here at the moment. */ int omap_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev, - uint32_t handle, uint64_t *offset) + u32 handle, u64 *offset) { struct drm_gem_object *obj; int ret = 0; @@ -665,7 +619,7 @@ int omap_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev, *offset = omap_gem_mmap_offset(obj); - drm_gem_object_unreference_unlocked(obj); + drm_gem_object_put(obj); fail: return ret; @@ -677,10 +631,10 @@ fail: * * Call only from non-atomic contexts. */ -int omap_gem_roll(struct drm_gem_object *obj, uint32_t roll) +int omap_gem_roll(struct drm_gem_object *obj, u32 roll) { struct omap_gem_object *omap_obj = to_omap_bo(obj); - uint32_t npages = obj->size >> PAGE_SHIFT; + u32 npages = obj->size >> PAGE_SHIFT; int ret = 0; if (roll > npages) { @@ -690,21 +644,22 @@ int omap_gem_roll(struct drm_gem_object *obj, uint32_t roll) omap_obj->roll = roll; - mutex_lock(&obj->dev->struct_mutex); + mutex_lock(&omap_obj->lock); /* if we aren't mapped yet, we don't need to do anything */ if (omap_obj->block) { - struct page **pages; - ret = get_pages(obj, &pages); + ret = omap_gem_attach_pages(obj); if (ret) goto fail; - ret = tiler_pin(omap_obj->block, pages, npages, roll, true); + + ret = tiler_pin(omap_obj->block, omap_obj->pages, npages, + roll, true); if (ret) dev_err(obj->dev->dev, "could not repin: %d\n", ret); } fail: - mutex_unlock(&obj->dev->struct_mutex); + mutex_unlock(&omap_obj->lock); return ret; } @@ -723,7 +678,7 @@ fail: * the omap_obj->dma_addrs[i] is set to the DMA address, and the page is * unmapped from the CPU. */ -static inline bool is_cached_coherent(struct drm_gem_object *obj) +static inline bool omap_gem_is_cached_coherent(struct drm_gem_object *obj) { struct omap_gem_object *omap_obj = to_omap_bo(obj); @@ -739,7 +694,7 @@ void omap_gem_cpu_sync_page(struct drm_gem_object *obj, int pgoff) struct drm_device *dev = obj->dev; struct omap_gem_object *omap_obj = to_omap_bo(obj); - if (is_cached_coherent(obj)) + if (omap_gem_is_cached_coherent(obj)) return; if (omap_obj->dma_addrs[pgoff]) { @@ -759,7 +714,7 @@ void omap_gem_dma_sync_buffer(struct drm_gem_object *obj, struct page **pages = omap_obj->pages; bool dirty = false; - if (is_cached_coherent(obj)) + if (omap_gem_is_cached_coherent(obj)) return; for (i = 0; i < npages; i++) { @@ -785,6 +740,46 @@ void omap_gem_dma_sync_buffer(struct drm_gem_object *obj, } } +static int omap_gem_pin_tiler(struct drm_gem_object *obj) +{ + struct omap_gem_object *omap_obj = to_omap_bo(obj); + u32 npages = obj->size >> PAGE_SHIFT; + enum tiler_fmt fmt = gem2fmt(omap_obj->flags); + struct tiler_block *block; + int ret; + + BUG_ON(omap_obj->block); + + if (omap_obj->flags & OMAP_BO_TILED_MASK) { + block = tiler_reserve_2d(fmt, omap_obj->width, omap_obj->height, + PAGE_SIZE); + } else { + block = tiler_reserve_1d(obj->size); + } + + if (IS_ERR(block)) { + ret = PTR_ERR(block); + dev_err(obj->dev->dev, "could not remap: %d (%d)\n", ret, fmt); + goto fail; + } + + /* TODO: enable async refill.. */ + ret = tiler_pin(block, omap_obj->pages, npages, omap_obj->roll, true); + if (ret) { + tiler_release(block); + dev_err(obj->dev->dev, "could not pin: %d\n", ret); + goto fail; + } + + omap_obj->dma_addr = tiler_ssptr(block); + omap_obj->block = block; + + DBG("got dma address: %pad", &omap_obj->dma_addr); + +fail: + return ret; +} + /** * omap_gem_pin() - Pin a GEM object in memory * @obj: the GEM object @@ -805,85 +800,62 @@ int omap_gem_pin(struct drm_gem_object *obj, dma_addr_t *dma_addr) struct omap_gem_object *omap_obj = to_omap_bo(obj); int ret = 0; - mutex_lock(&obj->dev->struct_mutex); + mutex_lock(&omap_obj->lock); - if (!is_contiguous(omap_obj) && priv->has_dmm) { - if (omap_obj->dma_addr_cnt == 0) { - struct page **pages; - uint32_t npages = obj->size >> PAGE_SHIFT; - enum tiler_fmt fmt = gem2fmt(omap_obj->flags); - struct tiler_block *block; + if (!omap_gem_is_contiguous(omap_obj)) { + if (refcount_read(&omap_obj->pin_cnt) == 0) { - BUG_ON(omap_obj->block); + refcount_set(&omap_obj->pin_cnt, 1); - ret = get_pages(obj, &pages); + ret = omap_gem_attach_pages(obj); if (ret) goto fail; - if (omap_obj->flags & OMAP_BO_TILED) { - block = tiler_reserve_2d(fmt, - omap_obj->width, - omap_obj->height, 0); - } else { - block = tiler_reserve_1d(obj->size); - } - - if (IS_ERR(block)) { - ret = PTR_ERR(block); - dev_err(obj->dev->dev, - "could not remap: %d (%d)\n", ret, fmt); - goto fail; - } - - /* TODO: enable async refill.. */ - ret = tiler_pin(block, pages, npages, - omap_obj->roll, true); - if (ret) { - tiler_release(block); - dev_err(obj->dev->dev, - "could not pin: %d\n", ret); - goto fail; + if (omap_obj->flags & OMAP_BO_SCANOUT) { + if (priv->has_dmm) { + ret = omap_gem_pin_tiler(obj); + if (ret) + goto fail; + } } - - omap_obj->dma_addr = tiler_ssptr(block); - omap_obj->block = block; - - DBG("got dma address: %pad", &omap_obj->dma_addr); + } else { + refcount_inc(&omap_obj->pin_cnt); } + } - omap_obj->dma_addr_cnt++; - - *dma_addr = omap_obj->dma_addr; - } else if (is_contiguous(omap_obj)) { + if (dma_addr) *dma_addr = omap_obj->dma_addr; - } else { - ret = -EINVAL; - goto fail; - } fail: - mutex_unlock(&obj->dev->struct_mutex); + mutex_unlock(&omap_obj->lock); return ret; } /** - * omap_gem_unpin() - Unpin a GEM object from memory + * omap_gem_unpin_locked() - Unpin a GEM object from memory * @obj: the GEM object * - * Unpin the given GEM object previously pinned with omap_gem_pin(). Pins are - * reference-counted, the actualy unpin will only be performed when the number - * of calls to this function matches the number of calls to omap_gem_pin(). + * omap_gem_unpin() without locking. */ -void omap_gem_unpin(struct drm_gem_object *obj) +static void omap_gem_unpin_locked(struct drm_gem_object *obj) { + struct omap_drm_private *priv = obj->dev->dev_private; struct omap_gem_object *omap_obj = to_omap_bo(obj); int ret; - mutex_lock(&obj->dev->struct_mutex); - if (omap_obj->dma_addr_cnt > 0) { - omap_obj->dma_addr_cnt--; - if (omap_obj->dma_addr_cnt == 0) { + if (omap_gem_is_contiguous(omap_obj)) + return; + + if (refcount_dec_and_test(&omap_obj->pin_cnt)) { + if (omap_obj->sgt) { + sg_free_table(omap_obj->sgt); + kfree(omap_obj->sgt); + omap_obj->sgt = NULL; + } + if (!(omap_obj->flags & OMAP_BO_SCANOUT)) + return; + if (priv->has_dmm) { ret = tiler_unpin(omap_obj->block); if (ret) { dev_err(obj->dev->dev, @@ -898,36 +870,54 @@ void omap_gem_unpin(struct drm_gem_object *obj) omap_obj->block = NULL; } } +} - mutex_unlock(&obj->dev->struct_mutex); +/** + * omap_gem_unpin() - Unpin a GEM object from memory + * @obj: the GEM object + * + * Unpin the given GEM object previously pinned with omap_gem_pin(). Pins are + * reference-counted, the actual unpin will only be performed when the number + * of calls to this function matches the number of calls to omap_gem_pin(). + */ +void omap_gem_unpin(struct drm_gem_object *obj) +{ + struct omap_gem_object *omap_obj = to_omap_bo(obj); + + mutex_lock(&omap_obj->lock); + omap_gem_unpin_locked(obj); + mutex_unlock(&omap_obj->lock); } /* Get rotated scanout address (only valid if already pinned), at the * specified orientation and x,y offset from top-left corner of buffer * (only valid for tiled 2d buffers) */ -int omap_gem_rotated_dma_addr(struct drm_gem_object *obj, uint32_t orient, +int omap_gem_rotated_dma_addr(struct drm_gem_object *obj, u32 orient, int x, int y, dma_addr_t *dma_addr) { struct omap_gem_object *omap_obj = to_omap_bo(obj); int ret = -EINVAL; - mutex_lock(&obj->dev->struct_mutex); - if ((omap_obj->dma_addr_cnt > 0) && omap_obj->block && - (omap_obj->flags & OMAP_BO_TILED)) { + mutex_lock(&omap_obj->lock); + + if ((refcount_read(&omap_obj->pin_cnt) > 0) && omap_obj->block && + (omap_obj->flags & OMAP_BO_TILED_MASK)) { *dma_addr = tiler_tsptr(omap_obj->block, orient, x, y); ret = 0; } - mutex_unlock(&obj->dev->struct_mutex); + + mutex_unlock(&omap_obj->lock); + return ret; } /* Get tiler stride for the buffer (only valid for 2d tiled buffers) */ -int omap_gem_tiled_stride(struct drm_gem_object *obj, uint32_t orient) +int omap_gem_tiled_stride(struct drm_gem_object *obj, u32 orient) { struct omap_gem_object *omap_obj = to_omap_bo(obj); int ret = -EINVAL; - if (omap_obj->flags & OMAP_BO_TILED) + if (omap_obj->flags & OMAP_BO_TILED_MASK) ret = tiler_stride(gem2fmt(omap_obj->flags), orient); return ret; } @@ -945,17 +935,27 @@ int omap_gem_tiled_stride(struct drm_gem_object *obj, uint32_t orient) int omap_gem_get_pages(struct drm_gem_object *obj, struct page ***pages, bool remap) { - int ret; - if (!remap) { - struct omap_gem_object *omap_obj = to_omap_bo(obj); - if (!omap_obj->pages) - return -ENOMEM; - *pages = omap_obj->pages; - return 0; + struct omap_gem_object *omap_obj = to_omap_bo(obj); + int ret = 0; + + mutex_lock(&omap_obj->lock); + + if (remap) { + ret = omap_gem_attach_pages(obj); + if (ret) + goto unlock; + } + + if (!omap_obj->pages) { + ret = -ENOMEM; + goto unlock; } - mutex_lock(&obj->dev->struct_mutex); - ret = get_pages(obj, pages); - mutex_unlock(&obj->dev->struct_mutex); + + *pages = omap_obj->pages; + +unlock: + mutex_unlock(&omap_obj->lock); + return ret; } @@ -969,24 +969,124 @@ int omap_gem_put_pages(struct drm_gem_object *obj) return 0; } +struct sg_table *omap_gem_get_sg(struct drm_gem_object *obj, + enum dma_data_direction dir) +{ + struct omap_gem_object *omap_obj = to_omap_bo(obj); + dma_addr_t addr; + struct sg_table *sgt; + struct scatterlist *sg; + unsigned int count, len, stride, i; + int ret; + + ret = omap_gem_pin(obj, &addr); + if (ret) + return ERR_PTR(ret); + + mutex_lock(&omap_obj->lock); + + sgt = omap_obj->sgt; + if (sgt) + goto out; + + sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); + if (!sgt) { + ret = -ENOMEM; + goto err_unpin; + } + + if (addr) { + if (omap_obj->flags & OMAP_BO_TILED_MASK) { + enum tiler_fmt fmt = gem2fmt(omap_obj->flags); + + len = omap_obj->width << (int)fmt; + count = omap_obj->height; + stride = tiler_stride(fmt, 0); + } else { + len = obj->size; + count = 1; + stride = 0; + } + } else { + count = obj->size >> PAGE_SHIFT; + } + + ret = sg_alloc_table(sgt, count, GFP_KERNEL); + if (ret) + goto err_free; + + /* this must be after omap_gem_pin() to ensure we have pages attached */ + omap_gem_dma_sync_buffer(obj, dir); + + if (addr) { + for_each_sg(sgt->sgl, sg, count, i) { + sg_set_page(sg, pfn_to_page(__phys_to_pfn(addr)), + len, offset_in_page(addr)); + sg_dma_address(sg) = addr; + sg_dma_len(sg) = len; + + addr += stride; + } + } else { + for_each_sg(sgt->sgl, sg, count, i) { + sg_set_page(sg, omap_obj->pages[i], PAGE_SIZE, 0); + sg_dma_address(sg) = omap_obj->dma_addrs[i]; + sg_dma_len(sg) = PAGE_SIZE; + } + } + + omap_obj->sgt = sgt; +out: + mutex_unlock(&omap_obj->lock); + return sgt; + +err_free: + kfree(sgt); +err_unpin: + mutex_unlock(&omap_obj->lock); + omap_gem_unpin(obj); + return ERR_PTR(ret); +} + +void omap_gem_put_sg(struct drm_gem_object *obj, struct sg_table *sgt) +{ + struct omap_gem_object *omap_obj = to_omap_bo(obj); + + if (WARN_ON(omap_obj->sgt != sgt)) + return; + + omap_gem_unpin(obj); +} + #ifdef CONFIG_DRM_FBDEV_EMULATION -/* Get kernel virtual address for CPU access.. this more or less only - * exists for omap_fbdev. This should be called with struct_mutex - * held. +/* + * Get kernel virtual address for CPU access.. this more or less only + * exists for omap_fbdev. */ void *omap_gem_vaddr(struct drm_gem_object *obj) { struct omap_gem_object *omap_obj = to_omap_bo(obj); - WARN_ON(!mutex_is_locked(&obj->dev->struct_mutex)); + void *vaddr; + int ret; + + mutex_lock(&omap_obj->lock); + if (!omap_obj->vaddr) { - struct page **pages; - int ret = get_pages(obj, &pages); - if (ret) - return ERR_PTR(ret); - omap_obj->vaddr = vmap(pages, obj->size >> PAGE_SHIFT, + ret = omap_gem_attach_pages(obj); + if (ret) { + vaddr = ERR_PTR(ret); + goto unlock; + } + + omap_obj->vaddr = vmap(omap_obj->pages, obj->size >> PAGE_SHIFT, VM_MAP, pgprot_writecombine(PAGE_KERNEL)); } - return omap_obj->vaddr; + + vaddr = omap_obj->vaddr; + +unlock: + mutex_unlock(&omap_obj->lock); + return vaddr; } #endif @@ -996,29 +1096,32 @@ void *omap_gem_vaddr(struct drm_gem_object *obj) #ifdef CONFIG_PM /* re-pin objects in DMM in resume path: */ -int omap_gem_resume(struct device *dev) +int omap_gem_resume(struct drm_device *dev) { - struct drm_device *drm_dev = dev_get_drvdata(dev); - struct omap_drm_private *priv = drm_dev->dev_private; + struct omap_drm_private *priv = dev->dev_private; struct omap_gem_object *omap_obj; int ret = 0; + mutex_lock(&priv->list_lock); list_for_each_entry(omap_obj, &priv->obj_list, mm_list) { if (omap_obj->block) { struct drm_gem_object *obj = &omap_obj->base; - uint32_t npages = obj->size >> PAGE_SHIFT; + u32 npages = obj->size >> PAGE_SHIFT; + WARN_ON(!omap_obj->pages); /* this can't happen */ ret = tiler_pin(omap_obj->block, omap_obj->pages, npages, omap_obj->roll, true); if (ret) { - dev_err(dev, "could not repin: %d\n", ret); - return ret; + dev_err(dev->dev, "could not repin: %d\n", ret); + goto done; } } } - return 0; +done: + mutex_unlock(&priv->list_lock); + return ret; } #endif @@ -1030,16 +1133,19 @@ int omap_gem_resume(struct device *dev) void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m) { struct omap_gem_object *omap_obj = to_omap_bo(obj); - uint64_t off; + u64 off; off = drm_vma_node_start(&obj->vma_node); + mutex_lock(&omap_obj->lock); + seq_printf(m, "%08x: %2d (%2d) %08llx %pad (%2d) %p %4d", omap_obj->flags, obj->name, kref_read(&obj->refcount), - off, &omap_obj->dma_addr, omap_obj->dma_addr_cnt, + off, &omap_obj->dma_addr, + refcount_read(&omap_obj->pin_cnt), omap_obj->vaddr, omap_obj->roll); - if (omap_obj->flags & OMAP_BO_TILED) { + if (omap_obj->flags & OMAP_BO_TILED_MASK) { seq_printf(m, " %dx%d", omap_obj->width, omap_obj->height); if (omap_obj->block) { struct tcm_area *area = &omap_obj->block->area; @@ -1051,6 +1157,8 @@ void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m) seq_printf(m, " %zu", obj->size); } + mutex_unlock(&omap_obj->lock); + seq_printf(m, "\n"); } @@ -1076,24 +1184,28 @@ void omap_gem_describe_objects(struct list_head *list, struct seq_file *m) * Constructor & Destructor */ -void omap_gem_free_object(struct drm_gem_object *obj) +static void omap_gem_free_object(struct drm_gem_object *obj) { struct drm_device *dev = obj->dev; struct omap_drm_private *priv = dev->dev_private; struct omap_gem_object *omap_obj = to_omap_bo(obj); - evict(obj); - - WARN_ON(!mutex_is_locked(&dev->struct_mutex)); + omap_gem_evict(obj); - spin_lock(&priv->list_lock); + mutex_lock(&priv->list_lock); list_del(&omap_obj->mm_list); - spin_unlock(&priv->list_lock); + mutex_unlock(&priv->list_lock); - /* this means the object is still pinned.. which really should - * not happen. I think.. + /* + * We own the sole reference to the object at this point, but to keep + * lockdep happy, we must still take the omap_obj_lock to call + * omap_gem_detach_pages(). This should hardly make any difference as + * there can't be any lock contention. */ - WARN_ON(omap_obj->dma_addr_cnt > 0); + mutex_lock(&omap_obj->lock); + + /* The object should not be pinned. */ + WARN_ON(refcount_read(&omap_obj->pin_cnt) > 0); if (omap_obj->pages) { if (omap_obj->flags & OMAP_BO_MEM_DMABUF) @@ -1111,14 +1223,63 @@ void omap_gem_free_object(struct drm_gem_object *obj) drm_prime_gem_destroy(obj, omap_obj->sgt); } + mutex_unlock(&omap_obj->lock); + drm_gem_object_release(obj); + mutex_destroy(&omap_obj->lock); + kfree(omap_obj); } +static bool omap_gem_validate_flags(struct drm_device *dev, u32 flags) +{ + struct omap_drm_private *priv = dev->dev_private; + + switch (flags & OMAP_BO_CACHE_MASK) { + case OMAP_BO_CACHED: + case OMAP_BO_WC: + case OMAP_BO_CACHE_MASK: + break; + + default: + return false; + } + + if (flags & OMAP_BO_TILED_MASK) { + if (!priv->usergart) + return false; + + switch (flags & OMAP_BO_TILED_MASK) { + case OMAP_BO_TILED_8: + case OMAP_BO_TILED_16: + case OMAP_BO_TILED_32: + break; + + default: + return false; + } + } + + return true; +} + +static const struct vm_operations_struct omap_gem_vm_ops = { + .fault = omap_gem_fault, + .open = drm_gem_vm_open, + .close = drm_gem_vm_close, +}; + +static const struct drm_gem_object_funcs omap_gem_object_funcs = { + .free = omap_gem_free_object, + .export = omap_gem_prime_export, + .mmap = omap_gem_object_mmap, + .vm_ops = &omap_gem_vm_ops, +}; + /* GEM buffer object constructor */ struct drm_gem_object *omap_gem_new(struct drm_device *dev, - union omap_gem_size gsize, uint32_t flags) + union omap_gem_size gsize, u32 flags) { struct omap_drm_private *priv = dev->dev_private; struct omap_gem_object *omap_obj; @@ -1127,18 +1288,15 @@ struct drm_gem_object *omap_gem_new(struct drm_device *dev, size_t size; int ret; - /* Validate the flags and compute the memory and cache flags. */ - if (flags & OMAP_BO_TILED) { - if (!priv->usergart) { - dev_err(dev->dev, "Tiled buffers require DMM\n"); - return NULL; - } + if (!omap_gem_validate_flags(dev, flags)) + return NULL; + /* Validate the flags and compute the memory and cache flags. */ + if (flags & OMAP_BO_TILED_MASK) { /* * Tiled buffers are always shmem paged backed. When they are * scanned out, they are remapped into DMM/TILER. */ - flags &= ~OMAP_BO_SCANOUT; flags |= OMAP_BO_MEM_SHMEM; /* @@ -1149,9 +1307,8 @@ struct drm_gem_object *omap_gem_new(struct drm_device *dev, flags |= tiler_get_cpu_cache_flags(); } else if ((flags & OMAP_BO_SCANOUT) && !priv->has_dmm) { /* - * OMAP_BO_SCANOUT hints that the buffer doesn't need to be - * tiled. However, to lower the pressure on memory allocation, - * use contiguous memory only if no TILER is available. + * If we don't have DMM, we must allocate scanout buffers + * from contiguous DMA memory. */ flags |= OMAP_BO_MEM_DMA_API; } else if (!(flags & OMAP_BO_MEM_DMABUF)) { @@ -1168,8 +1325,9 @@ struct drm_gem_object *omap_gem_new(struct drm_device *dev, obj = &omap_obj->base; omap_obj->flags = flags; + mutex_init(&omap_obj->lock); - if (flags & OMAP_BO_TILED) { + if (flags & OMAP_BO_TILED_MASK) { /* * For tiled buffers align dimensions to slot boundaries and * calculate size based on aligned dimensions. @@ -1186,6 +1344,8 @@ struct drm_gem_object *omap_gem_new(struct drm_device *dev, size = PAGE_ALIGN(gsize.bytes); } + obj->funcs = &omap_gem_object_funcs; + /* Initialize the GEM object. */ if (!(flags & OMAP_BO_MEM_SHMEM)) { drm_gem_private_object_init(dev, obj, size); @@ -1207,9 +1367,9 @@ struct drm_gem_object *omap_gem_new(struct drm_device *dev, goto err_release; } - spin_lock(&priv->list_lock); + mutex_lock(&priv->list_lock); list_add(&omap_obj->mm_list, &priv->obj_list); - spin_unlock(&priv->list_lock); + mutex_unlock(&priv->list_lock); return obj; @@ -1229,61 +1389,47 @@ struct drm_gem_object *omap_gem_new_dmabuf(struct drm_device *dev, size_t size, union omap_gem_size gsize; /* Without a DMM only physically contiguous buffers can be supported. */ - if (sgt->orig_nents != 1 && !priv->has_dmm) + if (!omap_gem_sgt_is_contiguous(sgt, size) && !priv->has_dmm) return ERR_PTR(-EINVAL); - mutex_lock(&dev->struct_mutex); - gsize.bytes = PAGE_ALIGN(size); obj = omap_gem_new(dev, gsize, OMAP_BO_MEM_DMABUF | OMAP_BO_WC); - if (!obj) { - obj = ERR_PTR(-ENOMEM); - goto done; - } + if (!obj) + return ERR_PTR(-ENOMEM); omap_obj = to_omap_bo(obj); + omap_obj->sgt = sgt; - if (sgt->orig_nents == 1) { + if (omap_gem_sgt_is_contiguous(sgt, size)) { omap_obj->dma_addr = sg_dma_address(sgt->sgl); } else { /* Create pages list from sgt */ - struct sg_page_iter iter; struct page **pages; unsigned int npages; - unsigned int i = 0; + unsigned int ret; npages = DIV_ROUND_UP(size, PAGE_SIZE); pages = kcalloc(npages, sizeof(*pages), GFP_KERNEL); if (!pages) { omap_gem_free_object(obj); - obj = ERR_PTR(-ENOMEM); - goto done; + return ERR_PTR(-ENOMEM); } omap_obj->pages = pages; - - for_each_sg_page(sgt->sgl, &iter, sgt->orig_nents, 0) { - pages[i++] = sg_page_iter_page(&iter); - if (i > npages) - break; - } - - if (WARN_ON(i != npages)) { + ret = drm_prime_sg_to_page_array(sgt, pages, npages); + if (ret) { omap_gem_free_object(obj); - obj = ERR_PTR(-ENOMEM); - goto done; + return ERR_PTR(-ENOMEM); } } -done: - mutex_unlock(&dev->struct_mutex); return obj; } /* convenience method to construct a GEM buffer object, and userspace handle */ int omap_gem_new_handle(struct drm_device *dev, struct drm_file *file, - union omap_gem_size gsize, uint32_t flags, uint32_t *handle) + union omap_gem_size gsize, u32 flags, u32 *handle) { struct drm_gem_object *obj; int ret; @@ -1299,7 +1445,7 @@ int omap_gem_new_handle(struct drm_device *dev, struct drm_file *file, } /* drop reference from allocate - handle holds it now */ - drm_gem_object_unreference_unlocked(obj); + drm_gem_object_put(obj); return 0; } @@ -1330,7 +1476,8 @@ void omap_gem_init(struct drm_device *dev) /* reserve 4k aligned/wide regions for userspace mappings: */ for (i = 0; i < ARRAY_SIZE(fmts); i++) { - uint16_t h = 1, w = PAGE_SIZE >> i; + u16 h = 1, w = PAGE_SIZE >> i; + tiler_align(fmts[i], &w, &h); /* note: since each region is 1 4kb page wide, and minimum * number of rows, the height ends up being the same as the diff --git a/drivers/gpu/drm/omapdrm/omap_gem.h b/drivers/gpu/drm/omapdrm/omap_gem.h new file mode 100644 index 000000000000..fec3fa0e4c33 --- /dev/null +++ b/drivers/gpu/drm/omapdrm/omap_gem.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * omap_gem.h -- OMAP DRM GEM Object Management + * + * Copyright (C) 2011 Texas Instruments + * Author: Rob Clark <rob@ti.com> + */ + +#ifndef __OMAPDRM_GEM_H__ +#define __OMAPDRM_GEM_H__ + +#include <linux/types.h> +#include <linux/mm_types.h> + +enum dma_data_direction; + +struct dma_buf; +struct drm_device; +struct drm_file; +struct drm_gem_object; +struct drm_mode_create_dumb; +struct file; +struct list_head; +struct page; +struct seq_file; +struct vm_area_struct; +struct vm_fault; + +union omap_gem_size; + +/* Initialization and Cleanup */ +void omap_gem_init(struct drm_device *dev); +void omap_gem_deinit(struct drm_device *dev); + +#ifdef CONFIG_PM +int omap_gem_resume(struct drm_device *dev); +#endif + +#ifdef CONFIG_DEBUG_FS +void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m); +void omap_gem_describe_objects(struct list_head *list, struct seq_file *m); +#endif + +/* GEM Object Creation and Deletion */ +struct drm_gem_object *omap_gem_new(struct drm_device *dev, + union omap_gem_size gsize, u32 flags); +struct drm_gem_object *omap_gem_new_dmabuf(struct drm_device *dev, size_t size, + struct sg_table *sgt); +int omap_gem_new_handle(struct drm_device *dev, struct drm_file *file, + union omap_gem_size gsize, u32 flags, u32 *handle); +void *omap_gem_vaddr(struct drm_gem_object *obj); + +/* Dumb Buffers Interface */ +int omap_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev, + u32 handle, u64 *offset); +int omap_gem_dumb_create(struct drm_file *file, struct drm_device *dev, + struct drm_mode_create_dumb *args); + +/* mmap() Interface */ +u64 omap_gem_mmap_offset(struct drm_gem_object *obj); +size_t omap_gem_mmap_size(struct drm_gem_object *obj); + +/* PRIME Interface */ +struct dma_buf *omap_gem_prime_export(struct drm_gem_object *obj, int flags); +struct drm_gem_object *omap_gem_prime_import(struct drm_device *dev, + struct dma_buf *buffer); + +int omap_gem_roll(struct drm_gem_object *obj, u32 roll); +void omap_gem_cpu_sync_page(struct drm_gem_object *obj, int pgoff); +void omap_gem_dma_sync_buffer(struct drm_gem_object *obj, + enum dma_data_direction dir); +int omap_gem_pin(struct drm_gem_object *obj, dma_addr_t *dma_addr); +void omap_gem_unpin(struct drm_gem_object *obj); +int omap_gem_get_pages(struct drm_gem_object *obj, struct page ***pages, + bool remap); +int omap_gem_put_pages(struct drm_gem_object *obj); + +u32 omap_gem_flags(struct drm_gem_object *obj); +int omap_gem_rotated_dma_addr(struct drm_gem_object *obj, u32 orient, + int x, int y, dma_addr_t *dma_addr); +int omap_gem_tiled_stride(struct drm_gem_object *obj, u32 orient); +struct sg_table *omap_gem_get_sg(struct drm_gem_object *obj, + enum dma_data_direction dir); +void omap_gem_put_sg(struct drm_gem_object *obj, struct sg_table *sgt); + +#endif /* __OMAPDRM_GEM_H__ */ diff --git a/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c b/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c index 863a881dd7cd..30cf1cdc1aa3 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c +++ b/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c @@ -1,26 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-only /* - * drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c - * - * Copyright (C) 2011 Texas Instruments + * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/ * Author: Rob Clark <rob.clark@linaro.org> - * - * 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/dma-buf.h> +#include <linux/highmem.h> + +#include <drm/drm_prime.h> #include "omap_drv.h" +MODULE_IMPORT_NS("DMA_BUF"); + /* ----------------------------------------------------------------------------- * DMABUF Export */ @@ -31,45 +23,18 @@ static struct sg_table *omap_gem_map_dma_buf( { struct drm_gem_object *obj = attachment->dmabuf->priv; struct sg_table *sg; - dma_addr_t dma_addr; - int ret; - - sg = kzalloc(sizeof(*sg), GFP_KERNEL); - if (!sg) - return ERR_PTR(-ENOMEM); - - /* camera, etc, need physically contiguous.. but we need a - * better way to know this.. - */ - ret = omap_gem_pin(obj, &dma_addr); - if (ret) - goto out; - - ret = sg_alloc_table(sg, 1, GFP_KERNEL); - if (ret) - goto out; - - sg_init_table(sg->sgl, 1); - sg_dma_len(sg->sgl) = obj->size; - sg_set_page(sg->sgl, pfn_to_page(PFN_DOWN(dma_addr)), obj->size, 0); - sg_dma_address(sg->sgl) = dma_addr; - - /* this must be after omap_gem_pin() to ensure we have pages attached */ - omap_gem_dma_sync_buffer(obj, dir); + sg = omap_gem_get_sg(obj, dir); + if (IS_ERR(sg)) + return sg; return sg; -out: - kfree(sg); - return ERR_PTR(ret); } static void omap_gem_unmap_dma_buf(struct dma_buf_attachment *attachment, struct sg_table *sg, enum dma_data_direction dir) { struct drm_gem_object *obj = attachment->dmabuf->priv; - omap_gem_unpin(obj); - sg_free_table(sg); - kfree(sg); + omap_gem_put_sg(obj, sg); } static int omap_gem_dmabuf_begin_cpu_access(struct dma_buf *buffer, @@ -77,7 +42,7 @@ static int omap_gem_dmabuf_begin_cpu_access(struct dma_buf *buffer, { struct drm_gem_object *obj = buffer->priv; struct page **pages; - if (omap_gem_flags(obj) & OMAP_BO_TILED) { + if (omap_gem_flags(obj) & OMAP_BO_TILED_MASK) { /* TODO we would need to pin at least part of the buffer to * get de-tiled view. For now just reject it. */ @@ -95,79 +60,34 @@ static int omap_gem_dmabuf_end_cpu_access(struct dma_buf *buffer, return 0; } - -static void *omap_gem_dmabuf_kmap_atomic(struct dma_buf *buffer, - unsigned long page_num) -{ - struct drm_gem_object *obj = buffer->priv; - struct page **pages; - omap_gem_get_pages(obj, &pages, false); - omap_gem_cpu_sync_page(obj, page_num); - return kmap_atomic(pages[page_num]); -} - -static void omap_gem_dmabuf_kunmap_atomic(struct dma_buf *buffer, - unsigned long page_num, void *addr) -{ - kunmap_atomic(addr); -} - -static void *omap_gem_dmabuf_kmap(struct dma_buf *buffer, - unsigned long page_num) -{ - struct drm_gem_object *obj = buffer->priv; - struct page **pages; - omap_gem_get_pages(obj, &pages, false); - omap_gem_cpu_sync_page(obj, page_num); - return kmap(pages[page_num]); -} - -static void omap_gem_dmabuf_kunmap(struct dma_buf *buffer, - unsigned long page_num, void *addr) -{ - struct drm_gem_object *obj = buffer->priv; - struct page **pages; - omap_gem_get_pages(obj, &pages, false); - kunmap(pages[page_num]); -} - static int omap_gem_dmabuf_mmap(struct dma_buf *buffer, struct vm_area_struct *vma) { struct drm_gem_object *obj = buffer->priv; - int ret = 0; - - ret = drm_gem_mmap_obj(obj, omap_gem_mmap_size(obj), vma); - if (ret < 0) - return ret; - return omap_gem_mmap_obj(obj, vma); + return drm_gem_mmap_obj(obj, omap_gem_mmap_size(obj), vma); } -static struct dma_buf_ops omap_dmabuf_ops = { +static const struct dma_buf_ops omap_dmabuf_ops = { .map_dma_buf = omap_gem_map_dma_buf, .unmap_dma_buf = omap_gem_unmap_dma_buf, .release = drm_gem_dmabuf_release, .begin_cpu_access = omap_gem_dmabuf_begin_cpu_access, .end_cpu_access = omap_gem_dmabuf_end_cpu_access, - .map_atomic = omap_gem_dmabuf_kmap_atomic, - .unmap_atomic = omap_gem_dmabuf_kunmap_atomic, - .map = omap_gem_dmabuf_kmap, - .unmap = omap_gem_dmabuf_kunmap, .mmap = omap_gem_dmabuf_mmap, }; -struct dma_buf *omap_gem_prime_export(struct drm_device *dev, - struct drm_gem_object *obj, int flags) +struct dma_buf *omap_gem_prime_export(struct drm_gem_object *obj, int flags) { DEFINE_DMA_BUF_EXPORT_INFO(exp_info); exp_info.ops = &omap_dmabuf_ops; - exp_info.size = obj->size; + exp_info.size = omap_gem_mmap_size(obj); exp_info.flags = flags; exp_info.priv = obj; + exp_info.resv = obj->resv; - return drm_gem_dmabuf_export(dev, &exp_info); + return drm_gem_dmabuf_export(obj->dev, &exp_info); } /* ----------------------------------------------------------------------------- @@ -189,7 +109,7 @@ struct drm_gem_object *omap_gem_prime_import(struct drm_device *dev, * Importing dmabuf exported from out own gem increases * refcount on gem itself instead of f_count of dmabuf. */ - drm_gem_object_reference(obj); + drm_gem_object_get(obj); return obj; } } @@ -200,7 +120,7 @@ struct drm_gem_object *omap_gem_prime_import(struct drm_device *dev, get_dma_buf(dma_buf); - sgt = dma_buf_map_attachment(attach, DMA_TO_DEVICE); + sgt = dma_buf_map_attachment_unlocked(attach, DMA_TO_DEVICE); if (IS_ERR(sgt)) { ret = PTR_ERR(sgt); goto fail_detach; @@ -217,7 +137,7 @@ struct drm_gem_object *omap_gem_prime_import(struct drm_device *dev, return obj; fail_unmap: - dma_buf_unmap_attachment(attach, sgt, DMA_TO_DEVICE); + dma_buf_unmap_attachment_unlocked(attach, sgt, DMA_TO_DEVICE); fail_detach: dma_buf_detach(dma_buf, attach); dma_buf_put(dma_buf); diff --git a/drivers/gpu/drm/omapdrm/omap_irq.c b/drivers/gpu/drm/omapdrm/omap_irq.c index 013b0bba712f..943c5307da00 100644 --- a/drivers/gpu/drm/omapdrm/omap_irq.c +++ b/drivers/gpu/drm/omapdrm/omap_irq.c @@ -1,28 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-only /* - * drivers/gpu/drm/omapdrm/omap_irq.c - * - * Copyright (C) 2012 Texas Instruments + * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/ * Author: Rob Clark <rob.clark@linaro.org> - * - * 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 <drm/drm_vblank.h> +#include <drm/drm_print.h> + #include "omap_drv.h" struct omap_irq_wait { struct list_head node; wait_queue_head_t wq; - uint32_t irqmask; + u32 irqmask; int count; }; @@ -31,7 +21,7 @@ static void omap_irq_update(struct drm_device *dev) { struct omap_drm_private *priv = dev->dev_private; struct omap_irq_wait *wait; - uint32_t irqmask = priv->irq_mask; + u32 irqmask = priv->irq_mask; assert_spin_locked(&priv->wait_lock); @@ -40,7 +30,7 @@ static void omap_irq_update(struct drm_device *dev) DBG("irqmask=%08x", irqmask); - priv->dispc_ops->write_irqenable(irqmask); + dispc_write_irqenable(priv->dispc, irqmask); } static void omap_irq_wait_handler(struct omap_irq_wait *wait) @@ -50,7 +40,7 @@ static void omap_irq_wait_handler(struct omap_irq_wait *wait) } struct omap_irq_wait * omap_irq_wait_init(struct drm_device *dev, - uint32_t irqmask, int count) + u32 irqmask, int count) { struct omap_drm_private *priv = dev->dev_private; struct omap_irq_wait *wait = kzalloc(sizeof(*wait), GFP_KERNEL); @@ -87,10 +77,31 @@ int omap_irq_wait(struct drm_device *dev, struct omap_irq_wait *wait, return ret == 0 ? -1 : 0; } +int omap_irq_enable_framedone(struct drm_crtc *crtc, bool enable) +{ + struct drm_device *dev = crtc->dev; + struct omap_drm_private *priv = dev->dev_private; + unsigned long flags; + enum omap_channel channel = omap_crtc_channel(crtc); + int framedone_irq = + dispc_mgr_get_framedone_irq(priv->dispc, channel); + + DBG("dev=%p, crtc=%u, enable=%d", dev, channel, enable); + + spin_lock_irqsave(&priv->wait_lock, flags); + if (enable) + priv->irq_mask |= framedone_irq; + else + priv->irq_mask &= ~framedone_irq; + omap_irq_update(dev); + spin_unlock_irqrestore(&priv->wait_lock, flags); + + return 0; +} + /** - * enable_vblank - enable vblank interrupt events - * @dev: DRM device - * @pipe: which irq to enable + * omap_irq_enable_vblank - enable vblank interrupt events + * @crtc: DRM CRTC * * Enable vblank interrupts for @crtc. If the device doesn't have * a hardware vblank counter, this routine should be a no-op, since @@ -110,7 +121,8 @@ int omap_irq_enable_vblank(struct drm_crtc *crtc) DBG("dev=%p, crtc=%u", dev, channel); spin_lock_irqsave(&priv->wait_lock, flags); - priv->irq_mask |= priv->dispc_ops->mgr_get_vsync_irq(channel); + priv->irq_mask |= dispc_mgr_get_vsync_irq(priv->dispc, + channel); omap_irq_update(dev); spin_unlock_irqrestore(&priv->wait_lock, flags); @@ -118,9 +130,8 @@ int omap_irq_enable_vblank(struct drm_crtc *crtc) } /** - * disable_vblank - disable vblank interrupt events - * @dev: DRM device - * @pipe: which irq to enable + * omap_irq_disable_vblank - disable vblank interrupt events + * @crtc: DRM CRTC * * Disable vblank interrupts for @crtc. If the device doesn't have * a hardware vblank counter, this routine should be a no-op, since @@ -136,7 +147,8 @@ void omap_irq_disable_vblank(struct drm_crtc *crtc) DBG("dev=%p, crtc=%u", dev, channel); spin_lock_irqsave(&priv->wait_lock, flags); - priv->irq_mask &= ~priv->dispc_ops->mgr_get_vsync_irq(channel); + priv->irq_mask &= ~dispc_mgr_get_vsync_irq(priv->dispc, + channel); omap_irq_update(dev); spin_unlock_irqrestore(&priv->wait_lock, flags); } @@ -200,23 +212,26 @@ static irqreturn_t omap_irq_handler(int irq, void *arg) unsigned int id; u32 irqstatus; - irqstatus = priv->dispc_ops->read_irqstatus(); - priv->dispc_ops->clear_irqstatus(irqstatus); - priv->dispc_ops->read_irqstatus(); /* flush posted write */ + irqstatus = dispc_read_irqstatus(priv->dispc); + dispc_clear_irqstatus(priv->dispc, irqstatus); + dispc_read_irqstatus(priv->dispc); /* flush posted write */ VERB("irqs: %08x", irqstatus); - for (id = 0; id < priv->num_crtcs; id++) { - struct drm_crtc *crtc = priv->crtcs[id]; + for (id = 0; id < priv->num_pipes; id++) { + struct drm_crtc *crtc = priv->pipes[id].crtc; enum omap_channel channel = omap_crtc_channel(crtc); - if (irqstatus & priv->dispc_ops->mgr_get_vsync_irq(channel)) { + if (irqstatus & dispc_mgr_get_vsync_irq(priv->dispc, channel)) { drm_handle_vblank(dev, id); omap_crtc_vblank_irq(crtc); } - if (irqstatus & priv->dispc_ops->mgr_get_sync_lost_irq(channel)) + if (irqstatus & dispc_mgr_get_sync_lost_irq(priv->dispc, channel)) omap_crtc_error_irq(crtc, irqstatus); + + if (irqstatus & dispc_mgr_get_framedone_irq(priv->dispc, channel)) + omap_crtc_framedone_irq(crtc, irqstatus); } omap_irq_ocp_error_handler(dev, irqstatus); @@ -239,17 +254,10 @@ static const u32 omap_underflow_irqs[] = { [OMAP_DSS_VIDEO3] = DISPC_IRQ_VID3_FIFO_UNDERFLOW, }; -/* - * We need a special version, instead of just using drm_irq_install(), - * because we need to register the irq via omapdss. Once omapdss and - * omapdrm are merged together we can assign the dispc hwmod data to - * ourselves and drop these and just use drm_irq_{install,uninstall}() - */ - int omap_drm_irq_install(struct drm_device *dev) { struct omap_drm_private *priv = dev->dev_private; - unsigned int num_mgrs = priv->dispc_ops->get_num_mgrs(); + unsigned int num_mgrs = dispc_get_num_mgrs(priv->dispc); unsigned int max_planes; unsigned int i; int ret; @@ -267,17 +275,17 @@ int omap_drm_irq_install(struct drm_device *dev) } for (i = 0; i < num_mgrs; ++i) - priv->irq_mask |= priv->dispc_ops->mgr_get_sync_lost_irq(i); + priv->irq_mask |= dispc_mgr_get_sync_lost_irq(priv->dispc, i); - priv->dispc_ops->runtime_get(); - priv->dispc_ops->clear_irqstatus(0xffffffff); - priv->dispc_ops->runtime_put(); + dispc_runtime_get(priv->dispc); + dispc_clear_irqstatus(priv->dispc, 0xffffffff); + dispc_runtime_put(priv->dispc); - ret = priv->dispc_ops->request_irq(omap_irq_handler, dev); + ret = dispc_request_irq(priv->dispc, omap_irq_handler, dev); if (ret < 0) return ret; - dev->irq_enabled = true; + priv->irq_enabled = true; return 0; } @@ -286,10 +294,10 @@ void omap_drm_irq_uninstall(struct drm_device *dev) { struct omap_drm_private *priv = dev->dev_private; - if (!dev->irq_enabled) + if (!priv->irq_enabled) return; - dev->irq_enabled = false; + priv->irq_enabled = false; - priv->dispc_ops->free_irq(dev); + dispc_free_irq(priv->dispc, dev); } diff --git a/drivers/gpu/drm/omapdrm/omap_irq.h b/drivers/gpu/drm/omapdrm/omap_irq.h new file mode 100644 index 000000000000..35e9586c4a51 --- /dev/null +++ b/drivers/gpu/drm/omapdrm/omap_irq.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * omap_irq.h -- OMAP DRM IRQ Handling + * + * Copyright (C) 2011 Texas Instruments + * Author: Rob Clark <rob@ti.com> + */ + +#ifndef __OMAPDRM_IRQ_H__ +#define __OMAPDRM_IRQ_H__ + +#include <linux/types.h> + +struct drm_crtc; +struct drm_device; +struct omap_irq_wait; + +int omap_irq_enable_vblank(struct drm_crtc *crtc); +int omap_irq_enable_framedone(struct drm_crtc *crtc, bool enable); +void omap_irq_disable_vblank(struct drm_crtc *crtc); +void omap_drm_irq_uninstall(struct drm_device *dev); +int omap_drm_irq_install(struct drm_device *dev); + +struct omap_irq_wait *omap_irq_wait_init(struct drm_device *dev, + u32 irqmask, int count); +int omap_irq_wait(struct drm_device *dev, struct omap_irq_wait *wait, + unsigned long timeout); + +#endif /* __OMAPDRM_IRQ_H__ */ diff --git a/drivers/gpu/drm/omapdrm/omap_overlay.c b/drivers/gpu/drm/omapdrm/omap_overlay.c new file mode 100644 index 000000000000..6fb7510cbebb --- /dev/null +++ b/drivers/gpu/drm/omapdrm/omap_overlay.c @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Benoit Parrot <bparrot@ti.com> + */ + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_print.h> + +#include "omap_dmm_tiler.h" +#include "omap_drv.h" + +/* + * overlay funcs + */ +static const char * const overlay_id_to_name[] = { + [OMAP_DSS_GFX] = "gfx", + [OMAP_DSS_VIDEO1] = "vid1", + [OMAP_DSS_VIDEO2] = "vid2", + [OMAP_DSS_VIDEO3] = "vid3", +}; + +/* + * Find a free overlay with the required caps and supported fourcc + */ +static struct omap_hw_overlay * +omap_plane_find_free_overlay(struct drm_device *dev, struct drm_plane *hwoverlay_to_plane[], + u32 caps, u32 fourcc) +{ + struct omap_drm_private *priv = dev->dev_private; + int i; + + DBG("caps: %x fourcc: %x", caps, fourcc); + + for (i = 0; i < priv->num_ovls; i++) { + struct omap_hw_overlay *cur = priv->overlays[i]; + + DBG("%d: id: %d cur->caps: %x", + cur->idx, cur->id, cur->caps); + + /* skip if already in-use */ + if (hwoverlay_to_plane[cur->idx]) + continue; + + /* skip if doesn't support some required caps: */ + if (caps & ~cur->caps) + continue; + + /* check supported format */ + if (!dispc_ovl_color_mode_supported(priv->dispc, + cur->id, fourcc)) + continue; + + return cur; + } + + DBG("no match"); + return NULL; +} + +/* + * Assign a new overlay to a plane with the required caps and supported fourcc + * If a plane need a new overlay, the previous one should have been released + * with omap_overlay_release() + * This should be called from the plane atomic_check() in order to prepare the + * next global overlay_map to be enabled when atomic transaction is valid. + */ +int omap_overlay_assign(struct drm_atomic_state *s, struct drm_plane *plane, + u32 caps, u32 fourcc, struct omap_hw_overlay **overlay, + struct omap_hw_overlay **r_overlay) +{ + /* Get the global state of the current atomic transaction */ + struct omap_global_state *state = omap_get_global_state(s); + struct drm_plane **overlay_map = state->hwoverlay_to_plane; + struct omap_hw_overlay *ovl, *r_ovl; + + ovl = omap_plane_find_free_overlay(s->dev, overlay_map, caps, fourcc); + if (!ovl) + return -ENOMEM; + + overlay_map[ovl->idx] = plane; + *overlay = ovl; + + if (r_overlay) { + r_ovl = omap_plane_find_free_overlay(s->dev, overlay_map, + caps, fourcc); + if (!r_ovl) { + overlay_map[ovl->idx] = NULL; + *overlay = NULL; + return -ENOMEM; + } + + overlay_map[r_ovl->idx] = plane; + *r_overlay = r_ovl; + } + + DBG("%s: assign to plane %s caps %x", ovl->name, plane->name, caps); + + if (r_overlay) { + DBG("%s: assign to right of plane %s caps %x", + r_ovl->name, plane->name, caps); + } + + return 0; +} + +/* + * Release an overlay from a plane if the plane gets not visible or the plane + * need a new overlay if overlay caps changes. + * This should be called from the plane atomic_check() in order to prepare the + * next global overlay_map to be enabled when atomic transaction is valid. + */ +void omap_overlay_release(struct drm_atomic_state *s, struct omap_hw_overlay *overlay) +{ + /* Get the global state of the current atomic transaction */ + struct omap_global_state *state = omap_get_global_state(s); + struct drm_plane **overlay_map = state->hwoverlay_to_plane; + + if (!overlay) + return; + + if (WARN_ON(!overlay_map[overlay->idx])) + return; + + DBG("%s: release from plane %s", overlay->name, overlay_map[overlay->idx]->name); + + overlay_map[overlay->idx] = NULL; +} + +/* + * Update an overlay state that was attached to a plane before the current atomic state. + * This should be called from the plane atomic_update() or atomic_disable(), + * where an overlay association to a plane could have changed between the old and current + * atomic state. + */ +void omap_overlay_update_state(struct omap_drm_private *priv, + struct omap_hw_overlay *overlay) +{ + struct omap_global_state *state = omap_get_existing_global_state(priv); + struct drm_plane **overlay_map = state->hwoverlay_to_plane; + + /* Check if this overlay is not used anymore, then disable it */ + if (!overlay_map[overlay->idx]) { + DBG("%s: disabled", overlay->name); + + /* disable the overlay */ + dispc_ovl_enable(priv->dispc, overlay->id, false); + } +} + +static void omap_overlay_destroy(struct omap_hw_overlay *overlay) +{ + kfree(overlay); +} + +static struct omap_hw_overlay *omap_overlay_init(enum omap_plane_id overlay_id, + enum omap_overlay_caps caps) +{ + struct omap_hw_overlay *overlay; + + overlay = kzalloc(sizeof(*overlay), GFP_KERNEL); + if (!overlay) + return ERR_PTR(-ENOMEM); + + overlay->name = overlay_id_to_name[overlay_id]; + overlay->id = overlay_id; + overlay->caps = caps; + + return overlay; +} + +int omap_hwoverlays_init(struct omap_drm_private *priv) +{ + static const enum omap_plane_id hw_plane_ids[] = { + OMAP_DSS_GFX, OMAP_DSS_VIDEO1, + OMAP_DSS_VIDEO2, OMAP_DSS_VIDEO3, + }; + u32 num_overlays = dispc_get_num_ovls(priv->dispc); + enum omap_overlay_caps caps; + int i, ret; + + for (i = 0; i < num_overlays; i++) { + struct omap_hw_overlay *overlay; + + caps = dispc_ovl_get_caps(priv->dispc, hw_plane_ids[i]); + overlay = omap_overlay_init(hw_plane_ids[i], caps); + if (IS_ERR(overlay)) { + ret = PTR_ERR(overlay); + dev_err(priv->dev, "failed to construct overlay for %s (%d)\n", + overlay_id_to_name[i], ret); + omap_hwoverlays_destroy(priv); + return ret; + } + overlay->idx = priv->num_ovls; + priv->overlays[priv->num_ovls++] = overlay; + } + + return 0; +} + +void omap_hwoverlays_destroy(struct omap_drm_private *priv) +{ + int i; + + for (i = 0; i < priv->num_ovls; i++) { + omap_overlay_destroy(priv->overlays[i]); + priv->overlays[i] = NULL; + } + + priv->num_ovls = 0; +} diff --git a/drivers/gpu/drm/omapdrm/omap_overlay.h b/drivers/gpu/drm/omapdrm/omap_overlay.h new file mode 100644 index 000000000000..e36a43f35563 --- /dev/null +++ b/drivers/gpu/drm/omapdrm/omap_overlay.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Benoit Parrot <bparrot@ti.com> + */ + +#ifndef __OMAPDRM_OVERLAY_H__ +#define __OMAPDRM_OVERLAY_H__ + +#include <linux/types.h> + +enum drm_plane_type; + +struct drm_device; +struct drm_mode_object; +struct drm_plane; + +/* Used to associate a HW overlay/plane to a plane */ +struct omap_hw_overlay { + unsigned int idx; + + const char *name; + enum omap_plane_id id; + + enum omap_overlay_caps caps; +}; + +int omap_hwoverlays_init(struct omap_drm_private *priv); +void omap_hwoverlays_destroy(struct omap_drm_private *priv); +int omap_overlay_assign(struct drm_atomic_state *s, struct drm_plane *plane, + u32 caps, u32 fourcc, struct omap_hw_overlay **overlay, + struct omap_hw_overlay **r_overlay); +void omap_overlay_release(struct drm_atomic_state *s, struct omap_hw_overlay *overlay); +void omap_overlay_update_state(struct omap_drm_private *priv, struct omap_hw_overlay *overlay); +#endif /* __OMAPDRM_OVERLAY_H__ */ diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c index 2160f64548e0..f9698890c989 100644 --- a/drivers/gpu/drm/omapdrm/omap_plane.c +++ b/drivers/gpu/drm/omapdrm/omap_plane.c @@ -1,25 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0-only /* - * drivers/gpu/drm/omapdrm/omap_plane.c - * - * Copyright (C) 2011 Texas Instruments + * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/ * Author: Rob Clark <rob.clark@linaro.org> - * - * 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 <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> -#include <drm/drm_plane_helper.h> +#include <drm/drm_blend.h> +#include <drm/drm_gem_atomic_helper.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_framebuffer.h> +#include <drm/drm_print.h> #include "omap_dmm_tiler.h" #include "omap_drv.h" @@ -28,20 +19,38 @@ * plane funcs */ +#define to_omap_plane_state(x) container_of(x, struct omap_plane_state, base) + +struct omap_plane_state { + /* Must be first. */ + struct drm_plane_state base; + + struct omap_hw_overlay *overlay; + struct omap_hw_overlay *r_overlay; /* right overlay */ +}; + #define to_omap_plane(x) container_of(x, struct omap_plane, base) struct omap_plane { struct drm_plane base; enum omap_plane_id id; - const char *name; }; +bool is_omap_plane_dual_overlay(struct drm_plane_state *state) +{ + struct omap_plane_state *omap_state = to_omap_plane_state(state); + + return !!omap_state->r_overlay; +} + static int omap_plane_prepare_fb(struct drm_plane *plane, struct drm_plane_state *new_state) { if (!new_state->fb) return 0; + drm_gem_plane_helper_prepare_fb(plane, new_state); + return omap_framebuffer_pin(new_state->fb); } @@ -53,91 +62,301 @@ static void omap_plane_cleanup_fb(struct drm_plane *plane, } static void omap_plane_atomic_update(struct drm_plane *plane, - struct drm_plane_state *old_state) + struct drm_atomic_state *state) { struct omap_drm_private *priv = plane->dev->dev_private; - struct omap_plane *omap_plane = to_omap_plane(plane); - struct drm_plane_state *state = plane->state; - struct omap_overlay_info info; + struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, + plane); + struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, + plane); + struct omap_plane_state *new_omap_state; + struct omap_plane_state *old_omap_state; + struct omap_overlay_info info, r_info; + enum omap_plane_id ovl_id, r_ovl_id; int ret; + bool dual_ovl; + + new_omap_state = to_omap_plane_state(new_state); + old_omap_state = to_omap_plane_state(old_state); + + dual_ovl = is_omap_plane_dual_overlay(new_state); - DBG("%s, crtc=%p fb=%p", omap_plane->name, state->crtc, state->fb); + /* Cleanup previously held overlay if needed */ + if (old_omap_state->overlay) + omap_overlay_update_state(priv, old_omap_state->overlay); + if (old_omap_state->r_overlay) + omap_overlay_update_state(priv, old_omap_state->r_overlay); + + if (!new_omap_state->overlay) { + DBG("[PLANE:%d:%s] no overlay attached", plane->base.id, plane->name); + return; + } + + ovl_id = new_omap_state->overlay->id; + DBG("%s, crtc=%p fb=%p", plane->name, new_state->crtc, + new_state->fb); memset(&info, 0, sizeof(info)); info.rotation_type = OMAP_DSS_ROT_NONE; info.rotation = DRM_MODE_ROTATE_0; - info.global_alpha = 0xff; - info.zorder = state->zpos; + info.global_alpha = new_state->alpha >> 8; + info.zorder = new_state->normalized_zpos; + if (new_state->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI) + info.pre_mult_alpha = 1; + else + info.pre_mult_alpha = 0; + info.color_encoding = new_state->color_encoding; + info.color_range = new_state->color_range; + + r_info = info; /* update scanout: */ - omap_framebuffer_update_scanout(state->fb, state, &info); + omap_framebuffer_update_scanout(new_state->fb, new_state, &info, + dual_ovl ? &r_info : NULL); - DBG("%dx%d -> %dx%d (%d)", info.width, info.height, - info.out_width, info.out_height, - info.screen_width); + DBG("%s: %dx%d -> %dx%d (%d)", + new_omap_state->overlay->name, info.width, info.height, + info.out_width, info.out_height, info.screen_width); DBG("%d,%d %pad %pad", info.pos_x, info.pos_y, &info.paddr, &info.p_uv_addr); + if (dual_ovl) { + r_ovl_id = new_omap_state->r_overlay->id; + /* + * If the current plane uses 2 hw planes the very next + * zorder is used by the r_overlay so we just use the + * main overlay zorder + 1 + */ + r_info.zorder = info.zorder + 1; + + DBG("%s: %dx%d -> %dx%d (%d)", + new_omap_state->r_overlay->name, + r_info.width, r_info.height, + r_info.out_width, r_info.out_height, r_info.screen_width); + DBG("%d,%d %pad %pad", r_info.pos_x, r_info.pos_y, + &r_info.paddr, &r_info.p_uv_addr); + } + /* and finally, update omapdss: */ - ret = priv->dispc_ops->ovl_setup(omap_plane->id, &info, - omap_crtc_timings(state->crtc), false, - omap_crtc_channel(state->crtc)); + ret = dispc_ovl_setup(priv->dispc, ovl_id, &info, + omap_crtc_timings(new_state->crtc), false, + omap_crtc_channel(new_state->crtc)); if (ret) { dev_err(plane->dev->dev, "Failed to setup plane %s\n", - omap_plane->name); - priv->dispc_ops->ovl_enable(omap_plane->id, false); + plane->name); + dispc_ovl_enable(priv->dispc, ovl_id, false); return; } - priv->dispc_ops->ovl_enable(omap_plane->id, true); + dispc_ovl_enable(priv->dispc, ovl_id, true); + + if (dual_ovl) { + ret = dispc_ovl_setup(priv->dispc, r_ovl_id, &r_info, + omap_crtc_timings(new_state->crtc), false, + omap_crtc_channel(new_state->crtc)); + if (ret) { + dev_err(plane->dev->dev, "Failed to setup plane right-overlay %s\n", + plane->name); + dispc_ovl_enable(priv->dispc, r_ovl_id, false); + dispc_ovl_enable(priv->dispc, ovl_id, false); + return; + } + + dispc_ovl_enable(priv->dispc, r_ovl_id, true); + } } static void omap_plane_atomic_disable(struct drm_plane *plane, - struct drm_plane_state *old_state) + struct drm_atomic_state *state) { struct omap_drm_private *priv = plane->dev->dev_private; struct omap_plane *omap_plane = to_omap_plane(plane); + struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, + plane); + struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, + plane); + struct omap_plane_state *new_omap_state; + struct omap_plane_state *old_omap_state; - plane->state->rotation = DRM_MODE_ROTATE_0; - plane->state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY - ? 0 : omap_plane->id; + new_omap_state = to_omap_plane_state(new_state); + old_omap_state = to_omap_plane_state(old_state); + + if (!old_omap_state->overlay) + return; - priv->dispc_ops->ovl_enable(omap_plane->id, false); + new_state->rotation = DRM_MODE_ROTATE_0; + new_state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : omap_plane->id; + + omap_overlay_update_state(priv, old_omap_state->overlay); + new_omap_state->overlay = NULL; + + if (is_omap_plane_dual_overlay(old_state)) { + omap_overlay_update_state(priv, old_omap_state->r_overlay); + new_omap_state->r_overlay = NULL; + } } +#define FRAC_16_16(mult, div) (((mult) << 16) / (div)) + static int omap_plane_atomic_check(struct drm_plane *plane, - struct drm_plane_state *state) + struct drm_atomic_state *state) { + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, + plane); + struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, + plane); + struct omap_drm_private *priv = plane->dev->dev_private; + struct omap_plane_state *omap_state = to_omap_plane_state(new_plane_state); + struct omap_global_state *omap_overlay_global_state; struct drm_crtc_state *crtc_state; + bool new_r_hw_overlay = false; + bool new_hw_overlay = false; + u32 max_width, max_height; + struct drm_crtc *crtc; + u16 width, height; + u32 caps = 0; + u32 fourcc; + int ret; - if (!state->fb) - return 0; + omap_overlay_global_state = omap_get_global_state(state); + if (IS_ERR(omap_overlay_global_state)) + return PTR_ERR(omap_overlay_global_state); + + dispc_ovl_get_max_size(priv->dispc, &width, &height); + max_width = width << 16; + max_height = height << 16; - /* crtc should only be NULL when disabling (i.e., !state->fb) */ - if (WARN_ON(!state->crtc)) + crtc = new_plane_state->crtc ? new_plane_state->crtc : plane->state->crtc; + if (!crtc) return 0; - crtc_state = drm_atomic_get_existing_crtc_state(state->state, state->crtc); + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); /* we should have a crtc state if the plane is attached to a crtc */ if (WARN_ON(!crtc_state)) return 0; - if (!crtc_state->enable) + /* + * Note: these are just sanity checks to filter out totally bad scaling + * factors. The real limits must be calculated case by case, and + * unfortunately we currently do those checks only at the commit + * phase in dispc. + */ + ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state, + FRAC_16_16(1, 8), FRAC_16_16(8, 1), + true, true); + if (ret) + return ret; + + DBG("%s: visible %d -> %d", plane->name, + old_plane_state->visible, new_plane_state->visible); + + if (!new_plane_state->visible) { + omap_overlay_release(state, omap_state->overlay); + omap_overlay_release(state, omap_state->r_overlay); + omap_state->overlay = NULL; + omap_state->r_overlay = NULL; return 0; + } - if (state->crtc_x < 0 || state->crtc_y < 0) + if (new_plane_state->crtc_x < 0 || new_plane_state->crtc_y < 0) return -EINVAL; - if (state->crtc_x + state->crtc_w > crtc_state->adjusted_mode.hdisplay) + if (new_plane_state->crtc_x + new_plane_state->crtc_w > crtc_state->adjusted_mode.hdisplay) return -EINVAL; - if (state->crtc_y + state->crtc_h > crtc_state->adjusted_mode.vdisplay) + if (new_plane_state->crtc_y + new_plane_state->crtc_h > crtc_state->adjusted_mode.vdisplay) return -EINVAL; - if (state->rotation != DRM_MODE_ROTATE_0 && - !omap_framebuffer_supports_rotation(state->fb)) + /* Make sure dimensions are within bounds. */ + if (new_plane_state->src_h > max_height || new_plane_state->crtc_h > height) return -EINVAL; + + if (new_plane_state->src_w > max_width || new_plane_state->crtc_w > width) { + bool is_fourcc_yuv = new_plane_state->fb->format->is_yuv; + + if (is_fourcc_yuv && (((new_plane_state->src_w >> 16) / 2 & 1) || + new_plane_state->crtc_w / 2 & 1)) { + /* + * When calculating the split overlay width + * and it yield an odd value we will need to adjust + * the indivual width +/- 1. So make sure it fits + */ + if (new_plane_state->src_w <= ((2 * width - 1) << 16) && + new_plane_state->crtc_w <= (2 * width - 1)) + new_r_hw_overlay = true; + else + return -EINVAL; + } else { + if (new_plane_state->src_w <= (2 * max_width) && + new_plane_state->crtc_w <= (2 * width)) + new_r_hw_overlay = true; + else + return -EINVAL; + } + } + + if (new_plane_state->rotation != DRM_MODE_ROTATE_0 && + !omap_framebuffer_supports_rotation(new_plane_state->fb)) + return -EINVAL; + + if ((new_plane_state->src_w >> 16) != new_plane_state->crtc_w || + (new_plane_state->src_h >> 16) != new_plane_state->crtc_h) + caps |= OMAP_DSS_OVL_CAP_SCALE; + + fourcc = new_plane_state->fb->format->format; + + /* + * (re)allocate hw overlay if we don't have one or + * there is a caps mismatch + */ + if (!omap_state->overlay || (caps & ~omap_state->overlay->caps)) { + new_hw_overlay = true; + } else { + /* check supported format */ + if (!dispc_ovl_color_mode_supported(priv->dispc, omap_state->overlay->id, + fourcc)) + new_hw_overlay = true; + } + + /* + * check if we need two overlays and only have 1 or + * if we had 2 overlays but will only need 1 + */ + if ((new_r_hw_overlay && !omap_state->r_overlay) || + (!new_r_hw_overlay && omap_state->r_overlay)) + new_hw_overlay = true; + + if (new_hw_overlay) { + struct omap_hw_overlay *old_ovl = omap_state->overlay; + struct omap_hw_overlay *old_r_ovl = omap_state->r_overlay; + struct omap_hw_overlay *new_ovl = NULL; + struct omap_hw_overlay *new_r_ovl = NULL; + + omap_overlay_release(state, old_ovl); + omap_overlay_release(state, old_r_ovl); + + ret = omap_overlay_assign(state, plane, caps, fourcc, &new_ovl, + new_r_hw_overlay ? &new_r_ovl : NULL); + if (ret) { + DBG("%s: failed to assign hw_overlay", plane->name); + omap_state->overlay = NULL; + omap_state->r_overlay = NULL; + return ret; + } + + omap_state->overlay = new_ovl; + if (new_r_hw_overlay) + omap_state->r_overlay = new_r_ovl; + else + omap_state->r_overlay = NULL; + } + + DBG("plane: %s overlay_id: %d", plane->name, omap_state->overlay->id); + + if (omap_state->r_overlay) + DBG("plane: %s r_overlay_id: %d", plane->name, omap_state->r_overlay->id); + return 0; } @@ -153,7 +372,7 @@ static void omap_plane_destroy(struct drm_plane *plane) { struct omap_plane *omap_plane = to_omap_plane(plane); - DBG("%s", omap_plane->name); + DBG("%s", plane->name); drm_plane_cleanup(plane); @@ -186,24 +405,63 @@ void omap_plane_install_properties(struct drm_plane *plane, static void omap_plane_reset(struct drm_plane *plane) { - struct omap_plane *omap_plane = to_omap_plane(plane); + struct omap_plane_state *omap_state; + + if (plane->state) + drm_atomic_helper_plane_destroy_state(plane, plane->state); - drm_atomic_helper_plane_reset(plane); - if (!plane->state) + omap_state = kzalloc(sizeof(*omap_state), GFP_KERNEL); + if (!omap_state) return; - /* - * Set the zpos default depending on whether we are a primary or overlay - * plane. - */ - plane->state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY - ? 0 : omap_plane->id; + __drm_atomic_helper_plane_reset(plane, &omap_state->base); +} + +static struct drm_plane_state * +omap_plane_atomic_duplicate_state(struct drm_plane *plane) +{ + struct omap_plane_state *state, *current_state; + + if (WARN_ON(!plane->state)) + return NULL; + + current_state = to_omap_plane_state(plane->state); + + state = kmalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return NULL; + + __drm_atomic_helper_plane_duplicate_state(plane, &state->base); + + state->overlay = current_state->overlay; + state->r_overlay = current_state->r_overlay; + + return &state->base; +} + +static void omap_plane_atomic_print_state(struct drm_printer *p, + const struct drm_plane_state *state) +{ + struct omap_plane_state *omap_state = to_omap_plane_state(state); + + if (omap_state->overlay) + drm_printf(p, "\toverlay=%s (caps=0x%x)\n", + omap_state->overlay->name, + omap_state->overlay->caps); + else + drm_printf(p, "\toverlay=None\n"); + if (omap_state->r_overlay) + drm_printf(p, "\tr_overlay=%s (caps=0x%x)\n", + omap_state->r_overlay->name, + omap_state->r_overlay->caps); + else + drm_printf(p, "\tr_overlay=None\n"); } static int omap_plane_atomic_set_property(struct drm_plane *plane, struct drm_plane_state *state, struct drm_property *property, - uint64_t val) + u64 val) { struct omap_drm_private *priv = plane->dev->dev_private; @@ -218,7 +476,7 @@ static int omap_plane_atomic_set_property(struct drm_plane *plane, static int omap_plane_atomic_get_property(struct drm_plane *plane, const struct drm_plane_state *state, struct drm_property *property, - uint64_t *val) + u64 *val) { struct omap_drm_private *priv = plane->dev->dev_private; @@ -235,26 +493,28 @@ static const struct drm_plane_funcs omap_plane_funcs = { .disable_plane = drm_atomic_helper_disable_plane, .reset = omap_plane_reset, .destroy = omap_plane_destroy, - .set_property = drm_atomic_helper_plane_set_property, - .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_duplicate_state = omap_plane_atomic_duplicate_state, .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, .atomic_set_property = omap_plane_atomic_set_property, .atomic_get_property = omap_plane_atomic_get_property, + .atomic_print_state = omap_plane_atomic_print_state, }; -static const char *plane_id_to_name[] = { - [OMAP_DSS_GFX] = "gfx", - [OMAP_DSS_VIDEO1] = "vid1", - [OMAP_DSS_VIDEO2] = "vid2", - [OMAP_DSS_VIDEO3] = "vid3", -}; +static bool omap_plane_supports_yuv(struct drm_plane *plane) +{ + struct omap_drm_private *priv = plane->dev->dev_private; + struct omap_plane *omap_plane = to_omap_plane(plane); + const u32 *formats = dispc_ovl_get_color_modes(priv->dispc, omap_plane->id); + u32 i; -static const enum omap_plane_id plane_idx_to_id[] = { - OMAP_DSS_GFX, - OMAP_DSS_VIDEO1, - OMAP_DSS_VIDEO2, - OMAP_DSS_VIDEO3, -}; + for (i = 0; formats[i]; i++) + if (formats[i] == DRM_FORMAT_YUYV || + formats[i] == DRM_FORMAT_UYVY || + formats[i] == DRM_FORMAT_NV12) + return true; + + return false; +} /* initialize plane */ struct drm_plane *omap_plane_init(struct drm_device *dev, @@ -262,49 +522,69 @@ struct drm_plane *omap_plane_init(struct drm_device *dev, u32 possible_crtcs) { struct omap_drm_private *priv = dev->dev_private; - unsigned int num_planes = priv->dispc_ops->get_num_ovls(); + unsigned int num_planes = dispc_get_num_ovls(priv->dispc); struct drm_plane *plane; struct omap_plane *omap_plane; - enum omap_plane_id id; + unsigned int zpos; int ret; u32 nformats; const u32 *formats; - if (WARN_ON(idx >= ARRAY_SIZE(plane_idx_to_id))) + if (WARN_ON(idx >= num_planes)) return ERR_PTR(-EINVAL); - id = plane_idx_to_id[idx]; - - DBG("%s: type=%d", plane_id_to_name[id], type); - omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL); if (!omap_plane) return ERR_PTR(-ENOMEM); - formats = priv->dispc_ops->ovl_get_color_modes(id); + omap_plane->id = idx; + + DBG("%d: type=%d", omap_plane->id, type); + DBG(" crtc_mask: 0x%04x", possible_crtcs); + + formats = dispc_ovl_get_color_modes(priv->dispc, omap_plane->id); for (nformats = 0; formats[nformats]; ++nformats) ; - omap_plane->id = id; - omap_plane->name = plane_id_to_name[id]; plane = &omap_plane->base; ret = drm_universal_plane_init(dev, plane, possible_crtcs, &omap_plane_funcs, formats, - nformats, type, NULL); + nformats, NULL, type, NULL); if (ret < 0) goto error; drm_plane_helper_add(plane, &omap_plane_helper_funcs); omap_plane_install_properties(plane, &plane->base); - drm_plane_create_zpos_property(plane, 0, 0, num_planes - 1); + + /* + * Set the zpos default depending on whether we are a primary or overlay + * plane. + */ + if (plane->type == DRM_PLANE_TYPE_PRIMARY) + zpos = 0; + else + zpos = omap_plane->id; + drm_plane_create_zpos_property(plane, zpos, 0, num_planes - 1); + drm_plane_create_alpha_property(plane); + drm_plane_create_blend_mode_property(plane, BIT(DRM_MODE_BLEND_PREMULTI) | + BIT(DRM_MODE_BLEND_COVERAGE)); + + if (omap_plane_supports_yuv(plane)) + drm_plane_create_color_properties(plane, + BIT(DRM_COLOR_YCBCR_BT601) | + BIT(DRM_COLOR_YCBCR_BT709), + BIT(DRM_COLOR_YCBCR_FULL_RANGE) | + BIT(DRM_COLOR_YCBCR_LIMITED_RANGE), + DRM_COLOR_YCBCR_BT601, + DRM_COLOR_YCBCR_FULL_RANGE); return plane; error: - dev_err(dev->dev, "%s(): could not create plane: %s\n", - __func__, plane_id_to_name[id]); + dev_err(dev->dev, "%s(): could not create plane: %d\n", + __func__, omap_plane->id); kfree(omap_plane); return NULL; diff --git a/drivers/gpu/drm/omapdrm/omap_plane.h b/drivers/gpu/drm/omapdrm/omap_plane.h new file mode 100644 index 000000000000..a9a33e12722a --- /dev/null +++ b/drivers/gpu/drm/omapdrm/omap_plane.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * omap_plane.h -- OMAP DRM Plane + * + * Copyright (C) 2011 Texas Instruments + * Author: Rob Clark <rob@ti.com> + */ + +#ifndef __OMAPDRM_PLANE_H__ +#define __OMAPDRM_PLANE_H__ + +#include <linux/types.h> + +enum drm_plane_type; + +struct drm_device; +struct drm_mode_object; +struct drm_plane; + +struct drm_plane *omap_plane_init(struct drm_device *dev, + int idx, enum drm_plane_type type, + u32 possible_crtcs); +void omap_plane_install_properties(struct drm_plane *plane, + struct drm_mode_object *obj); +bool is_omap_plane_dual_overlay(struct drm_plane_state *state); + +#endif /* __OMAPDRM_PLANE_H__ */ diff --git a/drivers/gpu/drm/omapdrm/tcm-sita.c b/drivers/gpu/drm/omapdrm/tcm-sita.c index c10fdfc0930f..fde0208ec01e 100644 --- a/drivers/gpu/drm/omapdrm/tcm-sita.c +++ b/drivers/gpu/drm/omapdrm/tcm-sita.c @@ -1,22 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0-only /* - * tcm-sita.c - * * SImple Tiler Allocator (SiTA): 2D and 1D allocation(reservation) algorithm * * Authors: Ravi Ramachandra <r.ramachandra@ti.com>, * Lajos Molnar <molnar@ti.com> * Andy Gross <andy.gross@ti.com> * - * Copyright (C) 2012 Texas Instruments, Inc. - * - * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED - * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. - * + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ */ #include <linux/init.h> #include <linux/module.h> @@ -35,8 +25,8 @@ static unsigned long mask[8]; * map ptr to bitmap * stride slots in a row */ -static void free_slots(unsigned long pos, uint16_t w, uint16_t h, - unsigned long *map, uint16_t stride) +static void free_slots(unsigned long pos, u16 w, u16 h, + unsigned long *map, u16 stride) { int i; @@ -50,7 +40,7 @@ static void free_slots(unsigned long pos, uint16_t w, uint16_t h, * map ptr to bitmap * num_bits number of bits in bitmap */ -static int r2l_b2t_1d(uint16_t w, unsigned long *pos, unsigned long *map, +static int r2l_b2t_1d(u16 w, unsigned long *pos, unsigned long *map, size_t num_bits) { unsigned long search_count = 0; @@ -86,13 +76,13 @@ static int r2l_b2t_1d(uint16_t w, unsigned long *pos, unsigned long *map, * num_bits = size of bitmap * stride = bits in one row of container */ -static int l2r_t2b(uint16_t w, uint16_t h, uint16_t a, int16_t offset, +static int l2r_t2b(u16 w, u16 h, u16 a, s16 offset, unsigned long *pos, unsigned long slot_bytes, unsigned long *map, size_t num_bits, size_t slot_stride) { int i; unsigned long index; - bool area_free; + bool area_free = false; unsigned long slots_per_band = PAGE_SIZE / slot_bytes; unsigned long bit_offset = (offset > 0) ? offset / slot_bytes : 0; unsigned long curr_bit = bit_offset; @@ -181,7 +171,7 @@ static s32 sita_reserve_1d(struct tcm *tcm, u32 num_slots, } static s32 sita_reserve_2d(struct tcm *tcm, u16 h, u16 w, u16 align, - int16_t offset, uint16_t slot_bytes, + s16 offset, u16 slot_bytes, struct tcm_area *area) { unsigned long pos; @@ -210,7 +200,7 @@ static void sita_deinit(struct tcm *tcm) static s32 sita_free(struct tcm *tcm, struct tcm_area *area) { unsigned long pos; - uint16_t w, h; + u16 w, h; pos = area->p0.x + area->p0.y * tcm->width; if (area->is2d) { @@ -256,6 +246,5 @@ struct tcm *sita_init(u16 width, u16 height) return tcm; error: - kfree(tcm); return NULL; } diff --git a/drivers/gpu/drm/omapdrm/tcm-sita.h b/drivers/gpu/drm/omapdrm/tcm-sita.h deleted file mode 100644 index 0444f868671c..000000000000 --- a/drivers/gpu/drm/omapdrm/tcm-sita.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * tcm_sita.h - * - * SImple Tiler Allocator (SiTA) private structures. - * - * Author: Ravi Ramachandra <r.ramachandra@ti.com> - * - * Copyright (C) 2009-2011 Texas Instruments, Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of Texas Instruments Incorporated nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef _TCM_SITA_H -#define _TCM_SITA_H - -#include "tcm.h" - -/* length between two coordinates */ -#define LEN(a, b) ((a) > (b) ? (a) - (b) + 1 : (b) - (a) + 1) - -enum criteria { - CR_MAX_NEIGHS = 0x01, - CR_FIRST_FOUND = 0x10, - CR_BIAS_HORIZONTAL = 0x20, - CR_BIAS_VERTICAL = 0x40, - CR_DIAGONAL_BALANCE = 0x80 -}; - -/* nearness to the beginning of the search field from 0 to 1000 */ -struct nearness_factor { - s32 x; - s32 y; -}; - -/* - * Statistics on immediately neighboring slots. Edge is the number of - * border segments that are also border segments of the scan field. Busy - * refers to the number of neighbors that are occupied. - */ -struct neighbor_stats { - u16 edge; - u16 busy; -}; - -/* structure to keep the score of a potential allocation */ -struct score { - struct nearness_factor f; - struct neighbor_stats n; - struct tcm_area a; - u16 neighs; /* number of busy neighbors */ -}; - -struct sita_pvt { - spinlock_t lock; /* spinlock to protect access */ - struct tcm_pt div_pt; /* divider point splitting container */ - struct tcm_area ***map; /* pointers to the parent area for each slot */ -}; - -/* assign coordinates to area */ -static inline -void assign(struct tcm_area *a, u16 x0, u16 y0, u16 x1, u16 y1) -{ - a->p0.x = x0; - a->p0.y = y0; - a->p1.x = x1; - a->p1.y = y1; -} - -#endif diff --git a/drivers/gpu/drm/omapdrm/tcm.h b/drivers/gpu/drm/omapdrm/tcm.h index ef7df7d6fc84..8efcda93c50d 100644 --- a/drivers/gpu/drm/omapdrm/tcm.h +++ b/drivers/gpu/drm/omapdrm/tcm.h @@ -1,6 +1,4 @@ /* - * tcm.h - * * TILER container manager specification and support functions for TI * TILER driver. * @@ -67,7 +65,7 @@ struct tcm { /* function table */ s32 (*reserve_2d)(struct tcm *tcm, u16 height, u16 width, u16 align, - int16_t offset, uint16_t slot_bytes, + s16 offset, u16 slot_bytes, struct tcm_area *area); s32 (*reserve_1d)(struct tcm *tcm, u32 slots, struct tcm_area *area); s32 (*free)(struct tcm *tcm, struct tcm_area *area); @@ -131,7 +129,7 @@ static inline void tcm_deinit(struct tcm *tcm) * allocation. */ static inline s32 tcm_reserve_2d(struct tcm *tcm, u16 width, u16 height, - u16 align, int16_t offset, uint16_t slot_bytes, + u16 align, s16 offset, u16 slot_bytes, struct tcm_area *area) { /* perform rudimentary error checking */ |
