From 620f74f51ccd9c954530655e46ce5907d88f1e9a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 13 Dec 2016 21:24:34 +0200 Subject: drm: Remove unused drm_platform midlayer Now that the last driver has been converted, the drm_platform midlayer is unused. Remove it. Signed-off-by: Laurent Pinchart Reviewed-by: Daniel Vetter --- include/drm/drmP.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include') diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 21a3a666a2fd..231a6cc925dd 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -819,9 +819,6 @@ static inline int drm_pci_set_busid(struct drm_device *dev, extern int drm_pcie_get_speed_cap_mask(struct drm_device *dev, u32 *speed_mask); extern int drm_pcie_get_max_link_width(struct drm_device *dev, u32 *mlw); -/* platform section */ -extern int drm_platform_init(struct drm_driver *driver, struct platform_device *platform_device); - /* returns true if currently okay to sleep */ static __inline__ bool drm_can_sleep(void) { -- cgit v1.2.3 From 76adb460fd939756db689f238d5c2ddb45469705 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 18 Dec 2016 00:01:19 +0200 Subject: drm: Remove the struct drm_device platformdev field The field contains a pointer to the parent platform device of the DRM device. As struct drm_device also contains a dev pointer to the struct device embedded in the platform_device structure, the platformdev field is redundant. Remove it and use the dev pointer directly. Signed-off-by: Laurent Pinchart Reviewed-by: Daniel Vetter Acked-by: Jyri Sarha Acked-by: Vincent Abriou # For sti Acked-by: Russell King # For armada Acked-by: Rob Clark # For msm Acked-by: Xinwei Kong --- drivers/gpu/drm/armada/armada_drv.c | 3 +-- drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c | 2 +- drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c | 2 +- drivers/gpu/drm/msm/mdp/mdp5/mdp5_mdss.c | 2 +- drivers/gpu/drm/msm/msm_drv.c | 1 - drivers/gpu/drm/nouveau/nouveau_drm.c | 3 +-- drivers/gpu/drm/sti/sti_drv.c | 2 -- drivers/gpu/drm/tilcdc/tilcdc_drv.c | 1 - include/drm/drmP.h | 1 - 9 files changed, 5 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index 63f42d001f33..b4e5e0529eae 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c @@ -154,10 +154,9 @@ static int armada_drm_bind(struct device *dev) return ret; } - priv->drm.platformdev = to_platform_device(dev); priv->drm.dev_private = priv; - platform_set_drvdata(priv->drm.platformdev, &priv->drm); + dev_set_drvdata(dev, &priv->drm); INIT_WORK(&priv->fb_unref_work, armada_drm_unref_work); INIT_KFIFO(priv->fb_unref); diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c index 94ea963519b2..a4e1206a66a8 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c @@ -434,7 +434,7 @@ fail: struct msm_kms *mdp4_kms_init(struct drm_device *dev) { - struct platform_device *pdev = dev->platformdev; + struct platform_device *pdev = to_platform_device(dev->dev); struct mdp4_platform_config *config = mdp4_get_config(pdev); struct mdp4_kms *mdp4_kms; struct msm_kms *kms = NULL; diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c index 34ab553f6897..ba2d017f6591 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c @@ -505,7 +505,7 @@ struct mdp5_cfg_handler *mdp5_cfg_init(struct mdp5_kms *mdp5_kms, uint32_t major, uint32_t minor) { struct drm_device *dev = mdp5_kms->dev; - struct platform_device *pdev = dev->platformdev; + struct platform_device *pdev = to_platform_device(dev->dev); struct mdp5_cfg_handler *cfg_handler; struct mdp5_cfg_platform *pconfig; int i, ret = 0; diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mdss.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mdss.c index d444a6901fff..f8f48d014978 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mdss.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mdss.c @@ -160,7 +160,7 @@ void msm_mdss_destroy(struct drm_device *dev) int msm_mdss_init(struct drm_device *dev) { - struct platform_device *pdev = dev->platformdev; + struct platform_device *pdev = to_platform_device(dev->dev); struct msm_drm_private *priv = dev->dev_private; struct msm_mdss *mdss; int ret; diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 70226eaa5cac..19435079b11f 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -383,7 +383,6 @@ static int msm_drm_init(struct device *dev, struct drm_driver *drv) } platform_set_drvdata(pdev, ddev); - ddev->platformdev = pdev; priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) { diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index dd7b52ab505a..e4ddd43c2c79 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -106,7 +106,7 @@ nouveau_name(struct drm_device *dev) if (dev->pdev) return nouveau_pci_name(dev->pdev); else - return nouveau_platform_name(dev->platformdev); + return nouveau_platform_name(to_platform_device(dev->dev)); } static int @@ -1088,7 +1088,6 @@ nouveau_platform_device_create(const struct nvkm_device_tegra_func *func, goto err_free; } - drm->platformdev = pdev; platform_set_drvdata(pdev, drm); return drm; diff --git a/drivers/gpu/drm/sti/sti_drv.c b/drivers/gpu/drm/sti/sti_drv.c index e6c1646b9c53..fedade400eeb 100644 --- a/drivers/gpu/drm/sti/sti_drv.c +++ b/drivers/gpu/drm/sti/sti_drv.c @@ -273,8 +273,6 @@ static int sti_bind(struct device *dev) if (IS_ERR(ddev)) return PTR_ERR(ddev); - ddev->platformdev = to_platform_device(dev); - ret = sti_init(ddev); if (ret) goto err_drm_dev_unref; diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index 372d86fbb093..ea6a228a217f 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -245,7 +245,6 @@ static int tilcdc_init(struct drm_driver *ddrv, struct device *dev) if (IS_ERR(ddev)) return PTR_ERR(ddev); - ddev->platformdev = pdev; ddev->dev_private = priv; platform_set_drvdata(pdev, ddev); drm_mode_config_init(ddev); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 231a6cc925dd..aa8f20314237 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -610,7 +610,6 @@ struct drm_device { struct pci_controller *hose; #endif - struct platform_device *platformdev; /**< Platform device struture */ struct virtio_device *virtdev; struct drm_sg_mem *sg; /**< Scatter gather memory */ -- cgit v1.2.3 From 51ffa12d90baebc56eef6781233873f7ad558d54 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Thu, 16 Feb 2017 15:47:07 +0100 Subject: drm/atomic: Make drm_atomic_plane_disabling easier to understand. This function becomes a lot simpler when having passed both the old and new state to it. Looking at all callers, it seems that old_plane_state is never NULL so the check can be dropped. Signed-off-by: Maarten Lankhorst Link: http://patchwork.freedesktop.org/patch/msgid/1487256430-7625-3-git-send-email-maarten.lankhorst@linux.intel.com Reviewed-by: Laurent Pinchart Tested-by: Laurent Pinchart --- drivers/gpu/drm/drm_atomic_helper.c | 7 ++++--- drivers/gpu/drm/drm_plane_helper.c | 2 +- include/drm/drm_atomic_helper.h | 26 ++++++++------------------ 3 files changed, 13 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index aa1e7609b024..bc96d31bd388 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1764,7 +1764,8 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev, if (!funcs) continue; - disabling = drm_atomic_plane_disabling(plane, old_plane_state); + disabling = drm_atomic_plane_disabling(old_plane_state, + new_plane_state); if (active_only) { /* @@ -1859,11 +1860,11 @@ drm_atomic_helper_commit_planes_on_crtc(struct drm_crtc_state *old_crtc_state) WARN_ON(plane->state->crtc && plane->state->crtc != crtc); - if (drm_atomic_plane_disabling(plane, old_plane_state) && + if (drm_atomic_plane_disabling(old_plane_state, plane->state) && plane_funcs->atomic_disable) plane_funcs->atomic_disable(plane, old_plane_state); else if (plane->state->crtc || - drm_atomic_plane_disabling(plane, old_plane_state)) + drm_atomic_plane_disabling(old_plane_state, plane->state)) plane_funcs->atomic_update(plane, old_plane_state); } diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c index 244cf2619294..de1ac5e08f4d 100644 --- a/drivers/gpu/drm/drm_plane_helper.c +++ b/drivers/gpu/drm/drm_plane_helper.c @@ -469,7 +469,7 @@ int drm_plane_helper_commit(struct drm_plane *plane, * Drivers may optionally implement the ->atomic_disable callback, so * special-case that here. */ - if (drm_atomic_plane_disabling(plane, plane_state) && + if (drm_atomic_plane_disabling(plane_state, plane->state) && plane_funcs->atomic_disable) plane_funcs->atomic_disable(plane, plane_state); else diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index 9ceda379ce58..dc16274987c7 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -220,10 +220,10 @@ int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc, __drm_atomic_get_current_plane_state((crtc_state)->state, \ plane))) -/* +/** * drm_atomic_plane_disabling - check whether a plane is being disabled - * @plane: plane object - * @old_state: previous atomic state + * @old_plane_state: old atomic plane state + * @new_plane_state: new atomic plane state * * Checks the atomic state of a plane to determine whether it's being disabled * or not. This also WARNs if it detects an invalid state (both CRTC and FB @@ -233,28 +233,18 @@ int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc, * True if the plane is being disabled, false otherwise. */ static inline bool -drm_atomic_plane_disabling(struct drm_plane *plane, - struct drm_plane_state *old_state) +drm_atomic_plane_disabling(struct drm_plane_state *old_plane_state, + struct drm_plane_state *new_plane_state) { /* * When disabling a plane, CRTC and FB should always be NULL together. * Anything else should be considered a bug in the atomic core, so we * gently warn about it. */ - WARN_ON((plane->state->crtc == NULL && plane->state->fb != NULL) || - (plane->state->crtc != NULL && plane->state->fb == NULL)); + WARN_ON((new_plane_state->crtc == NULL && new_plane_state->fb != NULL) || + (new_plane_state->crtc != NULL && new_plane_state->fb == NULL)); - /* - * When using the transitional helpers, old_state may be NULL. If so, - * we know nothing about the current state and have to assume that it - * might be enabled. - * - * When using the atomic helpers, old_state won't be NULL. Therefore - * this check assumes that either the driver will have reconstructed - * the correct state in ->reset() or that the driver will have taken - * appropriate measures to disable all planes. - */ - return (!old_state || old_state->crtc) && !plane->state->crtc; + return old_plane_state->crtc && !new_plane_state->crtc; } #endif /* DRM_ATOMIC_HELPER_H_ */ -- cgit v1.2.3 From 2107777c0249e95f9493f3341dcb4fd89b965385 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Thu, 16 Feb 2017 15:47:08 +0100 Subject: drm/atomic: Add macros to access existing old/new state, v2. After atomic commit, these macros should be used in place of get_existing_state. Also after commit get_xx_state should no longer be used because it may not have the required locks. The calls to drm_atomic_get_existing_$obj_state should no longer be used, and converted over to these new calls. Changes since v1: - Expand commit message. - Deprecate get_existing_*_state functions in the documentation. Signed-off-by: Maarten Lankhorst Link: http://patchwork.freedesktop.org/patch/msgid/1487256430-7625-4-git-send-email-maarten.lankhorst@linux.intel.com Reviewed-by: Laurent Pinchart Tested-by: Laurent Pinchart --- include/drm/drm_atomic.h | 108 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) (limited to 'include') diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index c6f355a970d2..0147a047878d 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -277,6 +277,9 @@ int drm_atomic_connector_set_property(struct drm_connector *connector, * * This function returns the crtc state for the given crtc, or NULL * if the crtc is not part of the global atomic state. + * + * This function is deprecated, @drm_atomic_get_old_crtc_state or + * @drm_atomic_get_new_crtc_state should be used instead. */ static inline struct drm_crtc_state * drm_atomic_get_existing_crtc_state(struct drm_atomic_state *state, @@ -285,6 +288,35 @@ drm_atomic_get_existing_crtc_state(struct drm_atomic_state *state, return state->crtcs[drm_crtc_index(crtc)].state; } +/** + * drm_atomic_get_old_crtc_state - get old crtc state, if it exists + * @state: global atomic state object + * @crtc: crtc to grab + * + * This function returns the old crtc state for the given crtc, or + * NULL if the crtc is not part of the global atomic state. + */ +static inline struct drm_crtc_state * +drm_atomic_get_old_crtc_state(struct drm_atomic_state *state, + struct drm_crtc *crtc) +{ + return state->crtcs[drm_crtc_index(crtc)].old_state; +} +/** + * drm_atomic_get_new_crtc_state - get new crtc state, if it exists + * @state: global atomic state object + * @crtc: crtc to grab + * + * This function returns the new crtc state for the given crtc, or + * NULL if the crtc is not part of the global atomic state. + */ +static inline struct drm_crtc_state * +drm_atomic_get_new_crtc_state(struct drm_atomic_state *state, + struct drm_crtc *crtc) +{ + return state->crtcs[drm_crtc_index(crtc)].new_state; +} + /** * drm_atomic_get_existing_plane_state - get plane state, if it exists * @state: global atomic state object @@ -292,6 +324,9 @@ drm_atomic_get_existing_crtc_state(struct drm_atomic_state *state, * * This function returns the plane state for the given plane, or NULL * if the plane is not part of the global atomic state. + * + * This function is deprecated, @drm_atomic_get_old_plane_state or + * @drm_atomic_get_new_plane_state should be used instead. */ static inline struct drm_plane_state * drm_atomic_get_existing_plane_state(struct drm_atomic_state *state, @@ -300,6 +335,36 @@ drm_atomic_get_existing_plane_state(struct drm_atomic_state *state, return state->planes[drm_plane_index(plane)].state; } +/** + * drm_atomic_get_old_plane_state - get plane state, if it exists + * @state: global atomic state object + * @plane: plane to grab + * + * This function returns the old plane state for the given plane, or + * NULL if the plane is not part of the global atomic state. + */ +static inline struct drm_plane_state * +drm_atomic_get_old_plane_state(struct drm_atomic_state *state, + struct drm_plane *plane) +{ + return state->planes[drm_plane_index(plane)].old_state; +} + +/** + * drm_atomic_get_new_plane_state - get plane state, if it exists + * @state: global atomic state object + * @plane: plane to grab + * + * This function returns the new plane state for the given plane, or + * NULL if the plane is not part of the global atomic state. + */ +static inline struct drm_plane_state * +drm_atomic_get_new_plane_state(struct drm_atomic_state *state, + struct drm_plane *plane) +{ + return state->planes[drm_plane_index(plane)].new_state; +} + /** * drm_atomic_get_existing_connector_state - get connector state, if it exists * @state: global atomic state object @@ -307,6 +372,9 @@ drm_atomic_get_existing_plane_state(struct drm_atomic_state *state, * * This function returns the connector state for the given connector, * or NULL if the connector is not part of the global atomic state. + * + * This function is deprecated, @drm_atomic_get_old_connector_state or + * @drm_atomic_get_new_connector_state should be used instead. */ static inline struct drm_connector_state * drm_atomic_get_existing_connector_state(struct drm_atomic_state *state, @@ -320,6 +388,46 @@ drm_atomic_get_existing_connector_state(struct drm_atomic_state *state, return state->connectors[index].state; } +/** + * drm_atomic_get_old_connector_state - get connector state, if it exists + * @state: global atomic state object + * @connector: connector to grab + * + * This function returns the old connector state for the given connector, + * or NULL if the connector is not part of the global atomic state. + */ +static inline struct drm_connector_state * +drm_atomic_get_old_connector_state(struct drm_atomic_state *state, + struct drm_connector *connector) +{ + int index = drm_connector_index(connector); + + if (index >= state->num_connector) + return NULL; + + return state->connectors[index].old_state; +} + +/** + * drm_atomic_get_new_connector_state - get connector state, if it exists + * @state: global atomic state object + * @connector: connector to grab + * + * This function returns the new connector state for the given connector, + * or NULL if the connector is not part of the global atomic state. + */ +static inline struct drm_connector_state * +drm_atomic_get_new_connector_state(struct drm_atomic_state *state, + struct drm_connector *connector) +{ + int index = drm_connector_index(connector); + + if (index >= state->num_connector) + return NULL; + + return state->connectors[index].new_state; +} + /** * __drm_atomic_get_current_plane_state - get current plane state * @state: global atomic state object -- cgit v1.2.3 From 4bb310fd9eea254b68b2f48b376fe149f65e3fbb Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Fri, 3 Mar 2017 14:39:33 +0100 Subject: drm/dp: add crtc backpointer to drm_dp_aux This backpointer allows DP helpers to access the crtc it's currently being used for. v6: Have the backpointer be to drm_crtc (Sean Paul) Signed-off-by: Tomeu Vizoso Signed-off-by: Sean Paul Link: http://patchwork.freedesktop.org/patch/msgid/20170303133936.14964-2-tomeu.vizoso@collabora.com --- include/drm/drm_dp_helper.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 04681359a6f5..afe515855f0a 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -732,6 +732,7 @@ struct drm_dp_aux_msg { * @name: user-visible name of this AUX channel and the I2C-over-AUX adapter * @ddc: I2C adapter that can be used for I2C-over-AUX communication * @dev: pointer to struct device that is the parent for this AUX channel + * @crtc: backpointer to the crtc that is currently using this AUX channel * @hw_mutex: internal mutex used for locking transfers * @transfer: transfers a message representing a single AUX transaction * @@ -768,6 +769,7 @@ struct drm_dp_aux { const char *name; struct i2c_adapter ddc; struct device *dev; + struct drm_crtc *crtc; struct mutex hw_mutex; ssize_t (*transfer)(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg); -- cgit v1.2.3 From 79c1da7c3bf74be9b1dae9450ef79f32dd22def6 Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Fri, 3 Mar 2017 14:39:34 +0100 Subject: drm/dp: add helpers for capture of frame CRCs Adds helpers for starting and stopping capture of frame CRCs through the DPCD. When capture is on, a worker waits for vblanks and retrieves the frame CRC to put it in the queue on the CRTC that is using the eDP connector, so it's passed to userspace. v2: Reuse drm_crtc_wait_one_vblank Update locking, as drm_crtc_add_crc_entry now takes the lock v3: Don't call wake_up_interruptible directly, that's now done in drm_crtc_add_crc_entry. v4: Style fixes (Sean Paul) Reworked retry of CRC reads (Sean Paul) Flush worker after stopping CRC generationa (Sean Paul) v5: Move back to make the retry explicitly once v6: Set and use the drm_crtc backpointer (Sean Paul) Signed-off-by: Tomeu Vizoso Signed-off-by: Sean Paul Link: http://patchwork.freedesktop.org/patch/msgid/20170303133936.14964-3-tomeu.vizoso@collabora.com --- drivers/gpu/drm/drm_dp_helper.c | 126 ++++++++++++++++++++++++++++++++++++++++ include/drm/drm_dp_helper.h | 7 +++ 2 files changed, 133 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 68908c1d5ca1..c40cfe2e63ab 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -981,6 +981,78 @@ static const struct i2c_lock_operations drm_dp_i2c_lock_ops = { .unlock_bus = unlock_bus, }; +static int drm_dp_aux_get_crc(struct drm_dp_aux *aux, u8 *crc) +{ + u8 buf, count; + int ret; + + ret = drm_dp_dpcd_readb(aux, DP_TEST_SINK, &buf); + if (ret < 0) + return ret; + + WARN_ON(!(buf & DP_TEST_SINK_START)); + + ret = drm_dp_dpcd_readb(aux, DP_TEST_SINK_MISC, &buf); + if (ret < 0) + return ret; + + count = buf & DP_TEST_COUNT_MASK; + if (count == aux->crc_count) + return -EAGAIN; /* No CRC yet */ + + aux->crc_count = count; + + /* + * At DP_TEST_CRC_R_CR, there's 6 bytes containing CRC data, 2 bytes + * per component (RGB or CrYCb). + */ + ret = drm_dp_dpcd_read(aux, DP_TEST_CRC_R_CR, crc, 6); + if (ret < 0) + return ret; + + return 0; +} + +static void drm_dp_aux_crc_work(struct work_struct *work) +{ + struct drm_dp_aux *aux = container_of(work, struct drm_dp_aux, + crc_work); + struct drm_crtc *crtc; + u8 crc_bytes[6]; + uint32_t crcs[3]; + int ret; + + if (WARN_ON(!aux->crtc)) + return; + + crtc = aux->crtc; + while (crtc->crc.opened) { + drm_crtc_wait_one_vblank(crtc); + if (!crtc->crc.opened) + break; + + ret = drm_dp_aux_get_crc(aux, crc_bytes); + if (ret == -EAGAIN) { + usleep_range(1000, 2000); + ret = drm_dp_aux_get_crc(aux, crc_bytes); + } + + if (ret == -EAGAIN) { + DRM_DEBUG_KMS("Get CRC failed after retrying: %d\n", + ret); + continue; + } else if (ret) { + DRM_DEBUG_KMS("Failed to get a CRC: %d\n", ret); + continue; + } + + crcs[0] = crc_bytes[0] | crc_bytes[1] << 8; + crcs[1] = crc_bytes[2] | crc_bytes[3] << 8; + crcs[2] = crc_bytes[4] | crc_bytes[5] << 8; + drm_crtc_add_crc_entry(crtc, false, 0, crcs); + } +} + /** * drm_dp_aux_init() - minimally initialise an aux channel * @aux: DisplayPort AUX channel @@ -993,6 +1065,7 @@ static const struct i2c_lock_operations drm_dp_i2c_lock_ops = { void drm_dp_aux_init(struct drm_dp_aux *aux) { mutex_init(&aux->hw_mutex); + INIT_WORK(&aux->crc_work, drm_dp_aux_crc_work); aux->ddc.algo = &drm_dp_i2c_algo; aux->ddc.algo_data = aux; @@ -1081,3 +1154,56 @@ int drm_dp_psr_setup_time(const u8 psr_cap[EDP_PSR_RECEIVER_CAP_SIZE]) EXPORT_SYMBOL(drm_dp_psr_setup_time); #undef PSR_SETUP_TIME + +/** + * drm_dp_start_crc() - start capture of frame CRCs + * @aux: DisplayPort AUX channel + * + * Returns 0 on success or a negative error code on failure. + */ +int drm_dp_start_crc(struct drm_dp_aux *aux, struct drm_crtc *crtc) +{ + u8 buf; + int ret; + + ret = drm_dp_dpcd_readb(aux, DP_TEST_SINK, &buf); + if (ret < 0) + return ret; + + ret = drm_dp_dpcd_writeb(aux, DP_TEST_SINK, buf | DP_TEST_SINK_START); + if (ret < 0) + return ret; + + aux->crc_count = 0; + aux->crtc = crtc; + schedule_work(&aux->crc_work); + + return 0; +} +EXPORT_SYMBOL(drm_dp_start_crc); + +/** + * drm_dp_stop_crc() - stop capture of frame CRCs + * @aux: DisplayPort AUX channel + * + * Returns 0 on success or a negative error code on failure. + */ +int drm_dp_stop_crc(struct drm_dp_aux *aux) +{ + u8 buf; + int ret; + + ret = drm_dp_dpcd_readb(aux, DP_TEST_SINK, &buf); + if (ret < 0) + return ret; + + ret = drm_dp_dpcd_writeb(aux, DP_TEST_SINK, buf & ~DP_TEST_SINK_START); + if (ret < 0) + return ret; + + flush_work(&aux->crc_work); + aux->crtc = NULL; + + return 0; +} +EXPORT_SYMBOL(drm_dp_stop_crc); diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index afe515855f0a..c34984c8d5d0 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -734,6 +734,8 @@ struct drm_dp_aux_msg { * @dev: pointer to struct device that is the parent for this AUX channel * @crtc: backpointer to the crtc that is currently using this AUX channel * @hw_mutex: internal mutex used for locking transfers + * @crc_work: worker that captures CRCs for each frame + * @crc_count: counter of captured frame CRCs * @transfer: transfers a message representing a single AUX transaction * * The .dev field should be set to a pointer to the device that implements @@ -771,6 +773,8 @@ struct drm_dp_aux { struct device *dev; struct drm_crtc *crtc; struct mutex hw_mutex; + struct work_struct crc_work; + u8 crc_count; ssize_t (*transfer)(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg); /** @@ -849,4 +853,7 @@ void drm_dp_aux_init(struct drm_dp_aux *aux); int drm_dp_aux_register(struct drm_dp_aux *aux); void drm_dp_aux_unregister(struct drm_dp_aux *aux); +int drm_dp_start_crc(struct drm_dp_aux *aux, struct drm_crtc *crtc); +int drm_dp_stop_crc(struct drm_dp_aux *aux); + #endif /* _DRM_DP_HELPER_H_ */ -- cgit v1.2.3 From 737d6e339f50dcdbd5a8444d04dede2f0d305361 Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Fri, 3 Mar 2017 14:39:35 +0100 Subject: drm/bridge: analogix_dp: add helpers for capture of frame CRCs Add two simple functions that just take the drm_dp_aux from our struct and calls the corresponding DP helpers with it. v6: Pass to the DP helper the drm_crtc of the current connector (Sean Paul) Signed-off-by: Tomeu Vizoso Signed-off-by: Sean Paul Link: http://patchwork.freedesktop.org/patch/msgid/20170303133936.14964-4-tomeu.vizoso@collabora.com --- drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 22 ++++++++++++++++++++++ include/drm/bridge/analogix_dp.h | 3 +++ 2 files changed, 25 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index e7cd1056ff2d..c26997afd3cf 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -1488,6 +1488,28 @@ int analogix_dp_resume(struct device *dev) EXPORT_SYMBOL_GPL(analogix_dp_resume); #endif +int analogix_dp_start_crc(struct drm_connector *connector) +{ + struct analogix_dp_device *dp = to_dp(connector); + + if (!connector->state->crtc) { + DRM_ERROR("Connector %s doesn't currently have a CRTC.\n", + connector->name); + return -EINVAL; + } + + return drm_dp_start_crc(&dp->aux, connector->state->crtc); +} +EXPORT_SYMBOL_GPL(analogix_dp_start_crc); + +int analogix_dp_stop_crc(struct drm_connector *connector) +{ + struct analogix_dp_device *dp = to_dp(connector); + + return drm_dp_stop_crc(&dp->aux); +} +EXPORT_SYMBOL_GPL(analogix_dp_stop_crc); + MODULE_AUTHOR("Jingoo Han "); MODULE_DESCRIPTION("Analogix DP Core Driver"); MODULE_LICENSE("GPL v2"); diff --git a/include/drm/bridge/analogix_dp.h b/include/drm/bridge/analogix_dp.h index f6f0c062205c..c99d6eaef1ac 100644 --- a/include/drm/bridge/analogix_dp.h +++ b/include/drm/bridge/analogix_dp.h @@ -49,4 +49,7 @@ int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev, struct analogix_dp_plat_data *plat_data); void analogix_dp_unbind(struct device *dev, struct device *master, void *data); +int analogix_dp_start_crc(struct drm_connector *connector); +int analogix_dp_stop_crc(struct drm_connector *connector); + #endif /* _ANALOGIX_DP_H_ */ -- cgit v1.2.3 From 47f6cdd2cb0bed3d8337626d45efc9301337bf1d Mon Sep 17 00:00:00 2001 From: Sean Paul Date: Mon, 6 Mar 2017 15:00:17 -0500 Subject: drm: Fix compilation error when CONFIG_DEBUG_FS is undefined MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch fixes the following compilation error when CONFIG_DEBUG_FS is not defined. ../drivers/gpu/drm/drm_dp_helper.c: In function ‘drm_dp_aux_crc_work’: ../drivers/gpu/drm/drm_dp_helper.c:1029:13: error: ‘struct drm_crtc’ has no member named ‘crc’ ../drivers/gpu/drm/drm_dp_helper.c:1031:12: error: ‘struct drm_crtc’ has no member named ‘crc’ make[4]: *** [drivers/gpu/drm/drm_dp_helper.o] Error 1 make[4]: *** Waiting for unfinished jobs.... Fixes: 79c1da7c3bf7 ("drm/dp: add helpers for capture of frame CRCs") Cc: Tomeu Vizoso Cc: Sean Paul Cc: Daniel Vetter Cc: Jani Nikula Cc: David Airlie Cc: dri-devel@lists.freedesktop.org Reviewed-by: Emil Velikov Signed-off-by: Sean Paul --- include/drm/drm_crtc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index bda9347554a1..cba29ffedafd 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -776,6 +776,7 @@ struct drm_crtc { * Debugfs directory for this CRTC. */ struct dentry *debugfs_entry; +#endif /** * @crc: @@ -783,7 +784,6 @@ struct drm_crtc { * Configuration settings of CRC capture. */ struct drm_crtc_crc crc; -#endif /** * @fence_context: -- cgit v1.2.3 From 1bdb5d5b8e571b8a1ce7b0f0e3abbb359c303bef Mon Sep 17 00:00:00 2001 From: Noralf Trønnes Date: Tue, 7 Mar 2017 21:49:23 +0100 Subject: drm/debugfs: Remove the drm_driver.debugfs_cleanup callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the .debugfs_cleanup() callback now that all the users are gone. Signed-off-by: Noralf Trønnes Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170307204924.1002-3-noralf@tronnes.org --- drivers/gpu/drm/drm_debugfs.c | 5 ----- include/drm/drm_drv.h | 1 - 2 files changed, 6 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index 8b2c61ae0004..1d2d18d82d2e 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -242,14 +242,9 @@ static void drm_debugfs_remove_all_files(struct drm_minor *minor) */ int drm_debugfs_cleanup(struct drm_minor *minor) { - struct drm_device *dev = minor->dev; - if (!minor->debugfs_root) return 0; - if (dev->driver->debugfs_cleanup) - dev->driver->debugfs_cleanup(minor); - drm_debugfs_remove_all_files(minor); debugfs_remove_recursive(minor->debugfs_root); diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h index 4e66fbb56773..4321d012c4ba 100644 --- a/include/drm/drm_drv.h +++ b/include/drm/drm_drv.h @@ -302,7 +302,6 @@ struct drm_driver { void (*master_drop)(struct drm_device *dev, struct drm_file *file_priv); int (*debugfs_init)(struct drm_minor *minor); - void (*debugfs_cleanup)(struct drm_minor *minor); /** * @gem_free_object: deconstructor for drm_gem_objects -- cgit v1.2.3 From c6bb9baa03b1ed413ebd449d9d7f9389fda1f309 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 8 Mar 2017 15:12:35 +0100 Subject: drm: Extract drm_prime.h Plus a little bit more documentation. v2: Untangle the missing forward decls to make drm_prime|gem.h free-standing. Reviewed-by: Gustavo Padovan Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170308141257.12119-3-daniel.vetter@ffwll.ch --- Documentation/gpu/drm-mm.rst | 3 ++ drivers/gpu/drm/drm_prime.c | 3 +- include/drm/drmP.h | 32 ++---------------- include/drm/drm_gem.h | 4 +++ include/drm/drm_prime.h | 79 +++++++++++++++++++++++++++++++++++++++++++ include/drm/drm_vma_manager.h | 1 - 6 files changed, 91 insertions(+), 31 deletions(-) create mode 100644 include/drm/drm_prime.h (limited to 'include') diff --git a/Documentation/gpu/drm-mm.rst b/Documentation/gpu/drm-mm.rst index fd35998acefc..d7a29d41789f 100644 --- a/Documentation/gpu/drm-mm.rst +++ b/Documentation/gpu/drm-mm.rst @@ -449,6 +449,9 @@ PRIME Helper Functions PRIME Function References ------------------------- +.. kernel-doc:: include/drm/drm_prime.h + :internal: + .. kernel-doc:: drivers/gpu/drm/drm_prime.c :export: diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index 866b294e7c61..9fb65b736a90 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -29,8 +29,9 @@ #include #include #include -#include +#include #include +#include #include "drm_internal.h" diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 6105c050d7bc..5f829d2372da 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -51,13 +51,13 @@ #include #include #include -#include #include #include #include #include #include #include +#include #include #include @@ -75,8 +75,8 @@ #include #include #include -#include #include +#include struct module; @@ -89,6 +89,7 @@ struct drm_dma_handle; struct drm_gem_object; struct drm_master; struct drm_vblank_crtc; +struct drm_vma_offset_manager; struct device_node; struct videomode; @@ -370,12 +371,6 @@ struct drm_pending_event { we deliver the event, for tracing only */ }; -struct drm_prime_file_private { - struct mutex lock; - struct rb_root dmabufs; - struct rb_root handles; -}; - /** File private data */ struct drm_file { unsigned authenticated :1; @@ -759,27 +754,6 @@ static inline int drm_debugfs_remove_files(const struct drm_info_list *files, } #endif -struct dma_buf_export_info; - -extern struct dma_buf *drm_gem_prime_export(struct drm_device *dev, - struct drm_gem_object *obj, - int flags); -extern int drm_gem_prime_handle_to_fd(struct drm_device *dev, - struct drm_file *file_priv, uint32_t handle, uint32_t flags, - int *prime_fd); -extern struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev, - struct dma_buf *dma_buf); -extern int drm_gem_prime_fd_to_handle(struct drm_device *dev, - struct drm_file *file_priv, int prime_fd, uint32_t *handle); -struct dma_buf *drm_gem_dmabuf_export(struct drm_device *dev, - struct dma_buf_export_info *exp_info); -extern void drm_gem_dmabuf_release(struct dma_buf *dma_buf); - -extern int drm_prime_sg_to_page_addr_arrays(struct sg_table *sgt, struct page **pages, - dma_addr_t *addrs, int max_pages); -extern struct sg_table *drm_prime_pages_to_sg(struct page **pages, unsigned int nr_pages); -extern void drm_prime_gem_destroy(struct drm_gem_object *obj, struct sg_table *sg); - extern struct drm_dma_handle *drm_pci_alloc(struct drm_device *dev, size_t size, size_t align); diff --git a/include/drm/drm_gem.h b/include/drm/drm_gem.h index 3b2a28f7f49f..b9ade75ecd82 100644 --- a/include/drm/drm_gem.h +++ b/include/drm/drm_gem.h @@ -34,6 +34,10 @@ * OTHER DEALINGS IN THE SOFTWARE. */ +#include + +#include + /** * struct drm_gem_object - GEM buffer object * diff --git a/include/drm/drm_prime.h b/include/drm/drm_prime.h new file mode 100644 index 000000000000..77ceca711bad --- /dev/null +++ b/include/drm/drm_prime.h @@ -0,0 +1,79 @@ +/* + * Copyright © 2012 Red Hat + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * Copyright (c) 2009-2010, Code Aurora Forum. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Dave Airlie + * Rob Clark + * + */ + +#ifndef __DRM_PRIME_H__ +#define __DRM_PRIME_H__ + +#include +#include + +/** + * struct drm_prime_file_private - per-file tracking for PRIME + * + * This just contains the internal &struct dma_buf and handle caches for each + * &struct drm_file used by the PRIME core code. + */ + +struct drm_prime_file_private { +/* private: */ + struct mutex lock; + struct rb_root dmabufs; + struct rb_root handles; +}; + +struct dma_buf_export_info; +struct dma_buf; + +struct drm_device; +struct drm_gem_object; +struct drm_file; + +extern struct dma_buf *drm_gem_prime_export(struct drm_device *dev, + struct drm_gem_object *obj, + int flags); +extern int drm_gem_prime_handle_to_fd(struct drm_device *dev, + struct drm_file *file_priv, uint32_t handle, uint32_t flags, + int *prime_fd); +extern struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev, + struct dma_buf *dma_buf); +extern int drm_gem_prime_fd_to_handle(struct drm_device *dev, + struct drm_file *file_priv, int prime_fd, uint32_t *handle); +struct dma_buf *drm_gem_dmabuf_export(struct drm_device *dev, + struct dma_buf_export_info *exp_info); +extern void drm_gem_dmabuf_release(struct dma_buf *dma_buf); + +extern int drm_prime_sg_to_page_addr_arrays(struct sg_table *sgt, struct page **pages, + dma_addr_t *addrs, int max_pages); +extern struct sg_table *drm_prime_pages_to_sg(struct page **pages, unsigned int nr_pages); +extern void drm_prime_gem_destroy(struct drm_gem_object *obj, struct sg_table *sg); + + +#endif /* __DRM_PRIME_H__ */ diff --git a/include/drm/drm_vma_manager.h b/include/drm/drm_vma_manager.h index 9c03895dc479..d84d52f6d2b1 100644 --- a/include/drm/drm_vma_manager.h +++ b/include/drm/drm_vma_manager.h @@ -25,7 +25,6 @@ #include #include -#include #include #include #include -- cgit v1.2.3 From d891b9c78ac83a14d0b7c5bbd652572bcd58c823 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 8 Mar 2017 15:12:36 +0100 Subject: drm: Move drm_lock_data out of drmP.h And remove the semi-kernel-doc stuff, to make sure no one uses this. Reviewed-by: Gustavo Padovan Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170308141257.12119-4-daniel.vetter@ffwll.ch --- include/drm/drmP.h | 15 --------------- include/drm/drm_auth.h | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 5f829d2372da..22b6f6f45b07 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -428,21 +428,6 @@ struct drm_file { struct drm_prime_file_private prime; }; -/** - * Lock data. - */ -struct drm_lock_data { - struct drm_hw_lock *hw_lock; /**< Hardware lock */ - /** Private of lock holder's file (NULL=kernel) */ - struct drm_file *file_priv; - wait_queue_head_t lock_queue; /**< Queue of blocked processes */ - unsigned long lock_time; /**< Time of last lock in jiffies */ - spinlock_t spinlock; - uint32_t kernel_waiters; - uint32_t user_waiters; - int idle_has_lock; -}; - /* Flags and return codes for get_vblank_timestamp() driver function. */ #define DRM_CALLED_FROM_VBLIRQ 1 #define DRM_VBLANKTIME_SCANOUTPOS_METHOD (1 << 0) diff --git a/include/drm/drm_auth.h b/include/drm/drm_auth.h index 1eb4a52cad8d..81a40c2a9a3e 100644 --- a/include/drm/drm_auth.h +++ b/include/drm/drm_auth.h @@ -28,6 +28,23 @@ #ifndef _DRM_AUTH_H_ #define _DRM_AUTH_H_ +/* + * Legacy DRI1 locking data structure. Only here instead of in drm_legacy.h for + * include ordering reasons. + * + * DO NOT USE. + */ +struct drm_lock_data { + struct drm_hw_lock *hw_lock; + struct drm_file *file_priv; + wait_queue_head_t lock_queue; + unsigned long lock_time; + spinlock_t spinlock; + uint32_t kernel_waiters; + uint32_t user_waiters; + int idle_has_lock; +}; + /** * struct drm_master - drm master structure * -- cgit v1.2.3 From 23ef59ef6dcc9b62bf077490a74df93b3bb0d530 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 8 Mar 2017 15:12:37 +0100 Subject: drm: Extract drm_pci.h Just another step in finally making drmP.h obsolete. Reviewed-by: Gustavo Padovan Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170308141257.12119-5-daniel.vetter@ffwll.ch --- drivers/gpu/drm/drm_pci.c | 7 +++++ include/drm/drmP.h | 43 +++----------------------- include/drm/drm_pci.h | 78 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 39 deletions(-) create mode 100644 include/drm/drm_pci.h (limited to 'include') diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index a3b356e70b35..1eb4fc3eee20 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include "drm_internal.h" #include "drm_legacy.h" @@ -36,6 +37,9 @@ * @size: size of block to allocate * @align: alignment of block * + * FIXME: This is a needless abstraction of the Linux dma-api and should be + * removed. + * * Return: A handle to the allocated memory block on success or NULL on * failure. */ @@ -104,6 +108,9 @@ void __drm_legacy_pci_free(struct drm_device * dev, drm_dma_handle_t * dmah) * drm_pci_free - Free a PCI consistent memory block * @dev: DRM device * @dmah: handle to memory block + * + * FIXME: This is a needless abstraction of the Linux dma-api and should be + * removed. */ void drm_pci_free(struct drm_device * dev, drm_dma_handle_t * dmah) { diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 22b6f6f45b07..1ad699de8769 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -47,7 +47,6 @@ #include #include #include -#include #include #include #include @@ -77,6 +76,7 @@ #include #include #include +#include struct module; @@ -85,7 +85,6 @@ struct drm_device; struct drm_agp_head; struct drm_local_map; struct drm_device_dma; -struct drm_dma_handle; struct drm_gem_object; struct drm_master; struct drm_vblank_crtc; @@ -96,6 +95,9 @@ struct videomode; struct reservation_object; struct dma_buf_attachment; +struct pci_dev; +struct pci_controller; + /* * The following categories are defined: * @@ -739,49 +741,12 @@ static inline int drm_debugfs_remove_files(const struct drm_info_list *files, } #endif - -extern struct drm_dma_handle *drm_pci_alloc(struct drm_device *dev, size_t size, - size_t align); -extern void drm_pci_free(struct drm_device *dev, struct drm_dma_handle * dmah); - /* sysfs support (drm_sysfs.c) */ extern void drm_sysfs_hotplug_event(struct drm_device *dev); /*@}*/ -extern int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver); -extern void drm_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver); -#ifdef CONFIG_PCI -extern int drm_get_pci_dev(struct pci_dev *pdev, - const struct pci_device_id *ent, - struct drm_driver *driver); -extern int drm_pci_set_busid(struct drm_device *dev, struct drm_master *master); -#else -static inline int drm_get_pci_dev(struct pci_dev *pdev, - const struct pci_device_id *ent, - struct drm_driver *driver) -{ - return -ENOSYS; -} - -static inline int drm_pci_set_busid(struct drm_device *dev, - struct drm_master *master) -{ - return -ENOSYS; -} -#endif - -#define DRM_PCIE_SPEED_25 1 -#define DRM_PCIE_SPEED_50 2 -#define DRM_PCIE_SPEED_80 4 - -extern int drm_pcie_get_speed_cap_mask(struct drm_device *dev, u32 *speed_mask); -extern int drm_pcie_get_max_link_width(struct drm_device *dev, u32 *mlw); - -/* platform section */ -extern int drm_platform_init(struct drm_driver *driver, struct platform_device *platform_device); - /* returns true if currently okay to sleep */ static __inline__ bool drm_can_sleep(void) { diff --git a/include/drm/drm_pci.h b/include/drm/drm_pci.h new file mode 100644 index 000000000000..5081b3eba309 --- /dev/null +++ b/include/drm/drm_pci.h @@ -0,0 +1,78 @@ +/* + * Internal Header for the Direct Rendering Manager + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * Copyright (c) 2009-2010, Code Aurora Forum. + * All rights reserved. + * + * Author: Rickard E. (Rik) Faith + * Author: Gareth Hughes + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _DRM_PCI_H_ +#define _DRM_PCI_H_ + +#include + +struct drm_dma_handle; +struct drm_device; +struct drm_driver; +struct drm_master; + +extern struct drm_dma_handle *drm_pci_alloc(struct drm_device *dev, size_t size, + size_t align); +extern void drm_pci_free(struct drm_device *dev, struct drm_dma_handle * dmah); + +extern int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver); +extern void drm_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver); +#ifdef CONFIG_PCI +extern int drm_get_pci_dev(struct pci_dev *pdev, + const struct pci_device_id *ent, + struct drm_driver *driver); +extern int drm_pci_set_busid(struct drm_device *dev, struct drm_master *master); +#else +static inline int drm_get_pci_dev(struct pci_dev *pdev, + const struct pci_device_id *ent, + struct drm_driver *driver) +{ + return -ENOSYS; +} + +static inline int drm_pci_set_busid(struct drm_device *dev, + struct drm_master *master) +{ + return -ENOSYS; +} +#endif + +#define DRM_PCIE_SPEED_25 1 +#define DRM_PCIE_SPEED_50 2 +#define DRM_PCIE_SPEED_80 4 + +extern int drm_pcie_get_speed_cap_mask(struct drm_device *dev, u32 *speed_mask); +extern int drm_pcie_get_max_link_width(struct drm_device *dev, u32 *mlw); + +/* platform section */ +extern int drm_platform_init(struct drm_driver *driver, struct platform_device *platform_device); + +#endif /* _DRM_PCI_H_ */ -- cgit v1.2.3 From 66f8c10003966130b0b2812fcbdfa6389bccd577 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 8 Mar 2017 15:12:39 +0100 Subject: drm/doc: document fallback behaviour for atomic events Worst case if the hw can't support completion signalling in a race-free way we want the event to be too late, not too early. Text adapted from a proposal from Laurent - the other side of how to make hw work correctly where it's possible is imo already sufficiently documented. v2: Review from Laurent. Cc: Laurent Pinchart Reviewed-by: Laurent Pinchart Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170308141257.12119-7-daniel.vetter@ffwll.ch --- include/drm/drm_crtc.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include') diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index cba29ffedafd..6ef59da3fd8e 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -204,6 +204,12 @@ struct drm_crtc_state { * drm_crtc_arm_vblank_event(). See the documentation of that function * for a detailed discussion of the constraints it needs to be used * safely. + * + * If the device can't notify of flip completion in a race-free way + * at all, then the event should be armed just after the page flip is + * committed. In the worst case the driver will send the event to + * userspace one frame too late. This doesn't allow for a real atomic + * update, but it should avoid tearing. */ struct drm_pending_vblank_event *event; -- cgit v1.2.3 From 9acdac68bcdc8c186353e5575a872465e103de74 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 8 Mar 2017 15:12:40 +0100 Subject: drm: rename drm_fops.c to drm_file.c It's not just file ops, but drm_file stuff in general. This is prep work to extracting a drm_file.h header in the next patch. Reviewed-by: Gustavo Padovan Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170308141257.12119-8-daniel.vetter@ffwll.ch --- Documentation/gpu/drm-internals.rst | 4 +- drivers/gpu/drm/Makefile | 2 +- drivers/gpu/drm/drm_file.c | 734 +++++++++++++++++++++++++++++++++++ drivers/gpu/drm/drm_fops.c | 737 ------------------------------------ drivers/gpu/drm/drm_internal.h | 2 +- include/drm/drmP.h | 2 +- 6 files changed, 739 insertions(+), 742 deletions(-) create mode 100644 drivers/gpu/drm/drm_file.c delete mode 100644 drivers/gpu/drm/drm_fops.c (limited to 'include') diff --git a/Documentation/gpu/drm-internals.rst b/Documentation/gpu/drm-internals.rst index e35920db1f4c..b00bf8229080 100644 --- a/Documentation/gpu/drm-internals.rst +++ b/Documentation/gpu/drm-internals.rst @@ -298,10 +298,10 @@ over. File Operations --------------- -.. kernel-doc:: drivers/gpu/drm/drm_fops.c +.. kernel-doc:: drivers/gpu/drm/drm_file.c :doc: file operations -.. kernel-doc:: drivers/gpu/drm/drm_fops.c +.. kernel-doc:: drivers/gpu/drm/drm_file.c :export: IOCTLs diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 3ee95793d122..eb54ffabacc5 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -4,7 +4,7 @@ drm-y := drm_auth.o drm_bufs.o drm_cache.o \ drm_context.o drm_dma.o \ - drm_fops.o drm_gem.o drm_ioctl.o drm_irq.o \ + drm_file.o drm_gem.o drm_ioctl.o drm_irq.o \ drm_lock.o drm_memory.o drm_drv.o \ drm_scatter.o drm_pci.o \ drm_platform.o drm_sysfs.o drm_hashtab.o drm_mm.o \ diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c new file mode 100644 index 000000000000..83e8c96ec4ab --- /dev/null +++ b/drivers/gpu/drm/drm_file.c @@ -0,0 +1,734 @@ +/* + * \author Rickard E. (Rik) Faith + * \author Daryll Strauss + * \author Gareth Hughes + */ + +/* + * Created: Mon Jan 4 08:58:31 1999 by faith@valinux.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include "drm_legacy.h" +#include "drm_internal.h" +#include "drm_crtc_internal.h" + +/* from BKL pushdown */ +DEFINE_MUTEX(drm_global_mutex); + +/** + * DOC: file operations + * + * Drivers must define the file operations structure that forms the DRM + * userspace API entry point, even though most of those operations are + * implemented in the DRM core. The mandatory functions are drm_open(), + * drm_read(), drm_ioctl() and drm_compat_ioctl() if CONFIG_COMPAT is enabled + * (note that drm_compat_ioctl will be NULL if CONFIG_COMPAT=n). Drivers which + * implement private ioctls that require 32/64 bit compatibility support must + * provide their own .compat_ioctl() handler that processes private ioctls and + * calls drm_compat_ioctl() for core ioctls. + * + * In addition drm_read() and drm_poll() provide support for DRM events. DRM + * events are a generic and extensible means to send asynchronous events to + * userspace through the file descriptor. They are used to send vblank event and + * page flip completions by the KMS API. But drivers can also use it for their + * own needs, e.g. to signal completion of rendering. + * + * The memory mapping implementation will vary depending on how the driver + * manages memory. Legacy drivers will use the deprecated drm_legacy_mmap() + * function, modern drivers should use one of the provided memory-manager + * specific implementations. For GEM-based drivers this is drm_gem_mmap(). + * + * No other file operations are supported by the DRM userspace API. Overall the + * following is an example #file_operations structure:: + * + * static const example_drm_fops = { + * .owner = THIS_MODULE, + * .open = drm_open, + * .release = drm_release, + * .unlocked_ioctl = drm_ioctl, + * .compat_ioctl = drm_compat_ioctl, // NULL if CONFIG_COMPAT=n + * .poll = drm_poll, + * .read = drm_read, + * .llseek = no_llseek, + * .mmap = drm_gem_mmap, + * }; + */ + +static int drm_open_helper(struct file *filp, struct drm_minor *minor); + +static int drm_setup(struct drm_device * dev) +{ + int ret; + + if (dev->driver->firstopen && + drm_core_check_feature(dev, DRIVER_LEGACY)) { + ret = dev->driver->firstopen(dev); + if (ret != 0) + return ret; + } + + ret = drm_legacy_dma_setup(dev); + if (ret < 0) + return ret; + + + DRM_DEBUG("\n"); + return 0; +} + +/** + * drm_open - open method for DRM file + * @inode: device inode + * @filp: file pointer. + * + * This function must be used by drivers as their .open() #file_operations + * method. It looks up the correct DRM device and instantiates all the per-file + * resources for it. + * + * RETURNS: + * + * 0 on success or negative errno value on falure. + */ +int drm_open(struct inode *inode, struct file *filp) +{ + struct drm_device *dev; + struct drm_minor *minor; + int retcode; + int need_setup = 0; + + minor = drm_minor_acquire(iminor(inode)); + if (IS_ERR(minor)) + return PTR_ERR(minor); + + dev = minor->dev; + if (!dev->open_count++) + need_setup = 1; + + /* share address_space across all char-devs of a single device */ + filp->f_mapping = dev->anon_inode->i_mapping; + + retcode = drm_open_helper(filp, minor); + if (retcode) + goto err_undo; + if (need_setup) { + retcode = drm_setup(dev); + if (retcode) + goto err_undo; + } + return 0; + +err_undo: + dev->open_count--; + drm_minor_release(minor); + return retcode; +} +EXPORT_SYMBOL(drm_open); + +/* + * Check whether DRI will run on this CPU. + * + * \return non-zero if the DRI will run on this CPU, or zero otherwise. + */ +static int drm_cpu_valid(void) +{ +#if defined(__sparc__) && !defined(__sparc_v9__) + return 0; /* No cmpxchg before v9 sparc. */ +#endif + return 1; +} + +/* + * Called whenever a process opens /dev/drm. + * + * \param filp file pointer. + * \param minor acquired minor-object. + * \return zero on success or a negative number on failure. + * + * Creates and initializes a drm_file structure for the file private data in \p + * filp and add it into the double linked list in \p dev. + */ +static int drm_open_helper(struct file *filp, struct drm_minor *minor) +{ + struct drm_device *dev = minor->dev; + struct drm_file *priv; + int ret; + + if (filp->f_flags & O_EXCL) + return -EBUSY; /* No exclusive opens */ + if (!drm_cpu_valid()) + return -EINVAL; + if (dev->switch_power_state != DRM_SWITCH_POWER_ON && dev->switch_power_state != DRM_SWITCH_POWER_DYNAMIC_OFF) + return -EINVAL; + + DRM_DEBUG("pid = %d, minor = %d\n", task_pid_nr(current), minor->index); + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + filp->private_data = priv; + priv->filp = filp; + priv->pid = get_pid(task_pid(current)); + priv->minor = minor; + + /* for compatibility root is always authenticated */ + priv->authenticated = capable(CAP_SYS_ADMIN); + priv->lock_count = 0; + + INIT_LIST_HEAD(&priv->lhead); + INIT_LIST_HEAD(&priv->fbs); + mutex_init(&priv->fbs_lock); + INIT_LIST_HEAD(&priv->blobs); + INIT_LIST_HEAD(&priv->pending_event_list); + INIT_LIST_HEAD(&priv->event_list); + init_waitqueue_head(&priv->event_wait); + priv->event_space = 4096; /* set aside 4k for event buffer */ + + mutex_init(&priv->event_read_lock); + + if (drm_core_check_feature(dev, DRIVER_GEM)) + drm_gem_open(dev, priv); + + if (drm_core_check_feature(dev, DRIVER_PRIME)) + drm_prime_init_file_private(&priv->prime); + + if (dev->driver->open) { + ret = dev->driver->open(dev, priv); + if (ret < 0) + goto out_prime_destroy; + } + + if (drm_is_primary_client(priv)) { + ret = drm_master_open(priv); + if (ret) + goto out_close; + } + + mutex_lock(&dev->filelist_mutex); + list_add(&priv->lhead, &dev->filelist); + mutex_unlock(&dev->filelist_mutex); + +#ifdef __alpha__ + /* + * Default the hose + */ + if (!dev->hose) { + struct pci_dev *pci_dev; + pci_dev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, NULL); + if (pci_dev) { + dev->hose = pci_dev->sysdata; + pci_dev_put(pci_dev); + } + if (!dev->hose) { + struct pci_bus *b = list_entry(pci_root_buses.next, + struct pci_bus, node); + if (b) + dev->hose = b->sysdata; + } + } +#endif + + return 0; + +out_close: + if (dev->driver->postclose) + dev->driver->postclose(dev, priv); +out_prime_destroy: + if (drm_core_check_feature(dev, DRIVER_PRIME)) + drm_prime_destroy_file_private(&priv->prime); + if (drm_core_check_feature(dev, DRIVER_GEM)) + drm_gem_release(dev, priv); + put_pid(priv->pid); + kfree(priv); + filp->private_data = NULL; + return ret; +} + +static void drm_events_release(struct drm_file *file_priv) +{ + struct drm_device *dev = file_priv->minor->dev; + struct drm_pending_event *e, *et; + unsigned long flags; + + spin_lock_irqsave(&dev->event_lock, flags); + + /* Unlink pending events */ + list_for_each_entry_safe(e, et, &file_priv->pending_event_list, + pending_link) { + list_del(&e->pending_link); + e->file_priv = NULL; + } + + /* Remove unconsumed events */ + list_for_each_entry_safe(e, et, &file_priv->event_list, link) { + list_del(&e->link); + kfree(e); + } + + spin_unlock_irqrestore(&dev->event_lock, flags); +} + +/* + * drm_legacy_dev_reinit + * + * Reinitializes a legacy/ums drm device in it's lastclose function. + */ +static void drm_legacy_dev_reinit(struct drm_device *dev) +{ + if (dev->irq_enabled) + drm_irq_uninstall(dev); + + mutex_lock(&dev->struct_mutex); + + drm_legacy_agp_clear(dev); + + drm_legacy_sg_cleanup(dev); + drm_legacy_vma_flush(dev); + drm_legacy_dma_takedown(dev); + + mutex_unlock(&dev->struct_mutex); + + dev->sigdata.lock = NULL; + + dev->context_flag = 0; + dev->last_context = 0; + dev->if_version = 0; + + DRM_DEBUG("lastclose completed\n"); +} + +/* + * Take down the DRM device. + * + * \param dev DRM device structure. + * + * Frees every resource in \p dev. + * + * \sa drm_device + */ +void drm_lastclose(struct drm_device * dev) +{ + DRM_DEBUG("\n"); + + if (dev->driver->lastclose) + dev->driver->lastclose(dev); + DRM_DEBUG("driver lastclose completed\n"); + + if (drm_core_check_feature(dev, DRIVER_LEGACY)) + drm_legacy_dev_reinit(dev); +} + +/** + * drm_release - release method for DRM file + * @inode: device inode + * @filp: file pointer. + * + * This function must be used by drivers as their .release() #file_operations + * method. It frees any resources associated with the open file, and if this is + * the last open file for the DRM device also proceeds to call drm_lastclose(). + * + * RETURNS: + * + * Always succeeds and returns 0. + */ +int drm_release(struct inode *inode, struct file *filp) +{ + struct drm_file *file_priv = filp->private_data; + struct drm_minor *minor = file_priv->minor; + struct drm_device *dev = minor->dev; + + mutex_lock(&drm_global_mutex); + + DRM_DEBUG("open_count = %d\n", dev->open_count); + + mutex_lock(&dev->filelist_mutex); + list_del(&file_priv->lhead); + mutex_unlock(&dev->filelist_mutex); + + if (dev->driver->preclose) + dev->driver->preclose(dev, file_priv); + + /* ======================================================== + * Begin inline drm_release + */ + + DRM_DEBUG("pid = %d, device = 0x%lx, open_count = %d\n", + task_pid_nr(current), + (long)old_encode_dev(file_priv->minor->kdev->devt), + dev->open_count); + + if (drm_core_check_feature(dev, DRIVER_LEGACY)) + drm_legacy_lock_release(dev, filp); + + if (drm_core_check_feature(dev, DRIVER_HAVE_DMA)) + drm_legacy_reclaim_buffers(dev, file_priv); + + drm_events_release(file_priv); + + if (drm_core_check_feature(dev, DRIVER_MODESET)) { + drm_fb_release(file_priv); + drm_property_destroy_user_blobs(dev, file_priv); + } + + if (drm_core_check_feature(dev, DRIVER_GEM)) + drm_gem_release(dev, file_priv); + + drm_legacy_ctxbitmap_flush(dev, file_priv); + + if (drm_is_primary_client(file_priv)) + drm_master_release(file_priv); + + if (dev->driver->postclose) + dev->driver->postclose(dev, file_priv); + + if (drm_core_check_feature(dev, DRIVER_PRIME)) + drm_prime_destroy_file_private(&file_priv->prime); + + WARN_ON(!list_empty(&file_priv->event_list)); + + put_pid(file_priv->pid); + kfree(file_priv); + + /* ======================================================== + * End inline drm_release + */ + + if (!--dev->open_count) { + drm_lastclose(dev); + if (drm_device_is_unplugged(dev)) + drm_put_dev(dev); + } + mutex_unlock(&drm_global_mutex); + + drm_minor_release(minor); + + return 0; +} +EXPORT_SYMBOL(drm_release); + +/** + * drm_read - read method for DRM file + * @filp: file pointer + * @buffer: userspace destination pointer for the read + * @count: count in bytes to read + * @offset: offset to read + * + * This function must be used by drivers as their .read() #file_operations + * method iff they use DRM events for asynchronous signalling to userspace. + * Since events are used by the KMS API for vblank and page flip completion this + * means all modern display drivers must use it. + * + * @offset is ignore, DRM events are read like a pipe. Therefore drivers also + * must set the .llseek() #file_operation to no_llseek(). Polling support is + * provided by drm_poll(). + * + * This function will only ever read a full event. Therefore userspace must + * supply a big enough buffer to fit any event to ensure forward progress. Since + * the maximum event space is currently 4K it's recommended to just use that for + * safety. + * + * RETURNS: + * + * Number of bytes read (always aligned to full events, and can be 0) or a + * negative error code on failure. + */ +ssize_t drm_read(struct file *filp, char __user *buffer, + size_t count, loff_t *offset) +{ + struct drm_file *file_priv = filp->private_data; + struct drm_device *dev = file_priv->minor->dev; + ssize_t ret; + + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + + ret = mutex_lock_interruptible(&file_priv->event_read_lock); + if (ret) + return ret; + + for (;;) { + struct drm_pending_event *e = NULL; + + spin_lock_irq(&dev->event_lock); + if (!list_empty(&file_priv->event_list)) { + e = list_first_entry(&file_priv->event_list, + struct drm_pending_event, link); + file_priv->event_space += e->event->length; + list_del(&e->link); + } + spin_unlock_irq(&dev->event_lock); + + if (e == NULL) { + if (ret) + break; + + if (filp->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + break; + } + + mutex_unlock(&file_priv->event_read_lock); + ret = wait_event_interruptible(file_priv->event_wait, + !list_empty(&file_priv->event_list)); + if (ret >= 0) + ret = mutex_lock_interruptible(&file_priv->event_read_lock); + if (ret) + return ret; + } else { + unsigned length = e->event->length; + + if (length > count - ret) { +put_back_event: + spin_lock_irq(&dev->event_lock); + file_priv->event_space -= length; + list_add(&e->link, &file_priv->event_list); + spin_unlock_irq(&dev->event_lock); + break; + } + + if (copy_to_user(buffer + ret, e->event, length)) { + if (ret == 0) + ret = -EFAULT; + goto put_back_event; + } + + ret += length; + kfree(e); + } + } + mutex_unlock(&file_priv->event_read_lock); + + return ret; +} +EXPORT_SYMBOL(drm_read); + +/** + * drm_poll - poll method for DRM file + * @filp: file pointer + * @wait: poll waiter table + * + * This function must be used by drivers as their .read() #file_operations + * method iff they use DRM events for asynchronous signalling to userspace. + * Since events are used by the KMS API for vblank and page flip completion this + * means all modern display drivers must use it. + * + * See also drm_read(). + * + * RETURNS: + * + * Mask of POLL flags indicating the current status of the file. + */ +unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait) +{ + struct drm_file *file_priv = filp->private_data; + unsigned int mask = 0; + + poll_wait(filp, &file_priv->event_wait, wait); + + if (!list_empty(&file_priv->event_list)) + mask |= POLLIN | POLLRDNORM; + + return mask; +} +EXPORT_SYMBOL(drm_poll); + +/** + * drm_event_reserve_init_locked - init a DRM event and reserve space for it + * @dev: DRM device + * @file_priv: DRM file private data + * @p: tracking structure for the pending event + * @e: actual event data to deliver to userspace + * + * This function prepares the passed in event for eventual delivery. If the event + * doesn't get delivered (because the IOCTL fails later on, before queuing up + * anything) then the even must be cancelled and freed using + * drm_event_cancel_free(). Successfully initialized events should be sent out + * using drm_send_event() or drm_send_event_locked() to signal completion of the + * asynchronous event to userspace. + * + * If callers embedded @p into a larger structure it must be allocated with + * kmalloc and @p must be the first member element. + * + * This is the locked version of drm_event_reserve_init() for callers which + * already hold &drm_device.event_lock. + * + * RETURNS: + * + * 0 on success or a negative error code on failure. + */ +int drm_event_reserve_init_locked(struct drm_device *dev, + struct drm_file *file_priv, + struct drm_pending_event *p, + struct drm_event *e) +{ + if (file_priv->event_space < e->length) + return -ENOMEM; + + file_priv->event_space -= e->length; + + p->event = e; + list_add(&p->pending_link, &file_priv->pending_event_list); + p->file_priv = file_priv; + + return 0; +} +EXPORT_SYMBOL(drm_event_reserve_init_locked); + +/** + * drm_event_reserve_init - init a DRM event and reserve space for it + * @dev: DRM device + * @file_priv: DRM file private data + * @p: tracking structure for the pending event + * @e: actual event data to deliver to userspace + * + * This function prepares the passed in event for eventual delivery. If the event + * doesn't get delivered (because the IOCTL fails later on, before queuing up + * anything) then the even must be cancelled and freed using + * drm_event_cancel_free(). Successfully initialized events should be sent out + * using drm_send_event() or drm_send_event_locked() to signal completion of the + * asynchronous event to userspace. + * + * If callers embedded @p into a larger structure it must be allocated with + * kmalloc and @p must be the first member element. + * + * Callers which already hold &drm_device.event_lock should use + * drm_event_reserve_init_locked() instead. + * + * RETURNS: + * + * 0 on success or a negative error code on failure. + */ +int drm_event_reserve_init(struct drm_device *dev, + struct drm_file *file_priv, + struct drm_pending_event *p, + struct drm_event *e) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&dev->event_lock, flags); + ret = drm_event_reserve_init_locked(dev, file_priv, p, e); + spin_unlock_irqrestore(&dev->event_lock, flags); + + return ret; +} +EXPORT_SYMBOL(drm_event_reserve_init); + +/** + * drm_event_cancel_free - free a DRM event and release it's space + * @dev: DRM device + * @p: tracking structure for the pending event + * + * This function frees the event @p initialized with drm_event_reserve_init() + * and releases any allocated space. + */ +void drm_event_cancel_free(struct drm_device *dev, + struct drm_pending_event *p) +{ + unsigned long flags; + spin_lock_irqsave(&dev->event_lock, flags); + if (p->file_priv) { + p->file_priv->event_space += p->event->length; + list_del(&p->pending_link); + } + spin_unlock_irqrestore(&dev->event_lock, flags); + + if (p->fence) + dma_fence_put(p->fence); + + kfree(p); +} +EXPORT_SYMBOL(drm_event_cancel_free); + +/** + * drm_send_event_locked - send DRM event to file descriptor + * @dev: DRM device + * @e: DRM event to deliver + * + * This function sends the event @e, initialized with drm_event_reserve_init(), + * to its associated userspace DRM file. Callers must already hold + * &drm_device.event_lock, see drm_send_event() for the unlocked version. + * + * Note that the core will take care of unlinking and disarming events when the + * corresponding DRM file is closed. Drivers need not worry about whether the + * DRM file for this event still exists and can call this function upon + * completion of the asynchronous work unconditionally. + */ +void drm_send_event_locked(struct drm_device *dev, struct drm_pending_event *e) +{ + assert_spin_locked(&dev->event_lock); + + if (e->completion) { + complete_all(e->completion); + e->completion_release(e->completion); + e->completion = NULL; + } + + if (e->fence) { + dma_fence_signal(e->fence); + dma_fence_put(e->fence); + } + + if (!e->file_priv) { + kfree(e); + return; + } + + list_del(&e->pending_link); + list_add_tail(&e->link, + &e->file_priv->event_list); + wake_up_interruptible(&e->file_priv->event_wait); +} +EXPORT_SYMBOL(drm_send_event_locked); + +/** + * drm_send_event - send DRM event to file descriptor + * @dev: DRM device + * @e: DRM event to deliver + * + * This function sends the event @e, initialized with drm_event_reserve_init(), + * to its associated userspace DRM file. This function acquires + * &drm_device.event_lock, see drm_send_event_locked() for callers which already + * hold this lock. + * + * Note that the core will take care of unlinking and disarming events when the + * corresponding DRM file is closed. Drivers need not worry about whether the + * DRM file for this event still exists and can call this function upon + * completion of the asynchronous work unconditionally. + */ +void drm_send_event(struct drm_device *dev, struct drm_pending_event *e) +{ + unsigned long irqflags; + + spin_lock_irqsave(&dev->event_lock, irqflags); + drm_send_event_locked(dev, e); + spin_unlock_irqrestore(&dev->event_lock, irqflags); +} +EXPORT_SYMBOL(drm_send_event); diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c deleted file mode 100644 index afdf5b147f39..000000000000 --- a/drivers/gpu/drm/drm_fops.c +++ /dev/null @@ -1,737 +0,0 @@ -/* - * \file drm_fops.c - * File operations for DRM - * - * \author Rickard E. (Rik) Faith - * \author Daryll Strauss - * \author Gareth Hughes - */ - -/* - * Created: Mon Jan 4 08:58:31 1999 by faith@valinux.com - * - * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. - * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include -#include -#include -#include -#include "drm_legacy.h" -#include "drm_internal.h" -#include "drm_crtc_internal.h" - -/* from BKL pushdown */ -DEFINE_MUTEX(drm_global_mutex); - -/** - * DOC: file operations - * - * Drivers must define the file operations structure that forms the DRM - * userspace API entry point, even though most of those operations are - * implemented in the DRM core. The mandatory functions are drm_open(), - * drm_read(), drm_ioctl() and drm_compat_ioctl() if CONFIG_COMPAT is enabled - * (note that drm_compat_ioctl will be NULL if CONFIG_COMPAT=n). Drivers which - * implement private ioctls that require 32/64 bit compatibility support must - * provide their own .compat_ioctl() handler that processes private ioctls and - * calls drm_compat_ioctl() for core ioctls. - * - * In addition drm_read() and drm_poll() provide support for DRM events. DRM - * events are a generic and extensible means to send asynchronous events to - * userspace through the file descriptor. They are used to send vblank event and - * page flip completions by the KMS API. But drivers can also use it for their - * own needs, e.g. to signal completion of rendering. - * - * The memory mapping implementation will vary depending on how the driver - * manages memory. Legacy drivers will use the deprecated drm_legacy_mmap() - * function, modern drivers should use one of the provided memory-manager - * specific implementations. For GEM-based drivers this is drm_gem_mmap(). - * - * No other file operations are supported by the DRM userspace API. Overall the - * following is an example #file_operations structure:: - * - * static const example_drm_fops = { - * .owner = THIS_MODULE, - * .open = drm_open, - * .release = drm_release, - * .unlocked_ioctl = drm_ioctl, - * .compat_ioctl = drm_compat_ioctl, // NULL if CONFIG_COMPAT=n - * .poll = drm_poll, - * .read = drm_read, - * .llseek = no_llseek, - * .mmap = drm_gem_mmap, - * }; - */ - -static int drm_open_helper(struct file *filp, struct drm_minor *minor); - -static int drm_setup(struct drm_device * dev) -{ - int ret; - - if (dev->driver->firstopen && - drm_core_check_feature(dev, DRIVER_LEGACY)) { - ret = dev->driver->firstopen(dev); - if (ret != 0) - return ret; - } - - ret = drm_legacy_dma_setup(dev); - if (ret < 0) - return ret; - - - DRM_DEBUG("\n"); - return 0; -} - -/** - * drm_open - open method for DRM file - * @inode: device inode - * @filp: file pointer. - * - * This function must be used by drivers as their .open() #file_operations - * method. It looks up the correct DRM device and instantiates all the per-file - * resources for it. - * - * RETURNS: - * - * 0 on success or negative errno value on falure. - */ -int drm_open(struct inode *inode, struct file *filp) -{ - struct drm_device *dev; - struct drm_minor *minor; - int retcode; - int need_setup = 0; - - minor = drm_minor_acquire(iminor(inode)); - if (IS_ERR(minor)) - return PTR_ERR(minor); - - dev = minor->dev; - if (!dev->open_count++) - need_setup = 1; - - /* share address_space across all char-devs of a single device */ - filp->f_mapping = dev->anon_inode->i_mapping; - - retcode = drm_open_helper(filp, minor); - if (retcode) - goto err_undo; - if (need_setup) { - retcode = drm_setup(dev); - if (retcode) - goto err_undo; - } - return 0; - -err_undo: - dev->open_count--; - drm_minor_release(minor); - return retcode; -} -EXPORT_SYMBOL(drm_open); - -/* - * Check whether DRI will run on this CPU. - * - * \return non-zero if the DRI will run on this CPU, or zero otherwise. - */ -static int drm_cpu_valid(void) -{ -#if defined(__sparc__) && !defined(__sparc_v9__) - return 0; /* No cmpxchg before v9 sparc. */ -#endif - return 1; -} - -/* - * Called whenever a process opens /dev/drm. - * - * \param filp file pointer. - * \param minor acquired minor-object. - * \return zero on success or a negative number on failure. - * - * Creates and initializes a drm_file structure for the file private data in \p - * filp and add it into the double linked list in \p dev. - */ -static int drm_open_helper(struct file *filp, struct drm_minor *minor) -{ - struct drm_device *dev = minor->dev; - struct drm_file *priv; - int ret; - - if (filp->f_flags & O_EXCL) - return -EBUSY; /* No exclusive opens */ - if (!drm_cpu_valid()) - return -EINVAL; - if (dev->switch_power_state != DRM_SWITCH_POWER_ON && dev->switch_power_state != DRM_SWITCH_POWER_DYNAMIC_OFF) - return -EINVAL; - - DRM_DEBUG("pid = %d, minor = %d\n", task_pid_nr(current), minor->index); - - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - filp->private_data = priv; - priv->filp = filp; - priv->pid = get_pid(task_pid(current)); - priv->minor = minor; - - /* for compatibility root is always authenticated */ - priv->authenticated = capable(CAP_SYS_ADMIN); - priv->lock_count = 0; - - INIT_LIST_HEAD(&priv->lhead); - INIT_LIST_HEAD(&priv->fbs); - mutex_init(&priv->fbs_lock); - INIT_LIST_HEAD(&priv->blobs); - INIT_LIST_HEAD(&priv->pending_event_list); - INIT_LIST_HEAD(&priv->event_list); - init_waitqueue_head(&priv->event_wait); - priv->event_space = 4096; /* set aside 4k for event buffer */ - - mutex_init(&priv->event_read_lock); - - if (drm_core_check_feature(dev, DRIVER_GEM)) - drm_gem_open(dev, priv); - - if (drm_core_check_feature(dev, DRIVER_PRIME)) - drm_prime_init_file_private(&priv->prime); - - if (dev->driver->open) { - ret = dev->driver->open(dev, priv); - if (ret < 0) - goto out_prime_destroy; - } - - if (drm_is_primary_client(priv)) { - ret = drm_master_open(priv); - if (ret) - goto out_close; - } - - mutex_lock(&dev->filelist_mutex); - list_add(&priv->lhead, &dev->filelist); - mutex_unlock(&dev->filelist_mutex); - -#ifdef __alpha__ - /* - * Default the hose - */ - if (!dev->hose) { - struct pci_dev *pci_dev; - pci_dev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, NULL); - if (pci_dev) { - dev->hose = pci_dev->sysdata; - pci_dev_put(pci_dev); - } - if (!dev->hose) { - struct pci_bus *b = list_entry(pci_root_buses.next, - struct pci_bus, node); - if (b) - dev->hose = b->sysdata; - } - } -#endif - - return 0; - -out_close: - if (dev->driver->postclose) - dev->driver->postclose(dev, priv); -out_prime_destroy: - if (drm_core_check_feature(dev, DRIVER_PRIME)) - drm_prime_destroy_file_private(&priv->prime); - if (drm_core_check_feature(dev, DRIVER_GEM)) - drm_gem_release(dev, priv); - put_pid(priv->pid); - kfree(priv); - filp->private_data = NULL; - return ret; -} - -static void drm_events_release(struct drm_file *file_priv) -{ - struct drm_device *dev = file_priv->minor->dev; - struct drm_pending_event *e, *et; - unsigned long flags; - - spin_lock_irqsave(&dev->event_lock, flags); - - /* Unlink pending events */ - list_for_each_entry_safe(e, et, &file_priv->pending_event_list, - pending_link) { - list_del(&e->pending_link); - e->file_priv = NULL; - } - - /* Remove unconsumed events */ - list_for_each_entry_safe(e, et, &file_priv->event_list, link) { - list_del(&e->link); - kfree(e); - } - - spin_unlock_irqrestore(&dev->event_lock, flags); -} - -/* - * drm_legacy_dev_reinit - * - * Reinitializes a legacy/ums drm device in it's lastclose function. - */ -static void drm_legacy_dev_reinit(struct drm_device *dev) -{ - if (dev->irq_enabled) - drm_irq_uninstall(dev); - - mutex_lock(&dev->struct_mutex); - - drm_legacy_agp_clear(dev); - - drm_legacy_sg_cleanup(dev); - drm_legacy_vma_flush(dev); - drm_legacy_dma_takedown(dev); - - mutex_unlock(&dev->struct_mutex); - - dev->sigdata.lock = NULL; - - dev->context_flag = 0; - dev->last_context = 0; - dev->if_version = 0; - - DRM_DEBUG("lastclose completed\n"); -} - -/* - * Take down the DRM device. - * - * \param dev DRM device structure. - * - * Frees every resource in \p dev. - * - * \sa drm_device - */ -void drm_lastclose(struct drm_device * dev) -{ - DRM_DEBUG("\n"); - - if (dev->driver->lastclose) - dev->driver->lastclose(dev); - DRM_DEBUG("driver lastclose completed\n"); - - if (drm_core_check_feature(dev, DRIVER_LEGACY)) - drm_legacy_dev_reinit(dev); -} - -/** - * drm_release - release method for DRM file - * @inode: device inode - * @filp: file pointer. - * - * This function must be used by drivers as their .release() #file_operations - * method. It frees any resources associated with the open file, and if this is - * the last open file for the DRM device also proceeds to call drm_lastclose(). - * - * RETURNS: - * - * Always succeeds and returns 0. - */ -int drm_release(struct inode *inode, struct file *filp) -{ - struct drm_file *file_priv = filp->private_data; - struct drm_minor *minor = file_priv->minor; - struct drm_device *dev = minor->dev; - - mutex_lock(&drm_global_mutex); - - DRM_DEBUG("open_count = %d\n", dev->open_count); - - mutex_lock(&dev->filelist_mutex); - list_del(&file_priv->lhead); - mutex_unlock(&dev->filelist_mutex); - - if (dev->driver->preclose) - dev->driver->preclose(dev, file_priv); - - /* ======================================================== - * Begin inline drm_release - */ - - DRM_DEBUG("pid = %d, device = 0x%lx, open_count = %d\n", - task_pid_nr(current), - (long)old_encode_dev(file_priv->minor->kdev->devt), - dev->open_count); - - if (drm_core_check_feature(dev, DRIVER_LEGACY)) - drm_legacy_lock_release(dev, filp); - - if (drm_core_check_feature(dev, DRIVER_HAVE_DMA)) - drm_legacy_reclaim_buffers(dev, file_priv); - - drm_events_release(file_priv); - - if (drm_core_check_feature(dev, DRIVER_MODESET)) { - drm_fb_release(file_priv); - drm_property_destroy_user_blobs(dev, file_priv); - } - - if (drm_core_check_feature(dev, DRIVER_GEM)) - drm_gem_release(dev, file_priv); - - drm_legacy_ctxbitmap_flush(dev, file_priv); - - if (drm_is_primary_client(file_priv)) - drm_master_release(file_priv); - - if (dev->driver->postclose) - dev->driver->postclose(dev, file_priv); - - if (drm_core_check_feature(dev, DRIVER_PRIME)) - drm_prime_destroy_file_private(&file_priv->prime); - - WARN_ON(!list_empty(&file_priv->event_list)); - - put_pid(file_priv->pid); - kfree(file_priv); - - /* ======================================================== - * End inline drm_release - */ - - if (!--dev->open_count) { - drm_lastclose(dev); - if (drm_device_is_unplugged(dev)) - drm_put_dev(dev); - } - mutex_unlock(&drm_global_mutex); - - drm_minor_release(minor); - - return 0; -} -EXPORT_SYMBOL(drm_release); - -/** - * drm_read - read method for DRM file - * @filp: file pointer - * @buffer: userspace destination pointer for the read - * @count: count in bytes to read - * @offset: offset to read - * - * This function must be used by drivers as their .read() #file_operations - * method iff they use DRM events for asynchronous signalling to userspace. - * Since events are used by the KMS API for vblank and page flip completion this - * means all modern display drivers must use it. - * - * @offset is ignore, DRM events are read like a pipe. Therefore drivers also - * must set the .llseek() #file_operation to no_llseek(). Polling support is - * provided by drm_poll(). - * - * This function will only ever read a full event. Therefore userspace must - * supply a big enough buffer to fit any event to ensure forward progress. Since - * the maximum event space is currently 4K it's recommended to just use that for - * safety. - * - * RETURNS: - * - * Number of bytes read (always aligned to full events, and can be 0) or a - * negative error code on failure. - */ -ssize_t drm_read(struct file *filp, char __user *buffer, - size_t count, loff_t *offset) -{ - struct drm_file *file_priv = filp->private_data; - struct drm_device *dev = file_priv->minor->dev; - ssize_t ret; - - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - - ret = mutex_lock_interruptible(&file_priv->event_read_lock); - if (ret) - return ret; - - for (;;) { - struct drm_pending_event *e = NULL; - - spin_lock_irq(&dev->event_lock); - if (!list_empty(&file_priv->event_list)) { - e = list_first_entry(&file_priv->event_list, - struct drm_pending_event, link); - file_priv->event_space += e->event->length; - list_del(&e->link); - } - spin_unlock_irq(&dev->event_lock); - - if (e == NULL) { - if (ret) - break; - - if (filp->f_flags & O_NONBLOCK) { - ret = -EAGAIN; - break; - } - - mutex_unlock(&file_priv->event_read_lock); - ret = wait_event_interruptible(file_priv->event_wait, - !list_empty(&file_priv->event_list)); - if (ret >= 0) - ret = mutex_lock_interruptible(&file_priv->event_read_lock); - if (ret) - return ret; - } else { - unsigned length = e->event->length; - - if (length > count - ret) { -put_back_event: - spin_lock_irq(&dev->event_lock); - file_priv->event_space -= length; - list_add(&e->link, &file_priv->event_list); - spin_unlock_irq(&dev->event_lock); - break; - } - - if (copy_to_user(buffer + ret, e->event, length)) { - if (ret == 0) - ret = -EFAULT; - goto put_back_event; - } - - ret += length; - kfree(e); - } - } - mutex_unlock(&file_priv->event_read_lock); - - return ret; -} -EXPORT_SYMBOL(drm_read); - -/** - * drm_poll - poll method for DRM file - * @filp: file pointer - * @wait: poll waiter table - * - * This function must be used by drivers as their .read() #file_operations - * method iff they use DRM events for asynchronous signalling to userspace. - * Since events are used by the KMS API for vblank and page flip completion this - * means all modern display drivers must use it. - * - * See also drm_read(). - * - * RETURNS: - * - * Mask of POLL flags indicating the current status of the file. - */ -unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait) -{ - struct drm_file *file_priv = filp->private_data; - unsigned int mask = 0; - - poll_wait(filp, &file_priv->event_wait, wait); - - if (!list_empty(&file_priv->event_list)) - mask |= POLLIN | POLLRDNORM; - - return mask; -} -EXPORT_SYMBOL(drm_poll); - -/** - * drm_event_reserve_init_locked - init a DRM event and reserve space for it - * @dev: DRM device - * @file_priv: DRM file private data - * @p: tracking structure for the pending event - * @e: actual event data to deliver to userspace - * - * This function prepares the passed in event for eventual delivery. If the event - * doesn't get delivered (because the IOCTL fails later on, before queuing up - * anything) then the even must be cancelled and freed using - * drm_event_cancel_free(). Successfully initialized events should be sent out - * using drm_send_event() or drm_send_event_locked() to signal completion of the - * asynchronous event to userspace. - * - * If callers embedded @p into a larger structure it must be allocated with - * kmalloc and @p must be the first member element. - * - * This is the locked version of drm_event_reserve_init() for callers which - * already hold &drm_device.event_lock. - * - * RETURNS: - * - * 0 on success or a negative error code on failure. - */ -int drm_event_reserve_init_locked(struct drm_device *dev, - struct drm_file *file_priv, - struct drm_pending_event *p, - struct drm_event *e) -{ - if (file_priv->event_space < e->length) - return -ENOMEM; - - file_priv->event_space -= e->length; - - p->event = e; - list_add(&p->pending_link, &file_priv->pending_event_list); - p->file_priv = file_priv; - - return 0; -} -EXPORT_SYMBOL(drm_event_reserve_init_locked); - -/** - * drm_event_reserve_init - init a DRM event and reserve space for it - * @dev: DRM device - * @file_priv: DRM file private data - * @p: tracking structure for the pending event - * @e: actual event data to deliver to userspace - * - * This function prepares the passed in event for eventual delivery. If the event - * doesn't get delivered (because the IOCTL fails later on, before queuing up - * anything) then the even must be cancelled and freed using - * drm_event_cancel_free(). Successfully initialized events should be sent out - * using drm_send_event() or drm_send_event_locked() to signal completion of the - * asynchronous event to userspace. - * - * If callers embedded @p into a larger structure it must be allocated with - * kmalloc and @p must be the first member element. - * - * Callers which already hold &drm_device.event_lock should use - * drm_event_reserve_init_locked() instead. - * - * RETURNS: - * - * 0 on success or a negative error code on failure. - */ -int drm_event_reserve_init(struct drm_device *dev, - struct drm_file *file_priv, - struct drm_pending_event *p, - struct drm_event *e) -{ - unsigned long flags; - int ret; - - spin_lock_irqsave(&dev->event_lock, flags); - ret = drm_event_reserve_init_locked(dev, file_priv, p, e); - spin_unlock_irqrestore(&dev->event_lock, flags); - - return ret; -} -EXPORT_SYMBOL(drm_event_reserve_init); - -/** - * drm_event_cancel_free - free a DRM event and release it's space - * @dev: DRM device - * @p: tracking structure for the pending event - * - * This function frees the event @p initialized with drm_event_reserve_init() - * and releases any allocated space. - */ -void drm_event_cancel_free(struct drm_device *dev, - struct drm_pending_event *p) -{ - unsigned long flags; - spin_lock_irqsave(&dev->event_lock, flags); - if (p->file_priv) { - p->file_priv->event_space += p->event->length; - list_del(&p->pending_link); - } - spin_unlock_irqrestore(&dev->event_lock, flags); - - if (p->fence) - dma_fence_put(p->fence); - - kfree(p); -} -EXPORT_SYMBOL(drm_event_cancel_free); - -/** - * drm_send_event_locked - send DRM event to file descriptor - * @dev: DRM device - * @e: DRM event to deliver - * - * This function sends the event @e, initialized with drm_event_reserve_init(), - * to its associated userspace DRM file. Callers must already hold - * &drm_device.event_lock, see drm_send_event() for the unlocked version. - * - * Note that the core will take care of unlinking and disarming events when the - * corresponding DRM file is closed. Drivers need not worry about whether the - * DRM file for this event still exists and can call this function upon - * completion of the asynchronous work unconditionally. - */ -void drm_send_event_locked(struct drm_device *dev, struct drm_pending_event *e) -{ - assert_spin_locked(&dev->event_lock); - - if (e->completion) { - complete_all(e->completion); - e->completion_release(e->completion); - e->completion = NULL; - } - - if (e->fence) { - dma_fence_signal(e->fence); - dma_fence_put(e->fence); - } - - if (!e->file_priv) { - kfree(e); - return; - } - - list_del(&e->pending_link); - list_add_tail(&e->link, - &e->file_priv->event_list); - wake_up_interruptible(&e->file_priv->event_wait); -} -EXPORT_SYMBOL(drm_send_event_locked); - -/** - * drm_send_event - send DRM event to file descriptor - * @dev: DRM device - * @e: DRM event to deliver - * - * This function sends the event @e, initialized with drm_event_reserve_init(), - * to its associated userspace DRM file. This function acquires - * &drm_device.event_lock, see drm_send_event_locked() for callers which already - * hold this lock. - * - * Note that the core will take care of unlinking and disarming events when the - * corresponding DRM file is closed. Drivers need not worry about whether the - * DRM file for this event still exists and can call this function upon - * completion of the asynchronous work unconditionally. - */ -void drm_send_event(struct drm_device *dev, struct drm_pending_event *e) -{ - unsigned long irqflags; - - spin_lock_irqsave(&dev->event_lock, irqflags); - drm_send_event_locked(dev, e); - spin_unlock_irqrestore(&dev->event_lock, irqflags); -} -EXPORT_SYMBOL(drm_send_event); diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h index f37388cb2fde..92ff4b9393b1 100644 --- a/drivers/gpu/drm/drm_internal.h +++ b/drivers/gpu/drm/drm_internal.h @@ -24,7 +24,7 @@ #define DRM_IF_MAJOR 1 #define DRM_IF_MINOR 4 -/* drm_fops.c */ +/* drm_file.c */ extern struct mutex drm_global_mutex; void drm_lastclose(struct drm_device *dev); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 1ad699de8769..826101785876 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -689,7 +689,7 @@ extern long drm_compat_ioctl(struct file *filp, #endif extern bool drm_ioctl_flags(unsigned int nr, unsigned int *flags); -/* File Operations (drm_fops.c) */ +/* File Operations (drm_file.c) */ int drm_open(struct inode *inode, struct file *filp); ssize_t drm_read(struct file *filp, char __user *buffer, size_t count, loff_t *offset); -- cgit v1.2.3 From 721dacbbeb062b99adea9ab6046b6ec04a8ec5fc Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 8 Mar 2017 15:12:41 +0100 Subject: drm: Remove DRM_MINOR_CNT This was originally added by David Herrmann for range checks, but entirely unused. It confused me, so let's remove it. Cc: David Herrmann Reviewed-by: David Herrmann Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170308141257.12119-9-daniel.vetter@ffwll.ch --- include/drm/drmP.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 826101785876..7bd6752683a4 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -444,7 +444,6 @@ enum drm_minor_type { DRM_MINOR_PRIMARY, DRM_MINOR_CONTROL, DRM_MINOR_RENDER, - DRM_MINOR_CNT, }; /** -- cgit v1.2.3 From a8f8b1d9b8701465f1309d551fba2ebda6760f49 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 8 Mar 2017 15:12:42 +0100 Subject: drm: Extract drm_file.h I'm torn on whether drm_minor really should be here or somewhere else. Maybe with more clarity after untangling drmP.h more this is easier to decide, for now I've put a FIXME comment right next to it. Right now we need struct drm_minor for the inline drm_file type helpers, and so it does kinda make sense to have them here. Next patch will kerneldoc-ify the entire pile. Reviewed-by: Gustavo Padovan Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170308141257.12119-10-daniel.vetter@ffwll.ch --- drivers/gpu/drm/drm_file.c | 5 +- include/drm/drmP.h | 127 +-------------------------------- include/drm/drm_file.h | 172 +++++++++++++++++++++++++++++++++++++++++++++ include/drm/drm_prime.h | 1 + 4 files changed, 178 insertions(+), 127 deletions(-) create mode 100644 include/drm/drm_file.h (limited to 'include') diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c index 83e8c96ec4ab..d9e63d73d3ec 100644 --- a/drivers/gpu/drm/drm_file.c +++ b/drivers/gpu/drm/drm_file.c @@ -31,10 +31,13 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -#include #include #include #include + +#include +#include + #include "drm_legacy.h" #include "drm_internal.h" #include "drm_crtc_internal.h" diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 7bd6752683a4..4a0260e2b067 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -77,10 +77,10 @@ #include #include #include +#include struct module; -struct drm_file; struct drm_device; struct drm_agp_head; struct drm_local_map; @@ -360,76 +360,6 @@ struct drm_ioctl_desc { .name = #ioctl \ } -/* Event queued up for userspace to read */ -struct drm_pending_event { - struct completion *completion; - void (*completion_release)(struct completion *completion); - struct drm_event *event; - struct dma_fence *fence; - struct list_head link; - struct list_head pending_link; - struct drm_file *file_priv; - pid_t pid; /* pid of requester, no guarantee it's valid by the time - we deliver the event, for tracing only */ -}; - -/** File private data */ -struct drm_file { - unsigned authenticated :1; - /* true when the client has asked us to expose stereo 3D mode flags */ - unsigned stereo_allowed :1; - /* - * true if client understands CRTC primary planes and cursor planes - * in the plane list - */ - unsigned universal_planes:1; - /* true if client understands atomic properties */ - unsigned atomic:1; - /* - * This client is the creator of @master. - * Protected by struct drm_device::master_mutex. - */ - unsigned is_master:1; - - struct pid *pid; - drm_magic_t magic; - struct list_head lhead; - struct drm_minor *minor; - unsigned long lock_count; - - /** Mapping of mm object handles to object pointers. */ - struct idr object_idr; - /** Lock for synchronization of access to object_idr. */ - spinlock_t table_lock; - - struct file *filp; - void *driver_priv; - - struct drm_master *master; /* master this node is currently associated with - N.B. not always dev->master */ - /** - * fbs - List of framebuffers associated with this file. - * - * Protected by fbs_lock. Note that the fbs list holds a reference on - * the fb object to prevent it from untimely disappearing. - */ - struct list_head fbs; - struct mutex fbs_lock; - - /** User-created blob properties; this retains a reference on the - * property. */ - struct list_head blobs; - - wait_queue_head_t event_wait; - struct list_head pending_event_list; - struct list_head event_list; - int event_space; - - struct mutex event_read_lock; - - struct drm_prime_file_private prime; -}; - /* Flags and return codes for get_vblank_timestamp() driver function. */ #define DRM_CALLED_FROM_VBLIRQ 1 #define DRM_VBLANKTIME_SCANOUTPOS_METHOD (1 << 0) @@ -440,12 +370,6 @@ struct drm_file { #define DRM_SCANOUTPOS_IN_VBLANK (1 << 1) #define DRM_SCANOUTPOS_ACCURATE (1 << 2) -enum drm_minor_type { - DRM_MINOR_PRIMARY, - DRM_MINOR_CONTROL, - DRM_MINOR_RENDER, -}; - /** * Info file list entry. This structure represents a debugfs or proc file to * be created by the drm core @@ -467,21 +391,6 @@ struct drm_info_node { struct dentry *dent; }; -/** - * DRM minor structure. This structure represents a drm minor number. - */ -struct drm_minor { - int index; /**< Minor device number */ - int type; /**< Control or render */ - struct device *kdev; /**< Linux device */ - struct drm_device *dev; - - struct dentry *debugfs_root; - - struct list_head debugfs_list; - struct mutex debugfs_lock; /* Protects debugfs_list. */ -}; - /** * DRM device structure. This structure represent a complete card that * may contain multiple heads. @@ -656,21 +565,6 @@ static inline int drm_device_is_unplugged(struct drm_device *dev) return ret; } -static inline bool drm_is_render_client(const struct drm_file *file_priv) -{ - return file_priv->minor->type == DRM_MINOR_RENDER; -} - -static inline bool drm_is_control_client(const struct drm_file *file_priv) -{ - return file_priv->minor->type == DRM_MINOR_CONTROL; -} - -static inline bool drm_is_primary_client(const struct drm_file *file_priv) -{ - return file_priv->minor->type == DRM_MINOR_PRIMARY; -} - /******************************************************************/ /** \name Internal function definitions */ /*@{*/ @@ -688,25 +582,6 @@ extern long drm_compat_ioctl(struct file *filp, #endif extern bool drm_ioctl_flags(unsigned int nr, unsigned int *flags); -/* File Operations (drm_file.c) */ -int drm_open(struct inode *inode, struct file *filp); -ssize_t drm_read(struct file *filp, char __user *buffer, - size_t count, loff_t *offset); -int drm_release(struct inode *inode, struct file *filp); -unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait); -int drm_event_reserve_init_locked(struct drm_device *dev, - struct drm_file *file_priv, - struct drm_pending_event *p, - struct drm_event *e); -int drm_event_reserve_init(struct drm_device *dev, - struct drm_file *file_priv, - struct drm_pending_event *p, - struct drm_event *e); -void drm_event_cancel_free(struct drm_device *dev, - struct drm_pending_event *p); -void drm_send_event_locked(struct drm_device *dev, struct drm_pending_event *e); -void drm_send_event(struct drm_device *dev, struct drm_pending_event *e); - /* Misc. IOCTL support (drm_ioctl.c) */ int drm_noop(struct drm_device *dev, void *data, struct drm_file *file_priv); diff --git a/include/drm/drm_file.h b/include/drm/drm_file.h new file mode 100644 index 000000000000..d1a25cc17fd1 --- /dev/null +++ b/include/drm/drm_file.h @@ -0,0 +1,172 @@ +/* + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * Copyright (c) 2009-2010, Code Aurora Forum. + * All rights reserved. + * + * Author: Rickard E. (Rik) Faith + * Author: Gareth Hughes + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _DRM_FILE_H_ +#define _DRM_FILE_H_ + +#include +#include + +#include + +#include + +struct dma_fence; +struct drm_file; +struct drm_device; + +/* + * FIXME: Not sure we want to have drm_minor here in the end, but to avoid + * header include loops we need it here for now. + */ +enum drm_minor_type { + DRM_MINOR_PRIMARY, + DRM_MINOR_CONTROL, + DRM_MINOR_RENDER, +}; + +/** + * DRM minor structure. This structure represents a drm minor number. + */ +struct drm_minor { + int index; /**< Minor device number */ + int type; /**< Control or render */ + struct device *kdev; /**< Linux device */ + struct drm_device *dev; + + struct dentry *debugfs_root; + + struct list_head debugfs_list; + struct mutex debugfs_lock; /* Protects debugfs_list. */ +}; + +/* Event queued up for userspace to read */ +struct drm_pending_event { + struct completion *completion; + void (*completion_release)(struct completion *completion); + struct drm_event *event; + struct dma_fence *fence; + struct list_head link; + struct list_head pending_link; + struct drm_file *file_priv; + pid_t pid; /* pid of requester, no guarantee it's valid by the time + we deliver the event, for tracing only */ +}; + +/** File private data */ +struct drm_file { + unsigned authenticated :1; + /* true when the client has asked us to expose stereo 3D mode flags */ + unsigned stereo_allowed :1; + /* + * true if client understands CRTC primary planes and cursor planes + * in the plane list + */ + unsigned universal_planes:1; + /* true if client understands atomic properties */ + unsigned atomic:1; + /* + * This client is the creator of @master. + * Protected by struct drm_device::master_mutex. + */ + unsigned is_master:1; + + struct pid *pid; + drm_magic_t magic; + struct list_head lhead; + struct drm_minor *minor; + unsigned long lock_count; + + /** Mapping of mm object handles to object pointers. */ + struct idr object_idr; + /** Lock for synchronization of access to object_idr. */ + spinlock_t table_lock; + + struct file *filp; + void *driver_priv; + + struct drm_master *master; /* master this node is currently associated with + N.B. not always dev->master */ + /** + * fbs - List of framebuffers associated with this file. + * + * Protected by fbs_lock. Note that the fbs list holds a reference on + * the fb object to prevent it from untimely disappearing. + */ + struct list_head fbs; + struct mutex fbs_lock; + + /** User-created blob properties; this retains a reference on the + * property. */ + struct list_head blobs; + + wait_queue_head_t event_wait; + struct list_head pending_event_list; + struct list_head event_list; + int event_space; + + struct mutex event_read_lock; + + struct drm_prime_file_private prime; +}; + +static inline bool drm_is_render_client(const struct drm_file *file_priv) +{ + return file_priv->minor->type == DRM_MINOR_RENDER; +} + +static inline bool drm_is_control_client(const struct drm_file *file_priv) +{ + return file_priv->minor->type == DRM_MINOR_CONTROL; +} + +static inline bool drm_is_primary_client(const struct drm_file *file_priv) +{ + return file_priv->minor->type == DRM_MINOR_PRIMARY; +} + +int drm_open(struct inode *inode, struct file *filp); +ssize_t drm_read(struct file *filp, char __user *buffer, + size_t count, loff_t *offset); +int drm_release(struct inode *inode, struct file *filp); +unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait); +int drm_event_reserve_init_locked(struct drm_device *dev, + struct drm_file *file_priv, + struct drm_pending_event *p, + struct drm_event *e); +int drm_event_reserve_init(struct drm_device *dev, + struct drm_file *file_priv, + struct drm_pending_event *p, + struct drm_event *e); +void drm_event_cancel_free(struct drm_device *dev, + struct drm_pending_event *p); +void drm_send_event_locked(struct drm_device *dev, struct drm_pending_event *e); +void drm_send_event(struct drm_device *dev, struct drm_pending_event *e); + +#endif /* _DRM_FILE_H_ */ diff --git a/include/drm/drm_prime.h b/include/drm/drm_prime.h index 77ceca711bad..d09563ecc4b7 100644 --- a/include/drm/drm_prime.h +++ b/include/drm/drm_prime.h @@ -34,6 +34,7 @@ #include #include +#include /** * struct drm_prime_file_private - per-file tracking for PRIME -- cgit v1.2.3 From f1585f6e29f5aba34e2cd6e3db9f0dd33b046809 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Mar 2017 01:36:15 +0200 Subject: drm: bridge: dw-hdmi: Create PHY operations The HDMI TX controller support different PHYs whose programming interface can vary significantly, especially with vendor PHYs that are not provided by Synopsys. To support them, create a PHY operation structure that can be provided by the platform glue layer. The existing PHY handling code (limited to Synopsys PHY support) is refactored into a set of default PHY operations that are used automatically when the platform glue doesn't provide its own operations. Signed-off-by: Laurent Pinchart Tested-by: Neil Armstrong Reviewed-by: Jose Abreu Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/20170305233615.11993-1-laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/bridge/dw-hdmi.c | 95 ++++++++++++++++++++++++++++------------ include/drm/bridge/dw_hdmi.h | 18 +++++++- 2 files changed, 82 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c index c25eac8ba47b..cb2703862be2 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/dw-hdmi.c @@ -141,8 +141,12 @@ struct dw_hdmi { u8 edid[HDMI_EDID_LEN]; bool cable_plugin; - const struct dw_hdmi_phy_data *phy; - bool phy_enabled; + struct { + const struct dw_hdmi_phy_ops *ops; + const char *name; + void *data; + bool enabled; + } phy; struct drm_display_mode previous_mode; @@ -831,6 +835,10 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi) HDMI_VP_CONF); } +/* ----------------------------------------------------------------------------- + * Synopsys PHY Handling + */ + static inline void hdmi_phy_test_clear(struct dw_hdmi *hdmi, unsigned char bit) { @@ -917,7 +925,7 @@ static void dw_hdmi_phy_sel_interface_control(struct dw_hdmi *hdmi, u8 enable) static void dw_hdmi_phy_power_off(struct dw_hdmi *hdmi) { - const struct dw_hdmi_phy_data *phy = hdmi->phy; + const struct dw_hdmi_phy_data *phy = hdmi->phy.data; unsigned int i; u16 val; @@ -951,7 +959,7 @@ static void dw_hdmi_phy_power_off(struct dw_hdmi *hdmi) static int dw_hdmi_phy_power_on(struct dw_hdmi *hdmi) { - const struct dw_hdmi_phy_data *phy = hdmi->phy; + const struct dw_hdmi_phy_data *phy = hdmi->phy.data; unsigned int i; u8 val; @@ -987,6 +995,7 @@ static int dw_hdmi_phy_power_on(struct dw_hdmi *hdmi) static int hdmi_phy_configure(struct dw_hdmi *hdmi) { + const struct dw_hdmi_phy_data *phy = hdmi->phy.data; const struct dw_hdmi_plat_data *pdata = hdmi->plat_data; const struct dw_hdmi_mpll_config *mpll_config = pdata->mpll_cfg; const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr; @@ -1019,7 +1028,7 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi) dw_hdmi_phy_power_off(hdmi); /* Leave low power consumption mode by asserting SVSRET. */ - if (hdmi->phy->has_svsret) + if (phy->has_svsret) dw_hdmi_phy_enable_svsret(hdmi, 1); /* PHY reset. The reset signal is active high on Gen2 PHYs. */ @@ -1057,7 +1066,8 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi) return dw_hdmi_phy_power_on(hdmi); } -static int dw_hdmi_phy_init(struct dw_hdmi *hdmi) +static int dw_hdmi_phy_init(struct dw_hdmi *hdmi, void *data, + struct drm_display_mode *mode) { int i, ret; @@ -1071,10 +1081,31 @@ static int dw_hdmi_phy_init(struct dw_hdmi *hdmi) return ret; } - hdmi->phy_enabled = true; return 0; } +static void dw_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data) +{ + dw_hdmi_phy_power_off(hdmi); +} + +static enum drm_connector_status dw_hdmi_phy_read_hpd(struct dw_hdmi *hdmi, + void *data) +{ + return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ? + connector_status_connected : connector_status_disconnected; +} + +static const struct dw_hdmi_phy_ops dw_hdmi_synopsys_phy_ops = { + .init = dw_hdmi_phy_init, + .disable = dw_hdmi_phy_disable, + .read_hpd = dw_hdmi_phy_read_hpd, +}; + +/* ----------------------------------------------------------------------------- + * HDMI TX Setup + */ + static void hdmi_tx_hdcp_config(struct dw_hdmi *hdmi) { u8 de; @@ -1289,16 +1320,6 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi, hdmi_writeb(hdmi, vsync_len, HDMI_FC_VSYNCINWIDTH); } -static void dw_hdmi_phy_disable(struct dw_hdmi *hdmi) -{ - if (!hdmi->phy_enabled) - return; - - dw_hdmi_phy_power_off(hdmi); - - hdmi->phy_enabled = false; -} - /* HDMI Initialization Step B.4 */ static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi) { @@ -1431,9 +1452,10 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode) hdmi_av_composer(hdmi, mode); /* HDMI Initializateion Step B.2 */ - ret = dw_hdmi_phy_init(hdmi); + ret = hdmi->phy.ops->init(hdmi, hdmi->phy.data, &hdmi->previous_mode); if (ret) return ret; + hdmi->phy.enabled = true; /* HDMI Initialization Step B.3 */ dw_hdmi_enable_video_path(hdmi); @@ -1548,7 +1570,11 @@ static void dw_hdmi_poweron(struct dw_hdmi *hdmi) static void dw_hdmi_poweroff(struct dw_hdmi *hdmi) { - dw_hdmi_phy_disable(hdmi); + if (hdmi->phy.enabled) { + hdmi->phy.ops->disable(hdmi, hdmi->phy.data); + hdmi->phy.enabled = false; + } + hdmi->bridge_is_on = false; } @@ -1611,8 +1637,7 @@ dw_hdmi_connector_detect(struct drm_connector *connector, bool force) dw_hdmi_update_phy_mask(hdmi); mutex_unlock(&hdmi->mutex); - return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ? - connector_status_connected : connector_status_disconnected; + return hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data); } static int dw_hdmi_connector_get_modes(struct drm_connector *connector) @@ -1898,19 +1923,31 @@ static int dw_hdmi_detect_phy(struct dw_hdmi *hdmi) phy_type = hdmi_readb(hdmi, HDMI_CONFIG2_ID); + if (phy_type == DW_HDMI_PHY_VENDOR_PHY) { + /* Vendor PHYs require support from the glue layer. */ + if (!hdmi->plat_data->phy_ops || !hdmi->plat_data->phy_name) { + dev_err(hdmi->dev, + "Vendor HDMI PHY not supported by glue layer\n"); + return -ENODEV; + } + + hdmi->phy.ops = hdmi->plat_data->phy_ops; + hdmi->phy.data = hdmi->plat_data->phy_data; + hdmi->phy.name = hdmi->plat_data->phy_name; + return 0; + } + + /* Synopsys PHYs are handled internally. */ for (i = 0; i < ARRAY_SIZE(dw_hdmi_phys); ++i) { if (dw_hdmi_phys[i].type == phy_type) { - hdmi->phy = &dw_hdmi_phys[i]; + hdmi->phy.ops = &dw_hdmi_synopsys_phy_ops; + hdmi->phy.name = dw_hdmi_phys[i].name; + hdmi->phy.data = (void *)&dw_hdmi_phys[i]; return 0; } } - if (phy_type == DW_HDMI_PHY_VENDOR_PHY) - dev_err(hdmi->dev, "Unsupported vendor HDMI PHY\n"); - else - dev_err(hdmi->dev, "Unsupported HDMI PHY type (%02x)\n", - phy_type); - + dev_err(hdmi->dev, "Unsupported HDMI PHY type (%02x)\n", phy_type); return -ENODEV; } @@ -2031,7 +2068,7 @@ __dw_hdmi_probe(struct platform_device *pdev, dev_info(dev, "Detected HDMI TX controller v%x.%03x %s HDCP (%s)\n", hdmi->version >> 12, hdmi->version & 0xfff, prod_id1 & HDMI_PRODUCT_ID1_HDCP ? "with" : "without", - hdmi->phy->name); + hdmi->phy.name); initialize_hdmi_ih_mutes(hdmi); diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h index b080a171a23f..0f583ca7e66e 100644 --- a/include/drm/bridge/dw_hdmi.h +++ b/include/drm/bridge/dw_hdmi.h @@ -57,13 +57,27 @@ struct dw_hdmi_phy_config { u16 vlev_ctr; /* voltage level control */ }; +struct dw_hdmi_phy_ops { + int (*init)(struct dw_hdmi *hdmi, void *data, + struct drm_display_mode *mode); + void (*disable)(struct dw_hdmi *hdmi, void *data); + enum drm_connector_status (*read_hpd)(struct dw_hdmi *hdmi, void *data); +}; + struct dw_hdmi_plat_data { enum dw_hdmi_devtype dev_type; + enum drm_mode_status (*mode_valid)(struct drm_connector *connector, + struct drm_display_mode *mode); + + /* Vendor PHY support */ + const struct dw_hdmi_phy_ops *phy_ops; + const char *phy_name; + void *phy_data; + + /* Synopsys PHY support */ const struct dw_hdmi_mpll_config *mpll_cfg; const struct dw_hdmi_curr_ctrl *cur_ctr; const struct dw_hdmi_phy_config *phy_config; - enum drm_mode_status (*mode_valid)(struct drm_connector *connector, - struct drm_display_mode *mode); }; int dw_hdmi_probe(struct platform_device *pdev, -- cgit v1.2.3 From 2ef9dfedefd60a12d2b02b1ee0a42be3506c43f2 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Fri, 3 Mar 2017 19:20:04 +0200 Subject: drm: bridge: dw-hdmi: Add support for custom PHY configuration The DWC HDMI TX controller interfaces with a companion PHY. While Synopsys provides multiple standard PHYs, SoC vendors can also integrate a custom PHY. Modularize PHY configuration to support vendor PHYs through platform data. The existing PHY configuration code was originally written to support the DWC HDMI 3D TX PHY, and seems to be compatible with the DWC MLP PHY. The HDMI 2.0 PHY will require a separate configuration function. Signed-off-by: Kieran Bingham Signed-off-by: Laurent Pinchart Tested-by: Neil Armstrong Reviewed-by: Jose Abreu Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/20170303172007.26541-8-laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/bridge/dw-hdmi.c | 109 ++++++++++++++++++++++++++------------- include/drm/bridge/dw_hdmi.h | 7 +++ 2 files changed, 81 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c index cb2703862be2..b835d81bb471 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/dw-hdmi.c @@ -118,6 +118,9 @@ struct dw_hdmi_phy_data { const char *name; unsigned int gen; bool has_svsret; + int (*configure)(struct dw_hdmi *hdmi, + const struct dw_hdmi_plat_data *pdata, + unsigned long mpixelclock); }; struct dw_hdmi { @@ -860,8 +863,8 @@ static bool hdmi_phy_wait_i2c_done(struct dw_hdmi *hdmi, int msec) return true; } -static void hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data, - unsigned char addr) +void dw_hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data, + unsigned char addr) { hdmi_writeb(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0); hdmi_writeb(hdmi, addr, HDMI_PHY_I2CM_ADDRESS_ADDR); @@ -873,6 +876,7 @@ static void hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data, HDMI_PHY_I2CM_OPERATION_ADDR); hdmi_phy_wait_i2c_done(hdmi, 1000); } +EXPORT_SYMBOL_GPL(dw_hdmi_phy_i2c_write); static void dw_hdmi_phy_enable_powerdown(struct dw_hdmi *hdmi, bool enable) { @@ -993,37 +997,67 @@ static int dw_hdmi_phy_power_on(struct dw_hdmi *hdmi) return 0; } -static int hdmi_phy_configure(struct dw_hdmi *hdmi) +/* + * PHY configuration function for the DWC HDMI 3D TX PHY. Based on the available + * information the DWC MHL PHY has the same register layout and is thus also + * supported by this function. + */ +static int hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi, + const struct dw_hdmi_plat_data *pdata, + unsigned long mpixelclock) { - const struct dw_hdmi_phy_data *phy = hdmi->phy.data; - const struct dw_hdmi_plat_data *pdata = hdmi->plat_data; const struct dw_hdmi_mpll_config *mpll_config = pdata->mpll_cfg; const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr; const struct dw_hdmi_phy_config *phy_config = pdata->phy_config; /* PLL/MPLL Cfg - always match on final entry */ for (; mpll_config->mpixelclock != ~0UL; mpll_config++) - if (hdmi->hdmi_data.video_mode.mpixelclock <= - mpll_config->mpixelclock) + if (mpixelclock <= mpll_config->mpixelclock) break; for (; curr_ctrl->mpixelclock != ~0UL; curr_ctrl++) - if (hdmi->hdmi_data.video_mode.mpixelclock <= - curr_ctrl->mpixelclock) + if (mpixelclock <= curr_ctrl->mpixelclock) break; for (; phy_config->mpixelclock != ~0UL; phy_config++) - if (hdmi->hdmi_data.video_mode.mpixelclock <= - phy_config->mpixelclock) + if (mpixelclock <= phy_config->mpixelclock) break; if (mpll_config->mpixelclock == ~0UL || curr_ctrl->mpixelclock == ~0UL || - phy_config->mpixelclock == ~0UL) { - dev_err(hdmi->dev, "Pixel clock %d - unsupported by HDMI\n", - hdmi->hdmi_data.video_mode.mpixelclock); + phy_config->mpixelclock == ~0UL) return -EINVAL; - } + + dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[0].cpce, + HDMI_3D_TX_PHY_CPCE_CTRL); + dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[0].gmp, + HDMI_3D_TX_PHY_GMPCTRL); + dw_hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[0], + HDMI_3D_TX_PHY_CURRCTRL); + + dw_hdmi_phy_i2c_write(hdmi, 0, HDMI_3D_TX_PHY_PLLPHBYCTRL); + dw_hdmi_phy_i2c_write(hdmi, HDMI_3D_TX_PHY_MSM_CTRL_CKO_SEL_FB_CLK, + HDMI_3D_TX_PHY_MSM_CTRL); + + dw_hdmi_phy_i2c_write(hdmi, phy_config->term, HDMI_3D_TX_PHY_TXTERM); + dw_hdmi_phy_i2c_write(hdmi, phy_config->sym_ctr, + HDMI_3D_TX_PHY_CKSYMTXCTRL); + dw_hdmi_phy_i2c_write(hdmi, phy_config->vlev_ctr, + HDMI_3D_TX_PHY_VLEVCTRL); + + /* Override and disable clock termination. */ + dw_hdmi_phy_i2c_write(hdmi, HDMI_3D_TX_PHY_CKCALCTRL_OVERRIDE, + HDMI_3D_TX_PHY_CKCALCTRL); + + return 0; +} + +static int hdmi_phy_configure(struct dw_hdmi *hdmi) +{ + const struct dw_hdmi_phy_data *phy = hdmi->phy.data; + const struct dw_hdmi_plat_data *pdata = hdmi->plat_data; + unsigned long mpixelclock = hdmi->hdmi_data.video_mode.mpixelclock; + int ret; dw_hdmi_phy_power_off(hdmi); @@ -1042,26 +1076,16 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi) HDMI_PHY_I2CM_SLAVE_ADDR); hdmi_phy_test_clear(hdmi, 0); - hdmi_phy_i2c_write(hdmi, mpll_config->res[0].cpce, - HDMI_3D_TX_PHY_CPCE_CTRL); - hdmi_phy_i2c_write(hdmi, mpll_config->res[0].gmp, - HDMI_3D_TX_PHY_GMPCTRL); - hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[0], - HDMI_3D_TX_PHY_CURRCTRL); - - hdmi_phy_i2c_write(hdmi, 0, HDMI_3D_TX_PHY_PLLPHBYCTRL); - hdmi_phy_i2c_write(hdmi, HDMI_3D_TX_PHY_MSM_CTRL_CKO_SEL_FB_CLK, - HDMI_3D_TX_PHY_MSM_CTRL); - - hdmi_phy_i2c_write(hdmi, phy_config->term, HDMI_3D_TX_PHY_TXTERM); - hdmi_phy_i2c_write(hdmi, phy_config->sym_ctr, - HDMI_3D_TX_PHY_CKSYMTXCTRL); - hdmi_phy_i2c_write(hdmi, phy_config->vlev_ctr, - HDMI_3D_TX_PHY_VLEVCTRL); - - /* Override and disable clock termination. */ - hdmi_phy_i2c_write(hdmi, HDMI_3D_TX_PHY_CKCALCTRL_OVERRIDE, - HDMI_3D_TX_PHY_CKCALCTRL); + /* Write to the PHY as configured by the platform */ + if (pdata->configure_phy) + ret = pdata->configure_phy(hdmi, pdata, mpixelclock); + else + ret = phy->configure(hdmi, pdata, mpixelclock); + if (ret) { + dev_err(hdmi->dev, "PHY configuration failed (clock %lu)\n", + mpixelclock); + return ret; + } return dw_hdmi_phy_power_on(hdmi); } @@ -1895,24 +1919,31 @@ static const struct dw_hdmi_phy_data dw_hdmi_phys[] = { .name = "DWC MHL PHY + HEAC PHY", .gen = 2, .has_svsret = true, + .configure = hdmi_phy_configure_dwc_hdmi_3d_tx, }, { .type = DW_HDMI_PHY_DWC_MHL_PHY, .name = "DWC MHL PHY", .gen = 2, .has_svsret = true, + .configure = hdmi_phy_configure_dwc_hdmi_3d_tx, }, { .type = DW_HDMI_PHY_DWC_HDMI_3D_TX_PHY_HEAC, .name = "DWC HDMI 3D TX PHY + HEAC PHY", .gen = 2, + .configure = hdmi_phy_configure_dwc_hdmi_3d_tx, }, { .type = DW_HDMI_PHY_DWC_HDMI_3D_TX_PHY, .name = "DWC HDMI 3D TX PHY", .gen = 2, + .configure = hdmi_phy_configure_dwc_hdmi_3d_tx, }, { .type = DW_HDMI_PHY_DWC_HDMI20_TX_PHY, .name = "DWC HDMI 2.0 TX PHY", .gen = 2, .has_svsret = true, + }, { + .type = DW_HDMI_PHY_VENDOR_PHY, + .name = "Vendor PHY", } }; @@ -1943,6 +1974,14 @@ static int dw_hdmi_detect_phy(struct dw_hdmi *hdmi) hdmi->phy.ops = &dw_hdmi_synopsys_phy_ops; hdmi->phy.name = dw_hdmi_phys[i].name; hdmi->phy.data = (void *)&dw_hdmi_phys[i]; + + if (!dw_hdmi_phys[i].configure && + !hdmi->plat_data->configure_phy) { + dev_err(hdmi->dev, "%s requires platform support\n", + hdmi->phy.name); + return -ENODEV; + } + return 0; } } diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h index 0f583ca7e66e..dd330259a3dc 100644 --- a/include/drm/bridge/dw_hdmi.h +++ b/include/drm/bridge/dw_hdmi.h @@ -78,6 +78,9 @@ struct dw_hdmi_plat_data { const struct dw_hdmi_mpll_config *mpll_cfg; const struct dw_hdmi_curr_ctrl *cur_ctr; const struct dw_hdmi_phy_config *phy_config; + int (*configure_phy)(struct dw_hdmi *hdmi, + const struct dw_hdmi_plat_data *pdata, + unsigned long mpixelclock); }; int dw_hdmi_probe(struct platform_device *pdev, @@ -91,4 +94,8 @@ void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate); void dw_hdmi_audio_enable(struct dw_hdmi *hdmi); void dw_hdmi_audio_disable(struct dw_hdmi *hdmi); +/* PHY configuration */ +void dw_hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data, + unsigned char addr); + #endif /* __IMX_HDMI_H__ */ -- cgit v1.2.3 From 2e6777e8d5dd883b983c8de8797ff92bcb158f7c Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Fri, 3 Mar 2017 19:20:05 +0200 Subject: drm: bridge: dw-hdmi: Remove device type from platform data The device type isn't used anymore now that workarounds and PHY-specific operations are performed based on version information read at runtime. Remove it. Signed-off-by: Kieran Bingham Signed-off-by: Laurent Pinchart Tested-by: Neil Armstrong Reviewed-by: Jose Abreu Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/20170303172007.26541-9-laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/bridge/dw-hdmi.c | 2 -- drivers/gpu/drm/imx/dw_hdmi-imx.c | 2 -- drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 1 - include/drm/bridge/dw_hdmi.h | 7 ------- 4 files changed, 12 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c index b835d81bb471..132c00685796 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/dw-hdmi.c @@ -127,7 +127,6 @@ struct dw_hdmi { struct drm_connector connector; struct drm_bridge bridge; - enum dw_hdmi_devtype dev_type; unsigned int version; struct platform_device *audio; @@ -2014,7 +2013,6 @@ __dw_hdmi_probe(struct platform_device *pdev, hdmi->plat_data = plat_data; hdmi->dev = dev; - hdmi->dev_type = plat_data->dev_type; hdmi->sample_rate = 48000; hdmi->disabled = true; hdmi->rxsense = true; diff --git a/drivers/gpu/drm/imx/dw_hdmi-imx.c b/drivers/gpu/drm/imx/dw_hdmi-imx.c index f645275e6e63..f039641070ac 100644 --- a/drivers/gpu/drm/imx/dw_hdmi-imx.c +++ b/drivers/gpu/drm/imx/dw_hdmi-imx.c @@ -175,7 +175,6 @@ static struct dw_hdmi_plat_data imx6q_hdmi_drv_data = { .mpll_cfg = imx_mpll_cfg, .cur_ctr = imx_cur_ctr, .phy_config = imx_phy_config, - .dev_type = IMX6Q_HDMI, .mode_valid = imx6q_hdmi_mode_valid, }; @@ -183,7 +182,6 @@ static struct dw_hdmi_plat_data imx6dl_hdmi_drv_data = { .mpll_cfg = imx_mpll_cfg, .cur_ctr = imx_cur_ctr, .phy_config = imx_phy_config, - .dev_type = IMX6DL_HDMI, .mode_valid = imx6dl_hdmi_mode_valid, }; diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c index a6d4a0236e8f..d53827413996 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -237,7 +237,6 @@ static const struct dw_hdmi_plat_data rockchip_hdmi_drv_data = { .mpll_cfg = rockchip_mpll_cfg, .cur_ctr = rockchip_cur_ctr, .phy_config = rockchip_phy_config, - .dev_type = RK3288_HDMI, }; static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = { diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h index dd330259a3dc..545f04fae3b6 100644 --- a/include/drm/bridge/dw_hdmi.h +++ b/include/drm/bridge/dw_hdmi.h @@ -21,12 +21,6 @@ enum { DW_HDMI_RES_MAX, }; -enum dw_hdmi_devtype { - IMX6Q_HDMI, - IMX6DL_HDMI, - RK3288_HDMI, -}; - enum dw_hdmi_phy_type { DW_HDMI_PHY_DWC_HDMI_TX_PHY = 0x00, DW_HDMI_PHY_DWC_MHL_PHY_HEAC = 0xb2, @@ -65,7 +59,6 @@ struct dw_hdmi_phy_ops { }; struct dw_hdmi_plat_data { - enum dw_hdmi_devtype dev_type; enum drm_mode_status (*mode_valid)(struct drm_connector *connector, struct drm_display_mode *mode); -- cgit v1.2.3 From 80e2f97968b537fc9c1789fce1b7f61609a5aae4 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Fri, 3 Mar 2017 19:20:06 +0200 Subject: drm: bridge: dw-hdmi: Switch to regmap for register access The Synopsys Designware HDMI TX Controller does not enforce register access on platforms instanciating it. The current driver supports two different types of memory-mapped flat register access, but in order to support the Amlogic Meson SoCs integration, and provide a more generic way to handle all sorts of register mapping, switch the register access to use the regmap infrastructure. In the case of registers that are not flat memory-mapped or do not conform to the current driver implementation, a regmap struct can be given in the plat_data and be used at probe or bind. Since the AHB audio driver is only available with direct memory access, only allow the I2S audio driver to be registered is directly memory-mapped. Signed-off-by: Neil Armstrong Reviewed-by: Laurent Pinchart Tested-by: Laurent Pinchart Tested-by: Neil Armstrong Reviewed-by: Jose Abreu Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/20170303172007.26541-10-laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/bridge/dw-hdmi.c | 109 +++++++++++++++++++++------------------ include/drm/bridge/dw_hdmi.h | 1 + 2 files changed, 59 insertions(+), 51 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c index 132c00685796..026a0dce7661 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/dw-hdmi.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -171,8 +172,8 @@ struct dw_hdmi { unsigned int audio_n; bool audio_enable; - void (*write)(struct dw_hdmi *hdmi, u8 val, int offset); - u8 (*read)(struct dw_hdmi *hdmi, int offset); + unsigned int reg_shift; + struct regmap *regm; }; #define HDMI_IH_PHY_STAT0_RX_SENSE \ @@ -183,42 +184,23 @@ struct dw_hdmi { (HDMI_PHY_RX_SENSE0 | HDMI_PHY_RX_SENSE1 | \ HDMI_PHY_RX_SENSE2 | HDMI_PHY_RX_SENSE3) -static void dw_hdmi_writel(struct dw_hdmi *hdmi, u8 val, int offset) -{ - writel(val, hdmi->regs + (offset << 2)); -} - -static u8 dw_hdmi_readl(struct dw_hdmi *hdmi, int offset) -{ - return readl(hdmi->regs + (offset << 2)); -} - -static void dw_hdmi_writeb(struct dw_hdmi *hdmi, u8 val, int offset) -{ - writeb(val, hdmi->regs + offset); -} - -static u8 dw_hdmi_readb(struct dw_hdmi *hdmi, int offset) -{ - return readb(hdmi->regs + offset); -} - static inline void hdmi_writeb(struct dw_hdmi *hdmi, u8 val, int offset) { - hdmi->write(hdmi, val, offset); + regmap_write(hdmi->regm, offset << hdmi->reg_shift, val); } static inline u8 hdmi_readb(struct dw_hdmi *hdmi, int offset) { - return hdmi->read(hdmi, offset); + unsigned int val = 0; + + regmap_read(hdmi->regm, offset << hdmi->reg_shift, &val); + + return val; } static void hdmi_modb(struct dw_hdmi *hdmi, u8 data, u8 mask, unsigned reg) { - u8 val = hdmi_readb(hdmi, reg) & ~mask; - - val |= data & mask; - hdmi_writeb(hdmi, val, reg); + regmap_update_bits(hdmi->regm, reg << hdmi->reg_shift, mask, data); } static void hdmi_mask_writeb(struct dw_hdmi *hdmi, u8 data, unsigned int reg, @@ -1989,6 +1971,20 @@ static int dw_hdmi_detect_phy(struct dw_hdmi *hdmi) return -ENODEV; } +static const struct regmap_config hdmi_regmap_8bit_config = { + .reg_bits = 32, + .val_bits = 8, + .reg_stride = 1, + .max_register = HDMI_I2CM_FS_SCL_LCNT_0_ADDR, +}; + +static const struct regmap_config hdmi_regmap_32bit_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = HDMI_I2CM_FS_SCL_LCNT_0_ADDR << 2, +}; + static struct dw_hdmi * __dw_hdmi_probe(struct platform_device *pdev, const struct dw_hdmi_plat_data *plat_data) @@ -1998,7 +1994,7 @@ __dw_hdmi_probe(struct platform_device *pdev, struct platform_device_info pdevinfo; struct device_node *ddc_node; struct dw_hdmi *hdmi; - struct resource *iores; + struct resource *iores = NULL; int irq; int ret; u32 val = 1; @@ -2022,22 +2018,6 @@ __dw_hdmi_probe(struct platform_device *pdev, mutex_init(&hdmi->audio_mutex); spin_lock_init(&hdmi->audio_lock); - of_property_read_u32(np, "reg-io-width", &val); - - switch (val) { - case 4: - hdmi->write = dw_hdmi_writel; - hdmi->read = dw_hdmi_readl; - break; - case 1: - hdmi->write = dw_hdmi_writeb; - hdmi->read = dw_hdmi_readb; - break; - default: - dev_err(dev, "reg-io-width must be 1 or 4\n"); - return ERR_PTR(-EINVAL); - } - ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0); if (ddc_node) { hdmi->ddc = of_get_i2c_adapter_by_node(ddc_node); @@ -2051,11 +2031,38 @@ __dw_hdmi_probe(struct platform_device *pdev, dev_dbg(hdmi->dev, "no ddc property found\n"); } - iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hdmi->regs = devm_ioremap_resource(dev, iores); - if (IS_ERR(hdmi->regs)) { - ret = PTR_ERR(hdmi->regs); - goto err_res; + if (!plat_data->regm) { + const struct regmap_config *reg_config; + + of_property_read_u32(np, "reg-io-width", &val); + switch (val) { + case 4: + reg_config = &hdmi_regmap_32bit_config; + hdmi->reg_shift = 2; + break; + case 1: + reg_config = &hdmi_regmap_8bit_config; + break; + default: + dev_err(dev, "reg-io-width must be 1 or 4\n"); + return ERR_PTR(-EINVAL); + } + + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); + hdmi->regs = devm_ioremap_resource(dev, iores); + if (IS_ERR(hdmi->regs)) { + ret = PTR_ERR(hdmi->regs); + goto err_res; + } + + hdmi->regm = devm_regmap_init_mmio(dev, hdmi->regs, reg_config); + if (IS_ERR(hdmi->regm)) { + dev_err(dev, "Failed to configure regmap\n"); + ret = PTR_ERR(hdmi->regm); + goto err_res; + } + } else { + hdmi->regm = plat_data->regm; } hdmi->isfr_clk = devm_clk_get(hdmi->dev, "isfr"); @@ -2165,7 +2172,7 @@ __dw_hdmi_probe(struct platform_device *pdev, config0 = hdmi_readb(hdmi, HDMI_CONFIG0_ID); config3 = hdmi_readb(hdmi, HDMI_CONFIG3_ID); - if (config3 & HDMI_CONFIG3_AHBAUDDMA) { + if (iores && config3 & HDMI_CONFIG3_AHBAUDDMA) { struct dw_hdmi_audio_data audio; audio.phys = iores->start; diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h index 545f04fae3b6..bcceee8114a4 100644 --- a/include/drm/bridge/dw_hdmi.h +++ b/include/drm/bridge/dw_hdmi.h @@ -59,6 +59,7 @@ struct dw_hdmi_phy_ops { }; struct dw_hdmi_plat_data { + struct regmap *regm; enum drm_mode_status (*mode_valid)(struct drm_connector *connector, struct drm_display_mode *mode); -- cgit v1.2.3