1*935774cdSBrian Starkey // SPDX-License-Identifier: GPL-2.0 2*935774cdSBrian Starkey /* 3*935774cdSBrian Starkey * (C) COPYRIGHT 2016 ARM Limited. All rights reserved. 4*935774cdSBrian Starkey * Author: Brian Starkey <brian.starkey@arm.com> 5*935774cdSBrian Starkey * 6*935774cdSBrian Starkey * This program is free software and is provided to you under the terms of the 7*935774cdSBrian Starkey * GNU General Public License version 2 as published by the Free Software 8*935774cdSBrian Starkey * Foundation, and any use by you of this program is subject to the terms 9*935774cdSBrian Starkey * of such GNU licence. 10*935774cdSBrian Starkey */ 11*935774cdSBrian Starkey 12*935774cdSBrian Starkey #include <drm/drm_crtc.h> 13*935774cdSBrian Starkey #include <drm/drm_modeset_helper_vtables.h> 14*935774cdSBrian Starkey #include <drm/drm_property.h> 15*935774cdSBrian Starkey #include <drm/drm_writeback.h> 16*935774cdSBrian Starkey #include <drm/drmP.h> 17*935774cdSBrian Starkey 18*935774cdSBrian Starkey /** 19*935774cdSBrian Starkey * DOC: overview 20*935774cdSBrian Starkey * 21*935774cdSBrian Starkey * Writeback connectors are used to expose hardware which can write the output 22*935774cdSBrian Starkey * from a CRTC to a memory buffer. They are used and act similarly to other 23*935774cdSBrian Starkey * types of connectors, with some important differences: 24*935774cdSBrian Starkey * - Writeback connectors don't provide a way to output visually to the user. 25*935774cdSBrian Starkey * - Writeback connectors should always report as "disconnected" (so that 26*935774cdSBrian Starkey * clients which don't understand them will ignore them). 27*935774cdSBrian Starkey * - Writeback connectors don't have EDID. 28*935774cdSBrian Starkey * 29*935774cdSBrian Starkey * A framebuffer may only be attached to a writeback connector when the 30*935774cdSBrian Starkey * connector is attached to a CRTC. The WRITEBACK_FB_ID property which sets the 31*935774cdSBrian Starkey * framebuffer applies only to a single commit (see below). A framebuffer may 32*935774cdSBrian Starkey * not be attached while the CRTC is off. 33*935774cdSBrian Starkey * 34*935774cdSBrian Starkey * Writeback connectors have some additional properties, which userspace 35*935774cdSBrian Starkey * can use to query and control them: 36*935774cdSBrian Starkey * 37*935774cdSBrian Starkey * "WRITEBACK_FB_ID": 38*935774cdSBrian Starkey * Write-only object property storing a DRM_MODE_OBJECT_FB: it stores the 39*935774cdSBrian Starkey * framebuffer to be written by the writeback connector. This property is 40*935774cdSBrian Starkey * similar to the FB_ID property on planes, but will always read as zero 41*935774cdSBrian Starkey * and is not preserved across commits. 42*935774cdSBrian Starkey * Userspace must set this property to an output buffer every time it 43*935774cdSBrian Starkey * wishes the buffer to get filled. 44*935774cdSBrian Starkey * 45*935774cdSBrian Starkey * "WRITEBACK_PIXEL_FORMATS": 46*935774cdSBrian Starkey * Immutable blob property to store the supported pixel formats table. The 47*935774cdSBrian Starkey * data is an array of u32 DRM_FORMAT_* fourcc values. 48*935774cdSBrian Starkey * Userspace can use this blob to find out what pixel formats are supported 49*935774cdSBrian Starkey * by the connector's writeback engine. 50*935774cdSBrian Starkey */ 51*935774cdSBrian Starkey 52*935774cdSBrian Starkey static int create_writeback_properties(struct drm_device *dev) 53*935774cdSBrian Starkey { 54*935774cdSBrian Starkey struct drm_property *prop; 55*935774cdSBrian Starkey 56*935774cdSBrian Starkey if (!dev->mode_config.writeback_fb_id_property) { 57*935774cdSBrian Starkey prop = drm_property_create_object(dev, DRM_MODE_PROP_ATOMIC, 58*935774cdSBrian Starkey "WRITEBACK_FB_ID", 59*935774cdSBrian Starkey DRM_MODE_OBJECT_FB); 60*935774cdSBrian Starkey if (!prop) 61*935774cdSBrian Starkey return -ENOMEM; 62*935774cdSBrian Starkey dev->mode_config.writeback_fb_id_property = prop; 63*935774cdSBrian Starkey } 64*935774cdSBrian Starkey 65*935774cdSBrian Starkey if (!dev->mode_config.writeback_pixel_formats_property) { 66*935774cdSBrian Starkey prop = drm_property_create(dev, DRM_MODE_PROP_BLOB | 67*935774cdSBrian Starkey DRM_MODE_PROP_ATOMIC | 68*935774cdSBrian Starkey DRM_MODE_PROP_IMMUTABLE, 69*935774cdSBrian Starkey "WRITEBACK_PIXEL_FORMATS", 0); 70*935774cdSBrian Starkey if (!prop) 71*935774cdSBrian Starkey return -ENOMEM; 72*935774cdSBrian Starkey dev->mode_config.writeback_pixel_formats_property = prop; 73*935774cdSBrian Starkey } 74*935774cdSBrian Starkey 75*935774cdSBrian Starkey return 0; 76*935774cdSBrian Starkey } 77*935774cdSBrian Starkey 78*935774cdSBrian Starkey static const struct drm_encoder_funcs drm_writeback_encoder_funcs = { 79*935774cdSBrian Starkey .destroy = drm_encoder_cleanup, 80*935774cdSBrian Starkey }; 81*935774cdSBrian Starkey 82*935774cdSBrian Starkey /** 83*935774cdSBrian Starkey * drm_writeback_connector_init - Initialize a writeback connector and its properties 84*935774cdSBrian Starkey * @dev: DRM device 85*935774cdSBrian Starkey * @wb_connector: Writeback connector to initialize 86*935774cdSBrian Starkey * @con_funcs: Connector funcs vtable 87*935774cdSBrian Starkey * @enc_helper_funcs: Encoder helper funcs vtable to be used by the internal encoder 88*935774cdSBrian Starkey * @formats: Array of supported pixel formats for the writeback engine 89*935774cdSBrian Starkey * @n_formats: Length of the formats array 90*935774cdSBrian Starkey * 91*935774cdSBrian Starkey * This function creates the writeback-connector-specific properties if they 92*935774cdSBrian Starkey * have not been already created, initializes the connector as 93*935774cdSBrian Starkey * type DRM_MODE_CONNECTOR_WRITEBACK, and correctly initializes the property 94*935774cdSBrian Starkey * values. It will also create an internal encoder associated with the 95*935774cdSBrian Starkey * drm_writeback_connector and set it to use the @enc_helper_funcs vtable for 96*935774cdSBrian Starkey * the encoder helper. 97*935774cdSBrian Starkey * 98*935774cdSBrian Starkey * Drivers should always use this function instead of drm_connector_init() to 99*935774cdSBrian Starkey * set up writeback connectors. 100*935774cdSBrian Starkey * 101*935774cdSBrian Starkey * Returns: 0 on success, or a negative error code 102*935774cdSBrian Starkey */ 103*935774cdSBrian Starkey int drm_writeback_connector_init(struct drm_device *dev, 104*935774cdSBrian Starkey struct drm_writeback_connector *wb_connector, 105*935774cdSBrian Starkey const struct drm_connector_funcs *con_funcs, 106*935774cdSBrian Starkey const struct drm_encoder_helper_funcs *enc_helper_funcs, 107*935774cdSBrian Starkey const u32 *formats, int n_formats) 108*935774cdSBrian Starkey { 109*935774cdSBrian Starkey struct drm_property_blob *blob; 110*935774cdSBrian Starkey struct drm_connector *connector = &wb_connector->base; 111*935774cdSBrian Starkey struct drm_mode_config *config = &dev->mode_config; 112*935774cdSBrian Starkey int ret = create_writeback_properties(dev); 113*935774cdSBrian Starkey 114*935774cdSBrian Starkey if (ret != 0) 115*935774cdSBrian Starkey return ret; 116*935774cdSBrian Starkey 117*935774cdSBrian Starkey blob = drm_property_create_blob(dev, n_formats * sizeof(*formats), 118*935774cdSBrian Starkey formats); 119*935774cdSBrian Starkey if (IS_ERR(blob)) 120*935774cdSBrian Starkey return PTR_ERR(blob); 121*935774cdSBrian Starkey 122*935774cdSBrian Starkey drm_encoder_helper_add(&wb_connector->encoder, enc_helper_funcs); 123*935774cdSBrian Starkey ret = drm_encoder_init(dev, &wb_connector->encoder, 124*935774cdSBrian Starkey &drm_writeback_encoder_funcs, 125*935774cdSBrian Starkey DRM_MODE_ENCODER_VIRTUAL, NULL); 126*935774cdSBrian Starkey if (ret) 127*935774cdSBrian Starkey goto fail; 128*935774cdSBrian Starkey 129*935774cdSBrian Starkey connector->interlace_allowed = 0; 130*935774cdSBrian Starkey 131*935774cdSBrian Starkey ret = drm_connector_init(dev, connector, con_funcs, 132*935774cdSBrian Starkey DRM_MODE_CONNECTOR_WRITEBACK); 133*935774cdSBrian Starkey if (ret) 134*935774cdSBrian Starkey goto connector_fail; 135*935774cdSBrian Starkey 136*935774cdSBrian Starkey ret = drm_mode_connector_attach_encoder(connector, 137*935774cdSBrian Starkey &wb_connector->encoder); 138*935774cdSBrian Starkey if (ret) 139*935774cdSBrian Starkey goto attach_fail; 140*935774cdSBrian Starkey 141*935774cdSBrian Starkey INIT_LIST_HEAD(&wb_connector->job_queue); 142*935774cdSBrian Starkey spin_lock_init(&wb_connector->job_lock); 143*935774cdSBrian Starkey 144*935774cdSBrian Starkey drm_object_attach_property(&connector->base, 145*935774cdSBrian Starkey config->writeback_fb_id_property, 0); 146*935774cdSBrian Starkey 147*935774cdSBrian Starkey drm_object_attach_property(&connector->base, 148*935774cdSBrian Starkey config->writeback_pixel_formats_property, 149*935774cdSBrian Starkey blob->base.id); 150*935774cdSBrian Starkey wb_connector->pixel_formats_blob_ptr = blob; 151*935774cdSBrian Starkey 152*935774cdSBrian Starkey return 0; 153*935774cdSBrian Starkey 154*935774cdSBrian Starkey attach_fail: 155*935774cdSBrian Starkey drm_connector_cleanup(connector); 156*935774cdSBrian Starkey connector_fail: 157*935774cdSBrian Starkey drm_encoder_cleanup(&wb_connector->encoder); 158*935774cdSBrian Starkey fail: 159*935774cdSBrian Starkey drm_property_blob_put(blob); 160*935774cdSBrian Starkey return ret; 161*935774cdSBrian Starkey } 162*935774cdSBrian Starkey EXPORT_SYMBOL(drm_writeback_connector_init); 163*935774cdSBrian Starkey 164*935774cdSBrian Starkey /** 165*935774cdSBrian Starkey * drm_writeback_queue_job - Queue a writeback job for later signalling 166*935774cdSBrian Starkey * @wb_connector: The writeback connector to queue a job on 167*935774cdSBrian Starkey * @job: The job to queue 168*935774cdSBrian Starkey * 169*935774cdSBrian Starkey * This function adds a job to the job_queue for a writeback connector. It 170*935774cdSBrian Starkey * should be considered to take ownership of the writeback job, and so any other 171*935774cdSBrian Starkey * references to the job must be cleared after calling this function. 172*935774cdSBrian Starkey * 173*935774cdSBrian Starkey * Drivers must ensure that for a given writeback connector, jobs are queued in 174*935774cdSBrian Starkey * exactly the same order as they will be completed by the hardware (and 175*935774cdSBrian Starkey * signaled via drm_writeback_signal_completion). 176*935774cdSBrian Starkey * 177*935774cdSBrian Starkey * For every call to drm_writeback_queue_job() there must be exactly one call to 178*935774cdSBrian Starkey * drm_writeback_signal_completion() 179*935774cdSBrian Starkey * 180*935774cdSBrian Starkey * See also: drm_writeback_signal_completion() 181*935774cdSBrian Starkey */ 182*935774cdSBrian Starkey void drm_writeback_queue_job(struct drm_writeback_connector *wb_connector, 183*935774cdSBrian Starkey struct drm_writeback_job *job) 184*935774cdSBrian Starkey { 185*935774cdSBrian Starkey unsigned long flags; 186*935774cdSBrian Starkey 187*935774cdSBrian Starkey spin_lock_irqsave(&wb_connector->job_lock, flags); 188*935774cdSBrian Starkey list_add_tail(&job->list_entry, &wb_connector->job_queue); 189*935774cdSBrian Starkey spin_unlock_irqrestore(&wb_connector->job_lock, flags); 190*935774cdSBrian Starkey } 191*935774cdSBrian Starkey EXPORT_SYMBOL(drm_writeback_queue_job); 192*935774cdSBrian Starkey 193*935774cdSBrian Starkey /* 194*935774cdSBrian Starkey * @cleanup_work: deferred cleanup of a writeback job 195*935774cdSBrian Starkey * 196*935774cdSBrian Starkey * The job cannot be cleaned up directly in drm_writeback_signal_completion, 197*935774cdSBrian Starkey * because it may be called in interrupt context. Dropping the framebuffer 198*935774cdSBrian Starkey * reference can sleep, and so the cleanup is deferred to a workqueue. 199*935774cdSBrian Starkey */ 200*935774cdSBrian Starkey static void cleanup_work(struct work_struct *work) 201*935774cdSBrian Starkey { 202*935774cdSBrian Starkey struct drm_writeback_job *job = container_of(work, 203*935774cdSBrian Starkey struct drm_writeback_job, 204*935774cdSBrian Starkey cleanup_work); 205*935774cdSBrian Starkey drm_framebuffer_put(job->fb); 206*935774cdSBrian Starkey kfree(job); 207*935774cdSBrian Starkey } 208*935774cdSBrian Starkey 209*935774cdSBrian Starkey 210*935774cdSBrian Starkey /** 211*935774cdSBrian Starkey * drm_writeback_signal_completion - Signal the completion of a writeback job 212*935774cdSBrian Starkey * @wb_connector: The writeback connector whose job is complete 213*935774cdSBrian Starkey * 214*935774cdSBrian Starkey * Drivers should call this to signal the completion of a previously queued 215*935774cdSBrian Starkey * writeback job. It should be called as soon as possible after the hardware 216*935774cdSBrian Starkey * has finished writing, and may be called from interrupt context. 217*935774cdSBrian Starkey * It is the driver's responsibility to ensure that for a given connector, the 218*935774cdSBrian Starkey * hardware completes writeback jobs in the same order as they are queued. 219*935774cdSBrian Starkey * 220*935774cdSBrian Starkey * Unless the driver is holding its own reference to the framebuffer, it must 221*935774cdSBrian Starkey * not be accessed after calling this function. 222*935774cdSBrian Starkey * 223*935774cdSBrian Starkey * See also: drm_writeback_queue_job() 224*935774cdSBrian Starkey */ 225*935774cdSBrian Starkey void 226*935774cdSBrian Starkey drm_writeback_signal_completion(struct drm_writeback_connector *wb_connector) 227*935774cdSBrian Starkey { 228*935774cdSBrian Starkey unsigned long flags; 229*935774cdSBrian Starkey struct drm_writeback_job *job; 230*935774cdSBrian Starkey 231*935774cdSBrian Starkey spin_lock_irqsave(&wb_connector->job_lock, flags); 232*935774cdSBrian Starkey job = list_first_entry_or_null(&wb_connector->job_queue, 233*935774cdSBrian Starkey struct drm_writeback_job, 234*935774cdSBrian Starkey list_entry); 235*935774cdSBrian Starkey if (job) 236*935774cdSBrian Starkey list_del(&job->list_entry); 237*935774cdSBrian Starkey spin_unlock_irqrestore(&wb_connector->job_lock, flags); 238*935774cdSBrian Starkey 239*935774cdSBrian Starkey if (WARN_ON(!job)) 240*935774cdSBrian Starkey return; 241*935774cdSBrian Starkey 242*935774cdSBrian Starkey INIT_WORK(&job->cleanup_work, cleanup_work); 243*935774cdSBrian Starkey queue_work(system_long_wq, &job->cleanup_work); 244*935774cdSBrian Starkey } 245*935774cdSBrian Starkey EXPORT_SYMBOL(drm_writeback_signal_completion); 246