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_probe_helper.h> |
7 | |
8 | static const struct drm_connector_funcs vkms_connector_funcs = { |
9 | .fill_modes = drm_helper_probe_single_connector_modes, |
10 | .destroy = drm_connector_cleanup, |
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 | |
16 | static const struct drm_encoder_funcs vkms_encoder_funcs = { |
17 | .destroy = drm_encoder_cleanup, |
18 | }; |
19 | |
20 | static int vkms_conn_get_modes(struct drm_connector *connector) |
21 | { |
22 | int count; |
23 | |
24 | count = drm_add_modes_noedid(connector, XRES_MAX, YRES_MAX); |
25 | drm_set_preferred_mode(connector, XRES_DEF, YRES_DEF); |
26 | |
27 | return count; |
28 | } |
29 | |
30 | static const struct drm_connector_helper_funcs vkms_conn_helper_funcs = { |
31 | .get_modes = vkms_conn_get_modes, |
32 | }; |
33 | |
34 | static int vkms_add_overlay_plane(struct vkms_device *vkmsdev, int index, |
35 | struct drm_crtc *crtc) |
36 | { |
37 | struct vkms_plane *overlay; |
38 | |
39 | overlay = vkms_plane_init(vkmsdev, type: DRM_PLANE_TYPE_OVERLAY, index); |
40 | if (IS_ERR(ptr: overlay)) |
41 | return PTR_ERR(ptr: overlay); |
42 | |
43 | if (!overlay->base.possible_crtcs) |
44 | overlay->base.possible_crtcs = drm_crtc_mask(crtc); |
45 | |
46 | return 0; |
47 | } |
48 | |
49 | int vkms_output_init(struct vkms_device *vkmsdev, int index) |
50 | { |
51 | struct vkms_output *output = &vkmsdev->output; |
52 | struct drm_device *dev = &vkmsdev->drm; |
53 | struct drm_connector *connector = &output->connector; |
54 | struct drm_encoder *encoder = &output->encoder; |
55 | struct drm_crtc *crtc = &output->crtc; |
56 | struct vkms_plane *primary, *cursor = NULL; |
57 | int ret; |
58 | int writeback; |
59 | unsigned int n; |
60 | |
61 | primary = vkms_plane_init(vkmsdev, type: DRM_PLANE_TYPE_PRIMARY, index); |
62 | if (IS_ERR(ptr: primary)) |
63 | return PTR_ERR(ptr: primary); |
64 | |
65 | if (vkmsdev->config->overlay) { |
66 | for (n = 0; n < NUM_OVERLAY_PLANES; n++) { |
67 | ret = vkms_add_overlay_plane(vkmsdev, index, crtc); |
68 | if (ret) |
69 | return ret; |
70 | } |
71 | } |
72 | |
73 | if (vkmsdev->config->cursor) { |
74 | cursor = vkms_plane_init(vkmsdev, type: DRM_PLANE_TYPE_CURSOR, index); |
75 | if (IS_ERR(ptr: cursor)) |
76 | return PTR_ERR(ptr: cursor); |
77 | } |
78 | |
79 | ret = vkms_crtc_init(dev, crtc, primary: &primary->base, cursor: &cursor->base); |
80 | if (ret) |
81 | return ret; |
82 | |
83 | ret = drm_connector_init(dev, connector, funcs: &vkms_connector_funcs, |
84 | DRM_MODE_CONNECTOR_VIRTUAL); |
85 | if (ret) { |
86 | DRM_ERROR("Failed to init connector\n" ); |
87 | goto err_connector; |
88 | } |
89 | |
90 | drm_connector_helper_add(connector, funcs: &vkms_conn_helper_funcs); |
91 | |
92 | ret = drm_encoder_init(dev, encoder, funcs: &vkms_encoder_funcs, |
93 | DRM_MODE_ENCODER_VIRTUAL, NULL); |
94 | if (ret) { |
95 | DRM_ERROR("Failed to init encoder\n" ); |
96 | goto err_encoder; |
97 | } |
98 | encoder->possible_crtcs = 1; |
99 | |
100 | ret = drm_connector_attach_encoder(connector, encoder); |
101 | if (ret) { |
102 | DRM_ERROR("Failed to attach connector to encoder\n" ); |
103 | goto err_attach; |
104 | } |
105 | |
106 | if (vkmsdev->config->writeback) { |
107 | writeback = vkms_enable_writeback_connector(vkmsdev); |
108 | if (writeback) |
109 | DRM_ERROR("Failed to init writeback connector\n" ); |
110 | } |
111 | |
112 | drm_mode_config_reset(dev); |
113 | |
114 | return 0; |
115 | |
116 | err_attach: |
117 | drm_encoder_cleanup(encoder); |
118 | |
119 | err_encoder: |
120 | drm_connector_cleanup(connector); |
121 | |
122 | err_connector: |
123 | drm_crtc_cleanup(crtc); |
124 | |
125 | return ret; |
126 | } |
127 | |