Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0-only
2
3#include <linux/delay.h>
4
5#include <drm/drm_atomic_helper.h>
6#include <drm/drm_edid.h>
7#include <drm/drm_managed.h>
8#include <drm/drm_probe_helper.h>
9
10#include "mgag200_drv.h"
11
12static struct mgag200_bmc_connector *to_mgag200_bmc_connector(struct drm_connector *connector)
13{
14 return container_of(connector, struct mgag200_bmc_connector, base);
15}
16
17void mgag200_bmc_disable_vidrst(struct mga_device *mdev)
18{
19 u8 tmp;
20 int iter_max;
21
22 /*
23 * 1 - The first step is to inform the BMC of an upcoming mode
24 * change. We are putting the misc<0> to output.
25 */
26
27 WREG8(DAC_INDEX, MGA1064_GEN_IO_CTL);
28 tmp = RREG8(DAC_DATA);
29 tmp |= 0x10;
30 WREG_DAC(MGA1064_GEN_IO_CTL, tmp);
31
32 /* we are putting a 1 on the misc<0> line */
33 WREG8(DAC_INDEX, MGA1064_GEN_IO_DATA);
34 tmp = RREG8(DAC_DATA);
35 tmp |= 0x10;
36 WREG_DAC(MGA1064_GEN_IO_DATA, tmp);
37
38 /*
39 * 2- Second step to mask any further scan request. This is
40 * done by asserting the remfreqmsk bit (XSPAREREG<7>)
41 */
42
43 WREG8(DAC_INDEX, MGA1064_SPAREREG);
44 tmp = RREG8(DAC_DATA);
45 tmp |= 0x80;
46 WREG_DAC(MGA1064_SPAREREG, tmp);
47
48 /*
49 * 3a- The third step is to verify if there is an active scan.
50 * We are waiting for a 0 on remhsyncsts <XSPAREREG<0>).
51 */
52 iter_max = 300;
53 while (!(tmp & 0x1) && iter_max) {
54 WREG8(DAC_INDEX, MGA1064_SPAREREG);
55 tmp = RREG8(DAC_DATA);
56 udelay(1000);
57 iter_max--;
58 }
59
60 /*
61 * 3b- This step occurs only if the remove is actually
62 * scanning. We are waiting for the end of the frame which is
63 * a 1 on remvsyncsts (XSPAREREG<1>)
64 */
65 if (iter_max) {
66 iter_max = 300;
67 while ((tmp & 0x2) && iter_max) {
68 WREG8(DAC_INDEX, MGA1064_SPAREREG);
69 tmp = RREG8(DAC_DATA);
70 udelay(1000);
71 iter_max--;
72 }
73 }
74}
75
76void mgag200_bmc_enable_vidrst(struct mga_device *mdev)
77{
78 u8 tmp;
79
80 /* Ensure that the vrsten and hrsten are set */
81 WREG8(MGAREG_CRTCEXT_INDEX, 1);
82 tmp = RREG8(MGAREG_CRTCEXT_DATA);
83 WREG8(MGAREG_CRTCEXT_DATA, tmp | 0x88);
84
85 /* Assert rstlvl2 */
86 WREG8(DAC_INDEX, MGA1064_REMHEADCTL2);
87 tmp = RREG8(DAC_DATA);
88 tmp |= 0x8;
89 WREG8(DAC_DATA, tmp);
90
91 udelay(10);
92
93 /* Deassert rstlvl2 */
94 tmp &= ~0x08;
95 WREG8(DAC_INDEX, MGA1064_REMHEADCTL2);
96 WREG8(DAC_DATA, tmp);
97
98 /* Remove mask of scan request */
99 WREG8(DAC_INDEX, MGA1064_SPAREREG);
100 tmp = RREG8(DAC_DATA);
101 tmp &= ~0x80;
102 WREG8(DAC_DATA, tmp);
103
104 /* Put back a 0 on the misc<0> line */
105 WREG8(DAC_INDEX, MGA1064_GEN_IO_DATA);
106 tmp = RREG8(DAC_DATA);
107 tmp &= ~0x10;
108 WREG_DAC(MGA1064_GEN_IO_DATA, tmp);
109}
110
111static const struct drm_encoder_funcs mgag200_bmc_encoder_funcs = {
112 .destroy = drm_encoder_cleanup,
113};
114
115static int mgag200_bmc_connector_helper_detect_ctx(struct drm_connector *connector,
116 struct drm_modeset_acquire_ctx *ctx,
117 bool force)
118{
119 struct mgag200_bmc_connector *bmc_connector = to_mgag200_bmc_connector(connector);
120 struct drm_connector *physical_connector = bmc_connector->physical_connector;
121
122 /*
123 * Most user-space compositors cannot handle more than one connected
124 * connector per CRTC. Hence, we only mark the BMC as connected if the
125 * physical connector is disconnected. If the physical connector's status
126 * is connected or unknown, the BMC remains disconnected. This has no
127 * effect on the output of the BMC.
128 *
129 * FIXME: Remove this logic once user-space compositors can handle more
130 * than one connector per CRTC. The BMC should always be connected.
131 */
132
133 if (physical_connector && physical_connector->status == connector_status_disconnected)
134 return connector_status_connected;
135
136 return connector_status_disconnected;
137}
138
139static int mgag200_bmc_connector_helper_get_modes(struct drm_connector *connector)
140{
141 struct drm_device *dev = connector->dev;
142 struct mga_device *mdev = to_mga_device(dev);
143 const struct mgag200_device_info *minfo = mdev->info;
144
145 return drm_add_modes_noedid(connector, minfo->max_hdisplay, minfo->max_vdisplay);
146}
147
148static const struct drm_connector_helper_funcs mgag200_bmc_connector_helper_funcs = {
149 .get_modes = mgag200_bmc_connector_helper_get_modes,
150 .detect_ctx = mgag200_bmc_connector_helper_detect_ctx,
151};
152
153static const struct drm_connector_funcs mgag200_bmc_connector_funcs = {
154 .reset = drm_atomic_helper_connector_reset,
155 .fill_modes = drm_helper_probe_single_connector_modes,
156 .destroy = drm_connector_cleanup,
157 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
158 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
159};
160
161static int mgag200_bmc_connector_init(struct drm_device *dev,
162 struct mgag200_bmc_connector *bmc_connector,
163 struct drm_connector *physical_connector)
164{
165 struct drm_connector *connector = &bmc_connector->base;
166 int ret;
167
168 ret = drm_connector_init(dev, connector, &mgag200_bmc_connector_funcs,
169 DRM_MODE_CONNECTOR_VIRTUAL);
170 if (ret)
171 return ret;
172 drm_connector_helper_add(connector, &mgag200_bmc_connector_helper_funcs);
173
174 bmc_connector->physical_connector = physical_connector;
175
176 return 0;
177}
178
179int mgag200_bmc_output_init(struct mga_device *mdev, struct drm_connector *physical_connector)
180{
181 struct drm_device *dev = &mdev->base;
182 struct drm_crtc *crtc = &mdev->crtc;
183 struct drm_encoder *encoder;
184 struct mgag200_bmc_connector *bmc_connector;
185 struct drm_connector *connector;
186 int ret;
187
188 encoder = &mdev->output.bmc.encoder;
189 ret = drm_encoder_init(dev, encoder, &mgag200_bmc_encoder_funcs,
190 DRM_MODE_ENCODER_VIRTUAL, NULL);
191 if (ret)
192 return ret;
193 encoder->possible_crtcs = drm_crtc_mask(crtc);
194
195 bmc_connector = &mdev->output.bmc.bmc_connector;
196 ret = mgag200_bmc_connector_init(dev, bmc_connector, physical_connector);
197 if (ret)
198 return ret;
199 connector = &bmc_connector->base;
200
201 ret = drm_connector_attach_encoder(connector, encoder);
202 if (ret)
203 return ret;
204
205 return 0;
206}