Den 20.01.2021 19.02, skrev Daniel Vetter: > On Wed, Jan 20, 2021 at 6:11 PM Noralf Trønnes <noralf@xxxxxxxxxxx> wrote: >> >> This adds a generic USB display driver with the intention that it can be >> used with future USB interfaced low end displays/adapters. The Linux >> gadget device driver will serve as the canonical device implementation. >> >> The following DRM properties are supported: >> - Plane rotation >> - Connector TV properties >> >> There is also support for backlight brightness exposed as a backlight >> device. >> >> Display modes can be made available to the host driver either as DRM >> display modes or through EDID. If both are present, EDID is just passed >> on to userspace. >> >> Performance is preferred over color depth, so if the device supports >> RGB565, DRM_CAP_DUMB_PREFERRED_DEPTH will return 16. >> >> If the device transfer buffer can't fit an uncompressed framebuffer >> update, the update is split up into parts that do fit. >> >> Optimal user experience is achieved by providing damage reports either by >> setting FB_DAMAGE_CLIPS on pageflips or calling DRM_IOCTL_MODE_DIRTYFB. >> >> LZ4 compression is used if the device supports it. >> >> The driver supports a one bit monochrome transfer format: R1. This is not >> implemented in the gadget driver. It is added in preparation for future >> monochrome e-ink displays. >> >> The driver is MIT licensed to smooth the path for any BSD port of the >> driver. >> >> v2: >> - Use devm_drm_dev_alloc() and drmm_mode_config_init() >> - drm_fbdev_generic_setup: Use preferred_bpp=0, 16 was a copy paste error >> - The drm_backlight_helper is dropped, copy in the code > > I think the backlight is still a bit problematic, since you're using > kms locks within the backlight callbacks. Other display drivers use > backlight within their kms locks. This means inconsistent locking > rules, which upsets lockdep. > > Since you're already handling brightness as a special case in many > places I don't think it's a big shuffle: > - add a mutex to the connector struct > - move brightness value to connector struct, out of the connector_state > - use the new mutex to protect backlight state both from modeset side > (if needed, I'm not entirely sure about that) and the backlight side > It's not enough to store the value I need to send it to the device as well. Currently I send the entire state each time there's a change. To continue that I would need to keep a copy of the state that I can use when brightness changes. Or I can treat backlight as an exception and add a USB control request just for backlight. There is some special treatment of the backlight in the driver, but I would really like to handle the backlight brightness through the atomic machinery. I want to avoid special treatment of backlight in the USB protocol. I can avoid the lockdep problem by letting a worker commit the state and schedule it from the backlight update callback. I'll do that unless you see other issues with that approach. Is it ok to take the connection_mutex lock in the get_brightness callback to get to the connector state and the brightness value? i915 takes that lock in intel_backlight_device_update_status() and intel_backlight_device_get_brightness(). > Some more things below, but in general I'd say Acked-by: Daniel Vetter > <daniel.vetter> fwiw (probably not so much). > Thanks for taking a look, much appreciated. Noralf. >> diff --git a/drivers/gpu/drm/gud/gud_connector.c b/drivers/gpu/drm/gud/gud_connector.c >> new file mode 100644 >> index 000000000000..a4b9bbf48e19 >> --- /dev/null >> +++ b/drivers/gpu/drm/gud/gud_connector.c >> @@ -0,0 +1,722 @@ >> +// SPDX-License-Identifier: MIT >> +/* >> + * Copyright 2020 Noralf Trønnes >> + */ >> + >> +#include <linux/backlight.h> >> + >> +#include <drm/drm_atomic.h> >> +#include <drm/drm_atomic_state_helper.h> >> +#include <drm/drm_connector.h> >> +#include <drm/drm_drv.h> >> +#include <drm/drm_encoder.h> >> +#include <drm/drm_file.h> >> +#include <drm/drm_modeset_helper_vtables.h> >> +#include <drm/drm_print.h> >> +#include <drm/drm_probe_helper.h> >> +#include <drm/drm_simple_kms_helper.h> >> +#include <drm/gud.h> >> + >> +#include "gud_internal.h" >> + >> +struct gud_connector { >> + struct drm_connector connector; >> + struct drm_encoder encoder; >> + struct backlight_device *backlight; >> + >> + /* Supported properties */ >> + u16 *properties; >> + unsigned int num_properties; >> + >> + /* Initial gadget tv state if applicable, applied on state reset */ >> + struct drm_tv_connector_state initial_tv_state; >> + >> + /* >> + * Initial gadget backlight brightness if applicable, applied on state reset. >> + * The value -ENODEV is used to signal no backlight. >> + */ >> + int initial_brightness; >> + >> + unsigned int num_modes; >> + size_t edid_len; >> +}; >> + >> +static inline struct gud_connector *to_gud_connector(struct drm_connector *connector) >> +{ >> + return container_of(connector, struct gud_connector, connector); >> +} >> + >> +static int gud_connector_backlight_update_status(struct backlight_device *bd) >> +{ >> + struct drm_connector *connector = bl_get_data(bd); >> + struct drm_connector_state *connector_state; >> + struct drm_device *dev = connector->dev; >> + struct drm_modeset_acquire_ctx ctx; >> + struct drm_atomic_state *state; >> + int ret; >> + >> + state = drm_atomic_state_alloc(dev); >> + if (!state) >> + return -ENOMEM; >> + >> + drm_modeset_acquire_init(&ctx, 0); >> + state->acquire_ctx = &ctx; >> +retry: >> + connector_state = drm_atomic_get_connector_state(state, connector); >> + if (IS_ERR(connector_state)) { >> + ret = PTR_ERR(connector_state); >> + goto out; >> + } >> + >> + /* Reuse tv.brightness to avoid having to subclass */ >> + connector_state->tv.brightness = bd->props.brightness; >> + >> + ret = drm_atomic_commit(state); >> +out: >> + if (ret == -EDEADLK) { >> + drm_atomic_state_clear(state); >> + drm_modeset_backoff(&ctx); >> + goto retry; >> + } >> + >> + drm_atomic_state_put(state); >> + >> + drm_modeset_drop_locks(&ctx); >> + drm_modeset_acquire_fini(&ctx); >> + >> + return ret; >> +} >> + >> +static int gud_connector_backlight_get_brightness(struct backlight_device *bd) >> +{ >> + struct drm_connector *connector = bl_get_data(bd); >> + struct drm_device *dev = connector->dev; >> + int brightness; >> + >> + drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); >> + brightness = connector->state->tv.brightness; >> + drm_modeset_unlock(&dev->mode_config.connection_mutex); >> + >> + return brightness; >> +} >> + >> +static const struct backlight_ops gud_connector_backlight_ops = { >> + .get_brightness = gud_connector_backlight_get_brightness, >> + .update_status = gud_connector_backlight_update_status, >> +}; >> + >> +static int gud_connector_backlight_register(struct gud_connector *gconn) >> +{ >> + struct drm_connector *connector = &gconn->connector; >> + struct backlight_device *bd; >> + const char *name; >> + const struct backlight_properties props = { >> + .type = BACKLIGHT_RAW, >> + .scale = BACKLIGHT_SCALE_NON_LINEAR, >> + .max_brightness = 100, >> + }; >> + >> + name = kasprintf(GFP_KERNEL, "card%d-%s-backlight", >> + connector->dev->primary->index, connector->name); >> + if (!name) >> + return -ENOMEM; >> + >> + bd = backlight_device_register(name, connector->kdev, connector, >> + &gud_connector_backlight_ops, &props); >> + kfree(name); >> + if (IS_ERR(bd)) >> + return PTR_ERR(bd); >> + >> + gconn->backlight = bd; >> + >> + return 0; >> +} >> + >> +static int gud_connector_status_request(struct drm_connector *connector) >> +{ >> + struct gud_connector *gconn = to_gud_connector(connector); >> + struct gud_device *gdrm = to_gud_device(connector->dev); >> + struct gud_connector_status_req req; >> + u16 num_modes, edid_len; >> + int ret; >> + >> + ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_STATUS, >> + connector->index, &req, sizeof(req)); >> + if (ret) >> + return ret; >> + >> + switch (req.status & GUD_CONNECTOR_STATUS_CONNECTED_MASK) { >> + case GUD_CONNECTOR_STATUS_DISCONNECTED: >> + ret = connector_status_disconnected; >> + break; >> + case GUD_CONNECTOR_STATUS_CONNECTED: >> + ret = connector_status_connected; >> + break; >> + default: >> + ret = connector_status_unknown; >> + break; >> + }; >> + >> + num_modes = le16_to_cpu(req.num_modes); >> + edid_len = le16_to_cpu(req.edid_len); >> + >> + if (edid_len % EDID_LENGTH) { >> + drm_err(connector->dev, "%s: Invalid EDID size: %u\n", connector->name, edid_len); >> + edid_len = 0; >> + } >> + >> + if (req.status & GUD_CONNECTOR_STATUS_CHANGED || >> + gconn->num_modes != num_modes || gconn->edid_len != edid_len) >> + connector->epoch_counter += 1; >> + >> + gconn->num_modes = num_modes; >> + gconn->edid_len = edid_len; >> + >> + if (!num_modes && !edid_len && ret != connector_status_disconnected) >> + drm_dbg_kms(connector->dev, "%s: No modes or EDID.\n", connector->name); >> + >> + return ret; >> +} >> + >> +static int gud_connector_detect(struct drm_connector *connector, >> + struct drm_modeset_acquire_ctx *ctx, bool force) >> +{ >> + struct gud_device *gdrm = to_gud_device(connector->dev); >> + int idx, ret; >> + >> + if (!drm_dev_enter(connector->dev, &idx)) >> + return connector_status_disconnected; >> + >> + if (force) { >> + ret = gud_usb_set(gdrm, GUD_REQ_SET_CONNECTOR_FORCE_DETECT, >> + connector->index, NULL, 0); >> + if (ret) { >> + ret = connector_status_unknown; >> + goto exit; >> + } >> + } >> + >> + ret = gud_connector_status_request(connector); >> + if (ret < 0) >> + ret = connector_status_unknown; >> +exit: >> + drm_dev_exit(idx); >> + >> + return ret; >> +} >> + >> +struct gud_connector_get_edid_ctx { >> + struct gud_connector *gconn; >> + void *buf; >> +}; >> + >> +static int gud_connector_get_edid_block(void *data, u8 *buf, unsigned int block, size_t len) >> +{ >> + struct gud_connector_get_edid_ctx *ctx = data; >> + struct gud_connector *gconn = ctx->gconn; >> + size_t start = block * EDID_LENGTH; >> + >> + if (start + len > gconn->edid_len) >> + return -1; >> + >> + if (!block) { >> + struct gud_device *gdrm = to_gud_device(gconn->connector.dev); >> + int ret; >> + >> + /* Check because drm_do_get_edid() will retry on failure */ >> + if (!ctx->buf) >> + ctx->buf = kmalloc(gconn->edid_len, GFP_KERNEL); >> + if (!ctx->buf) >> + return -1; >> + >> + ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_EDID, gconn->connector.index, >> + ctx->buf, gconn->edid_len); >> + if (ret) >> + return -1; >> + } >> + >> + memcpy(buf, ctx->buf + start, len); >> + >> + return 0; >> +} >> + >> +static int gud_connector_get_modes(struct drm_connector *connector) >> +{ >> + struct gud_connector *gconn = to_gud_connector(connector); >> + struct gud_device *gdrm = to_gud_device(connector->dev); >> + struct gud_connector_get_edid_ctx edid_ctx = { >> + .gconn = gconn, >> + }; >> + struct gud_display_mode_req *reqmodes = NULL; >> + unsigned int i, num_modes = 0; >> + struct edid *edid = NULL; >> + bool edid_override; >> + int idx, ret; >> + >> + if (!drm_dev_enter(connector->dev, &idx)) >> + return 0; >> + >> + if (connector->force) { >> + ret = gud_connector_status_request(connector); >> + if (ret < 0) >> + goto out; >> + } >> + >> + edid = drm_do_get_edid(connector, gud_connector_get_edid_block, &edid_ctx); >> + edid_override = edid && !edid_ctx.buf; >> + kfree(edid_ctx.buf); >> + drm_connector_update_edid_property(connector, edid); >> + >> + if (!gconn->num_modes || edid_override) { >> + num_modes = drm_add_edid_modes(connector, edid); >> + goto out; >> + } >> + >> + reqmodes = kmalloc_array(gconn->num_modes, sizeof(*reqmodes), GFP_KERNEL); >> + if (!reqmodes) >> + goto out; >> + >> + ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_MODES, connector->index, >> + reqmodes, gconn->num_modes * sizeof(*reqmodes)); >> + if (ret) >> + goto out; >> + >> + for (i = 0; i < gconn->num_modes; i++) { >> + struct drm_display_mode *mode; >> + >> + mode = drm_mode_create(connector->dev); >> + if (!mode) >> + goto out; >> + >> + gud_to_display_mode(mode, &reqmodes[i]); >> + drm_mode_probed_add(connector, mode); >> + num_modes++; >> + } >> +out: >> + kfree(reqmodes); >> + kfree(edid); >> + drm_dev_exit(idx); >> + >> + return num_modes; >> +} >> + >> +static int gud_connector_atomic_check(struct drm_connector *connector, >> + struct drm_atomic_state *state) >> +{ >> + struct drm_connector_state *new_state; >> + struct drm_crtc_state *new_crtc_state; >> + struct drm_connector_state *old_state; >> + >> + new_state = drm_atomic_get_new_connector_state(state, connector); >> + if (!new_state->crtc) >> + return 0; >> + >> + old_state = drm_atomic_get_old_connector_state(state, connector); >> + new_crtc_state = drm_atomic_get_new_crtc_state(state, new_state->crtc); >> + >> + if (old_state->tv.margins.left != new_state->tv.margins.left || >> + old_state->tv.margins.right != new_state->tv.margins.right || >> + old_state->tv.margins.top != new_state->tv.margins.top || >> + old_state->tv.margins.bottom != new_state->tv.margins.bottom || >> + old_state->tv.mode != new_state->tv.mode || >> + old_state->tv.brightness != new_state->tv.brightness || >> + old_state->tv.contrast != new_state->tv.contrast || >> + old_state->tv.flicker_reduction != new_state->tv.flicker_reduction || >> + old_state->tv.overscan != new_state->tv.overscan || >> + old_state->tv.saturation != new_state->tv.saturation || >> + old_state->tv.hue != new_state->tv.hue) >> + new_crtc_state->connectors_changed = true; >> + >> + return 0; >> +} >> + >> +static const struct drm_connector_helper_funcs gud_connector_helper_funcs = { >> + .detect_ctx = gud_connector_detect, >> + .get_modes = gud_connector_get_modes, >> + .atomic_check = gud_connector_atomic_check, >> +}; >> + >> +static int gud_connector_late_register(struct drm_connector *connector) >> +{ >> + struct gud_connector *gconn = to_gud_connector(connector); >> + >> + if (gconn->initial_brightness < 0) >> + return 0; >> + >> + return gud_connector_backlight_register(gconn); >> +} >> + >> +static void gud_connector_early_unregister(struct drm_connector *connector) >> +{ >> + struct gud_connector *gconn = to_gud_connector(connector); >> + >> + backlight_device_unregister(gconn->backlight); >> +} >> + >> +static void gud_connector_destroy(struct drm_connector *connector) >> +{ >> + struct gud_connector *gconn = to_gud_connector(connector); >> + >> + drm_connector_cleanup(connector); >> + kfree(gconn->properties); >> + kfree(gconn); >> +} >> + >> +static void gud_connector_reset(struct drm_connector *connector) >> +{ >> + struct gud_connector *gconn = to_gud_connector(connector); >> + >> + drm_atomic_helper_connector_reset(connector); >> + connector->state->tv = gconn->initial_tv_state; >> + /* Set margins from command line */ >> + drm_atomic_helper_connector_tv_reset(connector); >> + if (gconn->initial_brightness >= 0) >> + connector->state->tv.brightness = gconn->initial_brightness; >> +} >> + >> +static const struct drm_connector_funcs gud_connector_funcs = { >> + .fill_modes = drm_helper_probe_single_connector_modes, >> + .late_register = gud_connector_late_register, >> + .early_unregister = gud_connector_early_unregister, >> + .destroy = gud_connector_destroy, >> + .reset = gud_connector_reset, >> + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, >> + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, >> +}; >> + >> +/* >> + * The tv.mode property is shared among the connectors and its enum names are >> + * driver specific. This means that if more than one connector uses tv.mode, >> + * the enum names has to be the same. >> + */ >> +static int gud_connector_add_tv_mode(struct gud_device *gdrm, >> + struct drm_connector *connector, u64 val) >> +{ >> + unsigned int i, num_modes; >> + const char **modes; >> + size_t buf_len; >> + char *buf; >> + int ret; >> + >> + num_modes = val >> GUD_CONNECTOR_TV_MODE_NUM_SHIFT; >> + >> + if (!num_modes) >> + return -EINVAL; >> + >> + buf_len = num_modes * GUD_CONNECTOR_TV_MODE_NAME_LEN; >> + modes = kmalloc_array(num_modes, sizeof(*modes), GFP_KERNEL); >> + buf = kmalloc(buf_len, GFP_KERNEL); >> + if (!modes || !buf) { >> + ret = -ENOMEM; >> + goto free; >> + } >> + >> + ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_TV_MODE_VALUES, >> + connector->index, buf, buf_len); >> + if (ret) >> + goto free; >> + >> + for (i = 0; i < num_modes; i++) >> + modes[i] = &buf[i * GUD_CONNECTOR_TV_MODE_NAME_LEN]; >> + >> + ret = drm_mode_create_tv_properties(connector->dev, num_modes, modes); >> +free: >> + kfree(modes); >> + kfree(buf); >> + >> + return ret; >> +} >> + >> +static struct drm_property * >> +gud_connector_property_lookup(struct drm_connector *connector, u16 prop) >> +{ >> + struct drm_mode_config *config = &connector->dev->mode_config; >> + >> + switch (prop) { >> + case GUD_PROPERTY_TV_LEFT_MARGIN: >> + return config->tv_left_margin_property; >> + case GUD_PROPERTY_TV_RIGHT_MARGIN: >> + return config->tv_right_margin_property; >> + case GUD_PROPERTY_TV_TOP_MARGIN: >> + return config->tv_top_margin_property; >> + case GUD_PROPERTY_TV_BOTTOM_MARGIN: >> + return config->tv_bottom_margin_property; >> + case GUD_PROPERTY_TV_MODE: >> + return config->tv_mode_property; >> + case GUD_PROPERTY_TV_BRIGHTNESS: >> + return config->tv_brightness_property; >> + case GUD_PROPERTY_TV_CONTRAST: >> + return config->tv_contrast_property; >> + case GUD_PROPERTY_TV_FLICKER_REDUCTION: >> + return config->tv_flicker_reduction_property; >> + case GUD_PROPERTY_TV_OVERSCAN: >> + return config->tv_overscan_property; >> + case GUD_PROPERTY_TV_SATURATION: >> + return config->tv_saturation_property; >> + case GUD_PROPERTY_TV_HUE: >> + return config->tv_hue_property; >> + default: >> + return ERR_PTR(-EINVAL); >> + } >> +} >> + >> +static unsigned int *gud_connector_tv_state_val(u16 prop, struct drm_tv_connector_state *state) >> +{ >> + switch (prop) { >> + case GUD_PROPERTY_TV_LEFT_MARGIN: >> + return &state->margins.left; >> + case GUD_PROPERTY_TV_RIGHT_MARGIN: >> + return &state->margins.right; >> + case GUD_PROPERTY_TV_TOP_MARGIN: >> + return &state->margins.top; >> + case GUD_PROPERTY_TV_BOTTOM_MARGIN: >> + return &state->margins.bottom; >> + case GUD_PROPERTY_TV_MODE: >> + return &state->mode; >> + case GUD_PROPERTY_TV_BRIGHTNESS: >> + return &state->brightness; >> + case GUD_PROPERTY_TV_CONTRAST: >> + return &state->contrast; >> + case GUD_PROPERTY_TV_FLICKER_REDUCTION: >> + return &state->flicker_reduction; >> + case GUD_PROPERTY_TV_OVERSCAN: >> + return &state->overscan; >> + case GUD_PROPERTY_TV_SATURATION: >> + return &state->saturation; >> + case GUD_PROPERTY_TV_HUE: >> + return &state->hue; >> + default: >> + return ERR_PTR(-EINVAL); >> + } >> +} >> + >> +static int gud_connector_add_properties(struct gud_device *gdrm, struct gud_connector *gconn, >> + unsigned int num_properties) >> +{ >> + struct drm_device *drm = &gdrm->drm; >> + struct drm_connector *connector = &gconn->connector; >> + struct gud_property_req *properties; >> + unsigned int i; >> + int ret; >> + >> + gconn->properties = kcalloc(num_properties, sizeof(*gconn->properties), GFP_KERNEL); >> + if (!gconn->properties) >> + return -ENOMEM; >> + >> + properties = kcalloc(num_properties, sizeof(*properties), GFP_KERNEL); >> + if (!properties) >> + return -ENOMEM; >> + >> + ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_PROPERTIES, connector->index, >> + properties, num_properties * sizeof(*properties)); >> + if (ret) >> + goto out; >> + >> + for (i = 0; i < num_properties; i++) { >> + u16 prop = le16_to_cpu(properties[i].prop); >> + u64 val = le64_to_cpu(properties[i].val); >> + struct drm_property *property; >> + unsigned int *state_val; >> + >> + drm_dbg(drm, "property: %u = %llu(0x%llx)\n", prop, val, val); >> + >> + switch (prop) { >> + case GUD_PROPERTY_TV_LEFT_MARGIN: >> + fallthrough; >> + case GUD_PROPERTY_TV_RIGHT_MARGIN: >> + fallthrough; >> + case GUD_PROPERTY_TV_TOP_MARGIN: >> + fallthrough; >> + case GUD_PROPERTY_TV_BOTTOM_MARGIN: >> + ret = drm_mode_create_tv_margin_properties(drm); >> + if (ret) >> + goto out; >> + break; >> + case GUD_PROPERTY_TV_MODE: >> + ret = gud_connector_add_tv_mode(gdrm, connector, val); >> + if (ret) >> + goto out; >> + val = val & (BIT(GUD_CONNECTOR_TV_MODE_NUM_SHIFT) - 1); >> + break; >> + case GUD_PROPERTY_TV_BRIGHTNESS: >> + fallthrough; >> + case GUD_PROPERTY_TV_CONTRAST: >> + fallthrough; >> + case GUD_PROPERTY_TV_FLICKER_REDUCTION: >> + fallthrough; >> + case GUD_PROPERTY_TV_OVERSCAN: >> + fallthrough; >> + case GUD_PROPERTY_TV_SATURATION: >> + fallthrough; >> + case GUD_PROPERTY_TV_HUE: >> + /* This is a no-op if already added. */ >> + ret = drm_mode_create_tv_properties(drm, 0, NULL); >> + if (ret) >> + goto out; >> + break; >> + case GUD_PROPERTY_BACKLIGHT_BRIGHTNESS: >> + if (val > 100) { >> + ret = -EINVAL; >> + goto out; >> + } >> + gconn->initial_brightness = val; >> + break; >> + default: >> + /* New ones might show up in future devices, skip those we don't know. */ >> + drm_dbg(drm, "Unknown property: %u\n", prop); >> + continue; >> + } >> + >> + gconn->properties[gconn->num_properties++] = prop; >> + >> + if (prop == GUD_PROPERTY_BACKLIGHT_BRIGHTNESS) >> + continue; /* not a DRM property */ >> + >> + property = gud_connector_property_lookup(connector, prop); >> + if (WARN_ON(IS_ERR(property))) >> + continue; >> + >> + state_val = gud_connector_tv_state_val(prop, &gconn->initial_tv_state); >> + if (WARN_ON(IS_ERR(state_val))) >> + continue; >> + >> + *state_val = val; >> + drm_object_attach_property(&connector->base, property, 0); >> + } >> +out: >> + kfree(properties); >> + >> + return ret; >> +} >> + >> +int gud_connector_fill_properties(struct drm_connector *connector, >> + struct drm_connector_state *connector_state, >> + struct gud_property_req *properties) >> +{ >> + struct gud_connector *gconn; >> + unsigned int i; >> + >> + gconn = to_gud_connector(connector); >> + >> + /* Only interested in the count? */ >> + if (!connector_state) >> + return gconn->num_properties; >> + >> + for (i = 0; i < gconn->num_properties; i++) { >> + u16 prop = gconn->properties[i]; >> + u64 val; >> + >> + if (prop == GUD_PROPERTY_BACKLIGHT_BRIGHTNESS) { >> + val = connector_state->tv.brightness; >> + } else { >> + unsigned int *state_val; >> + >> + state_val = gud_connector_tv_state_val(prop, &connector_state->tv); >> + if (WARN_ON_ONCE(IS_ERR(state_val))) >> + return PTR_ERR(state_val); >> + >> + val = *state_val; >> + } >> + >> + properties[i].prop = cpu_to_le16(prop); >> + properties[i].val = cpu_to_le64(val); >> + } >> + >> + return gconn->num_properties; >> +} >> + >> +int gud_connector_create(struct gud_device *gdrm, unsigned int index) >> +{ >> + struct gud_connector_descriptor_req desc; >> + struct drm_device *drm = &gdrm->drm; >> + struct gud_connector *gconn; >> + struct drm_connector *connector; >> + struct drm_encoder *encoder; >> + int ret, connector_type; >> + u32 flags; >> + >> + ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR, index, &desc, sizeof(desc)); >> + if (ret) >> + return ret; >> + >> + gconn = kzalloc(sizeof(*gconn), GFP_KERNEL); > > Would be nice to do that with drmm_, but we don't have the > drmm_connector_alloc wrapper yet. > >> + if (!gconn) >> + return -ENOMEM; >> + >> + gconn->initial_brightness = -ENODEV; >> + flags = le32_to_cpu(desc.flags); >> + connector = &gconn->connector; >> + >> + drm_dbg(drm, "Connector: index=%u type=%u flags=0x%x num_properties=%u\n", >> + index, desc.connector_type, flags, desc.num_properties); >> + >> + switch (desc.connector_type) { >> + case GUD_CONNECTOR_TYPE_PANEL: >> + connector_type = DRM_MODE_CONNECTOR_USB; >> + break; >> + case GUD_CONNECTOR_TYPE_VGA: >> + connector_type = DRM_MODE_CONNECTOR_VGA; >> + break; >> + case GUD_CONNECTOR_TYPE_DVI: >> + connector_type = DRM_MODE_CONNECTOR_DVID; >> + break; >> + case GUD_CONNECTOR_TYPE_COMPOSITE: >> + connector_type = DRM_MODE_CONNECTOR_Composite; >> + break; >> + case GUD_CONNECTOR_TYPE_SVIDEO: >> + connector_type = DRM_MODE_CONNECTOR_SVIDEO; >> + break; >> + case GUD_CONNECTOR_TYPE_COMPONENT: >> + connector_type = DRM_MODE_CONNECTOR_Component; >> + break; >> + case GUD_CONNECTOR_TYPE_DISPLAYPORT: >> + connector_type = DRM_MODE_CONNECTOR_DisplayPort; >> + break; >> + case GUD_CONNECTOR_TYPE_HDMI: >> + connector_type = DRM_MODE_CONNECTOR_HDMIA; >> + break; >> + default: /* future types */ >> + connector_type = DRM_MODE_CONNECTOR_USB; >> + break; >> + }; >> + >> + drm_connector_helper_add(connector, &gud_connector_helper_funcs); >> + ret = drm_connector_init(drm, connector, &gud_connector_funcs, connector_type); >> + if (ret) { >> + kfree(connector); >> + return ret; >> + } >> + >> + if (WARN_ON(connector->index != index)) >> + return -EINVAL; >> + >> + if (flags & GUD_CONNECTOR_FLAGS_POLL_STATUS) >> + connector->polled = (DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT); >> + if (flags & GUD_CONNECTOR_FLAGS_INTERLACE) >> + connector->interlace_allowed = true; >> + if (flags & GUD_CONNECTOR_FLAGS_DOUBLESCAN) >> + connector->doublescan_allowed = true; >> + >> + if (desc.num_properties) { >> + ret = gud_connector_add_properties(gdrm, gconn, desc.num_properties); >> + if (ret) { >> + dev_err(drm->dev, "Failed to add connector/%u properties\n", index); >> + return ret; >> + } >> + } >> + >> + /* The first connector is attached to the existing simple pipe encoder */ >> + if (!connector->index) { >> + encoder = &gdrm->pipe.encoder; >> + } else { >> + encoder = &gconn->encoder; >> + >> + ret = drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_NONE); >> + if (ret) >> + return ret; >> + >> + encoder->possible_crtcs = 1; >> + } >> + >> + return drm_connector_attach_encoder(connector, encoder); >> +} _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/dri-devel