drm/virtio: add edid support
authorGerd Hoffmann <kraxel@redhat.com>
Tue, 30 Oct 2018 06:32:06 +0000 (07:32 +0100)
committerGerd Hoffmann <kraxel@redhat.com>
Wed, 21 Nov 2018 11:06:59 +0000 (12:06 +0100)
linux guest driver implementation of the VIRTIO_GPU_F_EDID feature.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Acked-by: Daniel Vetter <daniel@ffwll.ch>
Link: http://patchwork.freedesktop.org/patch/msgid/20181030063206.19528-3-kraxel@redhat.com
drivers/gpu/drm/virtio/virtgpu_display.c
drivers/gpu/drm/virtio/virtgpu_drv.c
drivers/gpu/drm/virtio/virtgpu_drv.h
drivers/gpu/drm/virtio/virtgpu_kms.c
drivers/gpu/drm/virtio/virtgpu_vq.c

index 8f8fed471e34b296d7d2efce36deb877656e61a6..b5580b11a063606e79d8d43005b4cf4423ef5629 100644 (file)
@@ -169,6 +169,12 @@ static int virtio_gpu_conn_get_modes(struct drm_connector *connector)
        struct drm_display_mode *mode = NULL;
        int count, width, height;
 
+       if (output->edid) {
+               count = drm_add_edid_modes(connector, output->edid);
+               if (count)
+                       return count;
+       }
+
        width  = le32_to_cpu(output->info.r.width);
        height = le32_to_cpu(output->info.r.height);
        count = drm_add_modes_noedid(connector, XRES_MAX, YRES_MAX);
@@ -287,6 +293,8 @@ static int vgdev_output_init(struct virtio_gpu_device *vgdev, int index)
        drm_connector_init(dev, connector, &virtio_gpu_connector_funcs,
                           DRM_MODE_CONNECTOR_VIRTUAL);
        drm_connector_helper_add(connector, &virtio_gpu_conn_helper_funcs);
+       if (vgdev->has_edid)
+               drm_connector_attach_edid_property(connector);
 
        drm_encoder_init(dev, encoder, &virtio_gpu_enc_funcs,
                         DRM_MODE_ENCODER_VIRTUAL, NULL);
@@ -378,6 +386,10 @@ int virtio_gpu_modeset_init(struct virtio_gpu_device *vgdev)
 
 void virtio_gpu_modeset_fini(struct virtio_gpu_device *vgdev)
 {
+       int i;
+
+       for (i = 0 ; i < vgdev->num_scanouts; ++i)
+               kfree(vgdev->outputs[i].edid);
        virtio_gpu_fbdev_fini(vgdev);
        drm_mode_config_cleanup(vgdev->ddev);
 }
