diff options
Diffstat (limited to 'drivers')
84 files changed, 2709 insertions, 1448 deletions
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index bc6a16a3c36e..576ba985e138 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -36,7 +36,8 @@ drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \ drm_plane_helper.o drm_dp_mst_topology.o drm_atomic_helper.o \ drm_kms_helper_common.o drm_dp_dual_mode_helper.o \ drm_simple_kms_helper.o drm_modeset_helper.o \ - drm_scdc_helper.o drm_gem_framebuffer_helper.o + drm_scdc_helper.o drm_gem_framebuffer_helper.o \ + drm_atomic_state_helper.o drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index 663043c8f0f5..8e9a65a15875 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -1104,7 +1104,7 @@ static int amdgpu_syncobj_lookup_and_add_to_sync(struct amdgpu_cs_parser *p, { int r; struct dma_fence *fence; - r = drm_syncobj_find_fence(p->filp, handle, 0, &fence); + r = drm_syncobj_find_fence(p->filp, handle, 0, 0, &fence); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c index e45e929aaab5..3e44d889f7af 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c @@ -339,8 +339,6 @@ static const struct dma_buf_ops amdgpu_dmabuf_ops = { .unmap_dma_buf = drm_gem_unmap_dma_buf, .release = drm_gem_dmabuf_release, .begin_cpu_access = amdgpu_gem_begin_cpu_access, - .map = drm_gem_dmabuf_kmap, - .unmap = drm_gem_dmabuf_kunmap, .mmap = drm_gem_dmabuf_mmap, .vmap = drm_gem_dmabuf_vmap, .vunmap = drm_gem_dmabuf_vunmap, diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index e224f23e2215..e5294d1a3049 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -3189,7 +3189,6 @@ amdgpu_dm_connector_helper_funcs = { */ .get_modes = get_modes, .mode_valid = amdgpu_dm_connector_mode_valid, - .best_encoder = drm_atomic_helper_best_encoder }; static void dm_crtc_helper_disable(struct drm_crtc *crtc) @@ -3592,14 +3591,17 @@ static int to_drm_connector_type(enum signal_type st) } } +static struct drm_encoder *amdgpu_dm_connector_to_encoder(struct drm_connector *connector) +{ + return drm_encoder_find(connector->dev, NULL, connector->encoder_ids[0]); +} + static void amdgpu_dm_get_native_mode(struct drm_connector *connector) { - const struct drm_connector_helper_funcs *helper = - connector->helper_private; struct drm_encoder *encoder; struct amdgpu_encoder *amdgpu_encoder; - encoder = helper->best_encoder(connector); + encoder = amdgpu_dm_connector_to_encoder(connector); if (encoder == NULL) return; @@ -3726,14 +3728,12 @@ static void amdgpu_dm_connector_ddc_get_modes(struct drm_connector *connector, static int amdgpu_dm_connector_get_modes(struct drm_connector *connector) { - const struct drm_connector_helper_funcs *helper = - connector->helper_private; struct amdgpu_dm_connector *amdgpu_dm_connector = to_amdgpu_dm_connector(connector); struct drm_encoder *encoder; struct edid *edid = amdgpu_dm_connector->edid; - encoder = helper->best_encoder(connector); + encoder = amdgpu_dm_connector_to_encoder(connector); if (!edid || !drm_edid_is_valid(edid)) { amdgpu_dm_connector->num_modes = diff --git a/drivers/gpu/drm/arc/arcpgu_crtc.c b/drivers/gpu/drm/arc/arcpgu_crtc.c index 965cda48dc13..62f51f70606d 100644 --- a/drivers/gpu/drm/arc/arcpgu_crtc.c +++ b/drivers/gpu/drm/arc/arcpgu_crtc.c @@ -158,8 +158,6 @@ static void arc_pgu_crtc_atomic_begin(struct drm_crtc *crtc, static const struct drm_crtc_helper_funcs arc_pgu_crtc_helper_funcs = { .mode_valid = arc_pgu_crtc_mode_valid, - .mode_set = drm_helper_crtc_mode_set, - .mode_set_base = drm_helper_crtc_mode_set_base, .mode_set_nofb = arc_pgu_crtc_mode_set_nofb, .atomic_begin = arc_pgu_crtc_atomic_begin, .atomic_enable = arc_pgu_crtc_atomic_enable, @@ -186,7 +184,6 @@ static const struct drm_plane_helper_funcs arc_pgu_plane_helper_funcs = { static void arc_pgu_plane_destroy(struct drm_plane *plane) { - drm_plane_helper_disable(plane, NULL); drm_plane_cleanup(plane); } diff --git a/drivers/gpu/drm/arc/arcpgu_drv.c b/drivers/gpu/drm/arc/arcpgu_drv.c index f067de4e1e82..e85e3a349f24 100644 --- a/drivers/gpu/drm/arc/arcpgu_drv.c +++ b/drivers/gpu/drm/arc/arcpgu_drv.c @@ -134,6 +134,7 @@ static int arcpgu_unload(struct drm_device *drm) arcpgu->fbdev = NULL; } drm_kms_helper_poll_fini(drm); + drm_atomic_helper_shutdown(drm); drm_mode_config_cleanup(drm); return 0; diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c index 9e34bce089d0..96f4082671fe 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c @@ -364,9 +364,7 @@ static void atmel_hlcdc_crtc_atomic_flush(struct drm_crtc *crtc, static const struct drm_crtc_helper_funcs lcdc_crtc_helper_funcs = { .mode_valid = atmel_hlcdc_crtc_mode_valid, - .mode_set = drm_helper_crtc_mode_set, .mode_set_nofb = atmel_hlcdc_crtc_mode_set_nofb, - .mode_set_base = drm_helper_crtc_mode_set_base, .atomic_check = atmel_hlcdc_crtc_atomic_check, .atomic_begin = atmel_hlcdc_crtc_atomic_begin, .atomic_flush = atmel_hlcdc_crtc_atomic_flush, diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c index 843cac222e60..fedbfa333bb0 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c @@ -763,7 +763,7 @@ static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev) ret = atmel_hlcdc_dc_load(ddev); if (ret) - goto err_unref; + goto err_put; ret = drm_dev_register(ddev, 0); if (ret) @@ -774,8 +774,8 @@ static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev) err_unload: atmel_hlcdc_dc_unload(ddev); -err_unref: - drm_dev_unref(ddev); +err_put: + drm_dev_put(ddev); return ret; } @@ -786,7 +786,7 @@ static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev) drm_dev_unregister(ddev); atmel_hlcdc_dc_unload(ddev); - drm_dev_unref(ddev); + drm_dev_put(ddev); return 0; } diff --git a/drivers/gpu/drm/bochs/bochs_mm.c b/drivers/gpu/drm/bochs/bochs_mm.c index a61c1ecb2bdc..e6ccf7fa92d4 100644 --- a/drivers/gpu/drm/bochs/bochs_mm.c +++ b/drivers/gpu/drm/bochs/bochs_mm.c @@ -414,7 +414,7 @@ int bochs_dumb_create(struct drm_file *file, struct drm_device *dev, return ret; ret = drm_gem_handle_create(file, gobj, &handle); - drm_gem_object_unreference_unlocked(gobj); + drm_gem_object_put_unlocked(gobj); if (ret) return ret; @@ -454,6 +454,6 @@ int bochs_dumb_mmap_offset(struct drm_file *file, struct drm_device *dev, bo = gem_to_bochs_bo(obj); *offset = bochs_bo_mmap_offset(bo); - drm_gem_object_unreference_unlocked(obj); + drm_gem_object_put_unlocked(obj); return 0; } diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 5971976284bf..5ac979d3450b 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -1957,7 +1957,6 @@ static const struct drm_connector_funcs dw_hdmi_connector_funcs = { static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = { .get_modes = dw_hdmi_connector_get_modes, - .best_encoder = drm_atomic_helper_best_encoder, }; static int dw_hdmi_bridge_attach(struct drm_bridge *bridge) @@ -2205,7 +2204,9 @@ static int dw_hdmi_detect_phy(struct dw_hdmi *hdmi) unsigned int i; u8 phy_type; - phy_type = hdmi_readb(hdmi, HDMI_CONFIG2_ID); + phy_type = hdmi->plat_data->phy_force_vendor ? + DW_HDMI_PHY_VENDOR_PHY : + hdmi_readb(hdmi, HDMI_CONFIG2_ID); if (phy_type == DW_HDMI_PHY_VENDOR_PHY) { /* Vendor PHYs require support from the glue layer. */ diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index e49b22381048..6f66777dca4b 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -92,6 +92,17 @@ drm_atomic_helper_plane_changed(struct drm_atomic_state *state, } } +/* + * For connectors that support multiple encoders, either the + * .atomic_best_encoder() or .best_encoder() operation must be implemented. + */ +static struct drm_encoder * +pick_single_encoder_for_connector(struct drm_connector *connector) +{ + WARN_ON(connector->encoder_ids[1]); + return drm_encoder_find(connector->dev, NULL, connector->encoder_ids[0]); +} + static int handle_conflicting_encoders(struct drm_atomic_state *state, bool disable_conflicting_encoders) { @@ -119,7 +130,7 @@ static int handle_conflicting_encoders(struct drm_atomic_state *state, else if (funcs->best_encoder) new_encoder = funcs->best_encoder(connector); else - new_encoder = drm_atomic_helper_best_encoder(connector); + new_encoder = pick_single_encoder_for_connector(connector); if (new_encoder) { if (encoder_mask & drm_encoder_mask(new_encoder)) { @@ -316,7 +327,7 @@ update_connector_routing(struct drm_atomic_state *state, else if (funcs->best_encoder) new_encoder = funcs->best_encoder(connector); else - new_encoder = drm_atomic_helper_best_encoder(connector); + new_encoder = pick_single_encoder_for_connector(connector); if (!new_encoder) { DRM_DEBUG_ATOMIC("No suitable encoder found for [CONNECTOR:%d:%s]\n", @@ -3388,586 +3399,3 @@ fail: return ret; } EXPORT_SYMBOL(drm_atomic_helper_page_flip_target); - -/** - * drm_atomic_helper_best_encoder - Helper for - * &drm_connector_helper_funcs.best_encoder callback - * @connector: Connector control structure - * - * This is a &drm_connector_helper_funcs.best_encoder callback helper for - * connectors that support exactly 1 encoder, statically determined at driver - * init time. - */ -struct drm_encoder * -drm_atomic_helper_best_encoder(struct drm_connector *connector) -{ - WARN_ON(connector->encoder_ids[1]); - return drm_encoder_find(connector->dev, NULL, connector->encoder_ids[0]); -} -EXPORT_SYMBOL(drm_atomic_helper_best_encoder); - -/** - * DOC: atomic state reset and initialization - * - * Both the drm core and the atomic helpers assume that there is always the full - * and correct atomic software state for all connectors, CRTCs and planes - * available. Which is a bit a problem on driver load and also after system - * suspend. One way to solve this is to have a hardware state read-out - * infrastructure which reconstructs the full software state (e.g. the i915 - * driver). - * - * The simpler solution is to just reset the software state to everything off, - * which is easiest to do by calling drm_mode_config_reset(). To facilitate this - * the atomic helpers provide default reset implementations for all hooks. - * - * On the upside the precise state tracking of atomic simplifies system suspend - * and resume a lot. For drivers using drm_mode_config_reset() a complete recipe - * is implemented in drm_atomic_helper_suspend() and drm_atomic_helper_resume(). - * For other drivers the building blocks are split out, see the documentation - * for these functions. - */ - -/** - * drm_atomic_helper_crtc_reset - default &drm_crtc_funcs.reset hook for CRTCs - * @crtc: drm CRTC - * - * Resets the atomic state for @crtc by freeing the state pointer (which might - * be NULL, e.g. at driver load time) and allocating a new empty state object. - */ -void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc) -{ - if (crtc->state) - __drm_atomic_helper_crtc_destroy_state(crtc->state); - - kfree(crtc->state); - crtc->state = kzalloc(sizeof(*crtc->state), GFP_KERNEL); - - if (crtc->state) - crtc->state->crtc = crtc; -} -EXPORT_SYMBOL(drm_atomic_helper_crtc_reset); - -/** - * __drm_atomic_helper_crtc_duplicate_state - copy atomic CRTC state - * @crtc: CRTC object - * @state: atomic CRTC state - * - * Copies atomic state from a CRTC's current state and resets inferred values. - * This is useful for drivers that subclass the CRTC state. - */ -void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc, - struct drm_crtc_state *state) -{ - memcpy(state, crtc->state, sizeof(*state)); - - if (state->mode_blob) - drm_property_blob_get(state->mode_blob); - if (state->degamma_lut) - drm_property_blob_get(state->degamma_lut); - if (state->ctm) - drm_property_blob_get(state->ctm); - if (state->gamma_lut) - drm_property_blob_get(state->gamma_lut); - state->mode_changed = false; - state->active_changed = false; - state->planes_changed = false; - state->connectors_changed = false; - state->color_mgmt_changed = false; - state->zpos_changed = false; - state->commit = NULL; - state->event = NULL; - state->pageflip_flags = 0; -} -EXPORT_SYMBOL(__drm_atomic_helper_crtc_duplicate_state); - -/** - * drm_atomic_helper_crtc_duplicate_state - default state duplicate hook - * @crtc: drm CRTC - * - * Default CRTC state duplicate hook for drivers which don't have their own - * subclassed CRTC state structure. - */ -struct drm_crtc_state * -drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc) -{ - struct drm_crtc_state *state; - - if (WARN_ON(!crtc->state)) - return NULL; - - state = kmalloc(sizeof(*state), GFP_KERNEL); - if (state) - __drm_atomic_helper_crtc_duplicate_state(crtc, state); - - return state; -} -EXPORT_SYMBOL(drm_atomic_helper_crtc_duplicate_state); - -/** - * __drm_atomic_helper_crtc_destroy_state - release CRTC state - * @state: CRTC state object to release - * - * Releases all resources stored in the CRTC state without actually freeing - * the memory of the CRTC state. This is useful for drivers that subclass the - * CRTC state. - */ -void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc_state *state) -{ - if (state->commit) { - /* - * In the event that a non-blocking commit returns - * -ERESTARTSYS before the commit_tail work is queued, we will - * have an extra reference to the commit object. Release it, if - * the event has not been consumed by the worker. - * - * state->event may be freed, so we can't directly look at - * state->event->base.completion. - */ - if (state->event && state->commit->abort_completion) - drm_crtc_commit_put(state->commit); - - kfree(state->commit->event); - state->commit->event = NULL; - - drm_crtc_commit_put(state->commit); - } - - drm_property_blob_put(state->mode_blob); - drm_property_blob_put(state->degamma_lut); - drm_property_blob_put(state->ctm); - drm_property_blob_put(state->gamma_lut); -} -EXPORT_SYMBOL(__drm_atomic_helper_crtc_destroy_state); - -/** - * drm_atomic_helper_crtc_destroy_state - default state destroy hook - * @crtc: drm CRTC - * @state: CRTC state object to release - * - * Default CRTC state destroy hook for drivers which don't have their own - * subclassed CRTC state structure. - */ -void drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc, - struct drm_crtc_state *state) -{ - __drm_atomic_helper_crtc_destroy_state(state); - kfree(state); -} -EXPORT_SYMBOL(drm_atomic_helper_crtc_destroy_state); - -/** - * __drm_atomic_helper_plane_reset - resets planes state to default values - * @plane: plane object, must not be NULL - * @state: atomic plane state, must not be NULL - * - * Initializes plane state to default. This is useful for drivers that subclass - * the plane state. - */ -void __drm_atomic_helper_plane_reset(struct drm_plane *plane, - struct drm_plane_state *state) -{ - state->plane = plane; - state->rotation = DRM_MODE_ROTATE_0; - - state->alpha = DRM_BLEND_ALPHA_OPAQUE; - state->pixel_blend_mode = DRM_MODE_BLEND_PREMULTI; - - plane->state = state; -} -EXPORT_SYMBOL(__drm_atomic_helper_plane_reset); - -/** - * drm_atomic_helper_plane_reset - default &drm_plane_funcs.reset hook for planes - * @plane: drm plane - * - * Resets the atomic state for @plane by freeing the state pointer (which might - * be NULL, e.g. at driver load time) and allocating a new empty state object. - */ -void drm_atomic_helper_plane_reset(struct drm_plane *plane) -{ - if (plane->state) - __drm_atomic_helper_plane_destroy_state(plane->state); - - kfree(plane->state); - plane->state = kzalloc(sizeof(*plane->state), GFP_KERNEL); - if (plane->state) - __drm_atomic_helper_plane_reset(plane, plane->state); -} -EXPORT_SYMBOL(drm_atomic_helper_plane_reset); - -/** - * __drm_atomic_helper_plane_duplicate_state - copy atomic plane state - * @plane: plane object - * @state: atomic plane state - * - * Copies atomic state from a plane's current state. This is useful for - * drivers that subclass the plane state. - */ -void __drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane, - struct drm_plane_state *state) -{ - memcpy(state, plane->state, sizeof(*state)); - - if (state->fb) - drm_framebuffer_get(state->fb); - - state->fence = NULL; - state->commit = NULL; -} -EXPORT_SYMBOL(__drm_atomic_helper_plane_duplicate_state); - -/** - * drm_atomic_helper_plane_duplicate_state - default state duplicate hook - * @plane: drm plane - * - * Default plane state duplicate hook for drivers which don't have their own - * subclassed plane state structure. - */ -struct drm_plane_state * -drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane) -{ - struct drm_plane_state *state; - - if (WARN_ON(!plane->state)) - return NULL; - - state = kmalloc(sizeof(*state), GFP_KERNEL); - if (state) - __drm_atomic_helper_plane_duplicate_state(plane, state); - - return state; -} -EXPORT_SYMBOL(drm_atomic_helper_plane_duplicate_state); - -/** - * __drm_atomic_helper_plane_destroy_state - release plane state - * @state: plane state object to release - * - * Releases all resources stored in the plane state without actually freeing - * the memory of the plane state. This is useful for drivers that subclass the - * plane state. - */ -void __drm_atomic_helper_plane_destroy_state(struct drm_plane_state *state) -{ - if (state->fb) - drm_framebuffer_put(state->fb); - - if (state->fence) - dma_fence_put(state->fence); - - if (state->commit) - drm_crtc_commit_put(state->commit); -} -EXPORT_SYMBOL(__drm_atomic_helper_plane_destroy_state); - -/** - * drm_atomic_helper_plane_destroy_state - default state destroy hook - * @plane: drm plane - * @state: plane state object to release - * - * Default plane state destroy hook for drivers which don't have their own - * subclassed plane state structure. - */ -void drm_atomic_helper_plane_destroy_state(struct drm_plane *plane, - struct drm_plane_state *state) -{ - __drm_atomic_helper_plane_destroy_state(state); - kfree(state); -} -EXPORT_SYMBOL(drm_atomic_helper_plane_destroy_state); - -/** - * __drm_atomic_helper_connector_reset - reset state on connector - * @connector: drm connector - * @conn_state: connector state to assign - * - * Initializes the newly allocated @conn_state and assigns it to - * the &drm_conector->state pointer of @connector, usually required when - * initializing the drivers or when called from the &drm_connector_funcs.reset - * hook. - * - * This is useful for drivers that subclass the connector state. - */ -void -__drm_atomic_helper_connector_reset(struct drm_connector *connector, - struct drm_connector_state *conn_state) -{ - if (conn_state) - conn_state->connector = connector; - - connector->state = conn_state; -} -EXPORT_SYMBOL(__drm_atomic_helper_connector_reset); - -/** - * drm_atomic_helper_connector_reset - default &drm_connector_funcs.reset hook for connectors - * @connector: drm connector - * - * Resets the atomic state for @connector by freeing the state pointer (which - * might be NULL, e.g. at driver load time) and allocating a new empty state - * object. - */ -void drm_atomic_helper_connector_reset(struct drm_connector *connector) -{ - struct drm_connector_state *conn_state = - kzalloc(sizeof(*conn_state), GFP_KERNEL); - - if (connector->state) - __drm_atomic_helper_connector_destroy_state(connector->state); - - kfree(connector->state); - __drm_atomic_helper_connector_reset(connector, conn_state); -} -EXPORT_SYMBOL(drm_atomic_helper_connector_reset); - -/** - * __drm_atomic_helper_connector_duplicate_state - copy atomic connector state - * @connector: connector object - * @state: atomic connector state - * - * Copies atomic state from a connector's current state. This is useful for - * drivers that subclass the connector state. - */ -void -__drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector, - struct drm_connector_state *state) -{ - memcpy(state, connector->state, sizeof(*state)); - if (state->crtc) - drm_connector_get(connector); - state->commit = NULL; - - /* Don't copy over a writeback job, they are used only once */ - state->writeback_job = NULL; -} -EXPORT_SYMBOL(__drm_atomic_helper_connector_duplicate_state); - -/** - * drm_atomic_helper_connector_duplicate_state - default state duplicate hook - * @connector: drm connector - * - * Default connector state duplicate hook for drivers which don't have their own - * subclassed connector state structure. - */ -struct drm_connector_state * -drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector) -{ - struct drm_connector_state *state; - - if (WARN_ON(!connector->state)) - return NULL; - - state = kmalloc(sizeof(*state), GFP_KERNEL); - if (state) - __drm_atomic_helper_connector_duplicate_state(connector, state); - - return state; -} -EXPORT_SYMBOL(drm_atomic_helper_connector_duplicate_state); - -/** - * drm_atomic_helper_duplicate_state - duplicate an atomic state object - * @dev: DRM device - * @ctx: lock acquisition context - * - * Makes a copy of the current atomic state by looping over all objects and - * duplicating their respective states. This is used for example by suspend/ - * resume support code to save the state prior to suspend such that it can - * be restored upon resume. - * - * Note that this treats atomic state as persistent between save and restore. - * Drivers must make sure that this is possible and won't result in confusion - * or erroneous behaviour. - * - * Note that if callers haven't already acquired all modeset locks this might - * return -EDEADLK, which must be handled by calling drm_modeset_backoff(). - * - * Returns: - * A pointer to the copy of the atomic state object on success or an - * ERR_PTR()-encoded error code on failure. - * - * See also: - * drm_atomic_helper_suspend(), drm_atomic_helper_resume() - */ -struct drm_atomic_state * -drm_atomic_helper_duplicate_state(struct drm_device *dev, - struct drm_modeset_acquire_ctx *ctx) -{ - struct drm_atomic_state *state; - struct drm_connector *conn; - struct drm_connector_list_iter conn_iter; - struct drm_plane *plane; - struct drm_crtc *crtc; - int err = 0; - - state = drm_atomic_state_alloc(dev); - if (!state) - return ERR_PTR(-ENOMEM); - - state->acquire_ctx = ctx; - - drm_for_each_crtc(crtc, dev) { - struct drm_crtc_state *crtc_state; - - crtc_state = drm_atomic_get_crtc_state(state, crtc); - if (IS_ERR(crtc_state)) { - err = PTR_ERR(crtc_state); - goto free; - } - } - - drm_for_each_plane(plane, dev) { - struct drm_plane_state *plane_state; - - plane_state = drm_atomic_get_plane_state(state, plane); - if (IS_ERR(plane_state)) { - err = PTR_ERR(plane_state); - goto free; - } - } - - drm_connector_list_iter_begin(dev, &conn_iter); - drm_for_each_connector_iter(conn, &conn_iter) { - struct drm_connector_state *conn_state; - - conn_state = drm_atomic_get_connector_state(state, conn); - if (IS_ERR(conn_state)) { - err = PTR_ERR(conn_state); - drm_connector_list_iter_end(&conn_iter); - goto free; - } - } - drm_connector_list_iter_end(&conn_iter); - - /* clear the acquire context so that it isn't accidentally reused */ - state->acquire_ctx = NULL; - -free: - if (err < 0) { - drm_atomic_state_put(state); - state = ERR_PTR(err); - } - - return state; -} -EXPORT_SYMBOL(drm_atomic_helper_duplicate_state); - -/** - * __drm_atomic_helper_connector_destroy_state - release connector state - * @state: connector state object to release - * - * Releases all resources stored in the connector state without actually - * freeing the memory of the connector state. This is useful for drivers that - * subclass the connector state. - */ -void -__drm_atomic_helper_connector_destroy_state(struct drm_connector_state *state) -{ - if (state->crtc) - drm_connector_put(state->connector); - - if (state->commit) - drm_crtc_commit_put(state->commit); -} -EXPORT_SYMBOL(__drm_atomic_helper_connector_destroy_state); - -/** - * drm_atomic_helper_connector_destroy_state - default state destroy hook - * @connector: drm connector - * @state: connector state object to release - * - * Default connector state destroy hook for drivers which don't have their own - * subclassed connector state structure. - */ -void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector, - struct drm_connector_state *state) -{ - __drm_atomic_helper_connector_destroy_state(state); - kfree(state); -} -EXPORT_SYMBOL(drm_atomic_helper_connector_destroy_state); - -/** - * drm_atomic_helper_legacy_gamma_set - set the legacy gamma correction table - * @crtc: CRTC object - * @red: red correction table - * @green: green correction table - * @blue: green correction table - * @size: size of the tables - * @ctx: lock acquire context - * - * Implements support for legacy gamma correction table for drivers - * that support color management through the DEGAMMA_LUT/GAMMA_LUT - * properties. See drm_crtc_enable_color_mgmt() and the containing chapter for - * how the atomic color management and gamma tables work. - */ -int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc, - u16 *red, u16 *green, u16 *blue, - uint32_t size, - struct drm_modeset_acquire_ctx *ctx) -{ - struct drm_device *dev = crtc->dev; - struct drm_atomic_state *state; - struct drm_crtc_state *crtc_state; - struct drm_property_blob *blob = NULL; - struct drm_color_lut *blob_data; - int i, ret = 0; - bool replaced; - - state = drm_atomic_state_alloc(crtc->dev); - if (!state) - return -ENOMEM; - - blob = drm_property_create_blob(dev, - sizeof(struct drm_color_lut) * size, - NULL); - if (IS_ERR(blob)) { - ret = PTR_ERR(blob); - blob = NULL; - goto fail; - } - - /* Prepare GAMMA_LUT with the legacy values. */ - blob_data = blob->data; - for (i = 0; i < size; i++) { - blob_data[i].red = red[i]; - blob_data[i].green = green[i]; - blob_data[i].blue = blue[i]; - } - - state->acquire_ctx = ctx; - crtc_state = drm_atomic_get_crtc_state(state, crtc); - if (IS_ERR(crtc_state)) { - ret = PTR_ERR(crtc_state); - goto fail; - } - - /* Reset DEGAMMA_LUT and CTM properties. */ - replaced = drm_property_replace_blob(&crtc_state->degamma_lut, NULL); - replaced |= drm_property_replace_blob(&crtc_state->ctm, NULL); - replaced |= drm_property_replace_blob(&crtc_state->gamma_lut, blob); - crtc_state->color_mgmt_changed |= replaced; - - ret = drm_atomic_commit(state); - -fail: - drm_atomic_state_put(state); - drm_property_blob_put(blob); - return ret; -} -EXPORT_SYMBOL(drm_atomic_helper_legacy_gamma_set); - -/** - * __drm_atomic_helper_private_duplicate_state - copy atomic private state - * @obj: CRTC object - * @state: new private object state - * - * Copies atomic state from a private objects's current state and resets inferred values. - * This is useful for drivers that subclass the private state. - */ -void __drm_atomic_helper_private_obj_duplicate_state(struct drm_private_obj *obj, - struct drm_private_state *state) -{ - memcpy(state, obj->state, sizeof(*state)); -} -EXPORT_SYMBOL(__drm_atomic_helper_private_obj_duplicate_state); diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c new file mode 100644 index 000000000000..3ba996069d69 --- /dev/null +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -0,0 +1,601 @@ +/* + * Copyright (C) 2018 Intel Corp. + * + * 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 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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: + * Rob Clark <robdclark@gmail.com> + * Daniel Vetter <daniel.vetter@ffwll.ch> + */ + +#include <drm/drm_atomic_state_helper.h> +#include <drm/drm_crtc.h> +#include <drm/drm_plane.h> +#include <drm/drm_connector.h> +#include <drm/drm_atomic.h> +#include <drm/drm_device.h> + +#include <linux/slab.h> +#include <linux/dma-fence.h> + +/** + * DOC: atomic state reset and initialization + * + * Both the drm core and the atomic helpers assume that there is always the full + * and correct atomic software state for all connectors, CRTCs and planes + * available. Which is a bit a problem on driver load and also after system + * suspend. One way to solve this is to have a hardware state read-out + * infrastructure which reconstructs the full software state (e.g. the i915 + * driver). + * + * The simpler solution is to just reset the software state to everything off, + * which is easiest to do by calling drm_mode_config_reset(). To facilitate this + * the atomic helpers provide default reset implementations for all hooks. + * + * On the upside the precise state tracking of atomic simplifies system suspend + * and resume a lot. For drivers using drm_mode_config_reset() a complete recipe + * is implemented in drm_atomic_helper_suspend() and drm_atomic_helper_resume(). + * For other drivers the building blocks are split out, see the documentation + * for these functions. + */ + +/** + * drm_atomic_helper_crtc_reset - default &drm_crtc_funcs.reset hook for CRTCs + * @crtc: drm CRTC + * + * Resets the atomic state for @crtc by freeing the state pointer (which might + * be NULL, e.g. at driver load time) and allocating a new empty state object. + */ +void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc) +{ + if (crtc->state) + __drm_atomic_helper_crtc_destroy_state(crtc->state); + + kfree(crtc->state); + crtc->state = kzalloc(sizeof(*crtc->state), GFP_KERNEL); + + if (crtc->state) + crtc->state->crtc = crtc; +} +EXPORT_SYMBOL(drm_atomic_helper_crtc_reset); + +/** + * __drm_atomic_helper_crtc_duplicate_state - copy atomic CRTC state + * @crtc: CRTC object + * @state: atomic CRTC state + * + * Copies atomic state from a CRTC's current state and resets inferred values. + * This is useful for drivers that subclass the CRTC state. + */ +void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + memcpy(state, crtc->state, sizeof(*state)); + + if (state->mode_blob) + drm_property_blob_get(state->mode_blob); + if (state->degamma_lut) + drm_property_blob_get(state->degamma_lut); + if (state->ctm) + drm_property_blob_get(state->ctm); + if (state->gamma_lut) + drm_property_blob_get(state->gamma_lut); + state->mode_changed = false; + state->active_changed = false; + state->planes_changed = false; + state->connectors_changed = false; + state->color_mgmt_changed = false; + state->zpos_changed = false; + state->commit = NULL; + state->event = NULL; + state->pageflip_flags = 0; +} +EXPORT_SYMBOL(__drm_atomic_helper_crtc_duplicate_state); + +/** + * drm_atomic_helper_crtc_duplicate_state - default state duplicate hook + * @crtc: drm CRTC + * + * Default CRTC state duplicate hook for drivers which don't have their own + * subclassed CRTC state structure. + */ +struct drm_crtc_state * +drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc) +{ + struct drm_crtc_state *state; + + if (WARN_ON(!crtc->state)) + return NULL; + + state = kmalloc(sizeof(*state), GFP_KERNEL); + if (state) + __drm_atomic_helper_crtc_duplicate_state(crtc, state); + + return state; +} +EXPORT_SYMBOL(drm_atomic_helper_crtc_duplicate_state); + +/** + * __drm_atomic_helper_crtc_destroy_state - release CRTC state + * @state: CRTC state object to release + * + * Releases all resources stored in the CRTC state without actually freeing + * the memory of the CRTC state. This is useful for drivers that subclass the + * CRTC state. + */ +void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc_state *state) +{ + if (state->commit) { + /* + * In the event that a non-blocking commit returns + * -ERESTARTSYS before the commit_tail work is queued, we will + * have an extra reference to the commit object. Release it, if + * the event has not been consumed by the worker. + * + * state->event may be freed, so we can't directly look at + * state->event->base.completion. + */ + if (state->event && state->commit->abort_completion) + drm_crtc_commit_put(state->commit); + + kfree(state->commit->event); + state->commit->event = NULL; + + drm_crtc_commit_put(state->commit); + } + + drm_property_blob_put(state->mode_blob); + drm_property_blob_put(state->degamma_lut); + drm_property_blob_put(state->ctm); + drm_property_blob_put(state->gamma_lut); +} +EXPORT_SYMBOL(__drm_atomic_helper_crtc_destroy_state); + +/** + * drm_atomic_helper_crtc_destroy_state - default state destroy hook + * @crtc: drm CRTC + * @state: CRTC state object to release + * + * Default CRTC state destroy hook for drivers which don't have their own + * subclassed CRTC state structure. + */ +void drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + __drm_atomic_helper_crtc_destroy_state(state); + kfree(state); +} +EXPORT_SYMBOL(drm_atomic_helper_crtc_destroy_state); + +/** + * __drm_atomic_helper_plane_reset - resets planes state to default values + * @plane: plane object, must not be NULL + * @state: atomic plane state, must not be NULL + * + * Initializes plane state to default. This is useful for drivers that subclass + * the plane state. + */ +void __drm_atomic_helper_plane_reset(struct drm_plane *plane, + struct drm_plane_state *state) +{ + state->plane = plane; + state->rotation = DRM_MODE_ROTATE_0; + + state->alpha = DRM_BLEND_ALPHA_OPAQUE; + state->pixel_blend_mode = DRM_MODE_BLEND_PREMULTI; + + plane->state = state; +} +EXPORT_SYMBOL(__drm_atomic_helper_plane_reset); + +/** + * drm_atomic_helper_plane_reset - default &drm_plane_funcs.reset hook for planes + * @plane: drm plane + * + * Resets the atomic state for @plane by freeing the state pointer (which might + * be NULL, e.g. at driver load time) and allocating a new empty state object. + */ +void drm_atomic_helper_plane_reset(struct drm_plane *plane) +{ + if (plane->state) + __drm_atomic_helper_plane_destroy_state(plane->state); + + kfree(plane->state); + plane->state = kzalloc(sizeof(*plane->state), GFP_KERNEL); + if (plane->state) + __drm_atomic_helper_plane_reset(plane, plane->state); +} +EXPORT_SYMBOL(drm_atomic_helper_plane_reset); + +/** + * __drm_atomic_helper_plane_duplicate_state - copy atomic plane state + * @plane: plane object + * @state: atomic plane state + * + * Copies atomic state from a plane's current state. This is useful for + * drivers that subclass the plane state. + */ +void __drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane, + struct drm_plane_state *state) +{ + memcpy(state, plane->state, sizeof(*state)); + + if (state->fb) + drm_framebuffer_get(state->fb); + + state->fence = NULL; + state->commit = NULL; +} +EXPORT_SYMBOL(__drm_atomic_helper_plane_duplicate_state); + +/** + * drm_atomic_helper_plane_duplicate_state - default state duplicate hook + * @plane: drm plane + * + * Default plane state duplicate hook for drivers which don't have their own + * subclassed plane state structure. + */ +struct drm_plane_state * +drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane) +{ + struct drm_plane_state *state; + + if (WARN_ON(!plane->state)) + return NULL; + + state = kmalloc(sizeof(*state), GFP_KERNEL); + if (state) + __drm_atomic_helper_plane_duplicate_state(plane, state); + + return state; +} +EXPORT_SYMBOL(drm_atomic_helper_plane_duplicate_state); + +/** + * __drm_atomic_helper_plane_destroy_state - release plane state + * @state: plane state object to release + * + * Releases all resources stored in the plane state without actually freeing + * the memory of the plane state. This is useful for drivers that subclass the + * plane state. + */ +void __drm_atomic_helper_plane_destroy_state(struct drm_plane_state *state) +{ + if (state->fb) + drm_framebuffer_put(state->fb); + + if (state->fence) + dma_fence_put(state->fence); + + if (state->commit) + drm_crtc_commit_put(state->commit); +} +EXPORT_SYMBOL(__drm_atomic_helper_plane_destroy_state); + +/** + * drm_atomic_helper_plane_destroy_state - default state destroy hook + * @plane: drm plane + * @state: plane state object to release + * + * Default plane state destroy hook for drivers which don't have their own + * subclassed plane state structure. + */ +void drm_atomic_helper_plane_destroy_state(struct drm_plane *plane, + struct drm_plane_state *state) +{ + __drm_atomic_helper_plane_destroy_state(state); + kfree(state); +} +EXPORT_SYMBOL(drm_atomic_helper_plane_destroy_state); + +/** + * __drm_atomic_helper_connector_reset - reset state on connector + * @connector: drm connector + * @conn_state: connector state to assign + * + * Initializes the newly allocated @conn_state and assigns it to + * the &drm_conector->state pointer of @connector, usually required when + * initializing the drivers or when called from the &drm_connector_funcs.reset + * hook. + * + * This is useful for drivers that subclass the connector state. + */ +void +__drm_atomic_helper_connector_reset(struct drm_connector *connector, + struct drm_connector_state *conn_state) +{ + if (conn_state) + conn_state->connector = connector; + + connector->state = conn_state; +} +EXPORT_SYMBOL(__drm_atomic_helper_connector_reset); + +/** + * drm_atomic_helper_connector_reset - default &drm_connector_funcs.reset hook for connectors + * @connector: drm connector + * + * Resets the atomic state for @connector by freeing the state pointer (which + * might be NULL, e.g. at driver load time) and allocating a new empty state + * object. + */ +void drm_atomic_helper_connector_reset(struct drm_connector *connector) +{ + struct drm_connector_state *conn_state = + kzalloc(sizeof(*conn_state), GFP_KERNEL); + + if (connector->state) + __drm_atomic_helper_connector_destroy_state(connector->state); + + kfree(connector->state); + __drm_atomic_helper_connector_reset(connector, conn_state); +} +EXPORT_SYMBOL(drm_atomic_helper_connector_reset); + +/** + * __drm_atomic_helper_connector_duplicate_state - copy atomic connector state + * @connector: connector object + * @state: atomic connector state + * + * Copies atomic state from a connector's current state. This is useful for + * drivers that subclass the connector state. + */ +void +__drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector, + struct drm_connector_state *state) +{ + memcpy(state, connector->state, sizeof(*state)); + if (state->crtc) + drm_connector_get(connector); + state->commit = NULL; + + /* Don't copy over a writeback job, they are used only once */ + state->writeback_job = NULL; +} +EXPORT_SYMBOL(__drm_atomic_helper_connector_duplicate_state); + +/** + * drm_atomic_helper_connector_duplicate_state - default state duplicate hook + * @connector: drm connector + * + * Default connector state duplicate hook for drivers which don't have their own + * subclassed connector state structure. + */ +struct drm_connector_state * +drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector) +{ + struct drm_connector_state *state; + + if (WARN_ON(!connector->state)) + return NULL; + + state = kmalloc(sizeof(*state), GFP_KERNEL); + if (state) + __drm_atomic_helper_connector_duplicate_state(connector, state); + + return state; +} +EXPORT_SYMBOL(drm_atomic_helper_connector_duplicate_state); + +/** + * drm_atomic_helper_duplicate_state - duplicate an atomic state object + * @dev: DRM device + * @ctx: lock acquisition context + * + * Makes a copy of the current atomic state by looping over all objects and + * duplicating their respective states. This is used for example by suspend/ + * resume support code to save the state prior to suspend such that it can + * be restored upon resume. + * + * Note that this treats atomic state as persistent between save and restore. + * Drivers must make sure that this is possible and won't result in confusion + * or erroneous behaviour. + * + * Note that if callers haven't already acquired all modeset locks this might + * return -EDEADLK, which must be handled by calling drm_modeset_backoff(). + * + * Returns: + * A pointer to the copy of the atomic state object on success or an + * ERR_PTR()-encoded error code on failure. + * + * See also: + * drm_atomic_helper_suspend(), drm_atomic_helper_resume() + */ +struct drm_atomic_state * +drm_atomic_helper_duplicate_state(struct drm_device *dev, + struct drm_modeset_acquire_ctx *ctx) +{ + struct drm_atomic_state *state; + struct drm_connector *conn; + struct drm_connector_list_iter conn_iter; + struct drm_plane *plane; + struct drm_crtc *crtc; + int err = 0; + + state = drm_atomic_state_alloc(dev); + if (!state) + return ERR_PTR(-ENOMEM); + + state->acquire_ctx = ctx; + + drm_for_each_crtc(crtc, dev) { + struct drm_crtc_state *crtc_state; + + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (IS_ERR(crtc_state)) { + err = PTR_ERR(crtc_state); + goto free; + } + } + + drm_for_each_plane(plane, dev) { + struct drm_plane_state *plane_state; + + plane_state = drm_atomic_get_plane_state(state, plane); + if (IS_ERR(plane_state)) { + err = PTR_ERR(plane_state); + goto free; + } + } + + drm_connector_list_iter_begin(dev, &conn_iter); + drm_for_each_connector_iter(conn, &conn_iter) { + struct drm_connector_state *conn_state; + + conn_state = drm_atomic_get_connector_state(state, conn); + if (IS_ERR(conn_state)) { + err = PTR_ERR(conn_state); + drm_connector_list_iter_end(&conn_iter); + goto free; + } + } + drm_connector_list_iter_end(&conn_iter); + + /* clear the acquire context so that it isn't accidentally reused */ + state->acquire_ctx = NULL; + +free: + if (err < 0) { + drm_atomic_state_put(state); + state = ERR_PTR(err); + } + + return state; +} +EXPORT_SYMBOL(drm_atomic_helper_duplicate_state); + +/** + * __drm_atomic_helper_connector_destroy_state - release connector state + * @state: connector state object to release + * + * Releases all resources stored in the connector state without actually + * freeing the memory of the connector state. This is useful for drivers that + * subclass the connector state. + */ +void +__drm_atomic_helper_connector_destroy_state(struct drm_connector_state *state) +{ + if (state->crtc) + drm_connector_put(state->connector); + + if (state->commit) + drm_crtc_commit_put(state->commit); +} +EXPORT_SYMBOL(__drm_atomic_helper_connector_destroy_state); + +/** + * drm_atomic_helper_connector_destroy_state - default state destroy hook + * @connector: drm connector + * @state: connector state object to release + * + * Default connector state destroy hook for drivers which don't have their own + * subclassed connector state structure. + */ +void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector, + struct drm_connector_state *state) +{ + __drm_atomic_helper_connector_destroy_state(state); + kfree(state); +} +EXPORT_SYMBOL(drm_atomic_helper_connector_destroy_state); + +/** + * drm_atomic_helper_legacy_gamma_set - set the legacy gamma correction table + * @crtc: CRTC object + * @red: red correction table + * @green: green correction table + * @blue: green correction table + * @size: size of the tables + * @ctx: lock acquire context + * + * Implements support for legacy gamma correction table for drivers + * that support color management through the DEGAMMA_LUT/GAMMA_LUT + * properties. See drm_crtc_enable_color_mgmt() and the containing chapter for + * how the atomic color management and gamma tables work. + */ +int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc, + u16 *red, u16 *green, u16 *blue, + uint32_t size, + struct drm_modeset_acquire_ctx *ctx) +{ + struct drm_device *dev = crtc->dev; + struct drm_atomic_state *state; + struct drm_crtc_state *crtc_state; + struct drm_property_blob *blob = NULL; + struct drm_color_lut *blob_data; + int i, ret = 0; + bool replaced; + + state = drm_atomic_state_alloc(crtc->dev); + if (!state) + return -ENOMEM; + + blob = drm_property_create_blob(dev, + sizeof(struct drm_color_lut) * size, + NULL); + if (IS_ERR(blob)) { + ret = PTR_ERR(blob); + blob = NULL; + goto fail; + } + + /* Prepare GAMMA_LUT with the legacy values. */ + blob_data = blob->data; + for (i = 0; i < size; i++) { + blob_data[i].red = red[i]; + blob_data[i].green = green[i]; + blob_data[i].blue = blue[i]; + } + + state->acquire_ctx = ctx; + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (IS_ERR(crtc_state)) { + ret = PTR_ERR(crtc_state); + goto fail; + } + + /* Reset DEGAMMA_LUT and CTM properties. */ + replaced = drm_property_replace_blob(&crtc_state->degamma_lut, NULL); + replaced |= drm_property_replace_blob(&crtc_state->ctm, NULL); + replaced |= drm_property_replace_blob(&crtc_state->gamma_lut, blob); + crtc_state->color_mgmt_changed |= replaced; + + ret = drm_atomic_commit(state); + +fail: + drm_atomic_state_put(state); + drm_property_blob_put(blob); + return ret; +} +EXPORT_SYMBOL(drm_atomic_helper_legacy_gamma_set); + +/** + * __drm_atomic_helper_private_duplicate_state - copy atomic private state + * @obj: CRTC object + * @state: new private object state + * + * Copies atomic state from a private objects's current state and resets inferred values. + * This is useful for drivers that subclass the private state. + */ +void __drm_atomic_helper_private_obj_duplicate_state(struct drm_private_obj *obj, + struct drm_private_state *state) +{ + memcpy(state, obj->state, sizeof(*state)); +} +EXPORT_SYMBOL(__drm_atomic_helper_private_obj_duplicate_state); diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c index 7412acaf3cde..d7d10cabb9bb 100644 --- a/drivers/gpu/drm/drm_bufs.c +++ b/drivers/gpu/drm/drm_bufs.c @@ -36,6 +36,8 @@ #include <drm/drmP.h> #include "drm_legacy.h" +#include <linux/nospec.h> + static struct drm_map_list *drm_find_matching_map(struct drm_device *dev, struct drm_local_map *map) { @@ -1417,6 +1419,7 @@ int drm_legacy_freebufs(struct drm_device *dev, void *data, idx, dma->buf_count - 1); return -EINVAL; } + idx = array_index_nospec(idx, dma->buf_count); buf = dma->buflist[idx]; if (buf->file_priv != file_priv) { DRM_ERROR("Process %d freeing buffer not owned\n", diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 1e40e5decbe9..5d01414ec9f7 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -260,9 +260,7 @@ int drm_connector_init(struct drm_device *dev, if (connector_type != DRM_MODE_CONNECTOR_VIRTUAL && connector_type != DRM_MODE_CONNECTOR_WRITEBACK) - drm_object_attach_property(&connector->base, - config->edid_property, - 0); + drm_connector_attach_edid_property(connector); drm_object_attach_property(&connector->base, config->dpms_property, 0); @@ -295,6 +293,25 @@ out_put: EXPORT_SYMBOL(drm_connector_init); /** + * drm_connector_attach_edid_property - attach edid property. + * @dev: DRM device + * @connector: the connector + * + * Some connector types like DRM_MODE_CONNECTOR_VIRTUAL do not get a + * edid property attached by default. This function can be used to + * explicitly enable the edid property in these cases. + */ +void drm_connector_attach_edid_property(struct drm_connector *connector) +{ + struct drm_mode_config *config = &connector->dev->mode_config; + + drm_object_attach_property(&connector->base, + config->edid_property, + 0); +} +EXPORT_SYMBOL(drm_connector_attach_edid_property); + +/** * drm_connector_attach_encoder - attach a connector to an encoder * @connector: connector to attach * @encoder: encoder to attach @connector to diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index ce75e9506e85..a3c81850e755 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -984,118 +984,3 @@ void drm_helper_resume_force_mode(struct drm_device *dev) drm_modeset_unlock_all(dev); } EXPORT_SYMBOL(drm_helper_resume_force_mode); - -/** - * drm_helper_crtc_mode_set - mode_set implementation for atomic plane helpers - * @crtc: DRM CRTC - * @mode: DRM display mode which userspace requested - * @adjusted_mode: DRM display mode adjusted by ->mode_fixup callbacks - * @x: x offset of the CRTC scanout area on the underlying framebuffer - * @y: y offset of the CRTC scanout area on the underlying framebuffer - * @old_fb: previous framebuffer - * - * This function implements a callback useable as the ->mode_set callback - * required by the CRTC helpers. Besides the atomic plane helper functions for - * the primary plane the driver must also provide the ->mode_set_nofb callback - * to set up the CRTC. - * - * This is a transitional helper useful for converting drivers to the atomic - * interfaces. - */ -int drm_helper_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, int x, int y, - struct drm_framebuffer *old_fb) -{ - struct drm_crtc_state *crtc_state; - const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; - int ret; - - if (crtc->funcs->atomic_duplicate_state) - crtc_state = crtc->funcs->atomic_duplicate_state(crtc); - else { - if (!crtc->state) - drm_atomic_helper_crtc_reset(crtc); - - crtc_state = drm_atomic_helper_crtc_duplicate_state(crtc); - } - - if (!crtc_state) - return -ENOMEM; - - crtc_state->planes_changed = true; - crtc_state->mode_changed = true; - ret = drm_atomic_set_mode_for_crtc(crtc_state, mode); - if (ret) - goto out; - drm_mode_copy(&crtc_state->adjusted_mode, adjusted_mode); - - if (crtc_funcs->atomic_check) { - ret = crtc_funcs->atomic_check(crtc, crtc_state); - if (ret) - goto out; - } - - swap(crtc->state, crtc_state); - - crtc_funcs->mode_set_nofb(crtc); - - ret = drm_helper_crtc_mode_set_base(crtc, x, y, old_fb); - -out: - if (crtc_state) { - if (crtc->funcs->atomic_destroy_state) - crtc->funcs->atomic_destroy_state(crtc, crtc_state); - else - drm_atomic_helper_crtc_destroy_state(crtc, crtc_state); - } - - return ret; -} -EXPORT_SYMBOL(drm_helper_crtc_mode_set); - -/** - * drm_helper_crtc_mode_set_base - mode_set_base implementation for atomic plane helpers - * @crtc: DRM CRTC - * @x: x offset of the CRTC scanout area on the underlying framebuffer - * @y: y offset of the CRTC scanout area on the underlying framebuffer - * @old_fb: previous framebuffer - * - * This function implements a callback useable as the ->mode_set_base used - * required by the CRTC helpers. The driver must provide the atomic plane helper - * functions for the primary plane. - * - * This is a transitional helper useful for converting drivers to the atomic - * interfaces. - */ -int drm_helper_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, - struct drm_framebuffer *old_fb) -{ - struct drm_plane_state *plane_state; - struct drm_plane *plane = crtc->primary; - - if (plane->funcs->atomic_duplicate_state) - plane_state = plane->funcs->atomic_duplicate_state(plane); - else { - if (!plane->state) - drm_atomic_helper_plane_reset(plane); - - plane_state = drm_atomic_helper_plane_duplicate_state(plane); - } - if (!plane_state) - return -ENOMEM; - plane_state->plane = plane; - - plane_state->crtc = crtc; - drm_atomic_set_fb_for_plane(plane_state, crtc->primary->fb); - plane_state->crtc_x = 0; - plane_state->crtc_y = 0; - plane_state->crtc_h = crtc->mode.vdisplay; - plane_state->crtc_w = crtc->mode.hdisplay; - plane_state->src_x = x << 16; - plane_state->src_y = y << 16; - plane_state->src_h = crtc->mode.vdisplay << 16; - plane_state->src_w = crtc->mode.hdisplay << 16; - - return drm_plane_helper_commit(plane, plane_state, old_fb); -} -EXPORT_SYMBOL(drm_helper_crtc_mode_set_base); diff --git a/drivers/gpu/drm/drm_dp_cec.c b/drivers/gpu/drm/drm_dp_cec.c index 8a718f85079a..b15cee85b702 100644 --- a/drivers/gpu/drm/drm_dp_cec.c +++ b/drivers/gpu/drm/drm_dp_cec.c @@ -424,8 +424,6 @@ void drm_dp_cec_register_connector(struct drm_dp_aux *aux, const char *name, aux->cec.parent = parent; INIT_DELAYED_WORK(&aux->cec.unregister_work, drm_dp_cec_unregister_work); - - drm_dp_cec_set_edid(aux, NULL); } EXPORT_SYMBOL(drm_dp_cec_register_connector); diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index 47e0e2f6642d..fc2b42dd3dc6 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -124,10 +124,7 @@ int drm_fb_cma_fbdev_init(struct drm_device *dev, unsigned int preferred_bpp, /* dev->fb_helper will indirectly point to fbdev_cma after this call */ fbdev_cma = drm_fbdev_cma_init(dev, preferred_bpp, max_conn_count); - if (IS_ERR(fbdev_cma)) - return PTR_ERR(fbdev_cma); - - return 0; + return PTR_ERR_OR_ZERO(fbdev_cma); } EXPORT_SYMBOL_GPL(drm_fb_cma_fbdev_init); @@ -224,21 +221,3 @@ void drm_fbdev_cma_hotplug_event(struct drm_fbdev_cma *fbdev_cma) drm_fb_helper_hotplug_event(&fbdev_cma->fb_helper); } EXPORT_SYMBOL_GPL(drm_fbdev_cma_hotplug_event); - -/** - * drm_fbdev_cma_set_suspend_unlocked - wrapper around - * drm_fb_helper_set_suspend_unlocked - * @fbdev_cma: The drm_fbdev_cma struct, may be NULL - * @state: desired state, zero to resume, non-zero to suspend - * - * Calls drm_fb_helper_set_suspend, which is a wrapper around - * fb_set_suspend implemented by fbdev core. - */ -void drm_fbdev_cma_set_suspend_unlocked(struct drm_fbdev_cma *fbdev_cma, - bool state) -{ - if (fbdev_cma) - drm_fb_helper_set_suspend_unlocked(&fbdev_cma->fb_helper, - state); -} -EXPORT_SYMBOL(drm_fbdev_cma_set_suspend_unlocked); diff --git a/drivers/gpu/drm/drm_fourcc.c b/drivers/gpu/drm/drm_fourcc.c index 90a1c846fc25..3934527e09dc 100644 --- a/drivers/gpu/drm/drm_fourcc.c +++ b/drivers/gpu/drm/drm_fourcc.c @@ -97,14 +97,14 @@ EXPORT_SYMBOL(drm_mode_legacy_fb_format); /** * drm_driver_legacy_fb_format - compute drm fourcc code from legacy description + * @dev: DRM device * @bpp: bits per pixels * @depth: bit depth per pixel - * @native: use host native byte order * * Computes a drm fourcc pixel format code for the given @bpp/@depth values. * Unlike drm_mode_legacy_fb_format() this looks at the drivers mode_config, - * and depending on the quirk_addfb_prefer_host_byte_order flag it returns - * little endian byte order or host byte order framebuffer formats. + * and depending on the &drm_mode_config.quirk_addfb_prefer_host_byte_order flag + * it returns little endian byte order or host byte order framebuffer formats. */ uint32_t drm_driver_legacy_fb_format(struct drm_device *dev, uint32_t bpp, uint32_t depth) diff --git a/drivers/gpu/drm/drm_memory.c b/drivers/gpu/drm/drm_memory.c index d69e4fc1ee77..40c4349cb939 100644 --- a/drivers/gpu/drm/drm_memory.c +++ b/drivers/gpu/drm/drm_memory.c @@ -51,7 +51,7 @@ #endif static void *agp_remap(unsigned long offset, unsigned long size, - struct drm_device * dev) + struct drm_device *dev) { unsigned long i, num_pages = PAGE_ALIGN(size) / PAGE_SIZE; @@ -94,26 +94,26 @@ static void *agp_remap(unsigned long offset, unsigned long size, } /** Wrapper around agp_free_memory() */ -void drm_free_agp(struct agp_memory * handle, int pages) +void drm_free_agp(struct agp_memory *handle, int pages) { agp_free_memory(handle); } /** Wrapper around agp_bind_memory() */ -int drm_bind_agp(struct agp_memory * handle, unsigned int start) +int drm_bind_agp(struct agp_memory *handle, unsigned int start) { return agp_bind_memory(handle, start); } /** Wrapper around agp_unbind_memory() */ -int drm_unbind_agp(struct agp_memory * handle) +int drm_unbind_agp(struct agp_memory *handle) { return agp_unbind_memory(handle); } #else /* CONFIG_AGP */ static inline void *agp_remap(unsigned long offset, unsigned long size, - struct drm_device * dev) + struct drm_device *dev) { return NULL; } diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 02db9ac82d7a..24a750436559 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -716,8 +716,8 @@ int of_get_drm_display_mode(struct device_node *np, if (bus_flags) drm_bus_flags_from_videomode(&vm, bus_flags); - pr_debug("%pOF: got %dx%d display mode from %s\n", - np, vm.hactive, vm.vactive, np->name); + pr_debug("%pOF: got %dx%d display mode\n", + np, vm.hactive, vm.vactive); drm_mode_debug_printmodeline(dmode); return 0; diff --git a/drivers/gpu/drm/drm_modeset_helper.c b/drivers/gpu/drm/drm_modeset_helper.c index f1c24ab0ef09..9150fa385bba 100644 --- a/drivers/gpu/drm/drm_modeset_helper.c +++ b/drivers/gpu/drm/drm_modeset_helper.c @@ -146,6 +146,21 @@ static struct drm_plane *create_primary_plane(struct drm_device *dev) * Initialize a CRTC object with a default helper-provided primary plane and no * cursor plane. * + * Note that we make some assumptions about hardware limitations that may not be + * true for all hardware: + * + * 1. Primary plane cannot be repositioned. + * 2. Primary plane cannot be scaled. + * 3. Primary plane must cover the entire CRTC. + * 4. Subpixel positioning is not supported. + * 5. The primary plane must always be on if the CRTC is enabled. + * + * This is purely a backwards compatibility helper for old drivers. Drivers + * should instead implement their own primary plane. Atomic drivers must do so. + * Drivers with the above hardware restriction can look into using &struct + * drm_simple_display_pipe, which encapsulates the above limitations into a nice + * interface. + * * Returns: * Zero on success, error code on failure. */ diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c index ee4a5e1221f1..ab4e70e63f6e 100644 --- a/drivers/gpu/drm/drm_panel_orientation_quirks.c +++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c @@ -59,6 +59,14 @@ static const struct drm_dmi_panel_orientation_data gpd_win = { .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, }; +static const struct drm_dmi_panel_orientation_data gpd_win2 = { + .width = 720, + .height = 1280, + .bios_dates = (const char * const []){ + "12/07/2017", "05/24/2018", NULL }, + .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, +}; + static const struct drm_dmi_panel_orientation_data itworks_tw891 = { .width = 800, .height = 1280, @@ -106,6 +114,14 @@ static const struct dmi_system_id orientation_data[] = { DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"), }, .driver_data = (void *)&gpd_win, + }, { /* GPD Win 2 (too generic strings, also match on bios date) */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Default string"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"), + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Default string"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "Default string"), + }, + .driver_data = (void *)&gpd_win2, }, { /* I.T.Works TW891 */ .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "To be filled by O.E.M."), diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index 48f615d38931..a9d9df6c85ad 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -61,15 +61,14 @@ drm_dma_handle_t *drm_pci_alloc(struct drm_device * dev, size_t size, size_t ali return NULL; dmah->size = size; - dmah->vaddr = dma_alloc_coherent(&dev->pdev->dev, size, &dmah->busaddr, GFP_KERNEL | __GFP_COMP); + dmah->vaddr = dma_zalloc_coherent(&dev->pdev->dev, size, &dmah->busaddr, + GFP_KERNEL | __GFP_COMP); if (dmah->vaddr == NULL) { kfree(dmah); return NULL; } - memset(dmah->vaddr, 0, size); - /* XXX - Is virt_to_page() legal for consistent mem? */ /* Reserve */ for (addr = (unsigned long)dmah->vaddr, sz = size; diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c index a393756b664e..0fff72dcd06d 100644 --- a/drivers/gpu/drm/drm_plane_helper.c +++ b/drivers/gpu/drm/drm_plane_helper.c @@ -42,11 +42,8 @@ * primary plane support on top of the normal CRTC configuration interface. * Since the legacy &drm_mode_config_funcs.set_config interface ties the primary * plane together with the CRTC state this does not allow userspace to disable - * the primary plane itself. To avoid too much duplicated code use - * drm_plane_helper_check_update() which can be used to enforce the same - * restrictions as primary planes had thus. The default primary plane only - * expose XRBG8888 and ARGB8888 as valid pixel formats for the attached - * framebuffer. + * the primary plane itself. The default primary plane only expose XRBG8888 and + * ARGB8888 as valid pixel formats for the attached framebuffer. * * Drivers are highly recommended to implement proper support for primary * planes, and newly merged drivers must not rely upon these transitional @@ -100,43 +97,17 @@ static int get_connectors_for_crtc(struct drm_crtc *crtc, return count; } -/** - * drm_plane_helper_check_update() - Check plane update for validity - * @plane: plane object to update - * @crtc: owning CRTC of owning plane - * @fb: framebuffer to flip onto plane - * @src: source coordinates in 16.16 fixed point - * @dst: integer destination coordinates - * @rotation: plane rotation - * @min_scale: minimum @src:@dest scaling factor in 16.16 fixed point - * @max_scale: maximum @src:@dest scaling factor in 16.16 fixed point - * @can_position: is it legal to position the plane such that it - * doesn't cover the entire crtc? This will generally - * only be false for primary planes. - * @can_update_disabled: can the plane be updated while the crtc - * is disabled? - * @visible: output parameter indicating whether plane is still visible after - * clipping - * - * Checks that a desired plane update is valid. Drivers that provide - * their own plane handling rather than helper-provided implementations may - * still wish to call this function to avoid duplication of error checking - * code. - * - * RETURNS: - * Zero if update appears valid, error code on failure - */ -int drm_plane_helper_check_update(struct drm_plane *plane, - struct drm_crtc *crtc, - struct drm_framebuffer *fb, - struct drm_rect *src, - struct drm_rect *dst, - unsigned int rotation, - int min_scale, - int max_scale, - bool can_position, - bool can_update_disabled, - bool *visible) +static int drm_plane_helper_check_update(struct drm_plane *plane, + struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_rect *src, + struct drm_rect *dst, + unsigned int rotation, + int min_scale, + int max_scale, + bool can_position, + bool can_update_disabled, + bool *visible) { struct drm_plane_state plane_state = { .plane = plane, @@ -173,52 +144,14 @@ int drm_plane_helper_check_update(struct drm_plane *plane, return 0; } -EXPORT_SYMBOL(drm_plane_helper_check_update); -/** - * drm_primary_helper_update() - Helper for primary plane update - * @plane: plane object to update - * @crtc: owning CRTC of owning plane - * @fb: framebuffer to flip onto plane - * @crtc_x: x offset of primary plane on crtc - * @crtc_y: y offset of primary plane on crtc - * @crtc_w: width of primary plane rectangle on crtc - * @crtc_h: height of primary plane rectangle on crtc - * @src_x: x offset of @fb for panning - * @src_y: y offset of @fb for panning - * @src_w: width of source rectangle in @fb - * @src_h: height of source rectangle in @fb - * @ctx: lock acquire context, not used here - * - * Provides a default plane update handler for primary planes. This is handler - * is called in response to a userspace SetPlane operation on the plane with a - * non-NULL framebuffer. We call the driver's modeset handler to update the - * framebuffer. - * - * SetPlane() on a primary plane of a disabled CRTC is not supported, and will - * return an error. - * - * Note that we make some assumptions about hardware limitations that may not be - * true for all hardware -- - * - * 1. Primary plane cannot be repositioned. - * 2. Primary plane cannot be scaled. - * 3. Primary plane must cover the entire CRTC. - * 4. Subpixel positioning is not supported. - * - * Drivers for hardware that don't have these restrictions can provide their - * own implementation rather than using this helper. - * - * RETURNS: - * Zero on success, error code on failure - */ -int drm_primary_helper_update(struct drm_plane *plane, struct drm_crtc *crtc, - struct drm_framebuffer *fb, - int crtc_x, int crtc_y, - unsigned int crtc_w, unsigned int crtc_h, - uint32_t src_x, uint32_t src_y, - uint32_t src_w, uint32_t src_h, - struct drm_modeset_acquire_ctx *ctx) +static int drm_primary_helper_update(struct drm_plane *plane, struct drm_crtc *crtc, + struct drm_framebuffer *fb, + int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h, + struct drm_modeset_acquire_ctx *ctx) { struct drm_mode_set set = { .crtc = crtc, @@ -285,35 +218,12 @@ int drm_primary_helper_update(struct drm_plane *plane, struct drm_crtc *crtc, kfree(connector_list); return ret; } -EXPORT_SYMBOL(drm_primary_helper_update); -/** - * drm_primary_helper_disable() - Helper for primary plane disable - * @plane: plane to disable - * @ctx: lock acquire context, not used here - * - * Provides a default plane disable handler for primary planes. This is handler - * is called in response to a userspace SetPlane operation on the plane with a - * NULL framebuffer parameter. It unconditionally fails the disable call with - * -EINVAL the only way to disable the primary plane without driver support is - * to disable the entire CRTC. Which does not match the plane - * &drm_plane_funcs.disable_plane hook. - * - * Note that some hardware may be able to disable the primary plane without - * disabling the whole CRTC. Drivers for such hardware should provide their - * own disable handler that disables just the primary plane (and they'll likely - * need to provide their own update handler as well to properly re-enable a - * disabled primary plane). - * - * RETURNS: - * Unconditionally returns -EINVAL. - */ -int drm_primary_helper_disable(struct drm_plane *plane, - struct drm_modeset_acquire_ctx *ctx) +static int drm_primary_helper_disable(struct drm_plane *plane, + struct drm_modeset_acquire_ctx *ctx) { return -EINVAL; } -EXPORT_SYMBOL(drm_primary_helper_disable); /** * drm_primary_helper_destroy() - Helper for primary plane destruction @@ -336,200 +246,3 @@ const struct drm_plane_funcs drm_primary_helper_funcs = { .destroy = drm_primary_helper_destroy, }; EXPORT_SYMBOL(drm_primary_helper_funcs); - -int drm_plane_helper_commit(struct drm_plane *plane, - struct drm_plane_state *plane_state, - struct drm_framebuffer *old_fb) -{ - const struct drm_plane_helper_funcs *plane_funcs; - struct drm_crtc *crtc[2]; - const struct drm_crtc_helper_funcs *crtc_funcs[2]; - int i, ret = 0; - - plane_funcs = plane->helper_private; - - /* Since this is a transitional helper we can't assume that plane->state - * is always valid. Hence we need to use plane->crtc instead of - * plane->state->crtc as the old crtc. */ - crtc[0] = plane->crtc; - crtc[1] = crtc[0] != plane_state->crtc ? plane_state->crtc : NULL; - - for (i = 0; i < 2; i++) - crtc_funcs[i] = crtc[i] ? crtc[i]->helper_private : NULL; - - if (plane_funcs->atomic_check) { - ret = plane_funcs->atomic_check(plane, plane_state); - if (ret) - goto out; - } - - if (plane_funcs->prepare_fb && plane_state->fb != old_fb) { - ret = plane_funcs->prepare_fb(plane, - plane_state); - if (ret) - goto out; - } - - /* Point of no return, commit sw state. */ - swap(plane->state, plane_state); - - for (i = 0; i < 2; i++) { - if (crtc_funcs[i] && crtc_funcs[i]->atomic_begin) - crtc_funcs[i]->atomic_begin(crtc[i], crtc[i]->state); - } - - /* - * Drivers may optionally implement the ->atomic_disable callback, so - * special-case that here. - */ - if (drm_atomic_plane_disabling(plane_state, plane->state) && - plane_funcs->atomic_disable) - plane_funcs->atomic_disable(plane, plane_state); - else - plane_funcs->atomic_update(plane, plane_state); - - for (i = 0; i < 2; i++) { - if (crtc_funcs[i] && crtc_funcs[i]->atomic_flush) - crtc_funcs[i]->atomic_flush(crtc[i], crtc[i]->state); - } - - /* - * If we only moved the plane and didn't change fb's, there's no need to - * wait for vblank. - */ - if (plane->state->fb == old_fb) - goto out; - - for (i = 0; i < 2; i++) { - if (!crtc[i]) - continue; - - if (crtc[i]->cursor == plane) - continue; - - /* There's no other way to figure out whether the crtc is running. */ - ret = drm_crtc_vblank_get(crtc[i]); - if (ret == 0) { - drm_crtc_wait_one_vblank(crtc[i]); - drm_crtc_vblank_put(crtc[i]); - } - - ret = 0; - } - - if (plane_funcs->cleanup_fb) - plane_funcs->cleanup_fb(plane, plane_state); -out: - if (plane->funcs->atomic_destroy_state) - plane->funcs->atomic_destroy_state(plane, plane_state); - else - drm_atomic_helper_plane_destroy_state(plane, plane_state); - - return ret; -} - -/** - * drm_plane_helper_update() - Transitional helper for plane update - * @plane: plane object to update - * @crtc: owning CRTC of owning plane - * @fb: framebuffer to flip onto plane - * @crtc_x: x offset of primary plane on crtc - * @crtc_y: y offset of primary plane on crtc - * @crtc_w: width of primary plane rectangle on crtc - * @crtc_h: height of primary plane rectangle on crtc - * @src_x: x offset of @fb for panning - * @src_y: y offset of @fb for panning - * @src_w: width of source rectangle in @fb - * @src_h: height of source rectangle in @fb - * @ctx: lock acquire context, not used here - * - * Provides a default plane update handler using the atomic plane update - * functions. It is fully left to the driver to check plane constraints and - * handle corner-cases like a fully occluded or otherwise invisible plane. - * - * This is useful for piecewise transitioning of a driver to the atomic helpers. - * - * RETURNS: - * Zero on success, error code on failure - */ -int drm_plane_helper_update(struct drm_plane *plane, struct drm_crtc *crtc, - struct drm_framebuffer *fb, - int crtc_x, int crtc_y, - unsigned int crtc_w, unsigned int crtc_h, - uint32_t src_x, uint32_t src_y, - uint32_t src_w, uint32_t src_h, - struct drm_modeset_acquire_ctx *ctx) -{ - struct drm_plane_state *plane_state; - - if (plane->funcs->atomic_duplicate_state) - plane_state = plane->funcs->atomic_duplicate_state(plane); - else { - if (!plane->state) - drm_atomic_helper_plane_reset(plane); - - plane_state = drm_atomic_helper_plane_duplicate_state(plane); - } - if (!plane_state) - return -ENOMEM; - plane_state->plane = plane; - - plane_state->crtc = crtc; - drm_atomic_set_fb_for_plane(plane_state, fb); - plane_state->crtc_x = crtc_x; - plane_state->crtc_y = crtc_y; - plane_state->crtc_h = crtc_h; - plane_state->crtc_w = crtc_w; - plane_state->src_x = src_x; - plane_state->src_y = src_y; - plane_state->src_h = src_h; - plane_state->src_w = src_w; - - return drm_plane_helper_commit(plane, plane_state, plane->fb); -} -EXPORT_SYMBOL(drm_plane_helper_update); - -/** - * drm_plane_helper_disable() - Transitional helper for plane disable - * @plane: plane to disable - * @ctx: lock acquire context, not used here - * - * Provides a default plane disable handler using the atomic plane update - * functions. It is fully left to the driver to check plane constraints and - * handle corner-cases like a fully occluded or otherwise invisible plane. - * - * This is useful for piecewise transitioning of a driver to the atomic helpers. - * - * RETURNS: - * Zero on success, error code on failure - */ -int drm_plane_helper_disable(struct drm_plane *plane, - struct drm_modeset_acquire_ctx *ctx) -{ - struct drm_plane_state *plane_state; - struct drm_framebuffer *old_fb; - - /* crtc helpers love to call disable functions for already disabled hw - * functions. So cope with that. */ - if (!plane->crtc) - return 0; - - if (plane->funcs->atomic_duplicate_state) - plane_state = plane->funcs->atomic_duplicate_state(plane); - else { - if (!plane->state) - drm_atomic_helper_plane_reset(plane); - - plane_state = drm_atomic_helper_plane_duplicate_state(plane); - } - if (!plane_state) - return -ENOMEM; - plane_state->plane = plane; - - plane_state->crtc = NULL; - old_fb = plane_state->fb; - drm_atomic_set_fb_for_plane(plane_state, NULL); - - return drm_plane_helper_commit(plane, plane_state, old_fb); -} -EXPORT_SYMBOL(drm_plane_helper_disable); diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index 3f0205fc0a1a..8d54d51a6b6b 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -434,34 +434,6 @@ void drm_gem_dmabuf_vunmap(struct dma_buf *dma_buf, void *vaddr) EXPORT_SYMBOL(drm_gem_dmabuf_vunmap); /** - * drm_gem_dmabuf_kmap - map implementation for GEM - * @dma_buf: buffer to be mapped - * @page_num: page number within the buffer - * - * Not implemented. This can be used as the &dma_buf_ops.map callback. - */ -void *drm_gem_dmabuf_kmap(struct dma_buf *dma_buf, unsigned long page_num) -{ - return NULL; -} -EXPORT_SYMBOL(drm_gem_dmabuf_kmap); - -/** - * drm_gem_dmabuf_kunmap - unmap implementation for GEM - * @dma_buf: buffer to be unmapped - * @page_num: page number within the buffer - * @addr: virtual address of the buffer - * - * Not implemented. This can be used as the &dma_buf_ops.unmap callback. - */ -void drm_gem_dmabuf_kunmap(struct dma_buf *dma_buf, unsigned long page_num, - void *addr) -{ - -} -EXPORT_SYMBOL(drm_gem_dmabuf_kunmap); - -/** * drm_gem_dmabuf_mmap - dma_buf mmap implementation for GEM * @dma_buf: buffer to be mapped * @vma: virtual address range @@ -489,8 +461,6 @@ static const struct dma_buf_ops drm_gem_prime_dmabuf_ops = { .map_dma_buf = drm_gem_map_dma_buf, .unmap_dma_buf = drm_gem_unmap_dma_buf, .release = drm_gem_dmabuf_release, - .map = drm_gem_dmabuf_kmap, - .unmap = drm_gem_dmabuf_kunmap, .mmap = drm_gem_dmabuf_mmap, .vmap = drm_gem_dmabuf_vmap, .vunmap = drm_gem_dmabuf_vunmap, diff --git a/drivers/gpu/drm/drm_syncobj.c b/drivers/gpu/drm/drm_syncobj.c index 5c2091dbd230..c8252cd4c02d 100644 --- a/drivers/gpu/drm/drm_syncobj.c +++ b/drivers/gpu/drm/drm_syncobj.c @@ -56,6 +56,9 @@ #include "drm_internal.h" #include <drm/drm_syncobj.h> +/* merge normal syncobj to timeline syncobj, the point interval is 1 */ +#define DRM_SYNCOBJ_BINARY_POINT 1 + struct drm_syncobj_stub_fence { struct dma_fence base; spinlock_t lock; @@ -71,6 +74,11 @@ static const struct dma_fence_ops drm_syncobj_stub_fence_ops = { .get_timeline_name = drm_syncobj_stub_fence_get_name, }; +struct drm_syncobj_signal_pt { + struct dma_fence_array *fence_array; + u64 value; + struct list_head list; +}; /** * drm_syncobj_find - lookup and reference a sync object. @@ -98,6 +106,42 @@ struct drm_syncobj *drm_syncobj_find(struct drm_file *file_private, } EXPORT_SYMBOL(drm_syncobj_find); +static struct dma_fence +*drm_syncobj_find_signal_pt_for_point(struct drm_syncobj *syncobj, + uint64_t point) +{ + struct drm_syncobj_signal_pt *signal_pt; + + if ((syncobj->type == DRM_SYNCOBJ_TYPE_TIMELINE) && + (point <= syncobj->timeline)) { + struct drm_syncobj_stub_fence *fence = + kzalloc(sizeof(struct drm_syncobj_stub_fence), + GFP_KERNEL); + + if (!fence) + return NULL; + spin_lock_init(&fence->lock); + dma_fence_init(&fence->base, + &drm_syncobj_stub_fence_ops, + &fence->lock, + syncobj->timeline_context, + point); + + dma_fence_signal(&fence->base); + return &fence->base; + } + + list_for_each_entry(signal_pt, &syncobj->signal_pt_list, list) { + if (point > signal_pt->value) + continue; + if ((syncobj->type == DRM_SYNCOBJ_TYPE_BINARY) && + (point != signal_pt->value)) + continue; + return dma_fence_get(&signal_pt->fence_array->base); + } + return NULL; +} + static void drm_syncobj_add_callback_locked(struct drm_syncobj *syncobj, struct drm_syncobj_cb *cb, drm_syncobj_func_t func) @@ -106,55 +150,167 @@ static void drm_syncobj_add_callback_locked(struct drm_syncobj *syncobj, list_add_tail(&cb->node, &syncobj->cb_list); } -static int drm_syncobj_fence_get_or_add_callback(struct drm_syncobj *syncobj, - struct dma_fence **fence, - struct drm_syncobj_cb *cb, - drm_syncobj_func_t func) +static void drm_syncobj_fence_get_or_add_callback(struct drm_syncobj *syncobj, + struct dma_fence **fence, + struct drm_syncobj_cb *cb, + drm_syncobj_func_t func) { - int ret; + u64 pt_value = 0; WARN_ON(*fence); - *fence = drm_syncobj_fence_get(syncobj); - if (*fence) - return 1; + if (syncobj->type == DRM_SYNCOBJ_TYPE_BINARY) { + /*BINARY syncobj always wait on last pt */ + pt_value = syncobj->signal_point; - spin_lock(&syncobj->lock); - /* We've already tried once to get a fence and failed. Now that we - * have the lock, try one more time just to be sure we don't add a - * callback when a fence has already been set. - */ - if (syncobj->fence) { - *fence = dma_fence_get(rcu_dereference_protected(syncobj->fence, - lockdep_is_held(&syncobj->lock))); - ret = 1; - } else { - *fence = NULL; - drm_syncobj_add_callback_locked(syncobj, cb, func); - ret = 0; + if (pt_value == 0) + pt_value += DRM_SYNCOBJ_BINARY_POINT; } - spin_unlock(&syncobj->lock); - return ret; + mutex_lock(&syncobj->cb_mutex); + spin_lock(&syncobj->pt_lock); + *fence = drm_syncobj_find_signal_pt_for_point(syncobj, pt_value); + spin_unlock(&syncobj->pt_lock); + if (!*fence) + drm_syncobj_add_callback_locked(syncobj, cb, func); + mutex_unlock(&syncobj->cb_mutex); } void drm_syncobj_add_callback(struct drm_syncobj *syncobj, struct drm_syncobj_cb *cb, drm_syncobj_func_t func) { - spin_lock(&syncobj->lock); + mutex_lock(&syncobj->cb_mutex); drm_syncobj_add_callback_locked(syncobj, cb, func); - spin_unlock(&syncobj->lock); + mutex_unlock(&syncobj->cb_mutex); } void drm_syncobj_remove_callback(struct drm_syncobj *syncobj, struct drm_syncobj_cb *cb) { - spin_lock(&syncobj->lock); + mutex_lock(&syncobj->cb_mutex); list_del_init(&cb->node); - spin_unlock(&syncobj->lock); + mutex_unlock(&syncobj->cb_mutex); +} + +static void drm_syncobj_init(struct drm_syncobj *syncobj) +{ + spin_lock(&syncobj->pt_lock); + syncobj->timeline_context = dma_fence_context_alloc(1); + syncobj->timeline = 0; + syncobj->signal_point = 0; + init_waitqueue_head(&syncobj->wq); + + INIT_LIST_HEAD(&syncobj->signal_pt_list); + spin_unlock(&syncobj->pt_lock); +} + +static void drm_syncobj_fini(struct drm_syncobj *syncobj) +{ + struct drm_syncobj_signal_pt *signal_pt = NULL, *tmp; + + spin_lock(&syncobj->pt_lock); + list_for_each_entry_safe(signal_pt, tmp, + &syncobj->signal_pt_list, list) { + list_del(&signal_pt->list); + dma_fence_put(&signal_pt->fence_array->base); + kfree(signal_pt); + } + spin_unlock(&syncobj->pt_lock); +} + +static int drm_syncobj_create_signal_pt(struct drm_syncobj *syncobj, + struct dma_fence *fence, + u64 point) +{ + struct drm_syncobj_signal_pt *signal_pt = + kzalloc(sizeof(struct drm_syncobj_signal_pt), GFP_KERNEL); + struct drm_syncobj_signal_pt *tail_pt; + struct dma_fence **fences; + int num_fences = 0; + int ret = 0, i; + + if (!signal_pt) + return -ENOMEM; + if (!fence) + goto out; + + fences = kmalloc_array(sizeof(void *), 2, GFP_KERNEL); + if (!fences) { + ret = -ENOMEM; + goto out; + } + fences[num_fences++] = dma_fence_get(fence); + /* timeline syncobj must take this dependency */ + if (syncobj->type == DRM_SYNCOBJ_TYPE_TIMELINE) { + spin_lock(&syncobj->pt_lock); + if (!list_empty(&syncobj->signal_pt_list)) { + tail_pt = list_last_entry(&syncobj->signal_pt_list, + struct drm_syncobj_signal_pt, list); + fences[num_fences++] = + dma_fence_get(&tail_pt->fence_array->base); + } + spin_unlock(&syncobj->pt_lock); + } + signal_pt->fence_array = dma_fence_array_create(num_fences, fences, + syncobj->timeline_context, + point, false); + if (!signal_pt->fence_array) { + ret = -ENOMEM; + goto fail; + } + + spin_lock(&syncobj->pt_lock); + if (syncobj->signal_point >= point) { + DRM_WARN("A later signal is ready!"); + spin_unlock(&syncobj->pt_lock); + goto exist; + } + signal_pt->value = point; + list_add_tail(&signal_pt->list, &syncobj->signal_pt_list); + syncobj->signal_point = point; + spin_unlock(&syncobj->pt_lock); + wake_up_all(&syncobj->wq); + + return 0; +exist: + dma_fence_put(&signal_pt->fence_array->base); +fail: + for (i = 0; i < num_fences; i++) + dma_fence_put(fences[i]); + kfree(fences); +out: + kfree(signal_pt); + return ret; } +static void drm_syncobj_garbage_collection(struct drm_syncobj *syncobj) +{ + struct drm_syncobj_signal_pt *signal_pt, *tmp, *tail_pt; + + spin_lock(&syncobj->pt_lock); + tail_pt = list_last_entry(&syncobj->signal_pt_list, + struct drm_syncobj_signal_pt, + list); + list_for_each_entry_safe(signal_pt, tmp, + &syncobj->signal_pt_list, list) { + if (syncobj->type == DRM_SYNCOBJ_TYPE_BINARY && + signal_pt == tail_pt) + continue; + if (dma_fence_is_signaled(&signal_pt->fence_array->base)) { + syncobj->timeline = signal_pt->value; + list_del(&signal_pt->list); + dma_fence_put(&signal_pt->fence_array->base); + kfree(signal_pt); + } else { + /*signal_pt is in order in list, from small to big, so + * the later must not be signal either */ + break; + } + } + + spin_unlock(&syncobj->pt_lock); +} /** * drm_syncobj_replace_fence - replace fence in a sync object. * @syncobj: Sync object to replace fence in @@ -167,28 +323,30 @@ void drm_syncobj_replace_fence(struct drm_syncobj *syncobj, u64 point, struct dma_fence *fence) { - struct dma_fence *old_fence; - struct drm_syncobj_cb *cur, *tmp; - - if (fence) - dma_fence_get(fence); - - spin_lock(&syncobj->lock); - - old_fence = rcu_dereference_protected(syncobj->fence, - lockdep_is_held(&syncobj->lock)); - rcu_assign_pointer(syncobj->fence, fence); + u64 pt_value = point; + + drm_syncobj_garbage_collection(syncobj); + if (syncobj->type == DRM_SYNCOBJ_TYPE_BINARY) { + if (!fence) { + drm_syncobj_fini(syncobj); + drm_syncobj_init(syncobj); + return; + } + pt_value = syncobj->signal_point + + DRM_SYNCOBJ_BINARY_POINT; + } + drm_syncobj_create_signal_pt(syncobj, fence, pt_value); + if (fence) { + struct drm_syncobj_cb *cur, *tmp; + LIST_HEAD(cb_list); - if (fence != old_fence) { + mutex_lock(&syncobj->cb_mutex); list_for_each_entry_safe(cur, tmp, &syncobj->cb_list, node) { list_del_init(&cur->node); cur->func(syncobj, cur); } + mutex_unlock(&syncobj->cb_mutex); } - - spin_unlock(&syncobj->lock); - - dma_fence_put(old_fence); } EXPORT_SYMBOL(drm_syncobj_replace_fence); @@ -211,34 +369,87 @@ static int drm_syncobj_assign_null_handle(struct drm_syncobj *syncobj) return 0; } +static int +drm_syncobj_point_get(struct drm_syncobj *syncobj, u64 point, u64 flags, + struct dma_fence **fence) +{ + int ret = 0; + + if (flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT) { + ret = wait_event_interruptible(syncobj->wq, + point <= syncobj->signal_point); + if (ret < 0) + return ret; + } + spin_lock(&syncobj->pt_lock); + *fence = drm_syncobj_find_signal_pt_for_point(syncobj, point); + if (!*fence) + ret = -EINVAL; + spin_unlock(&syncobj->pt_lock); + return ret; +} + +/** + * drm_syncobj_search_fence - lookup and reference the fence in a sync object or + * in a timeline point + * @syncobj: sync object pointer + * @point: timeline point + * @flags: DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT or not + * @fence: out parameter for the fence + * + * if flags is DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT, the function will block + * here until specific timeline points is reached. + * if not, you need a submit thread and block in userspace until all future + * timeline points have materialized, only then you can submit to the kernel, + * otherwise, function will fail to return fence. + * + * Returns 0 on success or a negative error value on failure. On success @fence + * contains a reference to the fence, which must be released by calling + * dma_fence_put(). + */ +int drm_syncobj_search_fence(struct drm_syncobj *syncobj, u64 point, + u64 flags, struct dma_fence **fence) +{ + u64 pt_value = point; + + if (!syncobj) + return -ENOENT; + + drm_syncobj_garbage_collection(syncobj); + if (syncobj->type == DRM_SYNCOBJ_TYPE_BINARY) { + /*BINARY syncobj always wait on last pt */ + pt_value = syncobj->signal_point; + + if (pt_value == 0) + pt_value += DRM_SYNCOBJ_BINARY_POINT; + } + return drm_syncobj_point_get(syncobj, pt_value, flags, fence); +} +EXPORT_SYMBOL(drm_syncobj_search_fence); + /** * drm_syncobj_find_fence - lookup and reference the fence in a sync object * @file_private: drm file private pointer * @handle: sync object handle to lookup. * @point: timeline point + * @flags: DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT or not * @fence: out parameter for the fence * * This is just a convenience function that combines drm_syncobj_find() and - * drm_syncobj_fence_get(). + * drm_syncobj_lookup_fence(). * * Returns 0 on success or a negative error value on failure. On success @fence * contains a reference to the fence, which must be released by calling * dma_fence_put(). */ int drm_syncobj_find_fence(struct drm_file *file_private, - u32 handle, u64 point, + u32 handle, u64 point, u64 flags, struct dma_fence **fence) { struct drm_syncobj *syncobj = drm_syncobj_find(file_private, handle); - int ret = 0; - - if (!syncobj) - return -ENOENT; + int ret; - *fence = drm_syncobj_fence_get(syncobj); - if (!*fence) { - ret = -EINVAL; - } + ret = drm_syncobj_search_fence(syncobj, point, flags, fence); drm_syncobj_put(syncobj); return ret; } @@ -255,7 +466,7 @@ void drm_syncobj_free(struct kref *kref) struct drm_syncobj *syncobj = container_of(kref, struct drm_syncobj, refcount); - drm_syncobj_replace_fence(syncobj, 0, NULL); + drm_syncobj_fini(syncobj); kfree(syncobj); } EXPORT_SYMBOL(drm_syncobj_free); @@ -284,7 +495,13 @@ int drm_syncobj_create(struct drm_syncobj **out_syncobj, uint32_t flags, kref_init(&syncobj->refcount); INIT_LIST_HEAD(&syncobj->cb_list); - spin_lock_init(&syncobj->lock); + spin_lock_init(&syncobj->pt_lock); + mutex_init(&syncobj->cb_mutex); + if (flags & DRM_SYNCOBJ_CREATE_TYPE_TIMELINE) + syncobj->type = DRM_SYNCOBJ_TYPE_TIMELINE; + else + syncobj->type = DRM_SYNCOBJ_TYPE_BINARY; + drm_syncobj_init(syncobj); if (flags & DRM_SYNCOBJ_CREATE_SIGNALED) { ret = drm_syncobj_assign_null_handle(syncobj); @@ -497,7 +714,7 @@ static int drm_syncobj_export_sync_file(struct drm_file *file_private, if (fd < 0) return fd; - ret = drm_syncobj_find_fence(file_private, handle, 0, &fence); + ret = drm_syncobj_find_fence(file_private, handle, 0, 0, &fence); if (ret) goto err_put_fd; @@ -567,7 +784,8 @@ drm_syncobj_create_ioctl(struct drm_device *dev, void *data, return -EOPNOTSUPP; /* no valid flags yet */ - if (args->flags & ~DRM_SYNCOBJ_CREATE_SIGNALED) + if (args->flags & ~(DRM_SYNCOBJ_CREATE_SIGNALED | + DRM_SYNCOBJ_CREATE_TYPE_TIMELINE)) return -EINVAL; return drm_syncobj_create_as_handle(file_private, @@ -660,9 +878,8 @@ static void syncobj_wait_syncobj_func(struct drm_syncobj *syncobj, struct syncobj_wait_entry *wait = container_of(cb, struct syncobj_wait_entry, syncobj_cb); - /* This happens inside the syncobj lock */ - wait->fence = dma_fence_get(rcu_dereference_protected(syncobj->fence, - lockdep_is_held(&syncobj->lock))); + drm_syncobj_search_fence(syncobj, 0, 0, &wait->fence); + wake_up_process(wait->task); } @@ -688,7 +905,8 @@ static signed long drm_syncobj_array_wait_timeout(struct drm_syncobj **syncobjs, signaled_count = 0; for (i = 0; i < count; ++i) { entries[i].task = current; - entries[i].fence = drm_syncobj_fence_get(syncobjs[i]); + drm_syncobj_search_fence(syncobjs[i], 0, 0, + &entries[i].fence); if (!entries[i].fence) { if (flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT) { continue; @@ -953,12 +1171,13 @@ drm_syncobj_reset_ioctl(struct drm_device *dev, void *data, if (ret < 0) return ret; - for (i = 0; i < args->count_handles; i++) - drm_syncobj_replace_fence(syncobjs[i], 0, NULL); - + for (i = 0; i < args->count_handles; i++) { + drm_syncobj_fini(syncobjs[i]); + drm_syncobj_init(syncobjs[i]); + } drm_syncobj_array_free(syncobjs, args->count_handles); - return 0; + return ret; } int diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c index 0e3752437e44..18afc94e4dff 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c @@ -17,6 +17,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> +#include <video/videomode.h> #include "fsl_dcu_drm_crtc.h" #include "fsl_dcu_drm_drv.h" @@ -85,40 +86,34 @@ static void fsl_dcu_drm_crtc_mode_set_nofb(struct drm_crtc *crtc) struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; struct drm_connector *con = &fsl_dev->connector.base; struct drm_display_mode *mode = &crtc->state->mode; - unsigned int hbp, hfp, hsw, vbp, vfp, vsw, index, pol = 0; + unsigned int pol = 0; + struct videomode vm; - index = drm_crtc_index(crtc); clk_set_rate(fsl_dev->pix_clk, mode->clock * 1000); - /* Configure timings: */ - hbp = mode->htotal - mode->hsync_end; - hfp = mode->hsync_start - mode->hdisplay; - hsw = mode->hsync_end - mode->hsync_start; - vbp = mode->vtotal - mode->vsync_end; - vfp = mode->vsync_start - mode->vdisplay; - vsw = mode->vsync_end - mode->vsync_start; + drm_display_mode_to_videomode(mode, &vm); /* INV_PXCK as default (most display sample data on rising edge) */ if (!(con->display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_POSEDGE)) pol |= DCU_SYN_POL_INV_PXCK; - if (mode->flags & DRM_MODE_FLAG_NHSYNC) + if (vm.flags & DISPLAY_FLAGS_HSYNC_LOW) pol |= DCU_SYN_POL_INV_HS_LOW; - if (mode->flags & DRM_MODE_FLAG_NVSYNC) + if (vm.flags & DISPLAY_FLAGS_VSYNC_LOW) pol |= DCU_SYN_POL_INV_VS_LOW; regmap_write(fsl_dev->regmap, DCU_HSYN_PARA, - DCU_HSYN_PARA_BP(hbp) | - DCU_HSYN_PARA_PW(hsw) | - DCU_HSYN_PARA_FP(hfp)); + DCU_HSYN_PARA_BP(vm.hback_porch) | + DCU_HSYN_PARA_PW(vm.hsync_len) | + DCU_HSYN_PARA_FP(vm.hfront_porch)); regmap_write(fsl_dev->regmap, DCU_VSYN_PARA, - DCU_VSYN_PARA_BP(vbp) | - DCU_VSYN_PARA_PW(vsw) | - DCU_VSYN_PARA_FP(vfp)); + DCU_VSYN_PARA_BP(vm.vback_porch) | + DCU_VSYN_PARA_PW(vm.vsync_len) | + DCU_VSYN_PARA_FP(vm.vfront_porch)); regmap_write(fsl_dev->regmap, DCU_DISP_SIZE, - DCU_DISP_SIZE_DELTA_Y(mode->vdisplay) | - DCU_DISP_SIZE_DELTA_X(mode->hdisplay)); + DCU_DISP_SIZE_DELTA_Y(vm.vactive) | + DCU_DISP_SIZE_DELTA_X(vm.hactive)); regmap_write(fsl_dev->regmap, DCU_SYN_POL, pol); regmap_write(fsl_dev->regmap, DCU_BGND, DCU_BGND_R(0) | DCU_BGND_G(0) | DCU_BGND_B(0)); diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 09187286d346..f90a09b83370 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -2157,7 +2157,7 @@ await_fence_array(struct i915_execbuffer *eb, if (!(flags & I915_EXEC_FENCE_WAIT)) continue; - fence = drm_syncobj_fence_get(syncobj); + drm_syncobj_search_fence(syncobj, 0, 0, &fence); if (!fence) return -EINVAL; diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c index 48b5304f460c..8edd80bb0428 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c @@ -1222,10 +1222,7 @@ static int a5xx_crashdumper_init(struct msm_gpu *gpu, SZ_1M, MSM_BO_UNCACHED, gpu->aspace, &dumper->bo, &dumper->iova); - if (IS_ERR(dumper->ptr)) - return PTR_ERR(dumper->ptr); - - return 0; + return PTR_ERR_OR_ZERO(dumper->ptr); } static void a5xx_crashdumper_free(struct msm_gpu *gpu, diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c index f549daf30fe6..d77a8cb15404 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c @@ -1179,8 +1179,6 @@ static void dpu_plane_destroy(struct drm_plane *plane) mutex_destroy(&pdpu->lock); - drm_plane_helper_disable(plane, NULL); - /* this will destroy the states as well */ drm_plane_cleanup(plane); diff --git a/drivers/gpu/drm/msm/disp/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/disp/mdp4/mdp4_plane.c index 79ff653d8081..7a499731ce93 100644 --- a/drivers/gpu/drm/msm/disp/mdp4/mdp4_plane.c +++ b/drivers/gpu/drm/msm/disp/mdp4/mdp4_plane.c @@ -68,7 +68,6 @@ static void mdp4_plane_destroy(struct drm_plane *plane) { struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane); - drm_plane_helper_disable(plane, NULL); drm_plane_cleanup(plane); kfree(mdp4_plane); diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c index 7f42c3e68a53..310459541e48 100644 --- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c +++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c @@ -46,7 +46,6 @@ static void mdp5_plane_destroy(struct drm_plane *plane) { struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); - drm_plane_helper_disable(plane, NULL); drm_plane_cleanup(plane); kfree(mdp5_plane); diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c index c79659ca5706..23670907a29d 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi.c @@ -579,7 +579,7 @@ static int msm_hdmi_bind(struct device *dev, struct device *master, void *data) hdmi_cfg = (struct hdmi_platform_config *) of_device_get_match_data(dev); if (!hdmi_cfg) { - dev_err(dev, "unknown hdmi_cfg: %s\n", of_node->name); + dev_err(dev, "unknown hdmi_cfg: %pOFn\n", of_node); return -ENXIO; } diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 4904d0d41409..5e758d95751a 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -312,6 +312,7 @@ static int msm_drm_uninit(struct device *dev) if (fbdev && priv->fbdev) msm_fbdev_free(ddev); #endif + drm_atomic_helper_shutdown(ddev); drm_mode_config_cleanup(ddev); pm_runtime_get_sync(dev); diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 6020c30a33b3..b25f6da9dc0b 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -90,6 +90,18 @@ config DRM_PANEL_LG_LG4573 Say Y here if you want to enable support for LG4573 RGB panel. To compile this driver as a module, choose M here. +config DRM_PANEL_OLIMEX_LCD_OLINUXINO + tristate "Olimex LCD-OLinuXino panel" + depends on OF + depends on I2C + depends on BACKLIGHT_CLASS_DEVICE + help + The panel is used with different sizes LCDs, from 480x272 to + 1280x800, and 24 bit per pixel. + + Say Y here if you want to enable support for Olimex Ltd. + LCD-OLinuXino panel. + config DRM_PANEL_ORISETECH_OTM8009A tristate "Orise Technology otm8009a 480x800 dsi 2dl panel" depends on OF @@ -126,6 +138,12 @@ config DRM_PANEL_RAYDIUM_RM68200 Say Y here if you want to enable support for Raydium RM68200 720x1280 DSI video mode panel. +config DRM_PANEL_SAMSUNG_S6D16D0 + tristate "Samsung S6D16D0 DSI video mode panel" + depends on OF + depends on DRM_MIPI_DSI + select VIDEOMODE_HELPERS + config DRM_PANEL_SAMSUNG_S6E3HA2 tristate "Samsung S6E3HA2 DSI video mode panel" depends on OF diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index 5ccaaa9d13af..b9cea8f50d2a 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -7,11 +7,13 @@ obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9881C) += panel-ilitek-ili9881c.o obj-$(CONFIG_DRM_PANEL_INNOLUX_P079ZCA) += panel-innolux-p079zca.o obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o +obj-$(CONFIG_DRM_PANEL_OLIMEX_LCD_OLINUXINO) += panel-olimex-lcd-olinuxino.o obj-$(CONFIG_DRM_PANEL_ORISETECH_OTM8009A) += panel-orisetech-otm8009a.o obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o obj-$(CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN) += panel-raspberrypi-touchscreen.o obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM68200) += panel-raydium-rm68200.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o +obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6D16D0) += panel-samsung-s6d16d0.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2) += panel-samsung-s6e3ha2.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03) += panel-samsung-s6e63j0x03.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o diff --git a/drivers/gpu/drm/panel/panel-innolux-p079zca.c b/drivers/gpu/drm/panel/panel-innolux-p079zca.c index 72edb334d997..ca4ae45dd307 100644 --- a/drivers/gpu/drm/panel/panel-innolux-p079zca.c +++ b/drivers/gpu/drm/panel/panel-innolux-p079zca.c @@ -506,8 +506,7 @@ static int innolux_panel_add(struct mipi_dsi_device *dsi, static void innolux_panel_del(struct innolux_panel *innolux) { - if (innolux->base.dev) - drm_panel_remove(&innolux->base); + drm_panel_remove(&innolux->base); } static int innolux_panel_probe(struct mipi_dsi_device *dsi) diff --git a/drivers/gpu/drm/panel/panel-olimex-lcd-olinuxino.c b/drivers/gpu/drm/panel/panel-olimex-lcd-olinuxino.c new file mode 100644 index 000000000000..5e8d4523e9ed --- /dev/null +++ b/drivers/gpu/drm/panel/panel-olimex-lcd-olinuxino.c @@ -0,0 +1,330 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * LCD-OLinuXino support for panel driver + * + * Copyright (C) 2018 Olimex Ltd. + * Author: Stefan Mavrodiev <stefan@olimex.com> + */ + +#include <linux/backlight.h> +#include <linux/crc32.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/regulator/consumer.h> + +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> +#include <drm/drmP.h> + +#include <video/videomode.h> +#include <video/display_timing.h> + +#define LCD_OLINUXINO_HEADER_MAGIC 0x4F4CB727 +#define LCD_OLINUXINO_DATA_LEN 256 + +struct lcd_olinuxino_mode { + u32 pixelclock; + u32 hactive; + u32 hfp; + u32 hbp; + u32 hpw; + u32 vactive; + u32 vfp; + u32 vbp; + u32 vpw; + u32 refresh; + u32 flags; +}; + +struct lcd_olinuxino_info { + char name[32]; + u32 width_mm; + u32 height_mm; + u32 bpc; + u32 bus_format; + u32 bus_flag; +} __attribute__((__packed__)); + +struct lcd_olinuxino_eeprom { + u32 header; + u32 id; + char revision[4]; + u32 serial; + struct lcd_olinuxino_info info; + u32 num_modes; + u8 reserved[180]; + u32 checksum; +} __attribute__((__packed__)); + +struct lcd_olinuxino { + struct drm_panel panel; + struct device *dev; + struct i2c_client *client; + struct mutex mutex; + + bool prepared; + bool enabled; + + struct backlight_device *backlight; + struct regulator *supply; + struct gpio_desc *enable_gpio; + + struct lcd_olinuxino_eeprom eeprom; +}; + +static inline struct lcd_olinuxino *to_lcd_olinuxino(struct drm_panel *panel) +{ + return container_of(panel, struct lcd_olinuxino, panel); +} + +static int lcd_olinuxino_disable(struct drm_panel *panel) +{ + struct lcd_olinuxino *lcd = to_lcd_olinuxino(panel); + + if (!lcd->enabled) + return 0; + + backlight_disable(lcd->backlight); + + lcd->enabled = false; + + return 0; +} + +static int lcd_olinuxino_unprepare(struct drm_panel *panel) +{ + struct lcd_olinuxino *lcd = to_lcd_olinuxino(panel); + + if (!lcd->prepared) + return 0; + + gpiod_set_value_cansleep(lcd->enable_gpio, 0); + regulator_disable(lcd->supply); + + lcd->prepared = false; + + return 0; +} + +static int lcd_olinuxino_prepare(struct drm_panel *panel) +{ + struct lcd_olinuxino *lcd = to_lcd_olinuxino(panel); + int ret; + + if (lcd->prepared) + return 0; + + ret = regulator_enable(lcd->supply); + if (ret < 0) + return ret; + + gpiod_set_value_cansleep(lcd->enable_gpio, 1); + lcd->prepared = true; + + return 0; +} + +static int lcd_olinuxino_enable(struct drm_panel *panel) +{ + struct lcd_olinuxino *lcd = to_lcd_olinuxino(panel); + + if (lcd->enabled) + return 0; + + backlight_enable(lcd->backlight); + + lcd->enabled = true; + + return 0; +} + +static int lcd_olinuxino_get_modes(struct drm_panel *panel) +{ + struct lcd_olinuxino *lcd = to_lcd_olinuxino(panel); + struct drm_connector *connector = lcd->panel.connector; + struct lcd_olinuxino_info *lcd_info = &lcd->eeprom.info; + struct drm_device *drm = lcd->panel.drm; + struct lcd_olinuxino_mode *lcd_mode; + struct drm_display_mode *mode; + u32 i, num = 0; + + for (i = 0; i < lcd->eeprom.num_modes; i++) { + lcd_mode = (struct lcd_olinuxino_mode *) + &lcd->eeprom.reserved[i * sizeof(*lcd_mode)]; + + mode = drm_mode_create(drm); + if (!mode) { + dev_err(drm->dev, "failed to add mode %ux%u@%u\n", + lcd_mode->hactive, + lcd_mode->vactive, + lcd_mode->refresh); + continue; + } + + mode->clock = lcd_mode->pixelclock; + mode->hdisplay = lcd_mode->hactive; + mode->hsync_start = lcd_mode->hactive + lcd_mode->hfp; + mode->hsync_end = lcd_mode->hactive + lcd_mode->hfp + + lcd_mode->hpw; + mode->htotal = lcd_mode->hactive + lcd_mode->hfp + + lcd_mode->hpw + lcd_mode->hbp; + mode->vdisplay = lcd_mode->vactive; + mode->vsync_start = lcd_mode->vactive + lcd_mode->vfp; + mode->vsync_end = lcd_mode->vactive + lcd_mode->vfp + + lcd_mode->vpw; + mode->vtotal = lcd_mode->vactive + lcd_mode->vfp + + lcd_mode->vpw + lcd_mode->vbp; + mode->vrefresh = lcd_mode->refresh; + + /* Always make the first mode preferred */ + if (i == 0) + mode->type |= DRM_MODE_TYPE_PREFERRED; + mode->type |= DRM_MODE_TYPE_DRIVER; + + drm_mode_set_name(mode); + drm_mode_probed_add(connector, mode); + + num++; + } + + memcpy(connector->display_info.name, lcd_info->name, 32); + connector->display_info.width_mm = lcd_info->width_mm; + connector->display_info.height_mm = lcd_info->height_mm; + connector->display_info.bpc = lcd_info->bpc; + + if (lcd_info->bus_format) + drm_display_info_set_bus_formats(&connector->display_info, + &lcd_info->bus_format, 1); + connector->display_info.bus_flags = lcd_info->bus_flag; + + return num; +} + +static const struct drm_panel_funcs lcd_olinuxino_funcs = { + .disable = lcd_olinuxino_disable, + .unprepare = lcd_olinuxino_unprepare, + .prepare = lcd_olinuxino_prepare, + .enable = lcd_olinuxino_enable, + .get_modes = lcd_olinuxino_get_modes, +}; + +static int lcd_olinuxino_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct lcd_olinuxino *lcd; + u32 checksum, i; + int ret = 0; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | + I2C_FUNC_SMBUS_READ_I2C_BLOCK)) + return -ENODEV; + + lcd = devm_kzalloc(dev, sizeof(*lcd), GFP_KERNEL); + if (!lcd) + return -ENOMEM; + + i2c_set_clientdata(client, lcd); + lcd->dev = dev; + lcd->client = client; + + mutex_init(&lcd->mutex); + + /* Copy data into buffer */ + for (i = 0; i < LCD_OLINUXINO_DATA_LEN; i += I2C_SMBUS_BLOCK_MAX) { + mutex_lock(&lcd->mutex); + ret = i2c_smbus_read_i2c_block_data(client, + i, + I2C_SMBUS_BLOCK_MAX, + (u8 *)&lcd->eeprom + i); + mutex_unlock(&lcd->mutex); + if (ret < 0) { + dev_err(dev, "error reading from device at %02x\n", i); + return ret; + } + } + + /* Check configuration checksum */ + checksum = ~crc32(~0, (u8 *)&lcd->eeprom, 252); + if (checksum != lcd->eeprom.checksum) { + dev_err(dev, "configuration checksum does not match!\n"); + return -EINVAL; + } + + /* Check magic header */ + if (lcd->eeprom.header != LCD_OLINUXINO_HEADER_MAGIC) { + dev_err(dev, "magic header does not match\n"); + return -EINVAL; + } + + dev_info(dev, "Detected %s, Rev. %s, Serial: %08x\n", + lcd->eeprom.info.name, + lcd->eeprom.revision, + lcd->eeprom.serial); + + /* + * The eeprom can hold up to 4 modes. + * If the stored value is bigger, overwrite it. + */ + if (lcd->eeprom.num_modes > 4) { + dev_warn(dev, "invalid number of modes, falling back to 4\n"); + lcd->eeprom.num_modes = 4; + } + + lcd->enabled = false; + lcd->prepared = false; + + lcd->supply = devm_regulator_get(dev, "power"); + if (IS_ERR(lcd->supply)) + return PTR_ERR(lcd->supply); + + lcd->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(lcd->enable_gpio)) + return PTR_ERR(lcd->enable_gpio); + + lcd->backlight = devm_of_find_backlight(dev); + if (IS_ERR(lcd->backlight)) + return PTR_ERR(lcd->backlight); + + drm_panel_init(&lcd->panel); + lcd->panel.dev = dev; + lcd->panel.funcs = &lcd_olinuxino_funcs; + + return drm_panel_add(&lcd->panel); +} + +static int lcd_olinuxino_remove(struct i2c_client *client) +{ + struct lcd_olinuxino *panel = i2c_get_clientdata(client); + + drm_panel_remove(&panel->panel); + + lcd_olinuxino_disable(&panel->panel); + lcd_olinuxino_unprepare(&panel->panel); + + return 0; +} + +static const struct of_device_id lcd_olinuxino_of_ids[] = { + { .compatible = "olimex,lcd-olinuxino" }, + { } +}; +MODULE_DEVICE_TABLE(of, lcd_olinuxino_of_ids); + +static struct i2c_driver lcd_olinuxino_driver = { + .driver = { + .name = "lcd_olinuxino", + .of_match_table = lcd_olinuxino_of_ids, + }, + .probe = lcd_olinuxino_probe, + .remove = lcd_olinuxino_remove, +}; + +module_i2c_driver(lcd_olinuxino_driver); + +MODULE_AUTHOR("Stefan Mavrodiev <stefan@olimex.com>"); +MODULE_DESCRIPTION("LCD-OLinuXino driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c b/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c new file mode 100644 index 000000000000..fa8bfa7c492d --- /dev/null +++ b/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * MIPI-DSI Samsung s6d16d0 panel driver. This is a 864x480 + * AMOLED panel with a command-only DSI interface. + */ + +#include <drm/drm_modes.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_panel.h> +#include <drm/drm_print.h> + +#include <linux/gpio/consumer.h> +#include <linux/regulator/consumer.h> +#include <linux/delay.h> +#include <linux/of_device.h> +#include <linux/module.h> + +struct s6d16d0 { + struct device *dev; + struct drm_panel panel; + struct regulator *supply; + struct gpio_desc *reset_gpio; +}; + +/* + * The timings are not very helpful as the display is used in + * command mode. + */ +static const struct drm_display_mode samsung_s6d16d0_mode = { + /* HS clock, (htotal*vtotal*vrefresh)/1000 */ + .clock = 420160, + .hdisplay = 864, + .hsync_start = 864 + 154, + .hsync_end = 864 + 154 + 16, + .htotal = 864 + 154 + 16 + 32, + .vdisplay = 480, + .vsync_start = 480 + 1, + .vsync_end = 480 + 1 + 1, + .vtotal = 480 + 1 + 1 + 1, + /* + * This depends on the clocking HS vs LP rate, this value + * is calculated as: + * vrefresh = (clock * 1000) / (htotal*vtotal) + */ + .vrefresh = 816, + .width_mm = 84, + .height_mm = 48, +}; + +static inline struct s6d16d0 *panel_to_s6d16d0(struct drm_panel *panel) +{ + return container_of(panel, struct s6d16d0, panel); +} + +static int s6d16d0_unprepare(struct drm_panel *panel) +{ + struct s6d16d0 *s6 = panel_to_s6d16d0(panel); + struct mipi_dsi_device *dsi = to_mipi_dsi_device(s6->dev); + int ret; + + /* Enter sleep mode */ + ret = mipi_dsi_dcs_enter_sleep_mode(dsi); + if (ret) { + DRM_DEV_ERROR(s6->dev, "failed to enter sleep mode (%d)\n", + ret); + return ret; + } + + /* Assert RESET */ + gpiod_set_value_cansleep(s6->reset_gpio, 1); + regulator_disable(s6->supply); + + return 0; +} + +static int s6d16d0_prepare(struct drm_panel *panel) +{ + struct s6d16d0 *s6 = panel_to_s6d16d0(panel); + struct mipi_dsi_device *dsi = to_mipi_dsi_device(s6->dev); + int ret; + + ret = regulator_enable(s6->supply); + if (ret) { + DRM_DEV_ERROR(s6->dev, "failed to enable supply (%d)\n", ret); + return ret; + } + + /* Assert RESET */ + gpiod_set_value_cansleep(s6->reset_gpio, 1); + udelay(10); + /* De-assert RESET */ + gpiod_set_value_cansleep(s6->reset_gpio, 0); + msleep(120); + + /* Enabe tearing mode: send TE (tearing effect) at VBLANK */ + ret = mipi_dsi_dcs_set_tear_on(dsi, + MIPI_DSI_DCS_TEAR_MODE_VBLANK); + if (ret) { + DRM_DEV_ERROR(s6->dev, "failed to enble vblank TE (%d)\n", + ret); + return ret; + } + /* Exit sleep mode and power on */ + ret = mipi_dsi_dcs_exit_sleep_mode(dsi); + if (ret) { + DRM_DEV_ERROR(s6->dev, "failed to exit sleep mode (%d)\n", + ret); + return ret; + } + + return 0; +} + +static int s6d16d0_enable(struct drm_panel *panel) +{ + struct s6d16d0 *s6 = panel_to_s6d16d0(panel); + struct mipi_dsi_device *dsi = to_mipi_dsi_device(s6->dev); + int ret; + + ret = mipi_dsi_dcs_set_display_on(dsi); + if (ret) { + DRM_DEV_ERROR(s6->dev, "failed to turn display on (%d)\n", + ret); + return ret; + } + + return 0; +} + +static int s6d16d0_disable(struct drm_panel *panel) +{ + struct s6d16d0 *s6 = panel_to_s6d16d0(panel); + struct mipi_dsi_device *dsi = to_mipi_dsi_device(s6->dev); + int ret; + + ret = mipi_dsi_dcs_set_display_off(dsi); + if (ret) { + DRM_DEV_ERROR(s6->dev, "failed to turn display off (%d)\n", + ret); + return ret; + } + + return 0; +} + +static int s6d16d0_get_modes(struct drm_panel *panel) +{ + struct drm_connector *connector = panel->connector; + struct drm_display_mode *mode; + + strncpy(connector->display_info.name, "Samsung S6D16D0\0", + DRM_DISPLAY_INFO_LEN); + + mode = drm_mode_duplicate(panel->drm, &samsung_s6d16d0_mode); + if (!mode) { + DRM_ERROR("bad mode or failed to add mode\n"); + return -EINVAL; + } + drm_mode_set_name(mode); + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; + + connector->display_info.width_mm = mode->width_mm; + connector->display_info.height_mm = mode->height_mm; + + drm_mode_probed_add(connector, mode); + + return 1; /* Number of modes */ +} + +static const struct drm_panel_funcs s6d16d0_drm_funcs = { + .disable = s6d16d0_disable, + .unprepare = s6d16d0_unprepare, + .prepare = s6d16d0_prepare, + .enable = s6d16d0_enable, + .get_modes = s6d16d0_get_modes, +}; + +static int s6d16d0_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct s6d16d0 *s6; + int ret; + + s6 = devm_kzalloc(dev, sizeof(struct s6d16d0), GFP_KERNEL); + if (!s6) + return -ENOMEM; + + mipi_dsi_set_drvdata(dsi, s6); + s6->dev = dev; + + dsi->lanes = 2; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->hs_rate = 420160000; + dsi->lp_rate = 19200000; + /* + * This display uses command mode so no MIPI_DSI_MODE_VIDEO + * or MIPI_DSI_MODE_VIDEO_SYNC_PULSE + * + * As we only send commands we do not need to be continuously + * clocked. + */ + dsi->mode_flags = + MIPI_DSI_CLOCK_NON_CONTINUOUS | + MIPI_DSI_MODE_EOT_PACKET; + + s6->supply = devm_regulator_get(dev, "vdd1"); + if (IS_ERR(s6->supply)) + return PTR_ERR(s6->supply); + + /* This asserts RESET by default */ + s6->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(s6->reset_gpio)) { + ret = PTR_ERR(s6->reset_gpio); + if (ret != -EPROBE_DEFER) + DRM_DEV_ERROR(dev, "failed to request GPIO (%d)\n", + ret); + return ret; + } + + drm_panel_init(&s6->panel); + s6->panel.dev = dev; + s6->panel.funcs = &s6d16d0_drm_funcs; + + ret = drm_panel_add(&s6->panel); + if (ret < 0) + return ret; + + ret = mipi_dsi_attach(dsi); + if (ret < 0) + drm_panel_remove(&s6->panel); + + return ret; +} + +static int s6d16d0_remove(struct mipi_dsi_device *dsi) +{ + struct s6d16d0 *s6 = mipi_dsi_get_drvdata(dsi); + + mipi_dsi_detach(dsi); + drm_panel_remove(&s6->panel); + + return 0; +} + +static const struct of_device_id s6d16d0_of_match[] = { + { .compatible = "samsung,s6d16d0" }, + { } +}; +MODULE_DEVICE_TABLE(of, s6d16d0_of_match); + +static struct mipi_dsi_driver s6d16d0_driver = { + .probe = s6d16d0_probe, + .remove = s6d16d0_remove, + .driver = { + .name = "panel-samsung-s6d16d0", + .of_match_table = s6d16d0_of_match, + }, +}; +module_mipi_dsi_driver(s6d16d0_driver); + +MODULE_AUTHOR("Linus Wallei <linus.walleij@linaro.org>"); +MODULE_DESCRIPTION("MIPI-DSI s6d16d0 Panel Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/panel/panel-seiko-43wvf1g.c b/drivers/gpu/drm/panel/panel-seiko-43wvf1g.c index 75f925390551..2d99e28ff117 100644 --- a/drivers/gpu/drm/panel/panel-seiko-43wvf1g.c +++ b/drivers/gpu/drm/panel/panel-seiko-43wvf1g.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2017 NXP Semiconductors. * Author: Marco Franchi <marco.franchi@nxp.com> * * Based on Panel Simple driver by Thierry Reding <treding@nvidia.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. */ #include <linux/backlight.h> @@ -366,6 +363,6 @@ static struct platform_driver seiko_panel_platform_driver = { }; module_platform_driver(seiko_panel_platform_driver); -MODULE_AUTHOR("Marco Franchi <marco.franchi@nxp.com"); +MODULE_AUTHOR("Marco Franchi <marco.franchi@nxp.com>"); MODULE_DESCRIPTION("Seiko 43WVF1G panel driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 97964f7f2ace..90296090340e 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -772,16 +772,38 @@ static const struct panel_desc avic_tm070ddh03 = { }, }; +static const struct drm_display_mode bananapi_s070wv20_ct16_mode = { + .clock = 30000, + .hdisplay = 800, + .hsync_start = 800 + 40, + .hsync_end = 800 + 40 + 48, + .htotal = 800 + 40 + 48 + 40, + .vdisplay = 480, + .vsync_start = 480 + 13, + .vsync_end = 480 + 13 + 3, + .vtotal = 480 + 13 + 3 + 29, +}; + +static const struct panel_desc bananapi_s070wv20_ct16 = { + .modes = &bananapi_s070wv20_ct16_mode, + .num_modes = 1, + .bpc = 6, + .size = { + .width = 154, + .height = 86, + }, +}; + static const struct drm_display_mode boe_hv070wsa_mode = { - .clock = 40800, + .clock = 42105, .hdisplay = 1024, - .hsync_start = 1024 + 90, - .hsync_end = 1024 + 90 + 90, - .htotal = 1024 + 90 + 90 + 90, + .hsync_start = 1024 + 30, + .hsync_end = 1024 + 30 + 30, + .htotal = 1024 + 30 + 30 + 30, .vdisplay = 600, - .vsync_start = 600 + 3, - .vsync_end = 600 + 3 + 4, - .vtotal = 600 + 3 + 4 + 3, + .vsync_start = 600 + 10, + .vsync_end = 600 + 10 + 10, + .vtotal = 600 + 10 + 10 + 10, .vrefresh = 60, }; @@ -836,6 +858,55 @@ static const struct panel_desc boe_nv101wxmn51 = { }, }; +static const struct drm_display_mode cdtech_s043wq26h_ct7_mode = { + .clock = 9000, + .hdisplay = 480, + .hsync_start = 480 + 5, + .hsync_end = 480 + 5 + 5, + .htotal = 480 + 5 + 5 + 40, + .vdisplay = 272, + .vsync_start = 272 + 8, + .vsync_end = 272 + 8 + 8, + .vtotal = 272 + 8 + 8 + 8, + .vrefresh = 60, + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, +}; + +static const struct panel_desc cdtech_s043wq26h_ct7 = { + .modes = &cdtech_s043wq26h_ct7_mode, + .num_modes = 1, + .bpc = 8, + .size = { + .width = 95, + .height = 54, + }, + .bus_flags = DRM_BUS_FLAG_PIXDATA_POSEDGE, +}; + +static const struct drm_display_mode cdtech_s070wv95_ct16_mode = { + .clock = 35000, + .hdisplay = 800, + .hsync_start = 800 + 40, + .hsync_end = 800 + 40 + 40, + .htotal = 800 + 40 + 40 + 48, + .vdisplay = 480, + .vsync_start = 480 + 29, + .vsync_end = 480 + 29 + 13, + .vtotal = 480 + 29 + 13 + 3, + .vrefresh = 60, + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, +}; + +static const struct panel_desc cdtech_s070wv95_ct16 = { + .modes = &cdtech_s070wv95_ct16_mode, + .num_modes = 1, + .bpc = 8, + .size = { + .width = 154, + .height = 85, + }, +}; + static const struct drm_display_mode chunghwa_claa070wp03xg_mode = { .clock = 66770, .hdisplay = 800, @@ -961,6 +1032,36 @@ static const struct panel_desc dlc_dlc0700yzg_1 = { .bus_format = MEDIA_BUS_FMT_RGB666_1X7X3_SPWG, }; +static const struct display_timing dlc_dlc1010gig_timing = { + .pixelclock = { 68900000, 71100000, 73400000 }, + .hactive = { 1280, 1280, 1280 }, + .hfront_porch = { 43, 53, 63 }, + .hback_porch = { 43, 53, 63 }, + .hsync_len = { 44, 54, 64 }, + .vactive = { 800, 800, 800 }, + .vfront_porch = { 5, 8, 11 }, + .vback_porch = { 5, 8, 11 }, + .vsync_len = { 5, 7, 11 }, + .flags = DISPLAY_FLAGS_DE_HIGH, +}; + +static const struct panel_desc dlc_dlc1010gig = { + .timings = &dlc_dlc1010gig_timing, + .num_timings = 1, + .bpc = 8, + .size = { + .width = 216, + .height = 135, + }, + .delay = { + .prepare = 60, + .enable = 150, + .disable = 100, + .unprepare = 60, + }, + .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, +}; + static const struct drm_display_mode edt_et057090dhu_mode = { .clock = 25175, .hdisplay = 640, @@ -2370,12 +2471,21 @@ static const struct of_device_id platform_of_match[] = { .compatible = "avic,tm070ddh03", .data = &avic_tm070ddh03, }, { + .compatible = "bananapi,s070wv20-ct16", + .data = &bananapi_s070wv20_ct16, + }, { .compatible = "boe,hv070wsa-100", .data = &boe_hv070wsa }, { .compatible = "boe,nv101wxmn51", .data = &boe_nv101wxmn51, }, { + .compatible = "cdtech,s043wq26h-ct7", + .data = &cdtech_s043wq26h_ct7, + }, { + .compatible = "cdtech,s070wv95-ct16", + .data = &cdtech_s070wv95_ct16, + }, { .compatible = "chunghwa,claa070wp03xg", .data = &chunghwa_claa070wp03xg, }, { @@ -2391,6 +2501,9 @@ static const struct of_device_id platform_of_match[] = { .compatible = "dlc,dlc0700yzg-1", .data = &dlc_dlc0700yzg_1, }, { + .compatible = "dlc,dlc1010gig", + .data = &dlc_dlc1010gig, + }, { .compatible = "edt,et057090dhu", .data = &edt_et057090dhu, }, { diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index 084f58df4a8c..7015974c247a 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -404,32 +404,15 @@ static struct drm_driver rcar_du_driver = { static int rcar_du_pm_suspend(struct device *dev) { struct rcar_du_device *rcdu = dev_get_drvdata(dev); - struct drm_atomic_state *state; - drm_kms_helper_poll_disable(rcdu->ddev); - drm_fbdev_cma_set_suspend_unlocked(rcdu->fbdev, true); - - state = drm_atomic_helper_suspend(rcdu->ddev); - if (IS_ERR(state)) { - drm_fbdev_cma_set_suspend_unlocked(rcdu->fbdev, false); - drm_kms_helper_poll_enable(rcdu->ddev); - return PTR_ERR(state); - } - - rcdu->suspend_state = state; - - return 0; + return drm_mode_config_helper_suspend(rcdu->ddev); } static int rcar_du_pm_resume(struct device *dev) { struct rcar_du_device *rcdu = dev_get_drvdata(dev); - drm_atomic_helper_resume(rcdu->ddev, rcdu->suspend_state); - drm_fbdev_cma_set_suspend_unlocked(rcdu->fbdev, false); - drm_kms_helper_poll_enable(rcdu->ddev); - - return 0; + return drm_mode_config_helper_resume(rcdu->ddev); } #endif diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h index 143c037e2c0f..9f5563296c5a 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h @@ -79,7 +79,6 @@ struct rcar_du_device { struct drm_device *ddev; struct drm_fbdev_cma *fbdev; - struct drm_atomic_state *suspend_state; struct rcar_du_crtc crtcs[RCAR_DU_MAX_CRTCS]; unsigned int num_crtcs; diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c index 11309a2a4e43..89c63cfde5c8 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -11,6 +11,7 @@ #include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/phy/phy.h> #include <linux/regmap.h> #include <drm/drm_of.h> @@ -24,6 +25,24 @@ #define RK3288_GRF_SOC_CON6 0x025C #define RK3288_HDMI_LCDC_SEL BIT(4) +#define RK3328_GRF_SOC_CON2 0x0408 + +#define RK3328_HDMI_SDAIN_MSK BIT(11) +#define RK3328_HDMI_SCLIN_MSK BIT(10) +#define RK3328_HDMI_HPD_IOE BIT(2) +#define RK3328_GRF_SOC_CON3 0x040c +/* need to be unset if hdmi or i2c should control voltage */ +#define RK3328_HDMI_SDA5V_GRF BIT(15) +#define RK3328_HDMI_SCL5V_GRF BIT(14) +#define RK3328_HDMI_HPD5V_GRF BIT(13) +#define RK3328_HDMI_CEC5V_GRF BIT(12) +#define RK3328_GRF_SOC_CON4 0x0410 +#define RK3328_HDMI_HPD_SARADC BIT(13) +#define RK3328_HDMI_CEC_5V BIT(11) +#define RK3328_HDMI_SDA_5V BIT(10) +#define RK3328_HDMI_SCL_5V BIT(9) +#define RK3328_HDMI_HPD_5V BIT(8) + #define RK3399_GRF_SOC_CON20 0x6250 #define RK3399_HDMI_LCDC_SEL BIT(6) @@ -36,7 +55,7 @@ * @lcdsel_lit: reg value of selecting vop little for HDMI */ struct rockchip_hdmi_chip_data { - u32 lcdsel_grf_reg; + int lcdsel_grf_reg; u32 lcdsel_big; u32 lcdsel_lit; }; @@ -49,6 +68,7 @@ struct rockchip_hdmi { struct clk *vpll_clk; struct clk *grf_clk; struct dw_hdmi *hdmi; + struct phy *phy; }; #define to_rockchip_hdmi(x) container_of(x, struct rockchip_hdmi, x) @@ -245,6 +265,9 @@ static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder) u32 val; int ret; + if (hdmi->chip_data->lcdsel_grf_reg < 0) + return; + ret = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder); if (ret) val = hdmi->chip_data->lcdsel_lit; @@ -287,6 +310,66 @@ static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_fun .atomic_check = dw_hdmi_rockchip_encoder_atomic_check, }; +static int dw_hdmi_rockchip_genphy_init(struct dw_hdmi *dw_hdmi, void *data, + struct drm_display_mode *mode) +{ + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + + return phy_power_on(hdmi->phy); +} + +static void dw_hdmi_rockchip_genphy_disable(struct dw_hdmi *dw_hdmi, void *data) +{ + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + + phy_power_off(hdmi->phy); +} + +static enum drm_connector_status +dw_hdmi_rk3328_read_hpd(struct dw_hdmi *dw_hdmi, void *data) +{ + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + enum drm_connector_status status; + + status = dw_hdmi_phy_read_hpd(dw_hdmi, data); + + if (status == connector_status_connected) + regmap_write(hdmi->regmap, + RK3328_GRF_SOC_CON4, + HIWORD_UPDATE(RK3328_HDMI_SDA_5V | RK3328_HDMI_SCL_5V, + RK3328_HDMI_SDA_5V | RK3328_HDMI_SCL_5V)); + else + regmap_write(hdmi->regmap, + RK3328_GRF_SOC_CON4, + HIWORD_UPDATE(0, RK3328_HDMI_SDA_5V | + RK3328_HDMI_SCL_5V)); + return status; +} + +static void dw_hdmi_rk3328_setup_hpd(struct dw_hdmi *dw_hdmi, void *data) +{ + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + + dw_hdmi_phy_setup_hpd(dw_hdmi, data); + + /* Enable and map pins to 3V grf-controlled io-voltage */ + regmap_write(hdmi->regmap, + RK3328_GRF_SOC_CON4, + HIWORD_UPDATE(0, RK3328_HDMI_HPD_SARADC | RK3328_HDMI_CEC_5V | + RK3328_HDMI_SDA_5V | RK3328_HDMI_SCL_5V | + RK3328_HDMI_HPD_5V)); + regmap_write(hdmi->regmap, + RK3328_GRF_SOC_CON3, + HIWORD_UPDATE(0, RK3328_HDMI_SDA5V_GRF | RK3328_HDMI_SCL5V_GRF | + RK3328_HDMI_HPD5V_GRF | + RK3328_HDMI_CEC5V_GRF)); + regmap_write(hdmi->regmap, + RK3328_GRF_SOC_CON2, + HIWORD_UPDATE(RK3328_HDMI_SDAIN_MSK | RK3328_HDMI_SCLIN_MSK, + RK3328_HDMI_SDAIN_MSK | RK3328_HDMI_SCLIN_MSK | + RK3328_HDMI_HPD_IOE)); +} + static struct rockchip_hdmi_chip_data rk3288_chip_data = { .lcdsel_grf_reg = RK3288_GRF_SOC_CON6, .lcdsel_big = HIWORD_UPDATE(0, RK3288_HDMI_LCDC_SEL), @@ -301,6 +384,29 @@ static const struct dw_hdmi_plat_data rk3288_hdmi_drv_data = { .phy_data = &rk3288_chip_data, }; +static const struct dw_hdmi_phy_ops rk3328_hdmi_phy_ops = { + .init = dw_hdmi_rockchip_genphy_init, + .disable = dw_hdmi_rockchip_genphy_disable, + .read_hpd = dw_hdmi_rk3328_read_hpd, + .update_hpd = dw_hdmi_phy_update_hpd, + .setup_hpd = dw_hdmi_rk3328_setup_hpd, +}; + +static struct rockchip_hdmi_chip_data rk3328_chip_data = { + .lcdsel_grf_reg = -1, +}; + +static const struct dw_hdmi_plat_data rk3328_hdmi_drv_data = { + .mode_valid = dw_hdmi_rockchip_mode_valid, + .mpll_cfg = rockchip_mpll_cfg, + .cur_ctr = rockchip_cur_ctr, + .phy_config = rockchip_phy_config, + .phy_data = &rk3328_chip_data, + .phy_ops = &rk3328_hdmi_phy_ops, + .phy_name = "inno_dw_hdmi_phy2", + .phy_force_vendor = true, +}; + static struct rockchip_hdmi_chip_data rk3399_chip_data = { .lcdsel_grf_reg = RK3399_GRF_SOC_CON20, .lcdsel_big = HIWORD_UPDATE(0, RK3399_HDMI_LCDC_SEL), @@ -319,6 +425,9 @@ static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = { { .compatible = "rockchip,rk3288-dw-hdmi", .data = &rk3288_hdmi_drv_data }, + { .compatible = "rockchip,rk3328-dw-hdmi", + .data = &rk3328_hdmi_drv_data + }, { .compatible = "rockchip,rk3399-dw-hdmi", .data = &rk3399_hdmi_drv_data }, @@ -330,7 +439,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, void *data) { struct platform_device *pdev = to_platform_device(dev); - const struct dw_hdmi_plat_data *plat_data; + struct dw_hdmi_plat_data *plat_data; const struct of_device_id *match; struct drm_device *drm = data; struct drm_encoder *encoder; @@ -345,9 +454,14 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, return -ENOMEM; match = of_match_node(dw_hdmi_rockchip_dt_ids, pdev->dev.of_node); - plat_data = match->data; + plat_data = devm_kmemdup(&pdev->dev, match->data, + sizeof(*plat_data), GFP_KERNEL); + if (!plat_data) + return -ENOMEM; + hdmi->dev = &pdev->dev; hdmi->chip_data = plat_data->phy_data; + plat_data->phy_data = hdmi; encoder = &hdmi->encoder; encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); @@ -373,6 +487,14 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, return ret; } + hdmi->phy = devm_phy_optional_get(dev, "hdmi"); + if (IS_ERR(hdmi->phy)) { + ret = PTR_ERR(hdmi->phy); + if (ret != -EPROBE_DEFER) + DRM_DEV_ERROR(hdmi->dev, "failed to get phy\n"); + return ret; + } + drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs); drm_encoder_init(drm, encoder, &dw_hdmi_rockchip_encoder_funcs, DRM_MODE_ENCODER_TMDS, NULL); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c index 79d00d861a31..01ff3c858875 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c @@ -189,12 +189,14 @@ EXPORT_SYMBOL(rockchip_drm_psr_flush_all); int rockchip_drm_psr_register(struct drm_encoder *encoder, int (*psr_set)(struct drm_encoder *, bool enable)) { - struct rockchip_drm_private *drm_drv = encoder->dev->dev_private; + struct rockchip_drm_private *drm_drv; struct psr_drv *psr; if (!encoder || !psr_set) return -EINVAL; + drm_drv = encoder->dev->dev_private; + psr = kzalloc(sizeof(struct psr_drv), GFP_KERNEL); if (!psr) return -ENOMEM; diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c index a6db3cd5544b..047a8867af90 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c @@ -361,7 +361,11 @@ static const struct vop_win_data rk3188_vop_win_data[] = { }; static const int rk3188_vop_intrs[] = { - 0, + /* + * hs_start interrupt fires at frame-start, so serves + * the same purpose as dsp_hold in the driver. + */ + DSP_HOLD_VALID_INTR, FS_INTR, LINE_FLAG_INTR, BUS_ERROR_INTR, diff --git a/drivers/gpu/drm/selftests/Makefile b/drivers/gpu/drm/selftests/Makefile index 9fc349fa18e9..7e6581921da0 100644 --- a/drivers/gpu/drm/selftests/Makefile +++ b/drivers/gpu/drm/selftests/Makefile @@ -1 +1,3 @@ -obj-$(CONFIG_DRM_DEBUG_SELFTEST) += test-drm_mm.o test-drm-helper.o +test-drm_modeset-y := test-drm_modeset_common.o test-drm_plane_helper.o + +obj-$(CONFIG_DRM_DEBUG_SELFTEST) += test-drm_mm.o test-drm_modeset.o diff --git a/drivers/gpu/drm/selftests/drm_helper_selftests.h b/drivers/gpu/drm/selftests/drm_modeset_selftests.h index 9771290ed228..9771290ed228 100644 --- a/drivers/gpu/drm/selftests/drm_helper_selftests.h +++ b/drivers/gpu/drm/selftests/drm_modeset_selftests.h diff --git a/drivers/gpu/drm/selftests/test-drm_modeset_common.c b/drivers/gpu/drm/selftests/test-drm_modeset_common.c new file mode 100644 index 000000000000..2a7f93774006 --- /dev/null +++ b/drivers/gpu/drm/selftests/test-drm_modeset_common.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Common file for modeset selftests. + */ + +#include <linux/module.h> + +#include "test-drm_modeset_common.h" + +#define TESTS "drm_modeset_selftests.h" +#include "drm_selftest.h" + +#include "drm_selftest.c" + +static int __init test_drm_modeset_init(void) +{ + int err; + + err = run_selftests(selftests, ARRAY_SIZE(selftests), NULL); + + return err > 0 ? 0 : err; +} + +static void __exit test_drm_modeset_exit(void) +{ +} + +module_init(test_drm_modeset_init); +module_exit(test_drm_modeset_exit); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/selftests/test-drm_modeset_common.h b/drivers/gpu/drm/selftests/test-drm_modeset_common.h new file mode 100644 index 000000000000..b0065a2eb067 --- /dev/null +++ b/drivers/gpu/drm/selftests/test-drm_modeset_common.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __TEST_DRM_MODESET_COMMON_H__ +#define __TEST_DRM_MODESET_COMMON_H__ + +#define FAIL(test, msg, ...) \ + do { \ + if (test) { \ + pr_err("%s/%u: " msg, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ + return -EINVAL; \ + } \ + } while (0) + +#define FAIL_ON(x) FAIL((x), "%s", "FAIL_ON(" __stringify(x) ")\n") + +int igt_check_plane_state(void *ignored); + +#endif diff --git a/drivers/gpu/drm/selftests/test-drm-helper.c b/drivers/gpu/drm/selftests/test-drm_plane_helper.c index a015712b43e8..0a9553f51796 100644 --- a/drivers/gpu/drm/selftests/test-drm-helper.c +++ b/drivers/gpu/drm/selftests/test-drm_plane_helper.c @@ -1,27 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * Test cases for the drm_kms_helper functions + * Test cases for the drm_plane_helper functions */ -#define pr_fmt(fmt) "drm_kms_helper: " fmt - -#include <linux/module.h> +#define pr_fmt(fmt) "drm_plane_helper: " fmt #include <drm/drm_atomic_helper.h> #include <drm/drm_plane_helper.h> #include <drm/drm_modes.h> -#define TESTS "drm_helper_selftests.h" -#include "drm_selftest.h" - -#define FAIL(test, msg, ...) \ - do { \ - if (test) { \ - pr_err("%s/%u: " msg, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ - return -EINVAL; \ - } \ - } while (0) - -#define FAIL_ON(x) FAIL((x), "%s", "FAIL_ON(" __stringify(x) ")\n") +#include "test-drm_modeset_common.h" static void set_src(struct drm_plane_state *plane_state, unsigned src_x, unsigned src_y, @@ -85,7 +73,7 @@ static bool check_crtc_eq(struct drm_plane_state *plane_state, return true; } -static int igt_check_plane_state(void *ignored) +int igt_check_plane_state(void *ignored) { int ret; @@ -229,19 +217,3 @@ static int igt_check_plane_state(void *ignored) return 0; } - -#include "drm_selftest.c" - -static int __init test_drm_helper_init(void) -{ - int err; - - err = run_selftests(selftests, ARRAY_SIZE(selftests), NULL); - - return err > 0 ? 0 : err; -} - -module_init(test_drm_helper_init); - -MODULE_AUTHOR("Intel Corporation"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/sti/sti_crtc.c b/drivers/gpu/drm/sti/sti_crtc.c index 5824e6aca8f4..61c2379fba87 100644 --- a/drivers/gpu/drm/sti/sti_crtc.c +++ b/drivers/gpu/drm/sti/sti_crtc.c @@ -40,6 +40,8 @@ static void sti_crtc_atomic_disable(struct drm_crtc *crtc, DRM_DEBUG_DRIVER("\n"); mixer->status = STI_MIXER_DISABLING; + + drm_crtc_wait_one_vblank(crtc); } static int diff --git a/drivers/gpu/drm/sti/sti_cursor.c b/drivers/gpu/drm/sti/sti_cursor.c index 57b870e1e696..bc908453ffb3 100644 --- a/drivers/gpu/drm/sti/sti_cursor.c +++ b/drivers/gpu/drm/sti/sti_cursor.c @@ -332,7 +332,6 @@ static void sti_cursor_destroy(struct drm_plane *drm_plane) { DRM_DEBUG_DRIVER("\n"); - drm_plane_helper_disable(drm_plane, NULL); drm_plane_cleanup(drm_plane); } diff --git a/drivers/gpu/drm/sti/sti_drv.c b/drivers/gpu/drm/sti/sti_drv.c index 6dced8abcf16..ac54e0f9caea 100644 --- a/drivers/gpu/drm/sti/sti_drv.c +++ b/drivers/gpu/drm/sti/sti_drv.c @@ -206,6 +206,8 @@ static void sti_cleanup(struct drm_device *ddev) struct sti_private *private = ddev->dev_private; drm_kms_helper_poll_fini(ddev); + drm_atomic_helper_shutdown(ddev); + drm_mode_config_cleanup(ddev); component_unbind_all(ddev->dev, ddev); kfree(private); ddev->dev_private = NULL; @@ -230,7 +232,7 @@ static int sti_bind(struct device *dev) ret = drm_dev_register(ddev, 0); if (ret) - goto err_register; + goto err_cleanup; drm_mode_config_reset(ddev); @@ -238,8 +240,6 @@ static int sti_bind(struct device *dev) return 0; -err_register: - drm_mode_config_cleanup(ddev); err_cleanup: sti_cleanup(ddev); err_drm_dev_put: diff --git a/drivers/gpu/drm/sti/sti_gdp.c b/drivers/gpu/drm/sti/sti_gdp.c index c32de6cbf061..cff7b2b5ee9e 100644 --- a/drivers/gpu/drm/sti/sti_gdp.c +++ b/drivers/gpu/drm/sti/sti_gdp.c @@ -517,7 +517,7 @@ static void sti_gdp_init(struct sti_gdp *gdp) /* Allocate all the nodes within a single memory page */ size = sizeof(struct sti_gdp_node) * GDP_NODE_PER_FIELD * GDP_NODE_NB_BANK; - base = dma_alloc_wc(gdp->dev, size, &dma_addr, GFP_KERNEL | GFP_DMA); + base = dma_alloc_wc(gdp->dev, size, &dma_addr, GFP_KERNEL); if (!base) { DRM_ERROR("Failed to allocate memory for GDP node\n"); @@ -883,7 +883,6 @@ static void sti_gdp_destroy(struct drm_plane *drm_plane) { DRM_DEBUG_DRIVER("\n"); - drm_plane_helper_disable(drm_plane, NULL); drm_plane_cleanup(drm_plane); } diff --git a/drivers/gpu/drm/sti/sti_hqvdp.c b/drivers/gpu/drm/sti/sti_hqvdp.c index 03ac3b4a4469..23565f52dd71 100644 --- a/drivers/gpu/drm/sti/sti_hqvdp.c +++ b/drivers/gpu/drm/sti/sti_hqvdp.c @@ -1260,7 +1260,6 @@ static void sti_hqvdp_destroy(struct drm_plane *drm_plane) { DRM_DEBUG_DRIVER("\n"); - drm_plane_helper_disable(drm_plane, NULL); drm_plane_cleanup(drm_plane); } diff --git a/drivers/gpu/drm/stm/drv.c b/drivers/gpu/drm/stm/drv.c index f2021b23554d..d05f8f80fe6f 100644 --- a/drivers/gpu/drm/stm/drv.c +++ b/drivers/gpu/drm/stm/drv.c @@ -72,6 +72,8 @@ static struct drm_driver drv_driver = { .gem_prime_vmap = drm_gem_cma_prime_vmap, .gem_prime_vunmap = drm_gem_cma_prime_vunmap, .gem_prime_mmap = drm_gem_cma_prime_mmap, + .get_scanout_position = ltdc_crtc_scanoutpos, + .get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos, }; static int drv_load(struct drm_device *ddev) diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c index 808d9fb627e9..61dd661aa0ac 100644 --- a/drivers/gpu/drm/stm/ltdc.c +++ b/drivers/gpu/drm/stm/ltdc.c @@ -148,6 +148,8 @@ #define IER_TERRIE BIT(2) /* Transfer ERRor Interrupt Enable */ #define IER_RRIE BIT(3) /* Register Reload Interrupt enable */ +#define CPSR_CYPOS GENMASK(15, 0) /* Current Y position */ + #define ISR_LIF BIT(0) /* Line Interrupt Flag */ #define ISR_FUIF BIT(1) /* Fifo Underrun Interrupt Flag */ #define ISR_TERRIF BIT(2) /* Transfer ERRor Interrupt Flag */ @@ -626,6 +628,49 @@ static void ltdc_crtc_disable_vblank(struct drm_crtc *crtc) reg_clear(ldev->regs, LTDC_IER, IER_LIE); } +bool ltdc_crtc_scanoutpos(struct drm_device *ddev, unsigned int pipe, + bool in_vblank_irq, int *vpos, int *hpos, + ktime_t *stime, ktime_t *etime, + const struct drm_display_mode *mode) +{ + struct ltdc_device *ldev = ddev->dev_private; + int line, vactive_start, vactive_end, vtotal; + + if (stime) + *stime = ktime_get(); + + /* The active area starts after vsync + front porch and ends + * at vsync + front porc + display size. + * The total height also include back porch. + * We have 3 possible cases to handle: + * - line < vactive_start: vpos = line - vactive_start and will be + * negative + * - vactive_start < line < vactive_end: vpos = line - vactive_start + * and will be positive + * - line > vactive_end: vpos = line - vtotal - vactive_start + * and will negative + * + * Computation for the two first cases are identical so we can + * simplify the code and only test if line > vactive_end + */ + line = reg_read(ldev->regs, LTDC_CPSR) & CPSR_CYPOS; + vactive_start = reg_read(ldev->regs, LTDC_BPCR) & BPCR_AVBP; + vactive_end = reg_read(ldev->regs, LTDC_AWCR) & AWCR_AAH; + vtotal = reg_read(ldev->regs, LTDC_TWCR) & TWCR_TOTALH; + + if (line > vactive_end) + *vpos = line - vtotal - vactive_start; + else + *vpos = line - vactive_start; + + *hpos = 0; + + if (etime) + *etime = ktime_get(); + + return true; +} + static const struct drm_crtc_funcs ltdc_crtc_funcs = { .destroy = drm_crtc_cleanup, .set_config = drm_atomic_helper_set_config, diff --git a/drivers/gpu/drm/stm/ltdc.h b/drivers/gpu/drm/stm/ltdc.h index d5afb8960867..e46f477a8494 100644 --- a/drivers/gpu/drm/stm/ltdc.h +++ b/drivers/gpu/drm/stm/ltdc.h @@ -38,6 +38,11 @@ struct ltdc_device { struct fps_info plane_fpsi[LTDC_MAX_LAYER]; }; +bool ltdc_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe, + bool in_vblank_irq, int *vpos, int *hpos, + ktime_t *stime, ktime_t *etime, + const struct drm_display_mode *mode); + int ltdc_load(struct drm_device *ddev); void ltdc_unload(struct drm_device *ddev); diff --git a/drivers/gpu/drm/sun4i/sun4i_lvds.c b/drivers/gpu/drm/sun4i/sun4i_lvds.c index af7dcb6da351..e7eb0d1e17be 100644 --- a/drivers/gpu/drm/sun4i/sun4i_lvds.c +++ b/drivers/gpu/drm/sun4i/sun4i_lvds.c @@ -75,7 +75,7 @@ static void sun4i_lvds_encoder_enable(struct drm_encoder *encoder) DRM_DEBUG_DRIVER("Enabling LVDS output\n"); - if (!IS_ERR(tcon->panel)) { + if (tcon->panel) { drm_panel_prepare(tcon->panel); drm_panel_enable(tcon->panel); } @@ -88,7 +88,7 @@ static void sun4i_lvds_encoder_disable(struct drm_encoder *encoder) DRM_DEBUG_DRIVER("Disabling LVDS output\n"); - if (!IS_ERR(tcon->panel)) { + if (tcon->panel) { drm_panel_disable(tcon->panel); drm_panel_unprepare(tcon->panel); } diff --git a/drivers/gpu/drm/sun4i/sun4i_rgb.c b/drivers/gpu/drm/sun4i/sun4i_rgb.c index bf068da6b12e..f4a22689eb54 100644 --- a/drivers/gpu/drm/sun4i/sun4i_rgb.c +++ b/drivers/gpu/drm/sun4i/sun4i_rgb.c @@ -135,7 +135,7 @@ static void sun4i_rgb_encoder_enable(struct drm_encoder *encoder) DRM_DEBUG_DRIVER("Enabling RGB output\n"); - if (!IS_ERR(tcon->panel)) { + if (tcon->panel) { drm_panel_prepare(tcon->panel); drm_panel_enable(tcon->panel); } @@ -148,7 +148,7 @@ static void sun4i_rgb_encoder_disable(struct drm_encoder *encoder) DRM_DEBUG_DRIVER("Disabling RGB output\n"); - if (!IS_ERR(tcon->panel)) { + if (tcon->panel) { drm_panel_disable(tcon->panel); drm_panel_unprepare(tcon->panel); } diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c index c78cd35a1294..f949287d926c 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c @@ -491,7 +491,8 @@ static void sun4i_tcon0_mode_set_rgb(struct sun4i_tcon *tcon, sun4i_tcon0_mode_set_common(tcon, mode); /* Set dithering if needed */ - sun4i_tcon0_mode_set_dithering(tcon, tcon->panel->connector); + if (tcon->panel) + sun4i_tcon0_mode_set_dithering(tcon, tcon->panel->connector); /* Adjust clock delay */ clk_delay = sun4i_tcon_get_clk_delay(mode, 0); @@ -555,7 +556,7 @@ static void sun4i_tcon0_mode_set_rgb(struct sun4i_tcon *tcon, * Following code is a way to avoid quirks all around TCON * and DOTCLOCK drivers. */ - if (!IS_ERR(tcon->panel)) { + if (tcon->panel) { struct drm_panel *panel = tcon->panel; struct drm_connector *connector = panel->connector; struct drm_display_info display_info = connector->display_info; diff --git a/drivers/gpu/drm/udl/udl_main.c b/drivers/gpu/drm/udl/udl_main.c index f455f095a146..1b014d92855b 100644 --- a/drivers/gpu/drm/udl/udl_main.c +++ b/drivers/gpu/drm/udl/udl_main.c @@ -350,15 +350,10 @@ int udl_driver_load(struct drm_device *dev, unsigned long flags) if (ret) goto err; - ret = drm_vblank_init(dev, 1); - if (ret) - goto err_fb; - drm_kms_helper_poll_init(dev); return 0; -err_fb: - udl_fbdev_cleanup(dev); + err: if (udl->urbs.count) udl_free_urb_list(dev); diff --git a/drivers/gpu/drm/v3d/v3d_debugfs.c b/drivers/gpu/drm/v3d/v3d_debugfs.c index 4db62c545748..eb2b2d2f8553 100644 --- a/drivers/gpu/drm/v3d/v3d_debugfs.c +++ b/drivers/gpu/drm/v3d/v3d_debugfs.c @@ -71,10 +71,13 @@ static int v3d_v3d_debugfs_regs(struct seq_file *m, void *unused) V3D_READ(v3d_hub_reg_defs[i].reg)); } - for (i = 0; i < ARRAY_SIZE(v3d_gca_reg_defs); i++) { - seq_printf(m, "%s (0x%04x): 0x%08x\n", - v3d_gca_reg_defs[i].name, v3d_gca_reg_defs[i].reg, - V3D_GCA_READ(v3d_gca_reg_defs[i].reg)); + if (v3d->ver < 41) { + for (i = 0; i < ARRAY_SIZE(v3d_gca_reg_defs); i++) { + seq_printf(m, "%s (0x%04x): 0x%08x\n", + v3d_gca_reg_defs[i].name, + v3d_gca_reg_defs[i].reg, + V3D_GCA_READ(v3d_gca_reg_defs[i].reg)); + } } for (core = 0; core < v3d->cores; core++) { @@ -176,9 +179,44 @@ static int v3d_debugfs_bo_stats(struct seq_file *m, void *unused) return 0; } +static int v3d_measure_clock(struct seq_file *m, void *unused) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_device *dev = node->minor->dev; + struct v3d_dev *v3d = to_v3d_dev(dev); + uint32_t cycles; + int core = 0; + int measure_ms = 1000; + + if (v3d->ver >= 40) { + V3D_CORE_WRITE(core, V3D_V4_PCTR_0_SRC_0_3, + V3D_SET_FIELD(V3D_PCTR_CYCLE_COUNT, + V3D_PCTR_S0)); + V3D_CORE_WRITE(core, V3D_V4_PCTR_0_CLR, 1); + V3D_CORE_WRITE(core, V3D_V4_PCTR_0_EN, 1); + } else { + V3D_CORE_WRITE(core, V3D_V3_PCTR_0_PCTRS0, + V3D_PCTR_CYCLE_COUNT); + V3D_CORE_WRITE(core, V3D_V3_PCTR_0_CLR, 1); + V3D_CORE_WRITE(core, V3D_V3_PCTR_0_EN, + V3D_V3_PCTR_0_EN_ENABLE | + 1); + } + msleep(measure_ms); + cycles = V3D_CORE_READ(core, V3D_PCTR_0_PCTR0); + + seq_printf(m, "cycles: %d (%d.%d Mhz)\n", + cycles, + cycles / (measure_ms * 1000), + (cycles / (measure_ms * 100)) % 10); + + return 0; +} + static const struct drm_info_list v3d_debugfs_list[] = { {"v3d_ident", v3d_v3d_debugfs_ident, 0}, {"v3d_regs", v3d_v3d_debugfs_regs, 0}, + {"measure_clock", v3d_measure_clock, 0}, {"bo_stats", v3d_debugfs_bo_stats, 0}, }; diff --git a/drivers/gpu/drm/v3d/v3d_drv.h b/drivers/gpu/drm/v3d/v3d_drv.h index e6fed696ad86..cbe5be0c47eb 100644 --- a/drivers/gpu/drm/v3d/v3d_drv.h +++ b/drivers/gpu/drm/v3d/v3d_drv.h @@ -198,6 +198,11 @@ struct v3d_exec_info { */ struct dma_fence *bin_done_fence; + /* Fence for when the scheduler considers the render to be + * done, for when the BOs reservations should be complete. + */ + struct dma_fence *render_done_fence; + struct kref refcount; /* This is the array of BOs that were looked up at the start of exec. */ diff --git a/drivers/gpu/drm/v3d/v3d_gem.c b/drivers/gpu/drm/v3d/v3d_gem.c index 70c54774400b..e688369ca82b 100644 --- a/drivers/gpu/drm/v3d/v3d_gem.c +++ b/drivers/gpu/drm/v3d/v3d_gem.c @@ -209,7 +209,7 @@ v3d_flush_caches(struct v3d_dev *v3d) static void v3d_attach_object_fences(struct v3d_exec_info *exec) { - struct dma_fence *out_fence = &exec->render.base.s_fence->finished; + struct dma_fence *out_fence = exec->render_done_fence; struct v3d_bo *bo; int i; @@ -409,6 +409,7 @@ v3d_exec_cleanup(struct kref *ref) dma_fence_put(exec->render.done_fence); dma_fence_put(exec->bin_done_fence); + dma_fence_put(exec->render_done_fence); for (i = 0; i < exec->bo_count; i++) drm_gem_object_put_unlocked(&exec->bo[i]->base); @@ -521,12 +522,12 @@ v3d_submit_cl_ioctl(struct drm_device *dev, void *data, kref_init(&exec->refcount); ret = drm_syncobj_find_fence(file_priv, args->in_sync_bcl, - 0, &exec->bin.in_fence); + 0, 0, &exec->bin.in_fence); if (ret == -EINVAL) goto fail; ret = drm_syncobj_find_fence(file_priv, args->in_sync_rcl, - 0, &exec->render.in_fence); + 0, 0, &exec->render.in_fence); if (ret == -EINVAL) goto fail; @@ -572,6 +573,9 @@ v3d_submit_cl_ioctl(struct drm_device *dev, void *data, if (ret) goto fail_unreserve; + exec->render_done_fence = + dma_fence_get(&exec->render.base.s_fence->finished); + kref_get(&exec->refcount); /* put by scheduler job completion */ drm_sched_entity_push_job(&exec->render.base, &v3d_priv->sched_entity[V3D_RENDER]); @@ -585,7 +589,7 @@ v3d_submit_cl_ioctl(struct drm_device *dev, void *data, sync_out = drm_syncobj_find(file_priv, args->out_sync); if (sync_out) { drm_syncobj_replace_fence(sync_out, 0, - &exec->render.base.s_fence->finished); + exec->render_done_fence); drm_syncobj_put(sync_out); } diff --git a/drivers/gpu/drm/v3d/v3d_regs.h b/drivers/gpu/drm/v3d/v3d_regs.h index 854046565989..c3a5e4e44f73 100644 --- a/drivers/gpu/drm/v3d/v3d_regs.h +++ b/drivers/gpu/drm/v3d/v3d_regs.h @@ -267,6 +267,36 @@ # define V3D_PTB_BXCF_RWORDERDISA BIT(1) # define V3D_PTB_BXCF_CLIPDISA BIT(0) +#define V3D_V3_PCTR_0_EN 0x00674 +#define V3D_V3_PCTR_0_EN_ENABLE BIT(31) +#define V3D_V4_PCTR_0_EN 0x00650 +/* When a bit is set, resets the counter to 0. */ +#define V3D_V3_PCTR_0_CLR 0x00670 +#define V3D_V4_PCTR_0_CLR 0x00654 +#define V3D_PCTR_0_OVERFLOW 0x00658 + +#define V3D_V3_PCTR_0_PCTRS0 0x00684 +#define V3D_V3_PCTR_0_PCTRS15 0x00660 +#define V3D_V3_PCTR_0_PCTRSX(x) (V3D_V3_PCTR_0_PCTRS0 + \ + 4 * (x)) +/* Each src reg muxes four counters each. */ +#define V3D_V4_PCTR_0_SRC_0_3 0x00660 +#define V3D_V4_PCTR_0_SRC_28_31 0x0067c +# define V3D_PCTR_S0_MASK V3D_MASK(6, 0) +# define V3D_PCTR_S0_SHIFT 0 +# define V3D_PCTR_S1_MASK V3D_MASK(14, 8) +# define V3D_PCTR_S1_SHIFT 8 +# define V3D_PCTR_S2_MASK V3D_MASK(22, 16) +# define V3D_PCTR_S2_SHIFT 16 +# define V3D_PCTR_S3_MASK V3D_MASK(30, 24) +# define V3D_PCTR_S3_SHIFT 24 +# define V3D_PCTR_CYCLE_COUNT 32 + +/* Output values of the counters. */ +#define V3D_PCTR_0_PCTR0 0x00680 +#define V3D_PCTR_0_PCTR31 0x006fc +#define V3D_PCTR_0_PCTRX(x) (V3D_PCTR_0_PCTR0 + \ + 4 * (x)) #define V3D_GMP_STATUS 0x00800 # define V3D_GMP_STATUS_GMPRST BIT(31) # define V3D_GMP_STATUS_WR_COUNT_MASK V3D_MASK(30, 24) diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 1f1780ccdbdf..f6f5cd80c04d 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -33,6 +33,7 @@ #include <linux/pm_runtime.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_fb_helper.h> +#include <drm/drm_atomic_helper.h> #include "uapi/drm/vc4_drm.h" #include "vc4_drv.h" @@ -308,6 +309,8 @@ static void vc4_drm_unbind(struct device *dev) drm_dev_unregister(drm); + drm_atomic_helper_shutdown(drm); + drm_mode_config_cleanup(drm); drm_atomic_private_obj_fini(&vc4->ctm_manager); diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index 5b22e996af6c..251198194c38 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -1173,7 +1173,7 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data, if (args->in_sync) { ret = drm_syncobj_find_fence(file_priv, args->in_sync, - 0, &in_fence); + 0, 0, &in_fence); if (ret) goto fail; diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c index 9dc3fcbd290b..98fae4daa08c 100644 --- a/drivers/gpu/drm/vc4/vc4_plane.c +++ b/drivers/gpu/drm/vc4/vc4_plane.c @@ -266,30 +266,59 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) u32 subpixel_src_mask = (1 << 16) - 1; u32 format = fb->format->format; int num_planes = fb->format->num_planes; - u32 h_subsample = 1; - u32 v_subsample = 1; - int i; + int min_scale = 1, max_scale = INT_MAX; + struct drm_crtc_state *crtc_state; + u32 h_subsample, v_subsample; + int i, ret; + + crtc_state = drm_atomic_get_existing_crtc_state(state->state, + state->crtc); + if (!crtc_state) { + DRM_DEBUG_KMS("Invalid crtc state\n"); + return -EINVAL; + } + + /* No configuring scaling on the cursor plane, since it gets + * non-vblank-synced updates, and scaling requires LBM changes which + * have to be vblank-synced. + */ + if (plane->type == DRM_PLANE_TYPE_CURSOR) { + min_scale = DRM_PLANE_HELPER_NO_SCALING; + max_scale = DRM_PLANE_HELPER_NO_SCALING; + } else { + min_scale = 1; + max_scale = INT_MAX; + } + + ret = drm_atomic_helper_check_plane_state(state, crtc_state, + min_scale, max_scale, + true, true); + if (ret) + return ret; + + h_subsample = drm_format_horz_chroma_subsampling(format); + v_subsample = drm_format_vert_chroma_subsampling(format); for (i = 0; i < num_planes; i++) vc4_state->offsets[i] = bo->paddr + fb->offsets[i]; /* We don't support subpixel source positioning for scaling. */ - if ((state->src_x & subpixel_src_mask) || - (state->src_y & subpixel_src_mask) || - (state->src_w & subpixel_src_mask) || - (state->src_h & subpixel_src_mask)) { + if ((state->src.x1 & subpixel_src_mask) || + (state->src.x2 & subpixel_src_mask) || + (state->src.y1 & subpixel_src_mask) || + (state->src.y2 & subpixel_src_mask)) { return -EINVAL; } - vc4_state->src_x = state->src_x >> 16; - vc4_state->src_y = state->src_y >> 16; - vc4_state->src_w[0] = state->src_w >> 16; - vc4_state->src_h[0] = state->src_h >> 16; + vc4_state->src_x = state->src.x1 >> 16; + vc4_state->src_y = state->src.y1 >> 16; + vc4_state->src_w[0] = (state->src.x2 - state->src.x1) >> 16; + vc4_state->src_h[0] = (state->src.y2 - state->src.y1) >> 16; - vc4_state->crtc_x = state->crtc_x; - vc4_state->crtc_y = state->crtc_y; - vc4_state->crtc_w = state->crtc_w; - vc4_state->crtc_h = state->crtc_h; + vc4_state->crtc_x = state->dst.x1; + vc4_state->crtc_y = state->dst.y1; + vc4_state->crtc_w = state->dst.x2 - state->dst.x1; + vc4_state->crtc_h = state->dst.y2 - state->dst.y1; vc4_state->x_scaling[0] = vc4_get_scaling_mode(vc4_state->src_w[0], vc4_state->crtc_w); @@ -302,8 +331,6 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) if (num_planes > 1) { vc4_state->is_yuv = true; - h_subsample = drm_format_horz_chroma_subsampling(format); - v_subsample = drm_format_vert_chroma_subsampling(format); vc4_state->src_w[1] = vc4_state->src_w[0] / h_subsample; vc4_state->src_h[1] = vc4_state->src_h[0] / v_subsample; @@ -321,45 +348,11 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) if (vc4_state->is_unity) vc4_state->x_scaling[0] = VC4_SCALING_PPF; } else { + vc4_state->is_yuv = false; vc4_state->x_scaling[1] = VC4_SCALING_NONE; vc4_state->y_scaling[1] = VC4_SCALING_NONE; } - /* No configuring scaling on the cursor plane, since it gets - non-vblank-synced updates, and scaling requires requires - LBM changes which have to be vblank-synced. - */ - if (plane->type == DRM_PLANE_TYPE_CURSOR && !vc4_state->is_unity) - return -EINVAL; - - /* Clamp the on-screen start x/y to 0. The hardware doesn't - * support negative y, and negative x wastes bandwidth. - */ - if (vc4_state->crtc_x < 0) { - for (i = 0; i < num_planes; i++) { - u32 cpp = fb->format->cpp[i]; - u32 subs = ((i == 0) ? 1 : h_subsample); - - vc4_state->offsets[i] += (cpp * - (-vc4_state->crtc_x) / subs); - } - vc4_state->src_w[0] += vc4_state->crtc_x; - vc4_state->src_w[1] += vc4_state->crtc_x / h_subsample; - vc4_state->crtc_x = 0; - } - - if (vc4_state->crtc_y < 0) { - for (i = 0; i < num_planes; i++) { - u32 subs = ((i == 0) ? 1 : v_subsample); - - vc4_state->offsets[i] += (fb->pitches[i] * - (-vc4_state->crtc_y) / subs); - } - vc4_state->src_h[0] += vc4_state->crtc_y; - vc4_state->src_h[1] += vc4_state->crtc_y / v_subsample; - vc4_state->crtc_y = 0; - } - return 0; } @@ -467,6 +460,7 @@ static int vc4_plane_mode_set(struct drm_plane *plane, const struct hvs_format *format = vc4_get_hvs_format(fb->format->format); u64 base_format_mod = fourcc_mod_broadcom_mod(fb->modifier); int num_planes = drm_format_num_planes(format->drm); + u32 h_subsample, v_subsample; bool mix_plane_alpha; bool covers_screen; u32 scl0, scl1, pitch0; @@ -512,26 +506,77 @@ static int vc4_plane_mode_set(struct drm_plane *plane, scl1 = vc4_get_scl_field(state, 0); } + h_subsample = drm_format_horz_chroma_subsampling(format->drm); + v_subsample = drm_format_vert_chroma_subsampling(format->drm); + switch (base_format_mod) { case DRM_FORMAT_MOD_LINEAR: tiling = SCALER_CTL0_TILING_LINEAR; pitch0 = VC4_SET_FIELD(fb->pitches[0], SCALER_SRC_PITCH); + + /* Adjust the base pointer to the first pixel to be scanned + * out. + */ + for (i = 0; i < num_planes; i++) { + vc4_state->offsets[i] += vc4_state->src_y / + (i ? v_subsample : 1) * + fb->pitches[i]; + vc4_state->offsets[i] += vc4_state->src_x / + (i ? h_subsample : 1) * + fb->format->cpp[i]; + } + break; case DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED: { - /* For T-tiled, the FB pitch is "how many bytes from - * one row to the next, such that pitch * tile_h == - * tile_size * tiles_per_row." - */ u32 tile_size_shift = 12; /* T tiles are 4kb */ + /* Whole-tile offsets, mostly for setting the pitch. */ + u32 tile_w_shift = fb->format->cpp[0] == 2 ? 6 : 5; u32 tile_h_shift = 5; /* 16 and 32bpp are 32 pixels high */ + u32 tile_w_mask = (1 << tile_w_shift) - 1; + /* The height mask on 32-bit-per-pixel tiles is 63, i.e. twice + * the height (in pixels) of a 4k tile. + */ + u32 tile_h_mask = (2 << tile_h_shift) - 1; + /* For T-tiled, the FB pitch is "how many bytes from one row to + * the next, such that + * + * pitch * tile_h == tile_size * tiles_per_row + */ u32 tiles_w = fb->pitches[0] >> (tile_size_shift - tile_h_shift); + u32 tiles_l = vc4_state->src_x >> tile_w_shift; + u32 tiles_r = tiles_w - tiles_l; + u32 tiles_t = vc4_state->src_y >> tile_h_shift; + /* Intra-tile offsets, which modify the base address (the + * SCALER_PITCH0_TILE_Y_OFFSET tells HVS how to walk from that + * base address). + */ + u32 tile_y = (vc4_state->src_y >> 4) & 1; + u32 subtile_y = (vc4_state->src_y >> 2) & 3; + u32 utile_y = vc4_state->src_y & 3; + u32 x_off = vc4_state->src_x & tile_w_mask; + u32 y_off = vc4_state->src_y & tile_h_mask; tiling = SCALER_CTL0_TILING_256B_OR_T; + pitch0 = (VC4_SET_FIELD(x_off, SCALER_PITCH0_SINK_PIX) | + VC4_SET_FIELD(y_off, SCALER_PITCH0_TILE_Y_OFFSET) | + VC4_SET_FIELD(tiles_l, SCALER_PITCH0_TILE_WIDTH_L) | + VC4_SET_FIELD(tiles_r, SCALER_PITCH0_TILE_WIDTH_R)); + vc4_state->offsets[0] += tiles_t * (tiles_w << tile_size_shift); + vc4_state->offsets[0] += subtile_y << 8; + vc4_state->offsets[0] += utile_y << 4; + + /* Rows of tiles alternate left-to-right and right-to-left. */ + if (tiles_t & 1) { + pitch0 |= SCALER_PITCH0_TILE_INITIAL_LINE_DIR; + vc4_state->offsets[0] += (tiles_w - tiles_l) << + tile_size_shift; + vc4_state->offsets[0] -= (1 + !tile_y) << 10; + } else { + vc4_state->offsets[0] += tiles_l << tile_size_shift; + vc4_state->offsets[0] += tile_y << 10; + } - pitch0 = (VC4_SET_FIELD(0, SCALER_PITCH0_TILE_Y_OFFSET) | - VC4_SET_FIELD(0, SCALER_PITCH0_TILE_WIDTH_L) | - VC4_SET_FIELD(tiles_w, SCALER_PITCH0_TILE_WIDTH_R)); break; } @@ -903,7 +948,6 @@ static const struct drm_plane_helper_funcs vc4_plane_helper_funcs = { static void vc4_plane_destroy(struct drm_plane *plane) { - drm_plane_helper_disable(plane, NULL); drm_plane_cleanup(plane); } diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h index d6864fa4bd14..931088014272 100644 --- a/drivers/gpu/drm/vc4/vc4_regs.h +++ b/drivers/gpu/drm/vc4/vc4_regs.h @@ -1037,14 +1037,18 @@ enum hvs_pixel_format { #define SCALER_TILE_HEIGHT_MASK VC4_MASK(15, 0) #define SCALER_TILE_HEIGHT_SHIFT 0 +/* Common PITCH0 fields */ +#define SCALER_PITCH0_SINK_PIX_MASK VC4_MASK(31, 26) +#define SCALER_PITCH0_SINK_PIX_SHIFT 26 + /* PITCH0 fields for T-tiled. */ #define SCALER_PITCH0_TILE_WIDTH_L_MASK VC4_MASK(22, 16) #define SCALER_PITCH0_TILE_WIDTH_L_SHIFT 16 #define SCALER_PITCH0_TILE_LINE_DIR BIT(15) #define SCALER_PITCH0_TILE_INITIAL_LINE_DIR BIT(14) /* Y offset within a tile. */ -#define SCALER_PITCH0_TILE_Y_OFFSET_MASK VC4_MASK(13, 7) -#define SCALER_PITCH0_TILE_Y_OFFSET_SHIFT 7 +#define SCALER_PITCH0_TILE_Y_OFFSET_MASK VC4_MASK(13, 8) +#define SCALER_PITCH0_TILE_Y_OFFSET_SHIFT 8 #define SCALER_PITCH0_TILE_WIDTH_R_MASK VC4_MASK(6, 0) #define SCALER_PITCH0_TILE_WIDTH_R_SHIFT 0 diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index dca04d4246ea..e6b11f6ae2e4 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -493,24 +493,24 @@ int vmw_du_cursor_plane_atomic_check(struct drm_plane *plane, struct drm_plane_state *new_state) { int ret = 0; + struct drm_crtc_state *crtc_state = NULL; struct vmw_surface *surface = NULL; struct drm_framebuffer *fb = new_state->fb; - struct drm_rect src = drm_plane_state_src(new_state); - struct drm_rect dest = drm_plane_state_dest(new_state); + if (new_state->crtc) + crtc_state = drm_atomic_get_new_crtc_state(new_state->state, + new_state->crtc); - /* Turning off */ - if (!fb) + ret = drm_atomic_helper_check_plane_state(new_state, crtc_state, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + true, true); + if (ret) return ret; - ret = drm_plane_helper_check_update(plane, new_state->crtc, fb, - &src, &dest, - DRM_MODE_ROTATE_0, - DRM_PLANE_HELPER_NO_SCALING, - DRM_PLANE_HELPER_NO_SCALING, - true, true, &new_state->visible); - if (!ret) - return ret; + /* Turning off */ + if (!fb) + return 0; /* A lot of the code assumes this */ if (new_state->crtc_w != 64 || new_state->crtc_h != 64) { diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index 723578117191..4b5378495eea 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -274,7 +274,6 @@ static const struct drm_connector_funcs vmw_legacy_connector_funcs = { static const struct drm_connector_helper_funcs vmw_ldu_connector_helper_funcs = { - .best_encoder = drm_atomic_helper_best_encoder, }; /* diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c index 53316b1bda3d..333418dc259f 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c @@ -389,7 +389,6 @@ static const struct drm_connector_funcs vmw_sou_connector_funcs = { static const struct drm_connector_helper_funcs vmw_sou_connector_helper_funcs = { - .best_encoder = drm_atomic_helper_best_encoder, }; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c index e086565c1da6..c3e435f444c1 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c @@ -1054,7 +1054,6 @@ static const struct drm_connector_funcs vmw_stdu_connector_funcs = { static const struct drm_connector_helper_funcs vmw_stdu_connector_helper_funcs = { - .best_encoder = drm_atomic_helper_best_encoder, }; diff --git a/drivers/gpu/drm/zte/zx_drm_drv.c b/drivers/gpu/drm/zte/zx_drm_drv.c index 11ef17c2d1c1..f5ea32ae8600 100644 --- a/drivers/gpu/drm/zte/zx_drm_drv.c +++ b/drivers/gpu/drm/zte/zx_drm_drv.c @@ -114,7 +114,7 @@ out_unbind: component_unbind_all(dev, drm); out_unregister: dev_set_drvdata(dev, NULL); - drm_dev_unref(drm); + drm_dev_put(drm); return ret; } @@ -124,10 +124,11 @@ static void zx_drm_unbind(struct device *dev) drm_dev_unregister(drm); drm_kms_helper_poll_fini(drm); + drm_atomic_helper_shutdown(drm); drm_mode_config_cleanup(drm); component_unbind_all(dev, drm); dev_set_drvdata(dev, NULL); - drm_dev_unref(drm); + drm_dev_put(drm); } static const struct component_master_ops zx_drm_master_ops = { diff --git a/drivers/gpu/drm/zte/zx_plane.c b/drivers/gpu/drm/zte/zx_plane.c index ae8c53b4b261..83d236fd893c 100644 --- a/drivers/gpu/drm/zte/zx_plane.c +++ b/drivers/gpu/drm/zte/zx_plane.c @@ -446,7 +446,6 @@ static const struct drm_plane_helper_funcs zx_gl_plane_helper_funcs = { static void zx_plane_destroy(struct drm_plane *plane) { - drm_plane_helper_disable(plane, NULL); drm_plane_cleanup(plane); } diff --git a/drivers/gpu/vga/vgaarb.c b/drivers/gpu/vga/vgaarb.c index c61b04555779..dc8e039bfab5 100644 --- a/drivers/gpu/vga/vgaarb.c +++ b/drivers/gpu/vga/vgaarb.c @@ -676,7 +676,7 @@ static bool vga_arbiter_add_pci_device(struct pci_dev *pdev) vga_arbiter_check_bridge_sharing(vgadev); /* Add to the list */ - list_add(&vgadev->list, &vga_list); + list_add_tail(&vgadev->list, &vga_list); vga_count++; vgaarb_info(&pdev->dev, "VGA device added: decodes=%s,owns=%s,locks=%s\n", vga_iostate_to_str(vgadev->decodes), @@ -1408,6 +1408,18 @@ static void __init vga_arb_select_default_device(void) struct vga_device *vgadev; #if defined(CONFIG_X86) || defined(CONFIG_IA64) + u64 base = screen_info.lfb_base; + u64 size = screen_info.lfb_size; + u64 limit; + resource_size_t start, end; + unsigned long flags; + int i; + + if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE) + base |= (u64)screen_info.ext_lfb_base << 32; + + limit = base + size; + list_for_each_entry(vgadev, &vga_list, list) { struct device *dev = &vgadev->pdev->dev; /* @@ -1418,11 +1430,6 @@ static void __init vga_arb_select_default_device(void) * Select the device owning the boot framebuffer if there is * one. */ - resource_size_t start, end, limit; - unsigned long flags; - int i; - - limit = screen_info.lfb_base + screen_info.lfb_size; /* Does firmware framebuffer belong to us? */ for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { @@ -1437,7 +1444,7 @@ static void __init vga_arb_select_default_device(void) if (!start || !end) continue; - if (screen_info.lfb_base < start || limit >= end) + if (base < start || limit >= end) continue; if (!vga_default_device()) diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c index 55c2ea0720d9..b85b181bbb6c 100644 --- a/drivers/media/i2c/adv7511.c +++ b/drivers/media/i2c/adv7511.c @@ -550,7 +550,7 @@ static void log_infoframe(struct v4l2_subdev *sd, const struct adv7511_cfg_read_ buffer[3] = 0; buffer[3] = hdmi_infoframe_checksum(buffer, len + 4); - if (hdmi_infoframe_unpack(&frame, buffer) < 0) { + if (hdmi_infoframe_unpack(&frame, buffer, sizeof(buffer)) < 0) { v4l2_err(sd, "%s: unpack of %s infoframe failed\n", __func__, cri->desc); return; } diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 668be2bca57a..2e7a28dbad4e 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -2418,7 +2418,7 @@ static int adv76xx_read_infoframe(struct v4l2_subdev *sd, int index, buffer[i + 3] = infoframe_read(sd, adv76xx_cri[index].payload_addr + i); - if (hdmi_infoframe_unpack(frame, buffer) < 0) { + if (hdmi_infoframe_unpack(frame, buffer, sizeof(buffer)) < 0) { v4l2_err(sd, "%s: unpack of %s infoframe failed\n", __func__, adv76xx_cri[index].desc); return -ENOENT; diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 4f8fbdd00e35..2cfd03f929b2 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -2563,7 +2563,7 @@ static void log_infoframe(struct v4l2_subdev *sd, struct adv7842_cfg_read_infofr for (i = 0; i < len; i++) buffer[i + 3] = infoframe_read(sd, cri->payload_addr + i); - if (hdmi_infoframe_unpack(&frame, buffer) < 0) { + if (hdmi_infoframe_unpack(&frame, buffer, sizeof(buffer)) < 0) { v4l2_err(sd, "%s: unpack of %s infoframe failed\n", __func__, cri->desc); return; } diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 44c41933415a..519bf92508d5 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -444,7 +444,7 @@ static void print_avi_infoframe(struct v4l2_subdev *sd) i2c_rd(sd, PK_AVI_0HEAD, buffer, HDMI_INFOFRAME_SIZE(AVI)); - if (hdmi_infoframe_unpack(&frame, buffer) < 0) { + if (hdmi_infoframe_unpack(&frame, buffer, sizeof(buffer)) < 0) { v4l2_err(sd, "%s: unpack of AVI infoframe failed\n", __func__); return; } diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c index d114ac5243ec..195a1fc74ee8 100644 --- a/drivers/media/i2c/tda1997x.c +++ b/drivers/media/i2c/tda1997x.c @@ -1253,7 +1253,7 @@ tda1997x_parse_infoframe(struct tda1997x_state *state, u16 addr) /* read data */ len = io_readn(sd, addr, sizeof(buffer), buffer); - err = hdmi_infoframe_unpack(&frame, buffer); + err = hdmi_infoframe_unpack(&frame, buffer, sizeof(buffer)); if (err) { v4l_err(state->client, "failed parsing %d byte infoframe: 0x%04x/0x%02x\n", @@ -1928,7 +1928,7 @@ static int tda1997x_log_infoframe(struct v4l2_subdev *sd, int addr) /* read data */ len = io_readn(sd, addr, sizeof(buffer), buffer); v4l2_dbg(1, debug, sd, "infoframe: addr=%d len=%d\n", addr, len); - err = hdmi_infoframe_unpack(&frame, buffer); + err = hdmi_infoframe_unpack(&frame, buffer, sizeof(buffer)); if (err) { v4l_err(state->client, "failed parsing %d byte infoframe: 0x%04x/0x%02x\n", diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c index 38716eb50408..08d94ab00467 100644 --- a/drivers/video/hdmi.c +++ b/drivers/video/hdmi.c @@ -31,7 +31,7 @@ #define hdmi_log(fmt, ...) dev_printk(level, dev, fmt, ##__VA_ARGS__) -static u8 hdmi_infoframe_checksum(u8 *ptr, size_t size) +static u8 hdmi_infoframe_checksum(const u8 *ptr, size_t size) { u8 csum = 0; size_t i; @@ -68,8 +68,36 @@ int hdmi_avi_infoframe_init(struct hdmi_avi_infoframe *frame) } EXPORT_SYMBOL(hdmi_avi_infoframe_init); +static int hdmi_avi_infoframe_check_only(const struct hdmi_avi_infoframe *frame) +{ + if (frame->type != HDMI_INFOFRAME_TYPE_AVI || + frame->version != 2 || + frame->length != HDMI_AVI_INFOFRAME_SIZE) + return -EINVAL; + + if (frame->picture_aspect > HDMI_PICTURE_ASPECT_16_9) + return -EINVAL; + + return 0; +} + /** - * hdmi_avi_infoframe_pack() - write HDMI AVI infoframe to binary buffer + * hdmi_avi_infoframe_check() - check a HDMI AVI infoframe + * @frame: HDMI AVI infoframe + * + * Validates that the infoframe is consistent and updates derived fields + * (eg. length) based on other fields. + * + * Returns 0 on success or a negative error code on failure. + */ +int hdmi_avi_infoframe_check(struct hdmi_avi_infoframe *frame) +{ + return hdmi_avi_infoframe_check_only(frame); +} +EXPORT_SYMBOL(hdmi_avi_infoframe_check); + +/** + * hdmi_avi_infoframe_pack_only() - write HDMI AVI infoframe to binary buffer * @frame: HDMI AVI infoframe * @buffer: destination buffer * @size: size of buffer @@ -82,20 +110,22 @@ EXPORT_SYMBOL(hdmi_avi_infoframe_init); * Returns the number of bytes packed into the binary buffer or a negative * error code on failure. */ -ssize_t hdmi_avi_infoframe_pack(struct hdmi_avi_infoframe *frame, void *buffer, - size_t size) +ssize_t hdmi_avi_infoframe_pack_only(const struct hdmi_avi_infoframe *frame, + void *buffer, size_t size) { u8 *ptr = buffer; size_t length; + int ret; + + ret = hdmi_avi_infoframe_check_only(frame); + if (ret) + return ret; length = HDMI_INFOFRAME_HEADER_SIZE + frame->length; if (size < length) return -ENOSPC; - if (frame->picture_aspect > HDMI_PICTURE_ASPECT_16_9) - return -EINVAL; - memset(buffer, 0, size); ptr[0] = frame->type; @@ -152,6 +182,36 @@ ssize_t hdmi_avi_infoframe_pack(struct hdmi_avi_infoframe *frame, void *buffer, return length; } +EXPORT_SYMBOL(hdmi_avi_infoframe_pack_only); + +/** + * hdmi_avi_infoframe_pack() - check a HDMI AVI infoframe, + * and write it to binary buffer + * @frame: HDMI AVI infoframe + * @buffer: destination buffer + * @size: size of buffer + * + * Validates that the infoframe is consistent and updates derived fields + * (eg. length) based on other fields, after which it packs the information + * contained in the @frame structure into a binary representation that + * can be written into the corresponding controller registers. This function + * also computes the checksum as required by section 5.3.5 of the HDMI 1.4 + * specification. + * + * Returns the number of bytes packed into the binary buffer or a negative + * error code on failure. + */ +ssize_t hdmi_avi_infoframe_pack(struct hdmi_avi_infoframe *frame, + void *buffer, size_t size) +{ + int ret; + + ret = hdmi_avi_infoframe_check(frame); + if (ret) + return ret; + + return hdmi_avi_infoframe_pack_only(frame, buffer, size); +} EXPORT_SYMBOL(hdmi_avi_infoframe_pack); /** @@ -178,8 +238,33 @@ int hdmi_spd_infoframe_init(struct hdmi_spd_infoframe *frame, } EXPORT_SYMBOL(hdmi_spd_infoframe_init); +static int hdmi_spd_infoframe_check_only(const struct hdmi_spd_infoframe *frame) +{ + if (frame->type != HDMI_INFOFRAME_TYPE_SPD || + frame->version != 1 || + frame->length != HDMI_SPD_INFOFRAME_SIZE) + return -EINVAL; + + return 0; +} + /** - * hdmi_spd_infoframe_pack() - write HDMI SPD infoframe to binary buffer + * hdmi_spd_infoframe_check() - check a HDMI SPD infoframe + * @frame: HDMI SPD infoframe + * + * Validates that the infoframe is consistent and updates derived fields + * (eg. length) based on other fields. + * + * Returns 0 on success or a negative error code on failure. + */ +int hdmi_spd_infoframe_check(struct hdmi_spd_infoframe *frame) +{ + return hdmi_spd_infoframe_check_only(frame); +} +EXPORT_SYMBOL(hdmi_spd_infoframe_check); + +/** + * hdmi_spd_infoframe_pack_only() - write HDMI SPD infoframe to binary buffer * @frame: HDMI SPD infoframe * @buffer: destination buffer * @size: size of buffer @@ -192,11 +277,16 @@ EXPORT_SYMBOL(hdmi_spd_infoframe_init); * Returns the number of bytes packed into the binary buffer or a negative * error code on failure. */ -ssize_t hdmi_spd_infoframe_pack(struct hdmi_spd_infoframe *frame, void *buffer, - size_t size) +ssize_t hdmi_spd_infoframe_pack_only(const struct hdmi_spd_infoframe *frame, + void *buffer, size_t size) { u8 *ptr = buffer; size_t length; + int ret; + + ret = hdmi_spd_infoframe_check_only(frame); + if (ret) + return ret; length = HDMI_INFOFRAME_HEADER_SIZE + frame->length; @@ -222,6 +312,36 @@ ssize_t hdmi_spd_infoframe_pack(struct hdmi_spd_infoframe *frame, void *buffer, return length; } +EXPORT_SYMBOL(hdmi_spd_infoframe_pack_only); + +/** + * hdmi_spd_infoframe_pack() - check a HDMI SPD infoframe, + * and write it to binary buffer + * @frame: HDMI SPD infoframe + * @buffer: destination buffer + * @size: size of buffer + * + * Validates that the infoframe is consistent and updates derived fields + * (eg. length) based on other fields, after which it packs the information + * contained in the @frame structure into a binary representation that + * can be written into the corresponding controller registers. This function + * also computes the checksum as required by section 5.3.5 of the HDMI 1.4 + * specification. + * + * Returns the number of bytes packed into the binary buffer or a negative + * error code on failure. + */ +ssize_t hdmi_spd_infoframe_pack(struct hdmi_spd_infoframe *frame, + void *buffer, size_t size) +{ + int ret; + + ret = hdmi_spd_infoframe_check(frame); + if (ret) + return ret; + + return hdmi_spd_infoframe_pack_only(frame, buffer, size); +} EXPORT_SYMBOL(hdmi_spd_infoframe_pack); /** @@ -242,8 +362,33 @@ int hdmi_audio_infoframe_init(struct hdmi_audio_infoframe *frame) } EXPORT_SYMBOL(hdmi_audio_infoframe_init); +static int hdmi_audio_infoframe_check_only(const struct hdmi_audio_infoframe *frame) +{ + if (frame->type != HDMI_INFOFRAME_TYPE_AUDIO || + frame->version != 1 || + frame->length != HDMI_AUDIO_INFOFRAME_SIZE) + return -EINVAL; + + return 0; +} + /** - * hdmi_audio_infoframe_pack() - write HDMI audio infoframe to binary buffer + * hdmi_audio_infoframe_check() - check a HDMI audio infoframe + * @frame: HDMI audio infoframe + * + * Validates that the infoframe is consistent and updates derived fields + * (eg. length) based on other fields. + * + * Returns 0 on success or a negative error code on failure. + */ +int hdmi_audio_infoframe_check(struct hdmi_audio_infoframe *frame) +{ + return hdmi_audio_infoframe_check_only(frame); +} +EXPORT_SYMBOL(hdmi_audio_infoframe_check); + +/** + * hdmi_audio_infoframe_pack_only() - write HDMI audio infoframe to binary buffer * @frame: HDMI audio infoframe * @buffer: destination buffer * @size: size of buffer @@ -256,12 +401,17 @@ EXPORT_SYMBOL(hdmi_audio_infoframe_init); * Returns the number of bytes packed into the binary buffer or a negative * error code on failure. */ -ssize_t hdmi_audio_infoframe_pack(struct hdmi_audio_infoframe *frame, - void *buffer, size_t size) +ssize_t hdmi_audio_infoframe_pack_only(const struct hdmi_audio_infoframe *frame, + void *buffer, size_t size) { unsigned char channels; u8 *ptr = buffer; size_t length; + int ret; + + ret = hdmi_audio_infoframe_check_only(frame); + if (ret) + return ret; length = HDMI_INFOFRAME_HEADER_SIZE + frame->length; @@ -297,6 +447,36 @@ ssize_t hdmi_audio_infoframe_pack(struct hdmi_audio_infoframe *frame, return length; } +EXPORT_SYMBOL(hdmi_audio_infoframe_pack_only); + +/** + * hdmi_audio_infoframe_pack() - check a HDMI Audio infoframe, + * and write it to binary buffer + * @frame: HDMI Audio infoframe + * @buffer: destination buffer + * @size: size of buffer + * + * Validates that the infoframe is consistent and updates derived fields + * (eg. length) based on other fields, after which it packs the information + * contained in the @frame structure into a binary representation that + * can be written into the corresponding controller registers. This function + * also computes the checksum as required by section 5.3.5 of the HDMI 1.4 + * specification. + * + * Returns the number of bytes packed into the binary buffer or a negative + * error code on failure. + */ +ssize_t hdmi_audio_infoframe_pack(struct hdmi_audio_infoframe *frame, + void *buffer, size_t size) +{ + int ret; + + ret = hdmi_audio_infoframe_check(frame); + if (ret) + return ret; + + return hdmi_audio_infoframe_pack_only(frame, buffer, size); +} EXPORT_SYMBOL(hdmi_audio_infoframe_pack); /** @@ -319,6 +499,7 @@ int hdmi_vendor_infoframe_init(struct hdmi_vendor_infoframe *frame) * value */ frame->s3d_struct = HDMI_3D_STRUCTURE_INVALID; + frame->length = 4; return 0; } @@ -335,8 +516,42 @@ static int hdmi_vendor_infoframe_length(const struct hdmi_vendor_infoframe *fram return 4; } +static int hdmi_vendor_infoframe_check_only(const struct hdmi_vendor_infoframe *frame) +{ + if (frame->type != HDMI_INFOFRAME_TYPE_VENDOR || + frame->version != 1 || + frame->oui != HDMI_IEEE_OUI) + return -EINVAL; + + /* only one of those can be supplied */ + if (frame->vic != 0 && frame->s3d_struct != HDMI_3D_STRUCTURE_INVALID) + return -EINVAL; + + if (frame->length != hdmi_vendor_infoframe_length(frame)) + return -EINVAL; + + return 0; +} + +/** + * hdmi_vendor_infoframe_check() - check a HDMI vendor infoframe + * @frame: HDMI infoframe + * + * Validates that the infoframe is consistent and updates derived fields + * (eg. length) based on other fields. + * + * Returns 0 on success or a negative error code on failure. + */ +int hdmi_vendor_infoframe_check(struct hdmi_vendor_infoframe *frame) +{ + frame->length = hdmi_vendor_infoframe_length(frame); + + return hdmi_vendor_infoframe_check_only(frame); +} +EXPORT_SYMBOL(hdmi_vendor_infoframe_check); + /** - * hdmi_vendor_infoframe_pack() - write a HDMI vendor infoframe to binary buffer + * hdmi_vendor_infoframe_pack_only() - write a HDMI vendor infoframe to binary buffer * @frame: HDMI infoframe * @buffer: destination buffer * @size: size of buffer @@ -349,17 +564,16 @@ static int hdmi_vendor_infoframe_length(const struct hdmi_vendor_infoframe *fram * Returns the number of bytes packed into the binary buffer or a negative * error code on failure. */ -ssize_t hdmi_vendor_infoframe_pack(struct hdmi_vendor_infoframe *frame, - void *buffer, size_t size) +ssize_t hdmi_vendor_infoframe_pack_only(const struct hdmi_vendor_infoframe *frame, + void *buffer, size_t size) { u8 *ptr = buffer; size_t length; + int ret; - /* only one of those can be supplied */ - if (frame->vic != 0 && frame->s3d_struct != HDMI_3D_STRUCTURE_INVALID) - return -EINVAL; - - frame->length = hdmi_vendor_infoframe_length(frame); + ret = hdmi_vendor_infoframe_check_only(frame); + if (ret) + return ret; length = HDMI_INFOFRAME_HEADER_SIZE + frame->length; @@ -394,24 +608,134 @@ ssize_t hdmi_vendor_infoframe_pack(struct hdmi_vendor_infoframe *frame, return length; } +EXPORT_SYMBOL(hdmi_vendor_infoframe_pack_only); + +/** + * hdmi_vendor_infoframe_pack() - check a HDMI Vendor infoframe, + * and write it to binary buffer + * @frame: HDMI Vendor infoframe + * @buffer: destination buffer + * @size: size of buffer + * + * Validates that the infoframe is consistent and updates derived fields + * (eg. length) based on other fields, after which it packs the information + * contained in the @frame structure into a binary representation that + * can be written into the corresponding controller registers. This function + * also computes the checksum as required by section 5.3.5 of the HDMI 1.4 + * specification. + * + * Returns the number of bytes packed into the binary buffer or a negative + * error code on failure. + */ +ssize_t hdmi_vendor_infoframe_pack(struct hdmi_vendor_infoframe *frame, + void *buffer, size_t size) +{ + int ret; + + ret = hdmi_vendor_infoframe_check(frame); + if (ret) + return ret; + + return hdmi_vendor_infoframe_pack_only(frame, buffer, size); +} EXPORT_SYMBOL(hdmi_vendor_infoframe_pack); +static int +hdmi_vendor_any_infoframe_check_only(const union hdmi_vendor_any_infoframe *frame) +{ + if (frame->any.type != HDMI_INFOFRAME_TYPE_VENDOR || + frame->any.version != 1) + return -EINVAL; + + return 0; +} + +/* + * hdmi_vendor_any_infoframe_check() - check a vendor infoframe + */ +static int +hdmi_vendor_any_infoframe_check(union hdmi_vendor_any_infoframe *frame) +{ + int ret; + + ret = hdmi_vendor_any_infoframe_check_only(frame); + if (ret) + return ret; + + /* we only know about HDMI vendor infoframes */ + if (frame->any.oui != HDMI_IEEE_OUI) + return -EINVAL; + + return hdmi_vendor_infoframe_check(&frame->hdmi); +} + /* - * hdmi_vendor_any_infoframe_pack() - write a vendor infoframe to binary buffer + * hdmi_vendor_any_infoframe_pack_only() - write a vendor infoframe to binary buffer */ static ssize_t -hdmi_vendor_any_infoframe_pack(union hdmi_vendor_any_infoframe *frame, - void *buffer, size_t size) +hdmi_vendor_any_infoframe_pack_only(const union hdmi_vendor_any_infoframe *frame, + void *buffer, size_t size) { + int ret; + + ret = hdmi_vendor_any_infoframe_check_only(frame); + if (ret) + return ret; + /* we only know about HDMI vendor infoframes */ if (frame->any.oui != HDMI_IEEE_OUI) return -EINVAL; - return hdmi_vendor_infoframe_pack(&frame->hdmi, buffer, size); + return hdmi_vendor_infoframe_pack_only(&frame->hdmi, buffer, size); +} + +/* + * hdmi_vendor_any_infoframe_pack() - check a vendor infoframe, + * and write it to binary buffer + */ +static ssize_t +hdmi_vendor_any_infoframe_pack(union hdmi_vendor_any_infoframe *frame, + void *buffer, size_t size) +{ + int ret; + + ret = hdmi_vendor_any_infoframe_check(frame); + if (ret) + return ret; + + return hdmi_vendor_any_infoframe_pack_only(frame, buffer, size); } /** - * hdmi_infoframe_pack() - write a HDMI infoframe to binary buffer + * hdmi_infoframe_check() - check a HDMI infoframe + * @frame: HDMI infoframe + * + * Validates that the infoframe is consistent and updates derived fields + * (eg. length) based on other fields. + * + * Returns 0 on success or a negative error code on failure. + */ +int +hdmi_infoframe_check(union hdmi_infoframe *frame) +{ + switch (frame->any.type) { + case HDMI_INFOFRAME_TYPE_AVI: + return hdmi_avi_infoframe_check(&frame->avi); + case HDMI_INFOFRAME_TYPE_SPD: + return hdmi_spd_infoframe_check(&frame->spd); + case HDMI_INFOFRAME_TYPE_AUDIO: + return hdmi_audio_infoframe_check(&frame->audio); + case HDMI_INFOFRAME_TYPE_VENDOR: + return hdmi_vendor_any_infoframe_check(&frame->vendor); + default: + WARN(1, "Bad infoframe type %d\n", frame->any.type); + return -EINVAL; + } +} +EXPORT_SYMBOL(hdmi_infoframe_check); + +/** + * hdmi_infoframe_pack_only() - write a HDMI infoframe to binary buffer * @frame: HDMI infoframe * @buffer: destination buffer * @size: size of buffer @@ -425,7 +749,56 @@ hdmi_vendor_any_infoframe_pack(union hdmi_vendor_any_infoframe *frame, * error code on failure. */ ssize_t -hdmi_infoframe_pack(union hdmi_infoframe *frame, void *buffer, size_t size) +hdmi_infoframe_pack_only(const union hdmi_infoframe *frame, void *buffer, size_t size) +{ + ssize_t length; + + switch (frame->any.type) { + case HDMI_INFOFRAME_TYPE_AVI: + length = hdmi_avi_infoframe_pack_only(&frame->avi, + buffer, size); + break; + case HDMI_INFOFRAME_TYPE_SPD: + length = hdmi_spd_infoframe_pack_only(&frame->spd, + buffer, size); + break; + case HDMI_INFOFRAME_TYPE_AUDIO: + length = hdmi_audio_infoframe_pack_only(&frame->audio, + buffer, size); + break; + case HDMI_INFOFRAME_TYPE_VENDOR: + length = hdmi_vendor_any_infoframe_pack_only(&frame->vendor, + buffer, size); + break; + default: + WARN(1, "Bad infoframe type %d\n", frame->any.type); + length = -EINVAL; + } + + return length; +} +EXPORT_SYMBOL(hdmi_infoframe_pack_only); + +/** + * hdmi_infoframe_pack() - check a HDMI infoframe, + * and write it to binary buffer + * @frame: HDMI infoframe + * @buffer: destination buffer + * @size: size of buffer + * + * Validates that the infoframe is consistent and updates derived fields + * (eg. length) based on other fields, after which it packs the information + * contained in the @frame structure into a binary representation that + * can be written into the corresponding controller registers. This function + * also computes the checksum as required by section 5.3.5 of the HDMI 1.4 + * specification. + * + * Returns the number of bytes packed into the binary buffer or a negative + * error code on failure. + */ +ssize_t +hdmi_infoframe_pack(union hdmi_infoframe *frame, + void *buffer, size_t size) { ssize_t length; @@ -471,7 +844,7 @@ static const char *hdmi_infoframe_type_get_name(enum hdmi_infoframe_type type) static void hdmi_infoframe_log_header(const char *level, struct device *dev, - struct hdmi_any_infoframe *frame) + const struct hdmi_any_infoframe *frame) { hdmi_log("HDMI infoframe: %s, version %u, length %u\n", hdmi_infoframe_type_get_name(frame->type), @@ -673,10 +1046,10 @@ hdmi_content_type_get_name(enum hdmi_content_type content_type) */ static void hdmi_avi_infoframe_log(const char *level, struct device *dev, - struct hdmi_avi_infoframe *frame) + const struct hdmi_avi_infoframe *frame) { hdmi_infoframe_log_header(level, dev, - (struct hdmi_any_infoframe *)frame); + (const struct hdmi_any_infoframe *)frame); hdmi_log(" colorspace: %s\n", hdmi_colorspace_get_name(frame->colorspace)); @@ -750,12 +1123,12 @@ static const char *hdmi_spd_sdi_get_name(enum hdmi_spd_sdi sdi) */ static void hdmi_spd_infoframe_log(const char *level, struct device *dev, - struct hdmi_spd_infoframe *frame) + const struct hdmi_spd_infoframe *frame) { u8 buf[17]; hdmi_infoframe_log_header(level, dev, - (struct hdmi_any_infoframe *)frame); + (const struct hdmi_any_infoframe *)frame); memset(buf, 0, sizeof(buf)); @@ -886,10 +1259,10 @@ hdmi_audio_coding_type_ext_get_name(enum hdmi_audio_coding_type_ext ctx) */ static void hdmi_audio_infoframe_log(const char *level, struct device *dev, - struct hdmi_audio_infoframe *frame) + const struct hdmi_audio_infoframe *frame) { hdmi_infoframe_log_header(level, dev, - (struct hdmi_any_infoframe *)frame); + (const struct hdmi_any_infoframe *)frame); if (frame->channels) hdmi_log(" channels: %u\n", frame->channels - 1); @@ -949,12 +1322,12 @@ hdmi_3d_structure_get_name(enum hdmi_3d_structure s3d_struct) static void hdmi_vendor_any_infoframe_log(const char *level, struct device *dev, - union hdmi_vendor_any_infoframe *frame) + const union hdmi_vendor_any_infoframe *frame) { - struct hdmi_vendor_infoframe *hvf = &frame->hdmi; + const struct hdmi_vendor_infoframe *hvf = &frame->hdmi; hdmi_infoframe_log_header(level, dev, - (struct hdmi_any_infoframe *)frame); + (const struct hdmi_any_infoframe *)frame); if (frame->any.oui != HDMI_IEEE_OUI) { hdmi_log(" not a HDMI vendor infoframe\n"); @@ -984,7 +1357,7 @@ hdmi_vendor_any_infoframe_log(const char *level, */ void hdmi_infoframe_log(const char *level, struct device *dev, - union hdmi_infoframe *frame) + const union hdmi_infoframe *frame) { switch (frame->any.type) { case HDMI_INFOFRAME_TYPE_AVI: @@ -1005,8 +1378,9 @@ EXPORT_SYMBOL(hdmi_infoframe_log); /** * hdmi_avi_infoframe_unpack() - unpack binary buffer to a HDMI AVI infoframe - * @buffer: source buffer * @frame: HDMI AVI infoframe + * @buffer: source buffer + * @size: size of buffer * * Unpacks the information contained in binary @buffer into a structured * @frame of the HDMI Auxiliary Video (AVI) information frame. @@ -1016,11 +1390,14 @@ EXPORT_SYMBOL(hdmi_infoframe_log); * Returns 0 on success or a negative error code on failure. */ static int hdmi_avi_infoframe_unpack(struct hdmi_avi_infoframe *frame, - void *buffer) + const void *buffer, size_t size) { - u8 *ptr = buffer; + const u8 *ptr = buffer; int ret; + if (size < HDMI_INFOFRAME_SIZE(AVI)) + return -EINVAL; + if (ptr[0] != HDMI_INFOFRAME_TYPE_AVI || ptr[1] != 2 || ptr[2] != HDMI_AVI_INFOFRAME_SIZE) @@ -1068,8 +1445,9 @@ static int hdmi_avi_infoframe_unpack(struct hdmi_avi_infoframe *frame, /** * hdmi_spd_infoframe_unpack() - unpack binary buffer to a HDMI SPD infoframe - * @buffer: source buffer * @frame: HDMI SPD infoframe + * @buffer: source buffer + * @size: size of buffer * * Unpacks the information contained in binary @buffer into a structured * @frame of the HDMI Source Product Description (SPD) information frame. @@ -1079,11 +1457,14 @@ static int hdmi_avi_infoframe_unpack(struct hdmi_avi_infoframe *frame, * Returns 0 on success or a negative error code on failure. */ static int hdmi_spd_infoframe_unpack(struct hdmi_spd_infoframe *frame, - void *buffer) + const void *buffer, size_t size) { - u8 *ptr = buffer; + const u8 *ptr = buffer; int ret; + if (size < HDMI_INFOFRAME_SIZE(SPD)) + return -EINVAL; + if (ptr[0] != HDMI_INFOFRAME_TYPE_SPD || ptr[1] != 1 || ptr[2] != HDMI_SPD_INFOFRAME_SIZE) { @@ -1106,8 +1487,9 @@ static int hdmi_spd_infoframe_unpack(struct hdmi_spd_infoframe *frame, /** * hdmi_audio_infoframe_unpack() - unpack binary buffer to a HDMI AUDIO infoframe - * @buffer: source buffer * @frame: HDMI Audio infoframe + * @buffer: source buffer + * @size: size of buffer * * Unpacks the information contained in binary @buffer into a structured * @frame of the HDMI Audio information frame. @@ -1117,11 +1499,14 @@ static int hdmi_spd_infoframe_unpack(struct hdmi_spd_infoframe *frame, * Returns 0 on success or a negative error code on failure. */ static int hdmi_audio_infoframe_unpack(struct hdmi_audio_infoframe *frame, - void *buffer) + const void *buffer, size_t size) { - u8 *ptr = buffer; + const u8 *ptr = buffer; int ret; + if (size < HDMI_INFOFRAME_SIZE(AUDIO)) + return -EINVAL; + if (ptr[0] != HDMI_INFOFRAME_TYPE_AUDIO || ptr[1] != 1 || ptr[2] != HDMI_AUDIO_INFOFRAME_SIZE) { @@ -1151,8 +1536,9 @@ static int hdmi_audio_infoframe_unpack(struct hdmi_audio_infoframe *frame, /** * hdmi_vendor_infoframe_unpack() - unpack binary buffer to a HDMI vendor infoframe - * @buffer: source buffer * @frame: HDMI Vendor infoframe + * @buffer: source buffer + * @size: size of buffer * * Unpacks the information contained in binary @buffer into a structured * @frame of the HDMI Vendor information frame. @@ -1163,14 +1549,17 @@ static int hdmi_audio_infoframe_unpack(struct hdmi_audio_infoframe *frame, */ static int hdmi_vendor_any_infoframe_unpack(union hdmi_vendor_any_infoframe *frame, - void *buffer) + const void *buffer, size_t size) { - u8 *ptr = buffer; + const u8 *ptr = buffer; size_t length; int ret; u8 hdmi_video_format; struct hdmi_vendor_infoframe *hvf = &frame->hdmi; + if (size < HDMI_INFOFRAME_HEADER_SIZE) + return -EINVAL; + if (ptr[0] != HDMI_INFOFRAME_TYPE_VENDOR || ptr[1] != 1 || (ptr[2] != 4 && ptr[2] != 5 && ptr[2] != 6)) @@ -1178,6 +1567,9 @@ hdmi_vendor_any_infoframe_unpack(union hdmi_vendor_any_infoframe *frame, length = ptr[2]; + if (size < HDMI_INFOFRAME_HEADER_SIZE + length) + return -EINVAL; + if (hdmi_infoframe_checksum(buffer, HDMI_INFOFRAME_HEADER_SIZE + length) != 0) return -EINVAL; @@ -1224,8 +1616,9 @@ hdmi_vendor_any_infoframe_unpack(union hdmi_vendor_any_infoframe *frame, /** * hdmi_infoframe_unpack() - unpack binary buffer to a HDMI infoframe - * @buffer: source buffer * @frame: HDMI infoframe + * @buffer: source buffer + * @size: size of buffer * * Unpacks the information contained in binary buffer @buffer into a structured * @frame of a HDMI infoframe. @@ -1234,23 +1627,27 @@ hdmi_vendor_any_infoframe_unpack(union hdmi_vendor_any_infoframe *frame, * * Returns 0 on success or a negative error code on failure. */ -int hdmi_infoframe_unpack(union hdmi_infoframe *frame, void *buffer) +int hdmi_infoframe_unpack(union hdmi_infoframe *frame, + const void *buffer, size_t size) { int ret; - u8 *ptr = buffer; + const u8 *ptr = buffer; + + if (size < HDMI_INFOFRAME_HEADER_SIZE) + return -EINVAL; switch (ptr[0]) { case HDMI_INFOFRAME_TYPE_AVI: - ret = hdmi_avi_infoframe_unpack(&frame->avi, buffer); + ret = hdmi_avi_infoframe_unpack(&frame->avi, buffer, size); break; case HDMI_INFOFRAME_TYPE_SPD: - ret = hdmi_spd_infoframe_unpack(&frame->spd, buffer); + ret = hdmi_spd_infoframe_unpack(&frame->spd, buffer, size); break; case HDMI_INFOFRAME_TYPE_AUDIO: - ret = hdmi_audio_infoframe_unpack(&frame->audio, buffer); + ret = hdmi_audio_infoframe_unpack(&frame->audio, buffer, size); break; case HDMI_INFOFRAME_TYPE_VENDOR: - ret = hdmi_vendor_any_infoframe_unpack(&frame->vendor, buffer); + ret = hdmi_vendor_any_infoframe_unpack(&frame->vendor, buffer, size); break; default: ret = -EINVAL; |