1 // SPDX-License-Identifier: GPL-2.0+
2 
3 #include "vkms_drv.h"
4 #include <drm/drm_atomic_helper.h>
5 #include <drm/drm_edid.h>
6 #include <drm/drm_managed.h>
7 #include <drm/drm_probe_helper.h>
8 
9 static const struct drm_connector_funcs vkms_connector_funcs = {
10 	.fill_modes = drm_helper_probe_single_connector_modes,
11 	.reset = drm_atomic_helper_connector_reset,
12 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
13 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
14 };
15 
vkms_conn_get_modes(struct drm_connector * connector)16 static int vkms_conn_get_modes(struct drm_connector *connector)
17 {
18 	int count;
19 
20 	/* Use the default modes list from DRM */
21 	count = drm_add_modes_noedid(connector, XRES_MAX, YRES_MAX);
22 	drm_set_preferred_mode(connector, XRES_DEF, YRES_DEF);
23 
24 	return count;
25 }
26 
27 static const struct drm_connector_helper_funcs vkms_conn_helper_funcs = {
28 	.get_modes    = vkms_conn_get_modes,
29 };
30 
vkms_output_init(struct vkms_device * vkmsdev)31 int vkms_output_init(struct vkms_device *vkmsdev)
32 {
33 	struct drm_device *dev = &vkmsdev->drm;
34 	struct drm_connector *connector;
35 	struct drm_encoder *encoder;
36 	struct vkms_output *output;
37 	struct vkms_plane *primary, *overlay, *cursor = NULL;
38 	int ret;
39 	int writeback;
40 	unsigned int n;
41 
42 	/*
43 	 * Initialize used plane. One primary plane is required to perform the composition.
44 	 *
45 	 * The overlay and cursor planes are not mandatory, but can be used to perform complex
46 	 * composition.
47 	 */
48 	primary = vkms_plane_init(vkmsdev, DRM_PLANE_TYPE_PRIMARY);
49 	if (IS_ERR(primary))
50 		return PTR_ERR(primary);
51 
52 	if (vkmsdev->config->cursor) {
53 		cursor = vkms_plane_init(vkmsdev, DRM_PLANE_TYPE_CURSOR);
54 		if (IS_ERR(cursor))
55 			return PTR_ERR(cursor);
56 	}
57 
58 	output = vkms_crtc_init(dev, &primary->base,
59 				cursor ? &cursor->base : NULL);
60 	if (IS_ERR(output)) {
61 		DRM_ERROR("Failed to allocate CRTC\n");
62 		return PTR_ERR(output);
63 	}
64 
65 	if (vkmsdev->config->overlay) {
66 		for (n = 0; n < NUM_OVERLAY_PLANES; n++) {
67 			overlay = vkms_plane_init(vkmsdev, DRM_PLANE_TYPE_OVERLAY);
68 			if (IS_ERR(overlay)) {
69 				DRM_DEV_ERROR(dev->dev, "Failed to init vkms plane\n");
70 				return PTR_ERR(overlay);
71 			}
72 			overlay->base.possible_crtcs = drm_crtc_mask(&output->crtc);
73 		}
74 	}
75 
76 	connector = drmm_kzalloc(dev, sizeof(*connector), GFP_KERNEL);
77 	if (!connector) {
78 		DRM_ERROR("Failed to allocate connector\n");
79 		return -ENOMEM;
80 	}
81 
82 	ret = drmm_connector_init(dev, connector, &vkms_connector_funcs,
83 				  DRM_MODE_CONNECTOR_VIRTUAL, NULL);
84 	if (ret) {
85 		DRM_ERROR("Failed to init connector\n");
86 		return ret;
87 	}
88 
89 	drm_connector_helper_add(connector, &vkms_conn_helper_funcs);
90 
91 	encoder = drmm_kzalloc(dev, sizeof(*encoder), GFP_KERNEL);
92 	if (!encoder) {
93 		DRM_ERROR("Failed to allocate encoder\n");
94 		return -ENOMEM;
95 	}
96 	ret = drmm_encoder_init(dev, encoder, NULL,
97 				DRM_MODE_ENCODER_VIRTUAL, NULL);
98 	if (ret) {
99 		DRM_ERROR("Failed to init encoder\n");
100 		return ret;
101 	}
102 	encoder->possible_crtcs = drm_crtc_mask(&output->crtc);
103 
104 	/* Attach the encoder and the connector */
105 	ret = drm_connector_attach_encoder(connector, encoder);
106 	if (ret) {
107 		DRM_ERROR("Failed to attach connector to encoder\n");
108 		return ret;
109 	}
110 
111 	/* Initialize the writeback component */
112 	if (vkmsdev->config->writeback) {
113 		writeback = vkms_enable_writeback_connector(vkmsdev, output);
114 		if (writeback)
115 			DRM_ERROR("Failed to init writeback connector\n");
116 	}
117 
118 	drm_mode_config_reset(dev);
119 
120 	return ret;
121 }
122