drm_edid_load.c 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. drm_edid_load.c: use a built-in EDID data set or load it via the firmware
  4. interface
  5. Copyright (C) 2012 Carsten Emde <C.Emde@osadl.org>
  6. */
  7. #include <linux/firmware.h>
  8. #include <linux/module.h>
  9. #include <linux/platform_device.h>
  10. #include <drm/drm_connector.h>
  11. #include <drm/drm_drv.h>
  12. #include <drm/drm_edid.h>
  13. #include <drm/drm_print.h>
  14. #include "drm_crtc_internal.h"
  15. static char edid_firmware[PATH_MAX];
  16. module_param_string(edid_firmware, edid_firmware, sizeof(edid_firmware), 0644);
  17. MODULE_PARM_DESC(edid_firmware,
  18. "Do not probe monitor, use specified EDID blob from /lib/firmware instead.");
  19. static const struct drm_edid *edid_load(struct drm_connector *connector, const char *name)
  20. {
  21. const struct firmware *fw = NULL;
  22. const struct drm_edid *drm_edid;
  23. int err;
  24. err = request_firmware(&fw, name, connector->dev->dev);
  25. if (err) {
  26. drm_err(connector->dev,
  27. "[CONNECTOR:%d:%s] Requesting EDID firmware \"%s\" failed (err=%d)\n",
  28. connector->base.id, connector->name,
  29. name, err);
  30. return ERR_PTR(err);
  31. }
  32. drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] Loaded external firmware EDID \"%s\"\n",
  33. connector->base.id, connector->name, name);
  34. drm_edid = drm_edid_alloc(fw->data, fw->size);
  35. if (!drm_edid_valid(drm_edid)) {
  36. drm_err(connector->dev, "Invalid firmware EDID \"%s\"\n", name);
  37. drm_edid_free(drm_edid);
  38. drm_edid = ERR_PTR(-EINVAL);
  39. }
  40. release_firmware(fw);
  41. return drm_edid;
  42. }
  43. const struct drm_edid *drm_edid_load_firmware(struct drm_connector *connector)
  44. {
  45. char *edidname, *last, *colon, *fwstr, *edidstr, *fallback = NULL;
  46. const struct drm_edid *drm_edid;
  47. if (edid_firmware[0] == '\0')
  48. return ERR_PTR(-ENOENT);
  49. /*
  50. * If there are multiple edid files specified and separated
  51. * by commas, search through the list looking for one that
  52. * matches the connector.
  53. *
  54. * If there's one or more that doesn't specify a connector, keep
  55. * the last one found one as a fallback.
  56. */
  57. fwstr = kstrdup(edid_firmware, GFP_KERNEL);
  58. if (!fwstr)
  59. return ERR_PTR(-ENOMEM);
  60. edidstr = fwstr;
  61. while ((edidname = strsep(&edidstr, ","))) {
  62. colon = strchr(edidname, ':');
  63. if (colon != NULL) {
  64. if (strncmp(connector->name, edidname, colon - edidname))
  65. continue;
  66. edidname = colon + 1;
  67. break;
  68. }
  69. if (*edidname != '\0') /* corner case: multiple ',' */
  70. fallback = edidname;
  71. }
  72. if (!edidname) {
  73. if (!fallback) {
  74. kfree(fwstr);
  75. return ERR_PTR(-ENOENT);
  76. }
  77. edidname = fallback;
  78. }
  79. last = edidname + strlen(edidname) - 1;
  80. if (*last == '\n')
  81. *last = '\0';
  82. drm_edid = edid_load(connector, edidname);
  83. kfree(fwstr);
  84. return drm_edid;
  85. }