index d9287c144fe5ea11d15d24aff55c5bf1d2ce2cdd..f7f32a885af79902a4c02ce4c4c801c1a119cc83 100644 (file)
@@ -80,6 +80,7 @@ static unsigned int features[] = {
         */
        VIRTIO_GPU_F_VIRGL,
 #endif
+       VIRTIO_GPU_F_EDID,
 };
 static struct virtio_driver virtio_gpu_driver = {
        .feature_table = features,
index 4632bd7e19725de4af5fbcf1f330572cdbce207d..7bec6e36886b09d58fcf06590b393524273c504f 100644 (file)
@@ -115,6 +115,7 @@ struct virtio_gpu_output {
        struct drm_encoder enc;
        struct virtio_gpu_display_one info;
        struct virtio_gpu_update_cursor cursor;
+       struct edid *edid;
        int cur_x;
        int cur_y;
        bool enabled;
@@ -204,6 +205,7 @@ struct virtio_gpu_device {
        struct ida      ctx_id_ida;
 
        bool has_virgl_3d;
+       bool has_edid;
 
        struct work_struct config_changed_work;
 
@@ -294,6 +296,7 @@ int virtio_gpu_cmd_get_capset_info(struct virtio_gpu_device *vgdev, int idx);
 int virtio_gpu_cmd_get_capset(struct virtio_gpu_device *vgdev,
                              int idx, int version,
                              struct virtio_gpu_drv_cap_cache **cache_p);
+int virtio_gpu_cmd_get_edids(struct virtio_gpu_device *vgdev);
 void virtio_gpu_cmd_context_create(struct virtio_gpu_device *vgdev, uint32_t id,
                                   uint32_t nlen, const char *name);
 void virtio_gpu_cmd_context_destroy(struct virtio_gpu_device *vgdev,
index 691b842d5f3a4ed6f374953f857894b952f8c324..3af6181c05a852cccf91880e34388f78723e1835 100644 (file)
@@ -44,6 +44,8 @@ static void virtio_gpu_config_changed_work_func(struct work_struct *work)
        virtio_cread(vgdev->vdev, struct virtio_gpu_config,
                     events_read, &events_read);
        if (events_read & VIRTIO_GPU_EVENT_DISPLAY) {
+               if (vgdev->has_edid)
+                       virtio_gpu_cmd_get_edids(vgdev);
                virtio_gpu_cmd_get_display_info(vgdev);
                drm_helper_hpd_irq_event(vgdev->ddev);
                events_clear |= VIRTIO_GPU_EVENT_DISPLAY;
@@ -156,6 +158,10 @@ int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags)
 #else
        DRM_INFO("virgl 3d acceleration not supported by guest\n");
 #endif
+       if (virtio_has_feature(vgdev->vdev, VIRTIO_GPU_F_EDID)) {
+               vgdev->has_edid = true;
+               DRM_INFO("EDID support available.\n");
+       }
 
        ret = virtio_find_vqs(vgdev->vdev, 2, vqs, callbacks, names, NULL);
        if (ret) {
@@ -201,6 +207,8 @@ int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags)
 
        if (num_capsets)
                virtio_gpu_get_capsets(vgdev, num_capsets);
+       if (vgdev->has_edid)
+               virtio_gpu_cmd_get_edids(vgdev);
        virtio_gpu_cmd_get_display_info(vgdev);
        wait_event_timeout(vgdev->resp_wq, !vgdev->display_info_pending,
                           5 * HZ);
index 93f2c3a51ee8bbca0d9d5a95c93d2104fabb2841..2c6764f08f18c5e230fbbf46f0b7be167c49520a 100644 (file)
@@ -584,6 +584,45 @@ static void virtio_gpu_cmd_capset_cb(struct virtio_gpu_device *vgdev,
        wake_up(&vgdev->resp_wq);
 }
 
+static int virtio_get_edid_block(void *data, u8 *buf,
+                                unsigned int block, size_t len)
+{
+       struct virtio_gpu_resp_edid *resp = data;
+       size_t start = block * EDID_LENGTH;
+
+       if (start + len > le32_to_cpu(resp->size))
+               return -1;
+       memcpy(buf, resp->edid + start, len);
+       return 0;
+}
+
+static void virtio_gpu_cmd_get_edid_cb(struct virtio_gpu_device *vgdev,
+                                      struct virtio_gpu_vbuffer *vbuf)
+{
+       struct virtio_gpu_cmd_get_edid *cmd =
+               (struct virtio_gpu_cmd_get_edid *)vbuf->buf;
+       struct virtio_gpu_resp_edid *resp =
+               (struct virtio_gpu_resp_edid *)vbuf->resp_buf;
+       uint32_t scanout = le32_to_cpu(cmd->scanout);
+       struct virtio_gpu_output *output;
+       struct edid *new_edid, *old_edid;
+
+       if (scanout >= vgdev->num_scanouts)
+               return;
+       output = vgdev->outputs + scanout;
+
+       new_edid = drm_do_get_edid(&output->conn, virtio_get_edid_block, resp);
+
+       spin_lock(&vgdev->display_info_lock);
+       old_edid = output->edid;
+       output->edid = new_edid;
+       drm_connector_update_edid_property(&output->conn, output->edid);
+       spin_unlock(&vgdev->display_info_lock);
+
+       kfree(old_edid);
+       wake_up(&vgdev->resp_wq);
+}
+
 int virtio_gpu_cmd_get_display_info(struct virtio_gpu_device *vgdev)
 {
        struct virtio_gpu_ctrl_hdr *cmd_p;
@@ -686,6 +725,34 @@ int virtio_gpu_cmd_get_capset(struct virtio_gpu_device *vgdev,
        return 0;
 }
 
+int virtio_gpu_cmd_get_edids(struct virtio_gpu_device *vgdev)
+{
+       struct virtio_gpu_cmd_get_edid *cmd_p;
+       struct virtio_gpu_vbuffer *vbuf;
+       void *resp_buf;
+       int scanout;
+
+       if (WARN_ON(!vgdev->has_edid))
+               return -EINVAL;
+
+       for (scanout = 0; scanout < vgdev->num_scanouts; scanout++) {
+               resp_buf = kzalloc(sizeof(struct virtio_gpu_resp_edid),
+                                  GFP_KERNEL);
+               if (!resp_buf)
+                       return -ENOMEM;
+
+               cmd_p = virtio_gpu_alloc_cmd_resp
+                       (vgdev, &virtio_gpu_cmd_get_edid_cb, &vbuf,
+                        sizeof(*cmd_p), sizeof(struct virtio_gpu_resp_edid),
+                        resp_buf);
+               cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_GET_EDID);
+               cmd_p->scanout = cpu_to_le32(scanout);
+               virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
+       }
+
+       return 0;
+}
+
 void virtio_gpu_cmd_context_create(struct virtio_gpu_device *vgdev, uint32_t id,
                                   uint32_t nlen, const char *name)
 {