For Z order property we have take example on exynos driver and keep the same property name to avoid forking. I would prefer to not hardcode Z order because we use it to do a kind of composition between graphical planes and video planes. It allow us to manage the Z order of some windows without using software composition. If you have a link to the plane properties talks we will happy to follow/participate and fix the driver according to what will be defined. Benjamin 2014-04-11 12:26 GMT+02:00 Daniel Vetter <daniel@xxxxxxxx>: > On Tue, Apr 08, 2014 at 02:19:24PM +0200, Benjamin Gaignard wrote: >> Make the link between all the hardware drivers and DRM/KMS interface. >> Create the driver itself and make it register all the sub-components. >> Use GEM CMA helpers for buffer allocation. >> >> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@xxxxxxxxxx> >> Signed-off-by: Vincent Abriou <vincent.abriou@xxxxxx> >> Signed-off-by: Fabien Dessenne <fabien.dessenne@xxxxxx> >> --- >> drivers/gpu/drm/sti/Kconfig | 8 + >> drivers/gpu/drm/sti/Makefile | 28 +- >> drivers/gpu/drm/sti/sti_compositor.c | 2 + >> drivers/gpu/drm/sti/sti_drm_connector.c | 195 +++++++++++++ >> drivers/gpu/drm/sti/sti_drm_connector.h | 16 ++ >> drivers/gpu/drm/sti/sti_drm_crtc.c | 440 ++++++++++++++++++++++++++++++ >> drivers/gpu/drm/sti/sti_drm_crtc.h | 21 ++ >> drivers/gpu/drm/sti/sti_drm_drv.c | 466 ++++++++++++++++++++++++++++++++ >> drivers/gpu/drm/sti/sti_drm_encoder.c | 201 ++++++++++++++ >> drivers/gpu/drm/sti/sti_drm_encoder.h | 16 ++ >> drivers/gpu/drm/sti/sti_drm_plane.c | 195 +++++++++++++ >> drivers/gpu/drm/sti/sti_drm_plane.h | 16 ++ >> drivers/gpu/drm/sti/sti_vtg_utils.h | 2 + >> 13 files changed, 1594 insertions(+), 12 deletions(-) >> create mode 100644 drivers/gpu/drm/sti/sti_drm_connector.c >> create mode 100644 drivers/gpu/drm/sti/sti_drm_connector.h >> create mode 100644 drivers/gpu/drm/sti/sti_drm_crtc.c >> create mode 100644 drivers/gpu/drm/sti/sti_drm_crtc.h >> create mode 100644 drivers/gpu/drm/sti/sti_drm_drv.c >> create mode 100644 drivers/gpu/drm/sti/sti_drm_encoder.c >> create mode 100644 drivers/gpu/drm/sti/sti_drm_encoder.h >> create mode 100644 drivers/gpu/drm/sti/sti_drm_plane.c >> create mode 100644 drivers/gpu/drm/sti/sti_drm_plane.h >> >> diff --git a/drivers/gpu/drm/sti/Kconfig b/drivers/gpu/drm/sti/Kconfig >> index a958398..61c8bd4 100644 >> --- a/drivers/gpu/drm/sti/Kconfig >> +++ b/drivers/gpu/drm/sti/Kconfig >> @@ -1,10 +1,18 @@ >> config DRM_STI >> tristate "DRM Support for STMicroelectronics SoC stiH41x Series" >> depends on DRM && (SOC_STIH415 || SOC_STIH416 || ARCH_MULTIPLATFORM) >> + select DRM_KMS_HELPER >> + select DRM_GEM_CMA_HELPER >> select DRM_KMS_CMA_HELPER >> help >> Choose this option to enable DRM on STM stiH41x chipset >> >> +config DRM_STI_FBDEV >> + tristate "DRM frame buffer device for STMicroelectronics SoC stiH41x Serie" >> + depends on DRM_STI >> + help >> + Choose this option to enable FBDEV on top of DRM for STM stiH41x chipset >> + >> config VTAC_STI >> tristate "Video Trafic Advance Communication Rx and Tx for STMicroelectronics SoC stiH41x Series" >> depends on DRM_STI >> diff --git a/drivers/gpu/drm/sti/Makefile b/drivers/gpu/drm/sti/Makefile >> index d598adc..17b4a68 100644 >> --- a/drivers/gpu/drm/sti/Makefile >> +++ b/drivers/gpu/drm/sti/Makefile >> @@ -1,17 +1,21 @@ >> ccflags-y := -Iinclude/drm >> >> -stidrm-y := \ >> - sti_compositor.o \ >> - sti_mixer.o \ >> - sti_layer.o \ >> - sti_gdp.o \ >> - sti_vid.o \ >> - sti_tvout.o \ >> - sti_hdmi.o \ >> - sti_hdmi_tx3g0c55phy.o \ >> - sti_hdmi_tx3g4c28phy.o \ >> - sti_hda.o \ >> - sti_ddc.o >> +stidrm-y := sti_drm_drv.o \ >> + sti_drm_crtc.o \ >> + sti_drm_plane.o \ >> + sti_drm_connector.o \ >> + sti_drm_encoder.o \ >> + sti_compositor.o \ >> + sti_mixer.o \ >> + sti_layer.o \ >> + sti_gdp.o \ >> + sti_vid.o \ >> + sti_tvout.o \ >> + sti_hdmi.o \ >> + sti_hdmi_tx3g0c55phy.o \ >> + sti_hdmi_tx3g4c28phy.o \ >> + sti_hda.o \ >> + sti_ddc.o >> >> obj-$(CONFIG_DRM_STI) += stidrm.o >> obj-$(CONFIG_VTAC_STI) += sti_vtac_tx.o sti_vtac_rx.o >> diff --git a/drivers/gpu/drm/sti/sti_compositor.c b/drivers/gpu/drm/sti/sti_compositor.c >> index a163344..4d8d0958 100644 >> --- a/drivers/gpu/drm/sti/sti_compositor.c >> +++ b/drivers/gpu/drm/sti/sti_compositor.c >> @@ -14,6 +14,7 @@ >> >> #include "sti_compositor.h" >> #include "sti_gdp.h" >> +#include "sti_drm_crtc.h" >> >> static const struct of_device_id compositor_match_types[]; >> >> @@ -98,6 +99,7 @@ static int sti_compositor_probe(struct platform_device *pdev) >> } >> DRM_DEBUG_DRIVER("Compositor %p\n", compo); >> compo->dev = dev; >> + compo->vtg_vblank_nb.notifier_call = sti_drm_crtc_vblank_cb; >> >> /* populate data structure depending on compatibility */ >> BUG_ON(!of_match_node(compositor_match_types, np)->data); >> diff --git a/drivers/gpu/drm/sti/sti_drm_connector.c b/drivers/gpu/drm/sti/sti_drm_connector.c >> new file mode 100644 >> index 0000000..b34a402 >> --- /dev/null >> +++ b/drivers/gpu/drm/sti/sti_drm_connector.c >> @@ -0,0 +1,195 @@ >> +/* >> + * Copyright (C) STMicroelectronics SA 2013 >> + * Author: Benjamin Gaignard <benjamin.gaignard@xxxxxx> for STMicroelectronics. >> + * License terms: GNU General Public License (GPL), version 2 >> + */ >> + >> +#include <drm/drmP.h> >> +#include <drm/drm_crtc_helper.h> >> + >> +#include "sti_drm_connector.h" >> +#include "sti_drm_drv.h" >> + >> +#define to_sti_connector(x) container_of(x, struct sti_connector, drm_connector) >> + >> +/* >> + * sti specific connector structure >> + * >> + * @drm_encoder: connector object >> + * @encoder: associated encoder >> + * @tvout: pointer on tvout driver >> + * @type: tvout connector type >> + */ >> +struct sti_connector { >> + struct drm_connector drm_connector; >> + struct drm_encoder *encoder; >> + struct sti_tvout *tvout; >> + enum sti_tvout_connector_type type; >> +}; >> + >> +static int sti_drm_connector_get_modes(struct drm_connector *connector) >> +{ >> + struct sti_connector *sti_connector = to_sti_connector(connector); >> + struct drm_device *dev = connector->dev; >> + >> + dev_dbg(dev->dev, "%s\n", __func__); >> + >> + return sti_tvout_get_modes(sti_connector->tvout, sti_connector->type, >> + connector); >> +} >> + >> +static int sti_drm_connector_mode_valid(struct drm_connector *connector, >> + struct drm_display_mode *mode) >> +{ >> + struct sti_connector *sti_connector = to_sti_connector(connector); >> + struct drm_device *dev = connector->dev; >> + int ret = MODE_BAD; >> + >> + dev_dbg(dev->dev, "%s\n", __func__); >> + >> + if (!sti_tvout_check_mode >> + (sti_connector->tvout, sti_connector->type, mode)) >> + ret = MODE_OK; >> + >> + return ret; >> +} >> + >> +struct drm_encoder *sti_drm_best_encoder(struct drm_connector *connector) >> +{ >> + struct sti_connector *sti_connector = to_sti_connector(connector); >> + struct drm_device *dev = connector->dev; >> + >> + dev_dbg(dev->dev, "%s\n", __func__); >> + >> + /* Best encoder is the one associated during connector creation */ >> + return sti_connector->encoder; >> +} >> + >> +static struct drm_connector_helper_funcs sti_drm_connector_helper_funcs = { >> + .get_modes = sti_drm_connector_get_modes, >> + .mode_valid = sti_drm_connector_mode_valid, >> + .best_encoder = sti_drm_best_encoder, >> +}; >> + >> +static void sti_drm_connector_dpms(struct drm_connector *connector, int mode) >> +{ >> + struct drm_device *dev = connector->dev; >> + >> + dev_dbg(dev->dev, "%s\n", __func__); >> + >> + drm_helper_connector_dpms(connector, mode); >> +} >> + >> +/* get detection status of display device. */ >> +static enum drm_connector_status >> +sti_drm_connector_detect(struct drm_connector *connector, bool force) >> +{ >> + enum drm_connector_status status = connector_status_disconnected; >> + struct sti_connector *sti_connector = to_sti_connector(connector); >> + struct drm_device *dev = connector->dev; >> + >> + dev_dbg(dev->dev, "%s\n", __func__); >> + >> + if (sti_tvout_connector_detect(sti_connector->tvout, >> + sti_connector->type)) >> + status = connector_status_connected; >> + >> + return status; >> +} >> + >> +static void sti_drm_connector_destroy(struct drm_connector *connector) >> +{ >> + struct sti_connector *sti_connector = to_sti_connector(connector); >> + struct drm_device *dev = connector->dev; >> + >> + dev_dbg(dev->dev, "%s\n", __func__); >> + >> + drm_sysfs_connector_remove(connector); >> + drm_connector_cleanup(connector); >> + kfree(sti_connector); >> +} >> + >> +static struct drm_connector_funcs sti_drm_connector_funcs = { >> + .dpms = sti_drm_connector_dpms, >> + .fill_modes = drm_helper_probe_single_connector_modes, >> + .detect = sti_drm_connector_detect, >> + .destroy = sti_drm_connector_destroy, >> +}; >> + >> +struct drm_connector *sti_drm_connector_create(struct drm_device *dev, >> + struct sti_tvout *tvout, >> + struct drm_encoder *encoder, >> + enum sti_tvout_connector_type >> + type) >> +{ >> + struct sti_connector *sti_connector; >> + struct drm_connector *connector; >> + int connector_type; >> + int err; >> + >> + dev_dbg(dev->dev, "%s\n", __func__); >> + >> + /* Create the tvout connector according to the type */ >> + tvout->connector[type] = tvout->connector_create[type] (tvout); >> + if (!tvout->connector[type]) { >> + DRM_INFO("%s: failed to create connector (type = %d)\n", >> + __func__, type); >> + return NULL; >> + } >> + >> + sti_connector = kzalloc(sizeof(*sti_connector), GFP_KERNEL); >> + if (!sti_connector) { >> + DRM_ERROR("failed to allocate connector\n"); >> + return NULL; >> + } >> + >> + connector = &sti_connector->drm_connector; >> + >> + switch (type) { >> + case STI_TVOUT_CONNECTOR_HDMI: >> + connector_type = DRM_MODE_CONNECTOR_HDMIA; >> + connector->polled = DRM_CONNECTOR_POLL_HPD; >> + break; >> + case STI_TVOUT_CONNECTOR_HDA: >> + connector_type = DRM_MODE_CONNECTOR_Component; >> + break; >> + case STI_TVOUT_CONNECTOR_DVO: >> + connector_type = DRM_MODE_CONNECTOR_LVDS; >> + break; >> + case STI_TVOUT_CONNECTOR_DENC: >> + connector_type = DRM_MODE_CONNECTOR_Composite; >> + break; >> + default: >> + connector_type = DRM_MODE_CONNECTOR_Unknown; >> + break; >> + } >> + >> + drm_connector_init(dev, connector, &sti_drm_connector_funcs, >> + connector_type); >> + drm_connector_helper_add(connector, &sti_drm_connector_helper_funcs); >> + >> + err = drm_sysfs_connector_add(connector); >> + if (err) >> + goto err_connector; >> + >> + sti_connector->tvout = tvout; >> + sti_connector->encoder = encoder; >> + sti_connector->type = type; >> + >> + err = drm_mode_connector_attach_encoder(connector, encoder); >> + if (err) { >> + DRM_ERROR("Failed to attach a connector to a encoder\n"); >> + goto err_sysfs; >> + } >> + >> + DRM_DEBUG_DRIVER("Connector has been created\n"); >> + >> + return connector; >> + >> +err_sysfs: >> + drm_sysfs_connector_remove(connector); >> +err_connector: >> + drm_connector_cleanup(connector); >> + kfree(sti_connector); >> + return NULL; >> +} >> diff --git a/drivers/gpu/drm/sti/sti_drm_connector.h b/drivers/gpu/drm/sti/sti_drm_connector.h >> new file mode 100644 >> index 0000000..068c674 >> --- /dev/null >> +++ b/drivers/gpu/drm/sti/sti_drm_connector.h >> @@ -0,0 +1,16 @@ >> +/* >> + * Copyright (C) STMicroelectronics SA 2013 >> + * Author: Benjamin Gaignard <benjamin.gaignard@xxxxxx> for STMicroelectronics. >> + * License terms: GNU General Public License (GPL), version 2 >> + */ >> + >> +#ifndef _STI_DRM_CONNECTOR_H_ >> +#define _STI_DRM_CONNECTOR_H_ >> + >> +#include "sti_tvout.h" >> + >> +struct drm_connector *sti_drm_connector_create(struct drm_device *dev, >> + struct sti_tvout *tvout, struct drm_encoder *encoder, >> + enum sti_tvout_connector_type type); >> + >> +#endif >> diff --git a/drivers/gpu/drm/sti/sti_drm_crtc.c b/drivers/gpu/drm/sti/sti_drm_crtc.c >> new file mode 100644 >> index 0000000..5c06d70 >> --- /dev/null >> +++ b/drivers/gpu/drm/sti/sti_drm_crtc.c >> @@ -0,0 +1,440 @@ >> +/* >> + * Copyright (C) STMicroelectronics SA 2013 >> + * Authors: Benjamin Gaignard <benjamin.gaignard@xxxxxx> >> + * Fabien Dessenne <fabien.dessenne@xxxxxx> >> + * for STMicroelectronics. >> + * License terms: GNU General Public License (GPL), version 2 >> + */ >> + >> +#include <linux/clk.h> >> + >> +#include <drm/drmP.h> >> +#include <drm/drm_crtc_helper.h> >> + >> +#include "sti_drm_drv.h" >> +#include "sti_drm_crtc.h" >> +#include "sti_compositor.h" >> +#include "sti_vtg_utils.h" >> + >> +static void sti_drm_crtc_dpms(struct drm_crtc *crtc, int mode) >> +{ >> + DRM_DEBUG_KMS("\n"); >> +} >> + >> +static void sti_drm_crtc_prepare(struct drm_crtc *crtc) >> +{ >> + struct sti_mixer *mixer = to_sti_mixer(crtc); >> + struct device *dev = mixer->dev; >> + struct sti_compositor *compo = dev_get_drvdata(dev); >> + >> + compo->enable = true; >> + >> + /* Prepare and enable the compo IP clock */ >> + if (mixer->id == STI_MIXER_MAIN) { >> + if (clk_prepare_enable(compo->clk_compo_main)) >> + DRM_INFO("Failed to prepare/enable compo_main clk\n"); >> + } else { >> + if (clk_prepare_enable(compo->clk_compo_aux)) >> + DRM_INFO("Failed to prepare/enable compo_aux clk\n"); >> + } >> +} >> + >> +static void sti_drm_crtc_commit(struct drm_crtc *crtc) >> +{ >> + struct sti_mixer *mixer = to_sti_mixer(crtc); >> + struct device *dev = mixer->dev; >> + struct sti_compositor *compo = dev_get_drvdata(dev); >> + struct sti_layer *layer; >> + >> + dev_dbg(dev, "%s\n", __func__); >> + if ((!mixer || !compo)) { >> + DRM_ERROR("Can not find mixer or compositor)\n"); >> + return; >> + } >> + >> + /* Find GDP0 which is reserved to the CRTC FB */ >> + layer = sti_layer_find_layer(compo->layer, STI_GDP_0); >> + if (layer) >> + sti_layer_commit(layer); >> + else >> + DRM_ERROR("Can not find CRTC dedicated plane (GDP0)\n"); >> + >> + /* Enable layer on mixer */ >> + if (sti_mixer_set_layer_status(mixer, layer, true)) >> + DRM_ERROR("Can not enable layer at mixer\n"); >> +} >> + >> +static bool sti_drm_crtc_mode_fixup(struct drm_crtc *crtc, >> + const struct drm_display_mode *mode, >> + struct drm_display_mode *adjusted_mode) >> +{ >> + /* accept the provided drm_display_mode, do not fix it up */ >> + dev_dbg(crtc->dev->dev, "%s\n", __func__); >> + return true; >> +} >> + >> +static int >> +sti_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, >> + struct drm_display_mode *adjusted_mode, int x, int y, >> + struct drm_framebuffer *old_fb) >> +{ >> + struct sti_mixer *mixer = to_sti_mixer(crtc); >> + struct device *dev = mixer->dev; >> + struct sti_compositor *compo = dev_get_drvdata(dev); >> + struct sti_layer *layer; >> + struct clk *clk; >> + int rate = mode->clock * 1000; >> + int res; >> + unsigned int w, h; >> + >> + DRM_DEBUG_KMS("CRTC:%d (%s) fb:%d mode:%d (%s)\n", >> + crtc->base.id, sti_mixer_to_str(mixer), >> + crtc->fb->base.id, mode->base.id, mode->name); >> + >> + DRM_DEBUG_KMS("%d %d %d %d %d %d %d %d %d %d 0x%x 0x%x\n", >> + 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); >> + >> + /* Set rate and prepare/enable pixel clock */ >> + if (mixer->id == STI_MIXER_MAIN) >> + clk = compo->clk_pix_main; >> + else >> + clk = compo->clk_pix_aux; >> + >> + res = clk_set_rate(clk, rate); >> + if (res < 0) { >> + DRM_ERROR("Cannot set rate (%dHz) for pix clk\n", rate); >> + return 1; >> + } >> + if (clk_prepare_enable(clk)) { >> + DRM_ERROR("Failed to prepare/enable pix clk\n"); >> + return 1; >> + } >> + >> + sti_vtg_setconfig(mixer->id == STI_MIXER_MAIN ? VTG_MAIN : VTG_AUX, >> + &crtc->mode); >> + >> + /* GDP0 is reserved to the CRTC FB */ >> + layer = sti_layer_find_layer(compo->layer, STI_GDP_0); >> + if (!layer) { >> + DRM_ERROR("Can not find GDP0)\n"); >> + return 1; >> + } >> + >> + /* copy the mode data adjusted by mode_fixup() into crtc->mode >> + * so that hardware can be set to proper mode */ >> + memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode)); >> + >> + res = sti_mixer_set_layer_depth(mixer, layer); >> + if (res) { >> + DRM_ERROR("Can not set layer depth\n"); >> + return 1; >> + } >> + res = sti_mixer_active_video_area(mixer, &crtc->mode); >> + if (res) { >> + DRM_ERROR("Can not set active video area\n"); >> + return 1; >> + } >> + >> + if ((mode->hdisplay != crtc->fb->width) || >> + (mode->vdisplay != crtc->fb->height)) >> + DRM_DEBUG_KMS("WARNING: fb and display mode sizes differ\n"); >> + >> + w = crtc->fb->width - x; >> + h = crtc->fb->height - y; >> + >> + if ((w <= 0) || (h <= 0)) { >> + DRM_ERROR("Coordinates outside FB\n"); >> + return -EINVAL; >> + } >> + >> + return sti_layer_prepare(layer, crtc->fb, &crtc->mode, mixer->id, >> + 0, 0, w, h, x, y, w, h); >> +} >> + >> +static int sti_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, >> + struct drm_framebuffer *old_fb) >> +{ >> + struct sti_mixer *mixer = to_sti_mixer(crtc); >> + struct device *dev = mixer->dev; >> + struct sti_compositor *compo = dev_get_drvdata(dev); >> + struct sti_layer *layer; >> + unsigned int w, h; >> + int ret = 0; >> + >> + DRM_DEBUG_KMS("CRTC:%d (%s) fb:%d (%d,%d)\n", >> + crtc->base.id, sti_mixer_to_str(mixer), >> + crtc->fb->base.id, x, y); >> + >> + /* GDP0 is reserved to the CRTC FB */ >> + layer = sti_layer_find_layer(compo->layer, STI_GDP_0); >> + if (!layer) { >> + DRM_ERROR("Can not find GDP0)\n"); >> + ret = -1; >> + goto out; >> + } >> + >> + w = crtc->fb->width - crtc->x; >> + h = crtc->fb->height - crtc->y; >> + >> + if ((w <= 0) || (h <= 0)) { >> + DRM_ERROR("Coordinates outside FB\n"); >> + ret = -EINVAL; >> + goto out; >> + } >> + >> + ret = sti_layer_prepare(layer, crtc->fb, &crtc->mode, mixer->id, >> + 0, 0, w, h, >> + crtc->x, crtc->y, w, h); >> + if (ret) { >> + DRM_ERROR("Can not prepare layer\n"); >> + goto out; >> + } >> + >> + sti_drm_crtc_commit(crtc); >> +out: >> + return ret; >> +} >> + >> +static void sti_drm_crtc_load_lut(struct drm_crtc *crtc) >> +{ >> + dev_dbg(crtc->dev->dev, "%s\n", __func__); >> +} >> + >> +static void sti_drm_crtc_disable(struct drm_crtc *crtc) >> +{ >> + struct sti_mixer *mixer = to_sti_mixer(crtc); >> + struct device *dev = mixer->dev; >> + struct sti_compositor *compo = dev_get_drvdata(dev); >> + struct sti_layer *layer; >> + >> + if (!compo->enable) >> + return; >> + >> + DRM_DEBUG_KMS("CRTC:%d (%s)\n", crtc->base.id, sti_mixer_to_str(mixer)); >> + >> + /* Disable Background */ >> + sti_mixer_set_background_status(mixer, false); >> + >> + /* Disable GDP0 */ >> + layer = sti_layer_find_layer(compo->layer, STI_GDP_0); >> + if (!layer) { >> + DRM_ERROR("Cannot find GDP0\n"); >> + return; >> + } >> + >> + /* Disable layer at mixer level */ >> + if (sti_mixer_set_layer_status(mixer, layer, false)) >> + DRM_ERROR("Can not disable %s layer at mixer\n", >> + sti_layer_to_str(layer)); >> + >> + /* Wait a while to be sure that a Vsync event is received */ >> + msleep(WAIT_NEXT_VSYNC_MS); >> + >> + /* Then disable layer itself */ >> + sti_layer_disable(layer); >> + >> + drm_vblank_off(crtc->dev, mixer->id); >> + >> + /* Disable pixel clock and compo IP clocks */ >> + if (mixer->id == STI_MIXER_MAIN) { >> + clk_disable_unprepare(compo->clk_pix_main); >> + clk_disable_unprepare(compo->clk_compo_main); >> + } else { >> + clk_disable_unprepare(compo->clk_pix_aux); >> + clk_disable_unprepare(compo->clk_compo_aux); >> + } >> + >> + compo->enable = false; >> +} >> + >> +static struct drm_crtc_helper_funcs sti_crtc_helper_funcs = { >> + .dpms = sti_drm_crtc_dpms, >> + .prepare = sti_drm_crtc_prepare, >> + .commit = sti_drm_crtc_commit, >> + .mode_fixup = sti_drm_crtc_mode_fixup, >> + .mode_set = sti_drm_crtc_mode_set, >> + .mode_set_base = sti_drm_crtc_mode_set_base, >> + .load_lut = sti_drm_crtc_load_lut, >> + .disable = sti_drm_crtc_disable, >> +}; >> + >> +static int sti_drm_crtc_page_flip(struct drm_crtc *crtc, >> + struct drm_framebuffer *fb, >> + struct drm_pending_vblank_event *event, >> + uint32_t page_flip_flags) >> +{ >> + struct drm_device *drm_dev = crtc->dev; >> + struct sti_drm_private *dev_priv = drm_dev->dev_private; >> + struct drm_framebuffer *old_fb; >> + struct sti_mixer *mixer = to_sti_mixer(crtc); >> + unsigned long flags; >> + int ret = 0; >> + >> + DRM_DEBUG_KMS("fb %d --> fb %d\n", crtc->fb->base.id, fb->base.id); >> + >> + mutex_lock(&drm_dev->struct_mutex); >> + >> + old_fb = crtc->fb; >> + crtc->fb = fb; >> + ret = sti_drm_crtc_mode_set_base(crtc, crtc->x, crtc->y, old_fb); >> + if (ret) { >> + DRM_ERROR("failed\n"); >> + crtc->fb = old_fb; >> + goto out; >> + } >> + >> + if (event) { >> + event->pipe = mixer->id; >> + >> + ret = drm_vblank_get(drm_dev, event->pipe); >> + if (ret) { >> + DRM_ERROR("Cannot get vblank\n"); >> + goto out; >> + } >> + >> + spin_lock_irqsave(&drm_dev->event_lock, flags); >> + list_add_tail(&event->base.link, &dev_priv->pageflip_evt_list); >> + spin_unlock_irqrestore(&drm_dev->event_lock, flags); >> + } >> +out: >> + mutex_unlock(&drm_dev->struct_mutex); >> + return ret; >> +} >> + >> +static void sti_drm_crtc_destroy(struct drm_crtc *crtc) >> +{ >> + DRM_DEBUG_KMS("\n"); >> + >> + drm_crtc_cleanup(crtc); >> +} >> + >> +static int sti_drm_crtc_set_property(struct drm_crtc *crtc, >> + struct drm_property *property, >> + uint64_t val) >> +{ >> + DRM_DEBUG_KMS("\n"); >> + >> + return 0; >> +} >> + >> +int sti_drm_crtc_vblank_cb(struct notifier_block *nb, >> + unsigned long event, void *data) >> +{ >> + struct sti_compositor *compo = container_of(nb, >> + struct sti_compositor, >> + vtg_vblank_nb); >> + struct drm_device *drm_dev; >> + int *crtc = data; >> + unsigned long flags; >> + struct drm_pending_vblank_event *e, *t; >> + struct sti_drm_private *priv; >> + >> + drm_dev = compo->mixer[*crtc]->drm_crtc.dev; >> + priv = drm_dev->dev_private; >> + >> + dev_dbg(drm_dev->dev, "%s\n", __func__); >> + >> + if ((event != VTG_TOP_FIELD_EVENT) && >> + (event != VTG_BOTTOM_FIELD_EVENT)) { >> + DRM_ERROR("unknown event: %lu\n", event); >> + return -EINVAL; >> + } >> + >> + drm_handle_vblank(drm_dev, *crtc); >> + >> + spin_lock_irqsave(&drm_dev->event_lock, flags); >> + list_for_each_entry_safe(e, t, &priv->pageflip_evt_list, base.link) { >> + if (*crtc != e->pipe) >> + continue; >> + >> + list_del(&e->base.link); >> + drm_send_vblank_event(drm_dev, -1, e); >> + drm_vblank_put(drm_dev, *crtc); >> + } >> + spin_unlock_irqrestore(&drm_dev->event_lock, flags); >> + >> + return 0; >> +} >> + >> +int sti_drm_crtc_enable_vblank(struct drm_device *dev, int crtc) >> +{ >> + struct sti_drm_private *dev_priv = dev->dev_private; >> + struct sti_compositor *compo = dev_priv->compo; >> + struct notifier_block *vtg_vblank_nb = &compo->vtg_vblank_nb; >> + DRM_DEBUG_DRIVER("\n"); >> + >> + if (sti_vtg_register_client(crtc, vtg_vblank_nb)) { >> + DRM_ERROR("Cannot register VTG notifier\n"); >> + return 1; >> + } >> + >> + return 0; >> +} >> + >> +void sti_drm_crtc_disable_vblank(struct drm_device *dev, int crtc) >> +{ >> + struct sti_drm_private *dev_priv = dev->dev_private; >> + struct sti_compositor *compo = dev_priv->compo; >> + struct notifier_block *vtg_vblank_nb = &compo->vtg_vblank_nb; >> + unsigned long flags; >> + struct drm_pending_vblank_event *e, *t; >> + struct sti_drm_private *priv = dev->dev_private; >> + DRM_DEBUG_DRIVER("\n"); >> + >> + if (sti_vtg_unregister_client(crtc, vtg_vblank_nb)) >> + DRM_DEBUG_DRIVER("Warning: cannot unregister VTG notifier\n"); >> + >> + /* free the resources of the pending requests */ >> + spin_lock_irqsave(&dev->event_lock, flags); >> + list_for_each_entry_safe(e, t, &priv->pageflip_evt_list, base.link) { >> + if (crtc != e->pipe) >> + continue; >> + list_del(&e->base.link); >> + drm_vblank_put(dev, crtc); >> + } >> + spin_unlock_irqrestore(&dev->event_lock, flags); >> + >> +} >> + >> +static struct drm_crtc_funcs sti_crtc_funcs = { >> + .set_config = drm_crtc_helper_set_config, >> + .page_flip = sti_drm_crtc_page_flip, >> + .destroy = sti_drm_crtc_destroy, >> + .set_property = sti_drm_crtc_set_property, >> +}; >> + >> +bool sti_drm_crtc_is_main(struct drm_crtc *crtc) >> +{ >> + struct sti_mixer *mixer = to_sti_mixer(crtc); >> + >> + if (mixer->id == STI_MIXER_MAIN) >> + return true; >> + >> + return false; >> +} >> + >> +int sti_drm_crtc_init(struct drm_device *drm_dev, struct sti_mixer *mixer) >> +{ >> + struct drm_crtc *crtc = &mixer->drm_crtc; >> + int res; >> + >> + dev_dbg(drm_dev->dev, "%s\n", __func__); >> + res = drm_crtc_init(drm_dev, crtc, &sti_crtc_funcs); >> + if (res) { >> + DRM_ERROR("Can not initialze CRTC\n"); >> + return 1; >> + } >> + >> + drm_crtc_helper_add(crtc, &sti_crtc_helper_funcs); >> + >> + DRM_DEBUG_DRIVER("drm CRTC:%d mapped to %s\n", >> + crtc->base.id, sti_mixer_to_str(mixer)); >> + >> + return 0; >> +} >> diff --git a/drivers/gpu/drm/sti/sti_drm_crtc.h b/drivers/gpu/drm/sti/sti_drm_crtc.h >> new file mode 100644 >> index 0000000..9d31e41 >> --- /dev/null >> +++ b/drivers/gpu/drm/sti/sti_drm_crtc.h >> @@ -0,0 +1,21 @@ >> +/* >> + * Copyright (C) STMicroelectronics SA 2013 >> + * Author: Benjamin Gaignard <benjamin.gaignard@xxxxxx> for STMicroelectronics. >> + * License terms: GNU General Public License (GPL), version 2 >> + */ >> + >> +#ifndef _STI_DRM_CRTC_H_ >> +#define _STI_DRM_CRTC_H_ >> + >> +#include <drm/drmP.h> >> + >> +#include "sti_mixer.h" >> + >> +int sti_drm_crtc_init(struct drm_device *drm_dev, struct sti_mixer *mixer); >> +int sti_drm_crtc_enable_vblank(struct drm_device *dev, int crtc); >> +void sti_drm_crtc_disable_vblank(struct drm_device *dev, int crtc); >> +int sti_drm_crtc_vblank_cb(struct notifier_block *nb, >> + unsigned long event, void *data); >> +bool sti_drm_crtc_is_main(struct drm_crtc *drm_crtc); >> + >> +#endif >> diff --git a/drivers/gpu/drm/sti/sti_drm_drv.c b/drivers/gpu/drm/sti/sti_drm_drv.c >> new file mode 100644 >> index 0000000..16ac771 >> --- /dev/null >> +++ b/drivers/gpu/drm/sti/sti_drm_drv.c >> @@ -0,0 +1,466 @@ >> +/* >> + * Copyright (C) STMicroelectronics SA 2013 >> + * Author: Benjamin Gaignard <benjamin.gaignard@xxxxxx> for STMicroelectronics. >> + * License terms: GNU General Public License (GPL), version 2 >> + */ >> + >> +#include <drm/drmP.h> >> + >> +#include <linux/kernel.h> >> +#include <linux/module.h> >> +#include <linux/debugfs.h> >> +#include <drm/drm_crtc_helper.h> >> +#include <drm/drm_gem_cma_helper.h> >> +#include <drm/drm_fb_cma_helper.h> >> + >> +#include "sti_drm_drv.h" >> +#include "sti_drm_crtc.h" >> +#include "sti_drm_plane.h" >> +#include "sti_compositor.h" >> +#include "sti_gdp.h" >> +#include "sti_drm_encoder.h" >> +#include "sti_drm_connector.h" >> +#include "sti_tvout.h" >> + >> +#define DRIVER_NAME "sti" >> +#define DRIVER_DESC "STMicroelectronics SoC DRM" >> +#define DRIVER_DATE "20130905" >> +#define DRIVER_MAJOR 1 >> +#define DRIVER_MINOR 0 >> + >> +/* platform device pointer for sti drm device. */ >> +static struct platform_device *sti_drm_pdev; >> +#ifdef CONFIG_DRM_STI_FBDEV >> +#define FBDEV_CREATE_DELAY 2000 >> +struct drm_device *sti_drm_device; >> + >> +static void stid_fbdev_create(struct work_struct *dummy) >> +{ >> + if (!sti_drm_device) >> + return; >> + >> + drm_fbdev_cma_init(sti_drm_device, 32, >> + sti_drm_device->mode_config.num_crtc, >> + sti_drm_device->mode_config.num_connector); >> +} >> + >> +static DECLARE_DELAYED_WORK(sti_fbdev_work, stid_fbdev_create); >> +#endif >> + >> +static int sti_drm_tvout_init(struct device *dev, void *data) >> +{ >> + struct sti_tvout *tvout = dev_get_drvdata(dev); >> + struct drm_device *drm_dev = data; >> + struct drm_encoder *encoder; >> + int i; >> + struct sti_drm_private *dev_priv = drm_dev->dev_private; >> + >> + dev_priv->tvout = tvout; >> + >> + tvout->drm_dev = drm_dev; >> + >> + /* Register all encoder/connector couples supported by tvout */ >> + for (i = 0; i < STI_TVOUT_CONNECTOR_MAX; i++) { >> + if (tvout->connector_create[i] != NULL) { >> + encoder = sti_drm_encoder_create(drm_dev, tvout, >> + 1 << 0, i); >> + sti_drm_connector_create(drm_dev, tvout, encoder, i); >> + } >> + } >> + >> + return 0; >> +} >> + >> +static int sti_drm_compositor_init(struct device *dev, void *data) >> +{ >> + struct sti_compositor *compo = dev_get_drvdata(dev); >> + struct drm_device *drm_dev = data; >> + int i, crtc = 0, plane = 0; >> + struct sti_drm_private *dev_priv = drm_dev->dev_private; >> + >> + dev_dbg(dev, "%s\n", __func__); >> + >> + dev_priv->compo = compo; >> + INIT_LIST_HEAD(&dev_priv->pageflip_evt_list); >> + >> + for (i = 0; i < compo->nb_mixers; i++) { >> + if (compo->mixer[i]) { >> + sti_drm_crtc_init(drm_dev, compo->mixer[i]); >> + crtc++; >> + } >> + } >> + if (crtc == 0) { >> + DRM_ERROR("No CRTC available\n"); >> + return 1; >> + } >> + >> + drm_vblank_init(drm_dev, crtc); >> + /* Allow usage of vblank without having to call drm_irq_install */ >> + drm_dev->irq_enabled = 1; >> + >> + for (i = 0; i < compo->nb_layers; i++) { >> + if (compo->layer[i]) { >> + /* Create planes for GDP. >> + * except GDP0 as it is reserved for CRTC FB */ >> + enum sti_layer_desc desc = compo->layer[i]->desc; >> + enum sti_layer_type type = desc & STI_LAYER_TYPE_MASK; >> + >> + if ((type == STI_GDP) && (desc != STI_GDP_0)) { >> + sti_drm_plane_init(drm_dev, compo->layer[i], >> + (1 << crtc) - 1); >> + plane++; >> + } >> + } >> + } >> + >> + DRM_DEBUG_DRIVER("Initialized %d DRM CRTC(s) and %d DRM plane(s)\n", >> + crtc, plane); >> + DRM_DEBUG_DRIVER("DRM plane(s) for VID/VDP not created yet\n"); >> + >> + return 0; >> +} >> + >> +static struct drm_mode_config_funcs sti_drm_mode_config_funcs = { >> + .fb_create = drm_fb_cma_create, >> +}; >> + >> +#ifdef CONFIG_DEBUG_FS >> +static struct drm_info_list sti_drm_dbg_list[] = { >> + {"gdp", sti_gdp_dbg_show, 0}, >> + {"node", sti_gdp_node_dbg_show, 0}, >> + {"mixer", sti_mixer_dbg_show, 0}, >> + {"hdmi", sti_tvout_hdmi_dbg_show, 0, NULL}, >> + {"hda", sti_tvout_hda_dbg_show, 0, NULL}, >> +}; >> + >> +static int sti_drm_fps_get(void *data, u64 *val) >> +{ >> + struct drm_device *dev = data; >> + struct sti_drm_private *dev_priv = dev->dev_private; >> + struct sti_compositor *compo = dev_priv->compo; >> + int i; >> + >> + *val = 0; >> + for (i = 0; i < compo->nb_layers; i++) >> + *val |= (compo->layer[i]->fps_info.output) << i; >> + >> + return 0; >> +} >> + >> +static int sti_drm_fps_set(void *data, u64 val) >> +{ >> + struct drm_device *dev = data; >> + struct sti_drm_private *dev_priv = dev->dev_private; >> + struct sti_compositor *compo = dev_priv->compo; >> + int i; >> + >> + for (i = 0; i < compo->nb_layers; i++) >> + compo->layer[i]->fps_info.output = (val >> i) & 1; >> + >> + return 0; >> +} >> + >> +DEFINE_SIMPLE_ATTRIBUTE(sti_drm_fps_fops, >> + sti_drm_fps_get, sti_drm_fps_set, "%llu\n"); >> + >> +static int sti_drm_debugfs_create(struct dentry *root, struct drm_minor *minor, >> + const char *name, >> + const struct file_operations *fops) >> +{ >> + struct drm_device *dev = minor->dev; >> + struct drm_info_node *node; >> + struct dentry *ent; >> + >> + ent = debugfs_create_file(name, S_IRUGO | S_IWUSR, root, dev, fops); >> + if (IS_ERR(ent)) >> + return PTR_ERR(ent); >> + >> + node = kmalloc(sizeof(struct drm_info_node), GFP_KERNEL); >> + if (node == NULL) { >> + debugfs_remove(ent); >> + return -ENOMEM; >> + } >> + >> + node->minor = minor; >> + node->dent = ent; >> + node->info_ent = (void *)fops; >> + >> + mutex_lock(&minor->debugfs_lock); >> + list_add(&node->list, &minor->debugfs_list); >> + mutex_unlock(&minor->debugfs_lock); >> + >> + return 0; >> +} >> + >> +int sti_drm_dbg_init(struct drm_minor *minor) >> +{ >> + int ret; >> + >> + ret = sti_drm_debugfs_create(minor->debugfs_root, minor, "fps_show", >> + &sti_drm_fps_fops); >> + if (ret) >> + goto err; >> + >> + ret = drm_debugfs_create_files(sti_drm_dbg_list, >> + ARRAY_SIZE(sti_drm_dbg_list), >> + minor->debugfs_root, minor); >> + if (ret) >> + goto err; >> + >> + DRM_INFO("%s debugfs installed\n", DRIVER_NAME); >> + >> + return ret; >> +err: >> + DRM_ERROR("Cannot install debugfs\n"); >> + return ret; >> +} >> + >> +void sti_drm_dbg_cleanup(struct drm_minor *minor) >> +{ >> + drm_debugfs_remove_files(sti_drm_dbg_list, >> + ARRAY_SIZE(sti_drm_dbg_list), minor); >> + >> + drm_debugfs_remove_files((struct drm_info_list *)&sti_drm_fps_fops, >> + 1, minor); >> +} >> +#endif >> + >> +static void sti_drm_mode_config_init(struct drm_device *dev) >> +{ >> + dev->mode_config.min_width = 0; >> + dev->mode_config.min_height = 0; >> + >> + /* >> + * set max width and height as default value. >> + * this value would be used to check framebuffer size limitation >> + * at drm_mode_addfb(). >> + */ >> + dev->mode_config.max_width = STI_MAX_FB_HEIGHT; >> + dev->mode_config.max_height = STI_MAX_FB_WIDTH; >> + >> + dev->mode_config.funcs = &sti_drm_mode_config_funcs; >> +} >> + >> +static int sti_drm_load(struct drm_device *dev, unsigned long flags) >> +{ >> + struct sti_drm_private *private; >> + struct device_driver *drv; >> + int err; >> + >> + DRM_INFO("%s drm_device: %p\n", __func__, dev); >> + >> + private = kzalloc(sizeof(struct sti_drm_private), GFP_KERNEL); >> + if (!private) { >> + DRM_ERROR("Failed to allocate private\n"); >> + return -ENOMEM; >> + } >> + dev->dev_private = (void *)private; >> + >> + drm_mode_config_init(dev); >> + drm_kms_helper_poll_init(dev); >> + >> + sti_drm_mode_config_init(dev); >> + >> + /* look for compositors HW */ >> + drv = driver_find("sti-compositor", &platform_bus_type); >> + if (!drv) { >> + DRM_ERROR("No compositor found\n"); >> + return -ENOMEM; >> + } >> + err = driver_for_each_device(drv, NULL, dev, sti_drm_compositor_init); >> + if (err) >> + DRM_ERROR("Failed to initialize CRTC\n"); >> + >> + /* look for TVOUT driver */ >> + drv = driver_find("sti-tvout", &platform_bus_type); >> + if (!drv) { >> + DRM_ERROR("No tvout found\n"); >> + return -ENOMEM; >> + } >> + err = driver_for_each_device(drv, NULL, dev, sti_drm_tvout_init); >> + if (err) >> + DRM_ERROR("Failed to initialize TVOUT\n"); >> + >> + drm_helper_disable_unused_functions(dev); >> +#ifdef CONFIG_DRM_STI_FBDEV >> + sti_drm_device = dev; >> + >> + schedule_delayed_work(&sti_fbdev_work, >> + msecs_to_jiffies(FBDEV_CREATE_DELAY)); >> +#endif >> + return err; >> +} >> + >> +static const struct file_operations sti_drm_driver_fops = { >> + .owner = THIS_MODULE, >> + .open = drm_open, >> + .mmap = drm_gem_cma_mmap, >> + .poll = drm_poll, >> + .read = drm_read, >> + .unlocked_ioctl = drm_ioctl, >> +#ifdef CONFIG_COMPAT >> + .compat_ioctl = drm_compat_ioctl, >> +#endif >> + .release = drm_release, >> +}; >> + >> +static struct dma_buf *sti_drm_gem_prime_export(struct drm_device *dev, >> + struct drm_gem_object *obj, >> + int flags) >> +{ >> + /* we want to be able to write in mmapped buffer */ >> + flags |= O_RDWR; >> + return drm_gem_prime_export(dev, obj, flags); >> +} >> + >> +static struct drm_driver sti_drm_driver = { >> + .driver_features = DRIVER_HAVE_IRQ | DRIVER_MODESET | >> + DRIVER_GEM | DRIVER_PRIME, >> + .load = sti_drm_load, >> + .gem_free_object = drm_gem_cma_free_object, >> + .gem_vm_ops = &drm_gem_cma_vm_ops, >> + .dumb_create = drm_gem_cma_dumb_create, >> + .dumb_map_offset = drm_gem_cma_dumb_map_offset, >> + .dumb_destroy = drm_gem_dumb_destroy, >> + .fops = &sti_drm_driver_fops, >> + >> + .get_vblank_counter = drm_vblank_count, >> + .enable_vblank = sti_drm_crtc_enable_vblank, >> + .disable_vblank = sti_drm_crtc_disable_vblank, >> + >> + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, >> + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, >> + .gem_prime_export = sti_drm_gem_prime_export, >> + .gem_prime_import = drm_gem_prime_import, >> + .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, >> + .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, >> + .gem_prime_vmap = drm_gem_cma_prime_vmap, >> + .gem_prime_vunmap = drm_gem_cma_prime_vunmap, >> + .gem_prime_mmap = drm_gem_cma_prime_mmap, >> + >> +#ifdef CONFIG_DEBUG_FS >> + .debugfs_init = sti_drm_dbg_init, >> + .debugfs_cleanup = sti_drm_dbg_cleanup, >> +#endif >> + .name = DRIVER_NAME, >> + .desc = DRIVER_DESC, >> + .date = DRIVER_DATE, >> + .major = DRIVER_MAJOR, >> + .minor = DRIVER_MINOR, >> +}; >> + >> +static int sti_drm_platform_probe(struct platform_device *pdev) >> +{ >> + DRM_INFO("%s\n", __func__); >> + >> + dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); >> + >> + return drm_platform_init(&sti_drm_driver, pdev); >> +} >> + >> +static int sti_drm_platform_remove(struct platform_device *pdev) >> +{ >> + DRM_INFO("%s\n", __func__); >> + >> + drm_put_dev(platform_get_drvdata(pdev)); >> + >> + return 0; >> +} >> + >> +static struct platform_driver sti_drm_platform_driver = { >> + .probe = sti_drm_platform_probe, >> + .remove = sti_drm_platform_remove, >> + .driver = { >> + .owner = THIS_MODULE, >> + .name = DRIVER_NAME, >> + }, >> +}; >> + >> +static int __init sti_drm_init(void) >> +{ >> + int ret; >> + >> + DRM_INFO("%s\n", __func__); >> +#ifdef CONFIG_VTAC_STI >> + ret = platform_driver_register(&sti_vtac_tx_driver); >> + if (ret < 0) >> + goto out_vtac_tx; >> + ret = platform_driver_register(&sti_vtac_rx_driver); >> + if (ret < 0) >> + goto out_vtac_rx; >> +#endif >> +#ifdef CONFIG_VTG_STI >> + ret = platform_driver_register(&sti_vtg_driver); >> + if (ret < 0) >> + goto out_vtg; >> +#endif >> + ret = platform_driver_register(&sti_compositor_driver); >> + if (ret < 0) >> + goto out_compo; >> + >> + ret = platform_driver_register(&sti_tvout_driver); >> + if (ret < 0) >> + goto out_tvout; >> + >> + ret = platform_driver_register(&sti_hdmi_driver); >> + if (ret < 0) >> + goto out_hdmi; >> + >> + ret = platform_driver_register(&sti_hda_driver); >> + if (ret < 0) >> + goto out_hda; >> + >> + ret = platform_driver_register(&sti_drm_platform_driver); >> + if (ret < 0) >> + goto out_drm; >> + >> + sti_drm_pdev = >> + platform_device_register_simple(DRIVER_NAME, -1, NULL, 0); >> + if (IS_ERR(sti_drm_pdev)) { >> + ret = PTR_ERR(sti_drm_pdev); >> + goto out_drm_device; >> + } >> + >> + return 0; >> + >> +out_drm_device: >> + platform_driver_unregister(&sti_drm_platform_driver); >> +out_drm: >> + platform_driver_unregister(&sti_hda_driver); >> +out_hda: >> + platform_driver_unregister(&sti_hdmi_driver); >> +out_hdmi: >> + platform_driver_unregister(&sti_tvout_driver); >> +out_tvout: >> + DRM_DEBUG_DRIVER("Can't register STI DRM platform driver"); >> + platform_driver_unregister(&sti_compositor_driver); >> +out_compo: >> +#ifdef CONFIG_VTG_STI >> + platform_driver_unregister(&sti_vtg_driver); >> +out_vtg: >> +#endif >> +#ifdef CONFIG_VTAC_STI >> + platform_driver_unregister(&sti_vtac_rx_driver); >> +out_vtac_rx: >> + platform_driver_unregister(&sti_vtac_tx_driver); >> +out_vtac_tx: >> +#endif >> + return ret; >> +} >> + >> +static void __exit sti_drm_exit(void) >> +{ >> + DRM_DEBUG_DRIVER("\n"); >> + >> + platform_device_unregister(sti_drm_pdev); >> + >> + platform_driver_unregister(&sti_drm_platform_driver); >> + platform_driver_unregister(&sti_compositor_driver); >> +} >> + >> +module_init(sti_drm_init); >> +module_exit(sti_drm_exit); >> + >> +MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@xxxxxx>"); >> +MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver"); >> +MODULE_LICENSE("GPL"); >> diff --git a/drivers/gpu/drm/sti/sti_drm_encoder.c b/drivers/gpu/drm/sti/sti_drm_encoder.c >> new file mode 100644 >> index 0000000..96194cf >> --- /dev/null >> +++ b/drivers/gpu/drm/sti/sti_drm_encoder.c >> @@ -0,0 +1,201 @@ >> +/* >> + * Copyright (C) STMicroelectronics SA 2013 >> + * Author: Benjamin Gaignard <benjamin.gaignard@xxxxxx> for STMicroelectronics. >> + * License terms: GNU General Public License (GPL), version 2 >> + */ >> + >> +#include <drm/drmP.h> >> +#include <drm/drm_crtc_helper.h> >> + >> +#include "sti_drm_crtc.h" >> +#include "sti_drm_encoder.h" >> +#include "sti_tvout.h" >> + >> +#define to_sti_encoder(x) container_of(x, struct sti_encoder, drm_encoder) >> + >> +#define ENCODER_MAIN_CRTC_MASK (1 << 0) >> + >> +/* >> + * sti specific encoder structure. >> + * >> + * @drm_encoder: encoder object. >> + * @tvout: pointer on tvout driver. >> + * @type: tvout connector type. >> + */ >> +struct sti_encoder { >> + struct drm_encoder drm_encoder; >> + struct sti_tvout *tvout; >> + enum sti_tvout_connector_type type; >> +}; >> + >> +static void sti_drm_encoder_dpms(struct drm_encoder *encoder, int mode) >> +{ >> +} >> + >> +static bool sti_drm_encoder_mode_fixup(struct drm_encoder *encoder, >> + const struct drm_display_mode *mode, >> + struct drm_display_mode *adjusted_mode) >> +{ >> + return true; >> +} >> + >> +static void sti_drm_encoder_mode_set(struct drm_encoder *encoder, >> + struct drm_display_mode *mode, >> + struct drm_display_mode *adjusted_mode) >> +{ >> + struct sti_encoder *sti_encoder = to_sti_encoder(encoder); >> + struct drm_device *dev = encoder->dev; >> + struct drm_connector *connector; >> + >> + dev_dbg(dev->dev, "%s\n", __func__); >> + >> + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { >> + if (connector->encoder == encoder) { >> + sti_tvout_set_mode(sti_encoder->tvout, mode, >> + sti_encoder->type); >> + break; >> + } >> + } >> +} >> + >> +static void sti_drm_encoder_prepare(struct drm_encoder *encoder) >> +{ >> + struct sti_encoder *sti_encoder = to_sti_encoder(encoder); >> + struct drm_device *dev = encoder->dev; >> + struct drm_connector *connector; >> + >> + dev_dbg(dev->dev, "%s\n", __func__); >> + >> + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { >> + if (connector->encoder == encoder) { >> + sti_tvout_prepare(sti_encoder->tvout, >> + sti_encoder->type); >> + break; >> + } >> + } >> +} >> + >> +static void sti_drm_encoder_commit(struct drm_encoder *encoder) >> +{ >> + struct sti_encoder *sti_encoder = to_sti_encoder(encoder); >> + struct drm_device *dev = encoder->dev; >> + struct drm_connector *connector; >> + >> + dev_dbg(dev->dev, "%s\n", __func__); >> + >> + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { >> + if (connector->encoder == encoder) { >> + sti_tvout_commit(sti_encoder->tvout, >> + sti_encoder->type, >> + sti_drm_crtc_is_main(encoder->crtc)); >> + break; >> + } >> + } >> +} >> + >> +static void sti_drm_encoder_disable(struct drm_encoder *encoder) >> +{ >> + struct sti_encoder *sti_encoder = to_sti_encoder(encoder); >> + struct drm_device *dev = encoder->dev; >> + struct drm_connector *connector; >> + struct drm_connector_helper_funcs *connector_funcs; >> + >> + dev_dbg(dev->dev, "%s\n", __func__); >> + >> + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { >> + connector_funcs = connector->helper_private; >> + if (connector_funcs->best_encoder(connector) == encoder) { >> + sti_tvout_disable(sti_encoder->tvout, >> + sti_encoder->type); >> + break; >> + } >> + } >> +} >> + >> +static const struct drm_encoder_helper_funcs sti_drm_encoder_helper_funcs = { >> + .dpms = sti_drm_encoder_dpms, >> + .mode_fixup = sti_drm_encoder_mode_fixup, >> + .mode_set = sti_drm_encoder_mode_set, >> + .prepare = sti_drm_encoder_prepare, >> + .commit = sti_drm_encoder_commit, >> + .disable = sti_drm_encoder_disable, >> +}; >> + >> +static void sti_drm_encoder_destroy(struct drm_encoder *encoder) >> +{ >> + struct sti_encoder *sti_encoder = to_sti_encoder(encoder); >> + struct drm_device *dev = encoder->dev; >> + >> + dev_dbg(dev->dev, "%s\n", __func__); >> + >> + drm_encoder_cleanup(encoder); >> + kfree(sti_encoder); >> +} >> + >> +static const struct drm_encoder_funcs sti_drm_encoder_funcs = { >> + .destroy = sti_drm_encoder_destroy, >> +}; >> + >> +struct drm_encoder *sti_drm_encoder_create(struct drm_device *dev, >> + struct sti_tvout *tvout, >> + unsigned int possible_crtcs, >> + enum sti_tvout_connector_type type) >> +{ >> + struct sti_encoder *sti_encoder; >> + struct drm_encoder *encoder; >> + int encoder_type; >> + >> + dev_dbg(dev->dev, "%s\n", __func__); >> + >> + if (!dev || !tvout || !possible_crtcs) >> + return NULL; >> + >> + sti_encoder = kzalloc(sizeof(*sti_encoder), GFP_KERNEL); >> + if (!sti_encoder) { >> + DRM_ERROR("failed to allocate encoder\n"); >> + return NULL; >> + } >> + >> + sti_encoder->tvout = tvout; >> + sti_encoder->type = type; >> + encoder = &sti_encoder->drm_encoder; >> + encoder->possible_crtcs = possible_crtcs; >> + >> + /* HDMI connector only supports main crtc */ >> + if (type == STI_TVOUT_CONNECTOR_HDMI) >> + encoder->possible_crtcs &= ENCODER_MAIN_CRTC_MASK; >> + >> + /* HD Analog connector may support aux CRTC. Not implemented yet */ >> + if (type == STI_TVOUT_CONNECTOR_HDA) >> + encoder->possible_crtcs &= ENCODER_MAIN_CRTC_MASK; >> + >> + DRM_DEBUG_DRIVER("possible_crtcs = 0x%x\n", encoder->possible_crtcs); >> + >> + switch (type) { >> + case STI_TVOUT_CONNECTOR_HDMI: >> + encoder_type = DRM_MODE_ENCODER_TMDS; >> + encoder->possible_clones = 1 << STI_TVOUT_CONNECTOR_HDA; >> + break; >> + case STI_TVOUT_CONNECTOR_HDA: >> + encoder_type = DRM_MODE_ENCODER_DAC; >> + encoder->possible_clones = 1 << STI_TVOUT_CONNECTOR_HDMI; >> + break; >> + case STI_TVOUT_CONNECTOR_DENC: >> + encoder_type = DRM_MODE_ENCODER_DAC; >> + break; >> + case STI_TVOUT_CONNECTOR_DVO: >> + encoder_type = DRM_MODE_ENCODER_LVDS; >> + break; >> + default: >> + encoder_type = DRM_MODE_ENCODER_NONE; >> + break; >> + } >> + >> + drm_encoder_init(dev, encoder, &sti_drm_encoder_funcs, encoder_type); >> + >> + drm_encoder_helper_add(encoder, &sti_drm_encoder_helper_funcs); >> + >> + DRM_DEBUG_DRIVER("encoder has been created\n"); >> + >> + return encoder; >> +} >> diff --git a/drivers/gpu/drm/sti/sti_drm_encoder.h b/drivers/gpu/drm/sti/sti_drm_encoder.h >> new file mode 100644 >> index 0000000..35d9945 >> --- /dev/null >> +++ b/drivers/gpu/drm/sti/sti_drm_encoder.h >> @@ -0,0 +1,16 @@ >> +/* >> + * Copyright (C) STMicroelectronics SA 2013 >> + * Author: Benjamin Gaignard <benjamin.gaignard@xxxxxx> for STMicroelectronics. >> + * License terms: GNU General Public License (GPL), version 2 >> + */ >> + >> +#ifndef _STI_DRM_ENCODER_H_ >> +#define _STI_DRM_ENCODER_H_ >> + >> +#include "sti_tvout.h" >> + >> +struct drm_encoder *sti_drm_encoder_create(struct drm_device *dev, >> + struct sti_tvout *tvout, unsigned int possible_crtcs, >> + enum sti_tvout_connector_type type); >> + >> +#endif >> diff --git a/drivers/gpu/drm/sti/sti_drm_plane.c b/drivers/gpu/drm/sti/sti_drm_plane.c >> new file mode 100644 >> index 0000000..e34c33a >> --- /dev/null >> +++ b/drivers/gpu/drm/sti/sti_drm_plane.c >> @@ -0,0 +1,195 @@ >> +/* >> + * Copyright (C) STMicroelectronics SA 2013 >> + * Authors: Benjamin Gaignard <benjamin.gaignard@xxxxxx> >> + * Fabien Dessenne <fabien.dessenne@xxxxxx> >> + * for STMicroelectronics. >> + * License terms: GNU General Public License (GPL), version 2 >> + */ >> +#include "sti_drm_drv.h" >> +#include "sti_drm_plane.h" >> +#include "sti_compositor.h" >> +#include "sti_vtg_utils.h" >> + >> +enum sti_layer_desc sti_layer_default_zorder[] = { >> + STI_GDP_0, >> + STI_VID_0, >> + STI_GDP_1, >> + STI_VID_1, >> + STI_GDP_2, >> + STI_GDP_3, >> +}; >> + >> +/* (Background) < GDP0 < VID0 < GDP1 < VID1 < GDP2 < GDP3 < (ForeGround) */ >> + >> +static int >> +sti_drm_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, >> + struct drm_framebuffer *fb, int crtc_x, int crtc_y, >> + unsigned int crtc_w, unsigned int crtc_h, >> + uint32_t src_x, uint32_t src_y, >> + uint32_t src_w, uint32_t src_h) >> +{ >> + struct sti_layer *layer = to_sti_layer(plane); >> + struct sti_mixer *mixer = to_sti_mixer(crtc); >> + int res; >> + >> + DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s) drm fb:%d\n", >> + crtc->base.id, sti_mixer_to_str(mixer), >> + plane->base.id, sti_layer_to_str(layer), fb->base.id); >> + DRM_DEBUG_KMS("(%dx%d)@(%d,%d)\n", crtc_w, crtc_h, crtc_x, crtc_y); >> + >> + res = sti_mixer_set_layer_depth(mixer, layer); >> + if (res) { >> + DRM_ERROR("Can not set layer depth\n"); >> + return res; >> + } >> + >> + /* src_x are in 16.16 format. */ >> + res = sti_layer_prepare(layer, fb, &crtc->mode, mixer->id, >> + crtc_x, crtc_y, crtc_w, crtc_h, >> + src_x >> 16, src_y >> 16, >> + src_w >> 16, src_h >> 16); >> + if (res) { >> + DRM_ERROR("Layer prepare failed\n"); >> + return res; >> + } >> + >> + res = sti_layer_commit(layer); >> + if (res) { >> + DRM_ERROR("Layer commit failed\n"); >> + return res; >> + } >> + >> + res = sti_mixer_set_layer_status(mixer, layer, true); >> + if (res) { >> + DRM_ERROR("Can not enable layer at mixer\n"); >> + return res; >> + } >> + >> + return 0; >> +} >> + >> +static int sti_drm_disable_plane(struct drm_plane *plane) >> +{ >> + struct sti_layer *layer; >> + struct sti_mixer *mixer; >> + int lay_res, mix_res; >> + >> + if (!plane->crtc) { >> + DRM_DEBUG_DRIVER("drm plane:%d not enabled\n", plane->base.id); >> + return 0; >> + } >> + layer = to_sti_layer(plane); >> + mixer = to_sti_mixer(plane->crtc); >> + >> + DRM_DEBUG_DRIVER("CRTC:%d (%s) drm plane:%d (%s)\n", >> + plane->crtc->base.id, sti_mixer_to_str(mixer), >> + plane->base.id, sti_layer_to_str(layer)); >> + >> + /* Disable layer at mixer level */ >> + mix_res = sti_mixer_set_layer_status(mixer, layer, false); >> + if (mix_res) >> + DRM_ERROR("Can not disable layer at mixer\n"); >> + >> + /* Wait a while to be sure that a Vsync event is received */ >> + msleep(WAIT_NEXT_VSYNC_MS); >> + >> + /* Then disable layer itself */ >> + lay_res = sti_layer_disable(layer); >> + if (lay_res) >> + DRM_ERROR("Layer disable failed\n"); >> + >> + if (lay_res || mix_res) >> + return 1; >> + else >> + return 0; >> +} >> + >> +static void sti_drm_plane_destroy(struct drm_plane *plane) >> +{ >> + DRM_DEBUG_DRIVER("\n"); >> + >> + sti_drm_disable_plane(plane); >> + drm_plane_cleanup(plane); >> +} >> + >> +static int sti_drm_plane_set_property(struct drm_plane *plane, >> + struct drm_property *property, >> + uint64_t val) >> +{ >> + struct drm_device *dev = plane->dev; >> + struct sti_drm_private *private = dev->dev_private; >> + struct sti_layer *layer = to_sti_layer(plane); >> + >> + DRM_DEBUG_DRIVER("\n"); >> + >> + if (property == private->plane_zorder_property) { >> + layer->zorder = val; >> + return 0; >> + } >> + >> + return -EINVAL; >> +} >> + >> +static struct drm_plane_funcs sti_drm_plane_funcs = { >> + .update_plane = sti_drm_update_plane, >> + .disable_plane = sti_drm_disable_plane, >> + .destroy = sti_drm_plane_destroy, >> + .set_property = sti_drm_plane_set_property, >> +}; >> + >> +static void sti_drm_plane_attach_zorder_property(struct drm_plane *plane, >> + uint64_t default_val) >> +{ >> + struct drm_device *dev = plane->dev; >> + struct sti_drm_private *private = dev->dev_private; >> + struct drm_property *prop; >> + struct sti_layer *layer = to_sti_layer(plane); >> + >> + dev_dbg(dev->dev, "%s zorder:%llu\n", __func__, default_val); >> + >> + prop = private->plane_zorder_property; >> + if (!prop) { >> + prop = drm_property_create_range(dev, 0, "zpos", 0, >> + GAM_MIXER_NB_DEPTH_LEVEL - 1); >> + if (!prop) >> + return; >> + >> + private->plane_zorder_property = prop; >> + } >> + >> + drm_object_attach_property(&plane->base, prop, default_val); >> + layer->zorder = default_val; >> +} > > There's lots of ongoing discussions about standardizing properties a bit > better. Especially about plane Z ordering and blending modes. I think it'd > be good to hold off on this a bit and participate in the larger > discussion about a spec for plane properties until things settle. > Especially for a new driver. > > I guess for now you could just hardcode Z order how you register the > planes. > -Daniel > >> + >> +struct drm_plane *sti_drm_plane_init(struct drm_device *dev, >> + struct sti_layer *layer, >> + unsigned int possible_crtcs) >> +{ >> + int err, i; >> + uint64_t default_zorder = 0; >> + >> + dev_dbg(dev->dev, "%s\n", __func__); >> + >> + err = drm_plane_init(dev, &layer->plane, possible_crtcs, >> + &sti_drm_plane_funcs, >> + sti_layer_get_formats(layer), >> + sti_layer_get_nb_formats(layer), false); >> + if (err) { >> + DRM_ERROR("Failed to initialize plane\n"); >> + return NULL; >> + } >> + >> + for (i = 0; i < ARRAY_SIZE(sti_layer_default_zorder); i++) >> + if (sti_layer_default_zorder[i] == layer->desc) >> + break; >> + >> + default_zorder = i; >> + >> + sti_drm_plane_attach_zorder_property(&layer->plane, default_zorder); >> + >> + DRM_DEBUG_DRIVER("drm plane:%d mapped to %s with zorder:%llu\n", >> + layer->plane.base.id, >> + sti_layer_to_str(layer), default_zorder); >> + >> + return &layer->plane; >> +} >> diff --git a/drivers/gpu/drm/sti/sti_drm_plane.h b/drivers/gpu/drm/sti/sti_drm_plane.h >> new file mode 100644 >> index 0000000..d182066 >> --- /dev/null >> +++ b/drivers/gpu/drm/sti/sti_drm_plane.h >> @@ -0,0 +1,16 @@ >> +/* >> + * Copyright (C) STMicroelectronics SA 2013 >> + * Author: Benjamin Gaignard <benjamin.gaignard@xxxxxx> for STMicroelectronics. >> + * License terms: GNU General Public License (GPL), version 2 >> + */ >> + >> +#ifndef _STI_DRM_PLANE_H_ >> +#define _STI_DRM_PLANE_H_ >> + >> +#include <drm/drmP.h> >> +#include "sti_layer.h" >> + >> +struct drm_plane *sti_drm_plane_init(struct drm_device *dev, >> + struct sti_layer *layer, unsigned int possible_crtcs); >> + >> +#endif >> diff --git a/drivers/gpu/drm/sti/sti_vtg_utils.h b/drivers/gpu/drm/sti/sti_vtg_utils.h >> index 8992a04..fea2852 100644 >> --- a/drivers/gpu/drm/sti/sti_vtg_utils.h >> +++ b/drivers/gpu/drm/sti/sti_vtg_utils.h >> @@ -11,6 +11,8 @@ >> >> #include <drm/drmP.h> >> >> +#define WAIT_NEXT_VSYNC_MS 50 /*ms*/ >> + >> #define VTG_MAIN 0 >> #define VTG_AUX 1 >> >> -- >> 1.9.0 >> >> _______________________________________________ >> dri-devel mailing list >> dri-devel@xxxxxxxxxxxxxxxxxxxxx >> http://lists.freedesktop.org/mailman/listinfo/dri-devel > > -- > Daniel Vetter > Software Engineer, Intel Corporation > +41 (0) 79 365 57 48 - http://blog.ffwll.ch -- Benjamin Gaignard Graphic Working Group Linaro.org │ Open source software for ARM SoCs Follow Linaro: Facebook | Twitter | Blog _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/dri-devel