1// SPDX-License-Identifier: MIT
2/*
3 * Copyright 2020 Noralf Trønnes
4 */
5
6#include <linux/backlight.h>
7#include <linux/workqueue.h>
8
9#include <drm/drm_atomic.h>
10#include <drm/drm_atomic_state_helper.h>
11#include <drm/drm_connector.h>
12#include <drm/drm_drv.h>
13#include <drm/drm_edid.h>
14#include <drm/drm_encoder.h>
15#include <drm/drm_file.h>
16#include <drm/drm_modeset_helper_vtables.h>
17#include <drm/drm_print.h>
18#include <drm/drm_probe_helper.h>
19#include <drm/drm_simple_kms_helper.h>
20#include <drm/gud.h>
21
22#include "gud_internal.h"
23
24struct gud_connector {
25 struct drm_connector connector;
26 struct drm_encoder encoder;
27 struct backlight_device *backlight;
28 struct work_struct backlight_work;
29
30 /* Supported properties */
31 u16 *properties;
32 unsigned int num_properties;
33
34 /* Initial gadget tv state if applicable, applied on state reset */
35 struct drm_tv_connector_state initial_tv_state;
36
37 /*
38 * Initial gadget backlight brightness if applicable, applied on state reset.
39 * The value -ENODEV is used to signal no backlight.
40 */
41 int initial_brightness;
42};
43
44static inline struct gud_connector *to_gud_connector(struct drm_connector *connector)
45{
46 return container_of(connector, struct gud_connector, connector);
47}
48
49static void gud_conn_err(struct drm_connector *connector, const char *msg, int ret)
50{
51 dev_err(connector->dev->dev, "%s: %s (ret=%d)\n", connector->name, msg, ret);
52}
53
54/*
55 * Use a worker to avoid taking kms locks inside the backlight lock.
56 * Other display drivers use backlight within their kms locks.
57 * This avoids inconsistent locking rules, which would upset lockdep.
58 */
59static void gud_connector_backlight_update_status_work(struct work_struct *work)
60{
61 struct gud_connector *gconn = container_of(work, struct gud_connector, backlight_work);
62 struct drm_connector *connector = &gconn->connector;
63 struct drm_connector_state *connector_state;
64 struct drm_device *drm = connector->dev;
65 struct drm_modeset_acquire_ctx ctx;
66 struct drm_atomic_state *state;
67 int idx, ret;
68
69 if (!drm_dev_enter(dev: drm, idx: &idx))
70 return;
71
72 state = drm_atomic_state_alloc(dev: drm);
73 if (!state) {
74 ret = -ENOMEM;
75 goto exit;
76 }
77
78 drm_modeset_acquire_init(ctx: &ctx, flags: 0);
79 state->acquire_ctx = &ctx;
80retry:
81 connector_state = drm_atomic_get_connector_state(state, connector);
82 if (IS_ERR(ptr: connector_state)) {
83 ret = PTR_ERR(ptr: connector_state);
84 goto out;
85 }
86
87 /* Reuse tv.brightness to avoid having to subclass */
88 connector_state->tv.brightness = gconn->backlight->props.brightness;
89
90 ret = drm_atomic_commit(state);
91out:
92 if (ret == -EDEADLK) {
93 drm_atomic_state_clear(state);
94 drm_modeset_backoff(ctx: &ctx);
95 goto retry;
96 }
97
98 drm_atomic_state_put(state);
99
100 drm_modeset_drop_locks(ctx: &ctx);
101 drm_modeset_acquire_fini(ctx: &ctx);
102exit:
103 drm_dev_exit(idx);
104
105 if (ret)
106 dev_err(drm->dev, "Failed to update backlight, err=%d\n", ret);
107}
108
109static int gud_connector_backlight_update_status(struct backlight_device *bd)
110{
111 struct drm_connector *connector = bl_get_data(bl_dev: bd);
112 struct gud_connector *gconn = to_gud_connector(connector);
113
114 /* The USB timeout is 5 seconds so use system_long_wq for worst case scenario */
115 queue_work(wq: system_long_wq, work: &gconn->backlight_work);
116
117 return 0;
118}
119
120static const struct backlight_ops gud_connector_backlight_ops = {
121 .update_status = gud_connector_backlight_update_status,
122};
123
124static int gud_connector_backlight_register(struct gud_connector *gconn)
125{
126 struct drm_connector *connector = &gconn->connector;
127 struct backlight_device *bd;
128 const char *name;
129 const struct backlight_properties props = {
130 .type = BACKLIGHT_RAW,
131 .scale = BACKLIGHT_SCALE_NON_LINEAR,
132 .max_brightness = 100,
133 .brightness = gconn->initial_brightness,
134 };
135
136 name = kasprintf(GFP_KERNEL, fmt: "card%d-%s-backlight",
137 connector->dev->primary->index, connector->name);
138 if (!name)
139 return -ENOMEM;
140
141 bd = backlight_device_register(name, dev: connector->kdev, devdata: connector,
142 ops: &gud_connector_backlight_ops, props: &props);
143 kfree(objp: name);
144 if (IS_ERR(ptr: bd))
145 return PTR_ERR(ptr: bd);
146
147 gconn->backlight = bd;
148
149 return 0;
150}
151
152static int gud_connector_detect(struct drm_connector *connector,
153 struct drm_modeset_acquire_ctx *ctx, bool force)
154{
155 struct gud_device *gdrm = to_gud_device(drm: connector->dev);
156 int idx, ret;
157 u8 status;
158
159 if (!drm_dev_enter(dev: connector->dev, idx: &idx))
160 return connector_status_disconnected;
161
162 if (force) {
163 ret = gud_usb_set(gdrm, GUD_REQ_SET_CONNECTOR_FORCE_DETECT,
164 index: connector->index, NULL, len: 0);
165 if (ret) {
166 ret = connector_status_unknown;
167 goto exit;
168 }
169 }
170
171 ret = gud_usb_get_u8(gdrm, GUD_REQ_GET_CONNECTOR_STATUS, index: connector->index, val: &status);
172 if (ret) {
173 ret = connector_status_unknown;
174 goto exit;
175 }
176
177 switch (status & GUD_CONNECTOR_STATUS_CONNECTED_MASK) {
178 case GUD_CONNECTOR_STATUS_DISCONNECTED:
179 ret = connector_status_disconnected;
180 break;
181 case GUD_CONNECTOR_STATUS_CONNECTED:
182 ret = connector_status_connected;
183 break;
184 default:
185 ret = connector_status_unknown;
186 break;
187 }
188
189 if (status & GUD_CONNECTOR_STATUS_CHANGED)
190 connector->epoch_counter += 1;
191exit:
192 drm_dev_exit(idx);
193
194 return ret;
195}
196
197struct gud_connector_get_edid_ctx {
198 void *buf;
199 size_t len;
200 bool edid_override;
201};
202
203static int gud_connector_get_edid_block(void *data, u8 *buf, unsigned int block, size_t len)
204{
205 struct gud_connector_get_edid_ctx *ctx = data;
206 size_t start = block * EDID_LENGTH;
207
208 ctx->edid_override = false;
209
210 if (start + len > ctx->len)
211 return -1;
212
213 memcpy(buf, ctx->buf + start, len);
214
215 return 0;
216}
217
218static int gud_connector_get_modes(struct drm_connector *connector)
219{
220 struct gud_device *gdrm = to_gud_device(drm: connector->dev);
221 struct gud_display_mode_req *reqmodes = NULL;
222 struct gud_connector_get_edid_ctx edid_ctx;
223 unsigned int i, num_modes = 0;
224 struct edid *edid = NULL;
225 int idx, ret;
226
227 if (!drm_dev_enter(dev: connector->dev, idx: &idx))
228 return 0;
229
230 edid_ctx.edid_override = true;
231 edid_ctx.buf = kmalloc(GUD_CONNECTOR_MAX_EDID_LEN, GFP_KERNEL);
232 if (!edid_ctx.buf)
233 goto out;
234
235 ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_EDID, index: connector->index,
236 buf: edid_ctx.buf, GUD_CONNECTOR_MAX_EDID_LEN);
237 if (ret > 0 && ret % EDID_LENGTH) {
238 gud_conn_err(connector, msg: "Invalid EDID size", ret);
239 } else if (ret > 0) {
240 edid_ctx.len = ret;
241 edid = drm_do_get_edid(connector, get_edid_block: gud_connector_get_edid_block, data: &edid_ctx);
242 }
243
244 kfree(objp: edid_ctx.buf);
245 drm_connector_update_edid_property(connector, edid);
246
247 if (edid && edid_ctx.edid_override)
248 goto out;
249
250 reqmodes = kmalloc_array(GUD_CONNECTOR_MAX_NUM_MODES, size: sizeof(*reqmodes), GFP_KERNEL);
251 if (!reqmodes)
252 goto out;
253
254 ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_MODES, index: connector->index,
255 buf: reqmodes, GUD_CONNECTOR_MAX_NUM_MODES * sizeof(*reqmodes));
256 if (ret <= 0)
257 goto out;
258 if (ret % sizeof(*reqmodes)) {
259 gud_conn_err(connector, msg: "Invalid display mode array size", ret);
260 goto out;
261 }
262
263 num_modes = ret / sizeof(*reqmodes);
264
265 for (i = 0; i < num_modes; i++) {
266 struct drm_display_mode *mode;
267
268 mode = drm_mode_create(dev: connector->dev);
269 if (!mode) {
270 num_modes = i;
271 goto out;
272 }
273
274 gud_to_display_mode(dst: mode, src: &reqmodes[i]);
275 drm_mode_probed_add(connector, mode);
276 }
277out:
278 if (!num_modes)
279 num_modes = drm_add_edid_modes(connector, edid);
280
281 kfree(objp: reqmodes);
282 kfree(objp: edid);
283 drm_dev_exit(idx);
284
285 return num_modes;
286}
287
288static int gud_connector_atomic_check(struct drm_connector *connector,
289 struct drm_atomic_state *state)
290{
291 struct drm_connector_state *new_state;
292 struct drm_crtc_state *new_crtc_state;
293 struct drm_connector_state *old_state;
294
295 new_state = drm_atomic_get_new_connector_state(state, connector);
296 if (!new_state->crtc)
297 return 0;
298
299 old_state = drm_atomic_get_old_connector_state(state, connector);
300 new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc: new_state->crtc);
301
302 if (old_state->tv.margins.left != new_state->tv.margins.left ||
303 old_state->tv.margins.right != new_state->tv.margins.right ||
304 old_state->tv.margins.top != new_state->tv.margins.top ||
305 old_state->tv.margins.bottom != new_state->tv.margins.bottom ||
306 old_state->tv.legacy_mode != new_state->tv.legacy_mode ||
307 old_state->tv.brightness != new_state->tv.brightness ||
308 old_state->tv.contrast != new_state->tv.contrast ||
309 old_state->tv.flicker_reduction != new_state->tv.flicker_reduction ||
310 old_state->tv.overscan != new_state->tv.overscan ||
311 old_state->tv.saturation != new_state->tv.saturation ||
312 old_state->tv.hue != new_state->tv.hue)
313 new_crtc_state->connectors_changed = true;
314
315 return 0;
316}
317
318static const struct drm_connector_helper_funcs gud_connector_helper_funcs = {
319 .detect_ctx = gud_connector_detect,
320 .get_modes = gud_connector_get_modes,
321 .atomic_check = gud_connector_atomic_check,
322};
323
324static int gud_connector_late_register(struct drm_connector *connector)
325{
326 struct gud_connector *gconn = to_gud_connector(connector);
327
328 if (gconn->initial_brightness < 0)
329 return 0;
330
331 return gud_connector_backlight_register(gconn);
332}
333
334static void gud_connector_early_unregister(struct drm_connector *connector)
335{
336 struct gud_connector *gconn = to_gud_connector(connector);
337
338 backlight_device_unregister(bd: gconn->backlight);
339 cancel_work_sync(work: &gconn->backlight_work);
340}
341
342static void gud_connector_destroy(struct drm_connector *connector)
343{
344 struct gud_connector *gconn = to_gud_connector(connector);
345
346 drm_connector_cleanup(connector);
347 kfree(objp: gconn->properties);
348 kfree(objp: gconn);
349}
350
351static void gud_connector_reset(struct drm_connector *connector)
352{
353 struct gud_connector *gconn = to_gud_connector(connector);
354
355 drm_atomic_helper_connector_reset(connector);
356 connector->state->tv = gconn->initial_tv_state;
357 /* Set margins from command line */
358 drm_atomic_helper_connector_tv_margins_reset(connector);
359 if (gconn->initial_brightness >= 0)
360 connector->state->tv.brightness = gconn->initial_brightness;
361}
362
363static const struct drm_connector_funcs gud_connector_funcs = {
364 .fill_modes = drm_helper_probe_single_connector_modes,
365 .late_register = gud_connector_late_register,
366 .early_unregister = gud_connector_early_unregister,
367 .destroy = gud_connector_destroy,
368 .reset = gud_connector_reset,
369 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
370 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
371};
372
373/*
374 * The tv.mode property is shared among the connectors and its enum names are
375 * driver specific. This means that if more than one connector uses tv.mode,
376 * the enum names has to be the same.
377 */
378static int gud_connector_add_tv_mode(struct gud_device *gdrm, struct drm_connector *connector)
379{
380 size_t buf_len = GUD_CONNECTOR_TV_MODE_MAX_NUM * GUD_CONNECTOR_TV_MODE_NAME_LEN;
381 const char *modes[GUD_CONNECTOR_TV_MODE_MAX_NUM];
382 unsigned int i, num_modes;
383 char *buf;
384 int ret;
385
386 buf = kmalloc(size: buf_len, GFP_KERNEL);
387 if (!buf)
388 return -ENOMEM;
389
390 ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_TV_MODE_VALUES,
391 index: connector->index, buf, len: buf_len);
392 if (ret < 0)
393 goto free;
394 if (!ret || ret % GUD_CONNECTOR_TV_MODE_NAME_LEN) {
395 ret = -EIO;
396 goto free;
397 }
398
399 num_modes = ret / GUD_CONNECTOR_TV_MODE_NAME_LEN;
400 for (i = 0; i < num_modes; i++)
401 modes[i] = &buf[i * GUD_CONNECTOR_TV_MODE_NAME_LEN];
402
403 ret = drm_mode_create_tv_properties_legacy(dev: connector->dev, num_modes, modes);
404free:
405 kfree(objp: buf);
406 if (ret < 0)
407 gud_conn_err(connector, msg: "Failed to add TV modes", ret);
408
409 return ret;
410}
411
412static struct drm_property *
413gud_connector_property_lookup(struct drm_connector *connector, u16 prop)
414{
415 struct drm_mode_config *config = &connector->dev->mode_config;
416
417 switch (prop) {
418 case GUD_PROPERTY_TV_LEFT_MARGIN:
419 return config->tv_left_margin_property;
420 case GUD_PROPERTY_TV_RIGHT_MARGIN:
421 return config->tv_right_margin_property;
422 case GUD_PROPERTY_TV_TOP_MARGIN:
423 return config->tv_top_margin_property;
424 case GUD_PROPERTY_TV_BOTTOM_MARGIN:
425 return config->tv_bottom_margin_property;
426 case GUD_PROPERTY_TV_MODE:
427 return config->legacy_tv_mode_property;
428 case GUD_PROPERTY_TV_BRIGHTNESS:
429 return config->tv_brightness_property;
430 case GUD_PROPERTY_TV_CONTRAST:
431 return config->tv_contrast_property;
432 case GUD_PROPERTY_TV_FLICKER_REDUCTION:
433 return config->tv_flicker_reduction_property;
434 case GUD_PROPERTY_TV_OVERSCAN:
435 return config->tv_overscan_property;
436 case GUD_PROPERTY_TV_SATURATION:
437 return config->tv_saturation_property;
438 case GUD_PROPERTY_TV_HUE:
439 return config->tv_hue_property;
440 default:
441 return ERR_PTR(error: -EINVAL);
442 }
443}
444
445static unsigned int *gud_connector_tv_state_val(u16 prop, struct drm_tv_connector_state *state)
446{
447 switch (prop) {
448 case GUD_PROPERTY_TV_LEFT_MARGIN:
449 return &state->margins.left;
450 case GUD_PROPERTY_TV_RIGHT_MARGIN:
451 return &state->margins.right;
452 case GUD_PROPERTY_TV_TOP_MARGIN:
453 return &state->margins.top;
454 case GUD_PROPERTY_TV_BOTTOM_MARGIN:
455 return &state->margins.bottom;
456 case GUD_PROPERTY_TV_MODE:
457 return &state->legacy_mode;
458 case GUD_PROPERTY_TV_BRIGHTNESS:
459 return &state->brightness;
460 case GUD_PROPERTY_TV_CONTRAST:
461 return &state->contrast;
462 case GUD_PROPERTY_TV_FLICKER_REDUCTION:
463 return &state->flicker_reduction;
464 case GUD_PROPERTY_TV_OVERSCAN:
465 return &state->overscan;
466 case GUD_PROPERTY_TV_SATURATION:
467 return &state->saturation;
468 case GUD_PROPERTY_TV_HUE:
469 return &state->hue;
470 default:
471 return ERR_PTR(error: -EINVAL);
472 }
473}
474
475static int gud_connector_add_properties(struct gud_device *gdrm, struct gud_connector *gconn)
476{
477 struct drm_connector *connector = &gconn->connector;
478 struct drm_device *drm = &gdrm->drm;
479 struct gud_property_req *properties;
480 unsigned int i, num_properties;
481 int ret;
482
483 properties = kcalloc(GUD_CONNECTOR_PROPERTIES_MAX_NUM, size: sizeof(*properties), GFP_KERNEL);
484 if (!properties)
485 return -ENOMEM;
486
487 ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_PROPERTIES, index: connector->index,
488 buf: properties, GUD_CONNECTOR_PROPERTIES_MAX_NUM * sizeof(*properties));
489 if (ret <= 0)
490 goto out;
491 if (ret % sizeof(*properties)) {
492 ret = -EIO;
493 goto out;
494 }
495
496 num_properties = ret / sizeof(*properties);
497 ret = 0;
498
499 gconn->properties = kcalloc(n: num_properties, size: sizeof(*gconn->properties), GFP_KERNEL);
500 if (!gconn->properties) {
501 ret = -ENOMEM;
502 goto out;
503 }
504
505 for (i = 0; i < num_properties; i++) {
506 u16 prop = le16_to_cpu(properties[i].prop);
507 u64 val = le64_to_cpu(properties[i].val);
508 struct drm_property *property;
509 unsigned int *state_val;
510
511 drm_dbg(drm, "property: %u = %llu(0x%llx)\n", prop, val, val);
512
513 switch (prop) {
514 case GUD_PROPERTY_TV_LEFT_MARGIN:
515 fallthrough;
516 case GUD_PROPERTY_TV_RIGHT_MARGIN:
517 fallthrough;
518 case GUD_PROPERTY_TV_TOP_MARGIN:
519 fallthrough;
520 case GUD_PROPERTY_TV_BOTTOM_MARGIN:
521 ret = drm_mode_create_tv_margin_properties(dev: drm);
522 if (ret)
523 goto out;
524 break;
525 case GUD_PROPERTY_TV_MODE:
526 ret = gud_connector_add_tv_mode(gdrm, connector);
527 if (ret)
528 goto out;
529 break;
530 case GUD_PROPERTY_TV_BRIGHTNESS:
531 fallthrough;
532 case GUD_PROPERTY_TV_CONTRAST:
533 fallthrough;
534 case GUD_PROPERTY_TV_FLICKER_REDUCTION:
535 fallthrough;
536 case GUD_PROPERTY_TV_OVERSCAN:
537 fallthrough;
538 case GUD_PROPERTY_TV_SATURATION:
539 fallthrough;
540 case GUD_PROPERTY_TV_HUE:
541 /* This is a no-op if already added. */
542 ret = drm_mode_create_tv_properties_legacy(dev: drm, num_modes: 0, NULL);
543 if (ret)
544 goto out;
545 break;
546 case GUD_PROPERTY_BACKLIGHT_BRIGHTNESS:
547 if (val > 100) {
548 ret = -EINVAL;
549 goto out;
550 }
551 gconn->initial_brightness = val;
552 break;
553 default:
554 /* New ones might show up in future devices, skip those we don't know. */
555 drm_dbg(drm, "Ignoring unknown property: %u\n", prop);
556 continue;
557 }
558
559 gconn->properties[gconn->num_properties++] = prop;
560
561 if (prop == GUD_PROPERTY_BACKLIGHT_BRIGHTNESS)
562 continue; /* not a DRM property */
563
564 property = gud_connector_property_lookup(connector, prop);
565 if (WARN_ON(IS_ERR(property)))
566 continue;
567
568 state_val = gud_connector_tv_state_val(prop, state: &gconn->initial_tv_state);
569 if (WARN_ON(IS_ERR(state_val)))
570 continue;
571
572 *state_val = val;
573 drm_object_attach_property(obj: &connector->base, property, init_val: 0);
574 }
575out:
576 kfree(objp: properties);
577
578 return ret;
579}
580
581int gud_connector_fill_properties(struct drm_connector_state *connector_state,
582 struct gud_property_req *properties)
583{
584 struct gud_connector *gconn = to_gud_connector(connector: connector_state->connector);
585 unsigned int i;
586
587 for (i = 0; i < gconn->num_properties; i++) {
588 u16 prop = gconn->properties[i];
589 u64 val;
590
591 if (prop == GUD_PROPERTY_BACKLIGHT_BRIGHTNESS) {
592 val = connector_state->tv.brightness;
593 } else {
594 unsigned int *state_val;
595
596 state_val = gud_connector_tv_state_val(prop, state: &connector_state->tv);
597 if (WARN_ON_ONCE(IS_ERR(state_val)))
598 return PTR_ERR(ptr: state_val);
599
600 val = *state_val;
601 }
602
603 properties[i].prop = cpu_to_le16(prop);
604 properties[i].val = cpu_to_le64(val);
605 }
606
607 return gconn->num_properties;
608}
609
610static int gud_connector_create(struct gud_device *gdrm, unsigned int index,
611 struct gud_connector_descriptor_req *desc)
612{
613 struct drm_device *drm = &gdrm->drm;
614 struct gud_connector *gconn;
615 struct drm_connector *connector;
616 struct drm_encoder *encoder;
617 int ret, connector_type;
618 u32 flags;
619
620 gconn = kzalloc(size: sizeof(*gconn), GFP_KERNEL);
621 if (!gconn)
622 return -ENOMEM;
623
624 INIT_WORK(&gconn->backlight_work, gud_connector_backlight_update_status_work);
625 gconn->initial_brightness = -ENODEV;
626 flags = le32_to_cpu(desc->flags);
627 connector = &gconn->connector;
628
629 drm_dbg(drm, "Connector: index=%u type=%u flags=0x%x\n", index, desc->connector_type, flags);
630
631 switch (desc->connector_type) {
632 case GUD_CONNECTOR_TYPE_PANEL:
633 connector_type = DRM_MODE_CONNECTOR_USB;
634 break;
635 case GUD_CONNECTOR_TYPE_VGA:
636 connector_type = DRM_MODE_CONNECTOR_VGA;
637 break;
638 case GUD_CONNECTOR_TYPE_DVI:
639 connector_type = DRM_MODE_CONNECTOR_DVID;
640 break;
641 case GUD_CONNECTOR_TYPE_COMPOSITE:
642 connector_type = DRM_MODE_CONNECTOR_Composite;
643 break;
644 case GUD_CONNECTOR_TYPE_SVIDEO:
645 connector_type = DRM_MODE_CONNECTOR_SVIDEO;
646 break;
647 case GUD_CONNECTOR_TYPE_COMPONENT:
648 connector_type = DRM_MODE_CONNECTOR_Component;
649 break;
650 case GUD_CONNECTOR_TYPE_DISPLAYPORT:
651 connector_type = DRM_MODE_CONNECTOR_DisplayPort;
652 break;
653 case GUD_CONNECTOR_TYPE_HDMI:
654 connector_type = DRM_MODE_CONNECTOR_HDMIA;
655 break;
656 default: /* future types */
657 connector_type = DRM_MODE_CONNECTOR_USB;
658 break;
659 }
660
661 drm_connector_helper_add(connector, funcs: &gud_connector_helper_funcs);
662 ret = drm_connector_init(dev: drm, connector, funcs: &gud_connector_funcs, connector_type);
663 if (ret) {
664 kfree(objp: connector);
665 return ret;
666 }
667
668 if (WARN_ON(connector->index != index))
669 return -EINVAL;
670
671 if (flags & GUD_CONNECTOR_FLAGS_POLL_STATUS)
672 connector->polled = (DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT);
673 if (flags & GUD_CONNECTOR_FLAGS_INTERLACE)
674 connector->interlace_allowed = true;
675 if (flags & GUD_CONNECTOR_FLAGS_DOUBLESCAN)
676 connector->doublescan_allowed = true;
677
678 ret = gud_connector_add_properties(gdrm, gconn);
679 if (ret) {
680 gud_conn_err(connector, msg: "Failed to add properties", ret);
681 return ret;
682 }
683
684 /* The first connector is attached to the existing simple pipe encoder */
685 if (!connector->index) {
686 encoder = &gdrm->pipe.encoder;
687 } else {
688 encoder = &gconn->encoder;
689
690 ret = drm_simple_encoder_init(dev: drm, encoder, DRM_MODE_ENCODER_NONE);
691 if (ret)
692 return ret;
693
694 encoder->possible_crtcs = 1;
695 }
696
697 return drm_connector_attach_encoder(connector, encoder);
698}
699
700int gud_get_connectors(struct gud_device *gdrm)
701{
702 struct gud_connector_descriptor_req *descs;
703 unsigned int i, num_connectors;
704 int ret;
705
706 descs = kmalloc_array(GUD_CONNECTORS_MAX_NUM, size: sizeof(*descs), GFP_KERNEL);
707 if (!descs)
708 return -ENOMEM;
709
710 ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTORS, index: 0,
711 buf: descs, GUD_CONNECTORS_MAX_NUM * sizeof(*descs));
712 if (ret < 0)
713 goto free;
714 if (!ret || ret % sizeof(*descs)) {
715 ret = -EIO;
716 goto free;
717 }
718
719 num_connectors = ret / sizeof(*descs);
720
721 for (i = 0; i < num_connectors; i++) {
722 ret = gud_connector_create(gdrm, index: i, desc: &descs[i]);
723 if (ret)
724 goto free;
725 }
726free:
727 kfree(objp: descs);
728
729 return ret;
730}
731

source code of linux/drivers/gpu/drm/gud/gud_connector.c