linux/drivers/gpu/drm/udl/udl_edid.c
Thomas Zimmermann 90e0fd1e6c drm/udl: Untangle .get_modes() and .detect_ctx()
Provide separate implementations of .get_modes() and .detect_ctx()
from struct drm_connector. Switch to struct drm_edid.

Udl's .detect() helper used to fetch the EDID from the adapter and the
.get_modes() helper provided display modes from the data. But this
relied on the DRM helpers to call the functions in the correct order.
When no EDID could be retrieved, .detect() regularly printed a warning
to the kernel log.

Switching to the new helpers around struct drm_edid separates both from
each other. The .get_modes() helper now fetches the EDID by itself and
the .detect_ctx() helper only tests for its presence. The patch does a
number of things to implement this.

- Move udl_get_edid_block() to udl_edid.c and rename it to
udl_read_edid_block(). Then use the helper to implement probing in
udl_probe_edid() and reading in udl_edid_read(). The latter helper
is build on top of DRM helpers.

- Replace the existing code in .get_modes() and .detect() with udl's
new EDID helpers. The new code behaves like DRM's similar DDC-based
helpers. Instead of .detect(), udl now implements .detect_ctx().

- Remove the edid data from struct udl_connector. The field cached
the EDID data between calls to .detect() and .get_modes(), but is now
unused.

v3:
- implement udl_probe_edid() with memchr_inv() (Jani)

v2:
- implement udl_probe_edid() within udl
- reword commit description

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Reviewed-by: Jani Nikula <jani.nikula@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20240510154841.11370-5-tzimmermann@suse.de
2024-05-13 13:35:52 +02:00

81 lines
1.6 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
#include <linux/string.h>
#include <drm/drm_drv.h>
#include <drm/drm_edid.h>
#include "udl_drv.h"
#include "udl_edid.h"
static int udl_read_edid_block(void *data, u8 *buf, unsigned int block, size_t len)
{
struct udl_device *udl = data;
struct drm_device *dev = &udl->drm;
struct usb_device *udev = udl_to_usb_device(udl);
u8 *read_buff;
int idx, ret;
size_t i;
read_buff = kmalloc(2, GFP_KERNEL);
if (!read_buff)
return -ENOMEM;
if (!drm_dev_enter(dev, &idx)) {
ret = -ENODEV;
goto err_kfree;
}
for (i = 0; i < len; i++) {
int bval = (i + block * EDID_LENGTH) << 8;
ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
0x02, (0x80 | (0x02 << 5)), bval,
0xA1, read_buff, 2, USB_CTRL_GET_TIMEOUT);
if (ret < 0) {
drm_err(dev, "Read EDID byte %zu failed err %x\n", i, ret);
goto err_drm_dev_exit;
} else if (ret < 1) {
ret = -EIO;
drm_err(dev, "Read EDID byte %zu failed\n", i);
goto err_drm_dev_exit;
}
buf[i] = read_buff[1];
}
drm_dev_exit(idx);
kfree(read_buff);
return 0;
err_drm_dev_exit:
drm_dev_exit(idx);
err_kfree:
kfree(read_buff);
return ret;
}
bool udl_probe_edid(struct udl_device *udl)
{
u8 hdr[8];
int ret;
ret = udl_read_edid_block(udl, hdr, 0, sizeof(hdr));
if (ret)
return false;
/*
* The adapter sends all-zeros if no monitor has been
* connected. We consider anything else a connection.
*/
return !!memchr_inv(hdr, 0, sizeof(hdr));
}
const struct drm_edid *udl_edid_read(struct drm_connector *connector)
{
struct udl_device *udl = to_udl(connector->dev);
return drm_edid_read_custom(connector, udl_read_edid_block, udl);
}