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