1 // SPDX-License-Identifier: GPL-2.0+ 2 3 #include "vkms_drv.h" 4 #include <drm/drm_fourcc.h> 5 #include <drm/drm_writeback.h> 6 #include <drm/drm_probe_helper.h> 7 #include <drm/drm_atomic_helper.h> 8 #include <drm/drm_gem_framebuffer_helper.h> 9 #include <drm/drm_gem_shmem_helper.h> 10 11 static const u32 vkms_wb_formats[] = { 12 DRM_FORMAT_XRGB8888, 13 }; 14 15 static const struct drm_connector_funcs vkms_wb_connector_funcs = { 16 .fill_modes = drm_helper_probe_single_connector_modes, 17 .destroy = drm_connector_cleanup, 18 .reset = drm_atomic_helper_connector_reset, 19 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 20 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 21 }; 22 23 static int vkms_wb_encoder_atomic_check(struct drm_encoder *encoder, 24 struct drm_crtc_state *crtc_state, 25 struct drm_connector_state *conn_state) 26 { 27 struct drm_framebuffer *fb; 28 const struct drm_display_mode *mode = &crtc_state->mode; 29 30 if (!conn_state->writeback_job || !conn_state->writeback_job->fb) 31 return 0; 32 33 fb = conn_state->writeback_job->fb; 34 if (fb->width != mode->hdisplay || fb->height != mode->vdisplay) { 35 DRM_DEBUG_KMS("Invalid framebuffer size %ux%u\n", 36 fb->width, fb->height); 37 return -EINVAL; 38 } 39 40 if (fb->format->format != vkms_wb_formats[0]) { 41 struct drm_format_name_buf format_name; 42 43 DRM_DEBUG_KMS("Invalid pixel format %s\n", 44 drm_get_format_name(fb->format->format, 45 &format_name)); 46 return -EINVAL; 47 } 48 49 return 0; 50 } 51 52 static const struct drm_encoder_helper_funcs vkms_wb_encoder_helper_funcs = { 53 .atomic_check = vkms_wb_encoder_atomic_check, 54 }; 55 56 static int vkms_wb_connector_get_modes(struct drm_connector *connector) 57 { 58 struct drm_device *dev = connector->dev; 59 60 return drm_add_modes_noedid(connector, dev->mode_config.max_width, 61 dev->mode_config.max_height); 62 } 63 64 static int vkms_wb_prepare_job(struct drm_writeback_connector *wb_connector, 65 struct drm_writeback_job *job) 66 { 67 struct drm_gem_object *gem_obj; 68 void *vaddr; 69 70 if (!job->fb) 71 return 0; 72 73 gem_obj = drm_gem_fb_get_obj(job->fb, 0); 74 vaddr = drm_gem_shmem_vmap(gem_obj); 75 if (IS_ERR(vaddr)) { 76 DRM_ERROR("vmap failed: %li\n", PTR_ERR(vaddr)); 77 return PTR_ERR(vaddr); 78 } 79 80 job->priv = vaddr; 81 82 return 0; 83 } 84 85 static void vkms_wb_cleanup_job(struct drm_writeback_connector *connector, 86 struct drm_writeback_job *job) 87 { 88 struct drm_gem_object *gem_obj; 89 struct vkms_device *vkmsdev; 90 91 if (!job->fb) 92 return; 93 94 gem_obj = drm_gem_fb_get_obj(job->fb, 0); 95 drm_gem_shmem_vunmap(gem_obj, job->priv); 96 97 vkmsdev = drm_device_to_vkms_device(gem_obj->dev); 98 vkms_set_composer(&vkmsdev->output, false); 99 } 100 101 static void vkms_wb_atomic_commit(struct drm_connector *conn, 102 struct drm_connector_state *state) 103 { 104 struct vkms_device *vkmsdev = drm_device_to_vkms_device(conn->dev); 105 struct vkms_output *output = &vkmsdev->output; 106 struct drm_writeback_connector *wb_conn = &output->wb_connector; 107 struct drm_connector_state *conn_state = wb_conn->base.state; 108 struct vkms_crtc_state *crtc_state = output->composer_state; 109 110 if (!conn_state) 111 return; 112 113 vkms_set_composer(&vkmsdev->output, true); 114 115 spin_lock_irq(&output->composer_lock); 116 crtc_state->active_writeback = conn_state->writeback_job->priv; 117 crtc_state->wb_pending = true; 118 spin_unlock_irq(&output->composer_lock); 119 drm_writeback_queue_job(wb_conn, state); 120 } 121 122 static const struct drm_connector_helper_funcs vkms_wb_conn_helper_funcs = { 123 .get_modes = vkms_wb_connector_get_modes, 124 .prepare_writeback_job = vkms_wb_prepare_job, 125 .cleanup_writeback_job = vkms_wb_cleanup_job, 126 .atomic_commit = vkms_wb_atomic_commit, 127 }; 128 129 int vkms_enable_writeback_connector(struct vkms_device *vkmsdev) 130 { 131 struct drm_writeback_connector *wb = &vkmsdev->output.wb_connector; 132 133 vkmsdev->output.wb_connector.encoder.possible_crtcs = 1; 134 drm_connector_helper_add(&wb->base, &vkms_wb_conn_helper_funcs); 135 136 return drm_writeback_connector_init(&vkmsdev->drm, wb, 137 &vkms_wb_connector_funcs, 138 &vkms_wb_encoder_helper_funcs, 139 vkms_wb_formats, 140 ARRAY_SIZE(vkms_wb_formats)); 141 } 142