cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

gud_connector.c (19732B)


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