Changes to create a i915 private pipeline to enable the WD transcoder without relying on the current drm_writeback framework. Signed-off-by: Suraj Kandpal <suraj.kandpal@xxxxxxxxx> --- drivers/gpu/drm/i915/Makefile | 1 + .../drm/i915/display/intel_display_types.h | 4 + .../gpu/drm/i915/display/intel_wb_connector.c | 296 ++++++++++++++++++ .../gpu/drm/i915/display/intel_wb_connector.h | 99 ++++++ drivers/gpu/drm/i915/i915_drv.h | 3 + 5 files changed, 403 insertions(+) create mode 100644 drivers/gpu/drm/i915/display/intel_wb_connector.c create mode 100644 drivers/gpu/drm/i915/display/intel_wb_connector.h diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index d2b18f03a33c..8bd1dd01dc81 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -298,6 +298,7 @@ i915-y += \ display/intel_tv.o \ display/intel_vdsc.o \ display/intel_vrr.o \ + display/intel_wb_connector.o\ display/vlv_dsi.o \ display/vlv_dsi_pll.o diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 408152f9f46a..c4ccb03424f1 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -52,6 +52,7 @@ #include "intel_display_power.h" #include "intel_dpll_mgr.h" #include "intel_pm_types.h" +#include "intel_wb_connector.h" struct drm_printer; struct __intel_global_objs_state; @@ -537,11 +538,14 @@ struct intel_connector { struct work_struct modeset_retry_work; struct intel_hdcp hdcp; + + struct intel_writeback_connector wb_conn; }; struct intel_digital_connector_state { struct drm_connector_state base; + struct intel_writeback_job *job; enum hdmi_force_audio force_audio; int broadcast_rgb; }; diff --git a/drivers/gpu/drm/i915/display/intel_wb_connector.c b/drivers/gpu/drm/i915/display/intel_wb_connector.c new file mode 100644 index 000000000000..65f4abef53d0 --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_wb_connector.c @@ -0,0 +1,296 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright © 2022 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Suraj Kandpal <suraj.kandpal@xxxxxxxxx> + * Arun Murthy <arun.r.murthy@xxxxxxxxx> + * + */ + + +#include <linux/dma-fence.h> + +#include <drm/drm_crtc.h> +#include <drm/drm_device.h> +#include <drm/drm_drv.h> +#include <drm/drm_modeset_helper_vtables.h> +#include <drm/drm_property.h> + +#include "i915_drv.h" +#include "intel_wb_connector.h" +#include "intel_display_types.h" + +#define fence_to_wb_connector(x) container_of(x->lock, \ + struct intel_writeback_connector, \ + fence_lock) + +static const char *intel_writeback_fence_get_driver_name(struct dma_fence *fence) +{ + struct intel_writeback_connector *wb_connector = + fence_to_wb_connector(fence); + + return wb_connector->base->dev->driver->name; +} + +static const char * +intel_writeback_fence_get_timeline_name(struct dma_fence *fence) +{ + struct intel_writeback_connector *wb_connector = + fence_to_wb_connector(fence); + + return wb_connector->timeline_name; +} + +static bool intel_writeback_fence_enable_signaling(struct dma_fence *fence) +{ + return true; +} + +static const struct dma_fence_ops intel_writeback_fence_ops = { + .get_driver_name = intel_writeback_fence_get_driver_name, + .get_timeline_name = intel_writeback_fence_get_timeline_name, + .enable_signaling = intel_writeback_fence_enable_signaling, +}; + +static int intel_create_writeback_properties(struct drm_device *dev) +{ + struct drm_property *prop; + struct drm_i915_private *i915 = to_i915(dev); + + if (!i915->wb_fb_id_property) { + prop = drm_property_create_object(dev, DRM_MODE_PROP_ATOMIC, + "WRITEBACK_FB_ID", + DRM_MODE_OBJECT_FB); + if (!prop) + return -ENOMEM; + i915->wb_fb_id_property = prop; + } + + if (!i915->wb_pixel_formats_property) { + prop = drm_property_create(dev, DRM_MODE_PROP_BLOB | + DRM_MODE_PROP_ATOMIC | + DRM_MODE_PROP_IMMUTABLE, + "WRITEBACK_PIXEL_FORMATS", 0); + if (!prop) + return -ENOMEM; + i915->wb_pixel_formats_property = prop; + } + + if (!i915->wb_out_fence_ptr_property) { + prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, + "WRITEBACK_OUT_FENCE_PTR", 0, + U64_MAX); + if (!prop) + return -ENOMEM; + i915->wb_out_fence_ptr_property = prop; + } + + return 0; +} + +static const struct drm_encoder_funcs intel_writeback_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +int intel_writeback_connector_init(struct drm_device *dev, + struct intel_writeback_connector *wb_connector, + const struct drm_connector_funcs *con_funcs, + const struct drm_encoder_helper_funcs *enc_helper_funcs, + const u32 *formats, int n_formats) +{ + struct drm_property_blob *blob; + struct drm_i915_private *i915 = to_i915(dev); + struct drm_connector *connector = wb_connector->base; + int ret; + + drm_dbg_kms(&i915->drm, "\n"); + ret = intel_create_writeback_properties(dev); + + if (ret != 0) + return ret; + + blob = drm_property_create_blob(dev, n_formats * sizeof(*formats), + formats); + if (IS_ERR(blob)) + return PTR_ERR(blob); + + drm_encoder_helper_add(wb_connector->encoder, enc_helper_funcs); + ret = drm_encoder_init(dev, wb_connector->encoder, + &intel_writeback_encoder_funcs, + DRM_MODE_ENCODER_VIRTUAL, NULL); + if (ret) + goto fail; + + connector->interlace_allowed = 0; + + ret = drm_connector_init(dev, connector, con_funcs, + DRM_MODE_CONNECTOR_WRITEBACK); + if (ret) + goto connector_fail; + + ret = drm_connector_attach_encoder(connector, + wb_connector->encoder); + if (ret) + goto attach_fail; + + INIT_LIST_HEAD(&wb_connector->job_queue); + spin_lock_init(&wb_connector->job_lock); + + wb_connector->fence_context = dma_fence_context_alloc(1); + spin_lock_init(&wb_connector->fence_lock); + snprintf(wb_connector->timeline_name, + sizeof(wb_connector->timeline_name), + "CONNECTOR:%d-%s", connector->base.id, connector->name); + + drm_object_attach_property(&connector->base, + i915->wb_out_fence_ptr_property, 0); + + drm_object_attach_property(&connector->base, + i915->wb_fb_id_property, 0); + + drm_object_attach_property(&connector->base, + i915->wb_pixel_formats_property, + blob->base.id); + wb_connector->pixel_formats_blob_ptr = blob; + + return 0; + +attach_fail: + drm_connector_cleanup(connector); +connector_fail: + drm_encoder_cleanup(wb_connector->encoder); +fail: + drm_property_blob_put(blob); + return ret; +} + +void intel_writeback_queue_job(struct intel_writeback_connector *wb_connector, + struct drm_connector_state *conn_state) +{ + struct intel_writeback_job *wb_job; + struct intel_digital_connector_state *intel_conn_state = + to_intel_digital_connector_state(conn_state); + unsigned long flags; + + wb_job = intel_conn_state->job; + intel_conn_state->job = NULL; + + spin_lock_irqsave(&wb_connector->job_lock, flags); + list_add_tail(&wb_job->list_entry, &wb_connector->job_queue); + spin_unlock_irqrestore(&wb_connector->job_lock, flags); +} + +int intel_writeback_set_fb(struct drm_connector_state *conn_state, + struct drm_framebuffer *fb) +{ + struct intel_connector *intel_connector = to_intel_connector(conn_state->connector); + struct intel_writeback_connector *wb_connector = &intel_connector->wb_conn; + struct intel_digital_connector_state *intel_conn_state = + to_intel_digital_connector_state(conn_state); + WARN_ON(conn_state->connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK); + + if (!intel_conn_state->job) { + intel_conn_state->job = + kzalloc(sizeof(*intel_conn_state->job), GFP_KERNEL); + if (!intel_conn_state->job) + return -ENOMEM; + + intel_conn_state->job->connector = wb_connector; + } + + drm_framebuffer_assign(&intel_conn_state->job->fb, fb); + return 0; +} + +void intel_writeback_cleanup_job(struct intel_writeback_job *job) +{ + if (job->fb) + drm_framebuffer_put(job->fb); + + if (job->out_fence) + dma_fence_put(job->out_fence); + + kfree(job); +} + +static void cleanup_work(struct work_struct *work) +{ + struct intel_writeback_job *job = container_of(work, + struct intel_writeback_job, + cleanup_work); + + intel_writeback_cleanup_job(job); +} + +void +intel_writeback_signal_completion(struct intel_writeback_connector *wb_connector, + int status) +{ + unsigned long flags; + struct intel_writeback_job *job; + struct dma_fence *out_fence; + + spin_lock_irqsave(&wb_connector->job_lock, flags); + job = list_first_entry_or_null(&wb_connector->job_queue, + struct intel_writeback_job, + list_entry); + if (job) + list_del(&job->list_entry); + + spin_unlock_irqrestore(&wb_connector->job_lock, flags); + + if (WARN_ON(!job)) + return; + + out_fence = job->out_fence; + if (out_fence) { + if (status) + dma_fence_set_error(out_fence, status); + dma_fence_signal(out_fence); + dma_fence_put(out_fence); + job->out_fence = NULL; + } + + INIT_WORK(&job->cleanup_work, cleanup_work); + queue_work(system_long_wq, &job->cleanup_work); +} + +struct dma_fence * +intel_writeback_get_out_fence(struct intel_writeback_connector *wb_connector) +{ + struct dma_fence *fence; + + if (WARN_ON(wb_connector->base->connector_type != + DRM_MODE_CONNECTOR_WRITEBACK)) + return NULL; + + fence = kzalloc(sizeof(*fence), GFP_KERNEL); + if (!fence) + return NULL; + + dma_fence_init(fence, &intel_writeback_fence_ops, + &wb_connector->fence_lock, wb_connector->fence_context, + ++wb_connector->fence_seqno); + + return fence; +} diff --git a/drivers/gpu/drm/i915/display/intel_wb_connector.h b/drivers/gpu/drm/i915/display/intel_wb_connector.h new file mode 100644 index 000000000000..71142457b7c1 --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_wb_connector.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0 + * Copyright © 2022 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Suraj Kandpal <suraj.kandpal@xxxxxxxxx> + * Arun Murthy <arun.r.murthy@xxxxxxxxx> + * + */ + +#ifndef __INTEL_WB_CONNECTOR_H__ +#define __INTEL_WB_CONNECTOR_H__ + +#include <drm/drm_connector.h> +#include <drm/drm_encoder.h> +#include <linux/workqueue.h> +#include "intel_display.h" + +/** + * struct intel_writeback_connector - Intel writeback connector + * to get a idea of each individual variable please look at + * documentation in drm_writeback.h we emulate the same structure + * ditto for intel_writeback_job structure. + */ +struct intel_writeback_connector { + struct drm_connector *base; + struct drm_encoder *encoder; + struct drm_property_blob *pixel_formats_blob_ptr; + spinlock_t job_lock; + struct list_head job_queue; + unsigned int fence_context; + spinlock_t fence_lock; + unsigned long fence_seqno; + char timeline_name[32]; +}; + +/** + * struct intel_writeback_job - DRM writeback job + */ +struct intel_writeback_job { + struct intel_writeback_connector *connector; + bool *prepared; + struct work_struct cleanup_work; + struct list_head list_entry; + struct drm_framebuffer *fb; + struct dma_fence *out_fence; + void *priv; +}; + +int intel_writeback_connector_init(struct drm_device *dev, + struct intel_writeback_connector *wb_connector, + const struct drm_connector_funcs *con_funcs, + const struct drm_encoder_helper_funcs *enc_helper_funcs, + const u32 *formats, int n_formats); + +int intel_writeback_set_fb(struct drm_connector_state *conn_state, + struct drm_framebuffer *fb); + +int intel_writeback_prepare_job(struct intel_writeback_job *job); + +void intel_writeback_queue_job(struct intel_writeback_connector *wb_connector, + struct drm_connector_state *conn_state); + +void intel_writeback_cleanup_job(struct intel_writeback_job *job); + +void +intel_writeback_signal_completion(struct intel_writeback_connector *wb_connector, + int status); + +struct dma_fence * +intel_writeback_get_out_fence(struct intel_writeback_connector *wb_connector); +struct intel_wb_connector *intel_wb_connector_alloc(void); +void intel_wb_connector_free(struct intel_wb_connector *connector); +void intel_wb_connector_destroy(struct drm_connector *connector); +bool intel_wb_connector_get_hw_state(struct intel_wb_connector *connector); +enum pipe intel_wb_connector_get_pipe(struct intel_wb_connector *connector); +void intel_wb_connector_attach_encoder(struct intel_wb_connector *connector, + struct intel_encoder *encoder); + +#endif /* __INTEL_WB_CONNECTOR_H__ */ + diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 24111bf42ce0..3c8ac44d9fa3 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -673,6 +673,9 @@ struct drm_i915_private { struct drm_property *broadcast_rgb_property; struct drm_property *force_audio_property; + struct drm_property *wb_fb_id_property; + struct drm_property *wb_out_fence_ptr_property; + struct drm_property *wb_pixel_formats_property; u32 fdi_rx_config; -- 2.35.1