linux/drivers/gpu/drm/drm_edid_load.c
<<
>>
Prefs
   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
   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_crtc.h>
  15#include <drm/drm_crtc_helper.h>
  16#include <drm/drm_drv.h>
  17#include <drm/drm_edid.h>
  18#include <drm/drm_print.h>
  19
  20static char edid_firmware[PATH_MAX];
  21module_param_string(edid_firmware, edid_firmware, sizeof(edid_firmware), 0644);
  22MODULE_PARM_DESC(edid_firmware, "Do not probe monitor, use specified EDID blob "
  23        "from built-in data or /lib/firmware instead. ");
  24
  25/* Use only for backward compatibility with drm_kms_helper.edid_firmware */
  26int __drm_set_edid_firmware_path(const char *path)
  27{
  28        scnprintf(edid_firmware, sizeof(edid_firmware), "%s", path);
  29
  30        return 0;
  31}
  32EXPORT_SYMBOL(__drm_set_edid_firmware_path);
  33
  34/* Use only for backward compatibility with drm_kms_helper.edid_firmware */
  35int __drm_get_edid_firmware_path(char *buf, size_t bufsize)
  36{
  37        return scnprintf(buf, bufsize, "%s", edid_firmware);
  38}
  39EXPORT_SYMBOL(__drm_get_edid_firmware_path);
  40
  41#define GENERIC_EDIDS 6
  42static const char * const generic_edid_name[GENERIC_EDIDS] = {
  43        "edid/800x600.bin",
  44        "edid/1024x768.bin",
  45        "edid/1280x1024.bin",
  46        "edid/1600x1200.bin",
  47        "edid/1680x1050.bin",
  48        "edid/1920x1080.bin",
  49};
  50
  51static const u8 generic_edid[GENERIC_EDIDS][128] = {
  52        {
  53        0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
  54        0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  55        0x05, 0x16, 0x01, 0x03, 0x6d, 0x1b, 0x14, 0x78,
  56        0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
  57        0x20, 0x50, 0x54, 0x01, 0x00, 0x00, 0x45, 0x40,
  58        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
  59        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xa0, 0x0f,
  60        0x20, 0x00, 0x31, 0x58, 0x1c, 0x20, 0x28, 0x80,
  61        0x14, 0x00, 0x15, 0xd0, 0x10, 0x00, 0x00, 0x1e,
  62        0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
  63        0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
  64        0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
  65        0x3d, 0x24, 0x26, 0x05, 0x00, 0x0a, 0x20, 0x20,
  66        0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
  67        0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x53,
  68        0x56, 0x47, 0x41, 0x0a, 0x20, 0x20, 0x00, 0xc2,
  69        },
  70        {
  71        0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
  72        0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  73        0x05, 0x16, 0x01, 0x03, 0x6d, 0x23, 0x1a, 0x78,
  74        0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
  75        0x20, 0x50, 0x54, 0x00, 0x08, 0x00, 0x61, 0x40,
  76        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
  77        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x64, 0x19,
  78        0x00, 0x40, 0x41, 0x00, 0x26, 0x30, 0x08, 0x90,
  79        0x36, 0x00, 0x63, 0x0a, 0x11, 0x00, 0x00, 0x18,
  80        0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
  81        0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
  82        0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
  83        0x3d, 0x2f, 0x31, 0x07, 0x00, 0x0a, 0x20, 0x20,
  84        0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
  85        0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x58,
  86        0x47, 0x41, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x55,
  87        },
  88        {
  89        0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
  90        0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  91        0x05, 0x16, 0x01, 0x03, 0x6d, 0x2c, 0x23, 0x78,
  92        0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
  93        0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0x81, 0x80,
  94        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
  95        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x30, 0x2a,
  96        0x00, 0x98, 0x51, 0x00, 0x2a, 0x40, 0x30, 0x70,
  97        0x13, 0x00, 0xbc, 0x63, 0x11, 0x00, 0x00, 0x1e,
  98        0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
  99        0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
 100        0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
 101        0x3d, 0x3e, 0x40, 0x0b, 0x00, 0x0a, 0x20, 0x20,
 102        0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
 103        0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x53,
 104        0x58, 0x47, 0x41, 0x0a, 0x20, 0x20, 0x00, 0xa0,
 105        },
 106        {
 107        0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
 108        0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 109        0x05, 0x16, 0x01, 0x03, 0x6d, 0x37, 0x29, 0x78,
 110        0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
 111        0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xa9, 0x40,
 112        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
 113        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x48, 0x3f,
 114        0x40, 0x30, 0x62, 0xb0, 0x32, 0x40, 0x40, 0xc0,
 115        0x13, 0x00, 0x2b, 0xa0, 0x21, 0x00, 0x00, 0x1e,
 116        0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
 117        0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
 118        0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
 119        0x3d, 0x4a, 0x4c, 0x11, 0x00, 0x0a, 0x20, 0x20,
 120        0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
 121        0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x55,
 122        0x58, 0x47, 0x41, 0x0a, 0x20, 0x20, 0x00, 0x9d,
 123        },
 124        {
 125        0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
 126        0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 127        0x05, 0x16, 0x01, 0x03, 0x6d, 0x2b, 0x1b, 0x78,
 128        0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
 129        0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xb3, 0x00,
 130        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
 131        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x21, 0x39,
 132        0x90, 0x30, 0x62, 0x1a, 0x27, 0x40, 0x68, 0xb0,
 133        0x36, 0x00, 0xb5, 0x11, 0x11, 0x00, 0x00, 0x1e,
 134        0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
 135        0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
 136        0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
 137        0x3d, 0x40, 0x42, 0x0f, 0x00, 0x0a, 0x20, 0x20,
 138        0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
 139        0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x57,
 140        0x53, 0x58, 0x47, 0x41, 0x0a, 0x20, 0x00, 0x26,
 141        },
 142        {
 143        0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
 144        0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 145        0x05, 0x16, 0x01, 0x03, 0x6d, 0x32, 0x1c, 0x78,
 146        0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
 147        0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xd1, 0xc0,
 148        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
 149        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a,
 150        0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c,
 151        0x45, 0x00, 0xf4, 0x19, 0x11, 0x00, 0x00, 0x1e,
 152        0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
 153        0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
 154        0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
 155        0x3d, 0x42, 0x44, 0x0f, 0x00, 0x0a, 0x20, 0x20,
 156        0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
 157        0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x46,
 158        0x48, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x05,
 159        },
 160};
 161
 162static int edid_size(const u8 *edid, int data_size)
 163{
 164        if (data_size < EDID_LENGTH)
 165                return 0;
 166
 167        return (edid[0x7e] + 1) * EDID_LENGTH;
 168}
 169
 170static void *edid_load(struct drm_connector *connector, const char *name,
 171                        const char *connector_name)
 172{
 173        const struct firmware *fw = NULL;
 174        const u8 *fwdata;
 175        u8 *edid;
 176        int fwsize, builtin;
 177        int i, valid_extensions = 0;
 178        bool print_bad_edid = !connector->bad_edid_counter || drm_debug_enabled(DRM_UT_KMS);
 179
 180        builtin = match_string(generic_edid_name, GENERIC_EDIDS, name);
 181        if (builtin >= 0) {
 182                fwdata = generic_edid[builtin];
 183                fwsize = sizeof(generic_edid[builtin]);
 184        } else {
 185                struct platform_device *pdev;
 186                int err;
 187
 188                pdev = platform_device_register_simple(connector_name, -1, NULL, 0);
 189                if (IS_ERR(pdev)) {
 190                        DRM_ERROR("Failed to register EDID firmware platform device "
 191                                  "for connector \"%s\"\n", connector_name);
 192                        return ERR_CAST(pdev);
 193                }
 194
 195                err = request_firmware(&fw, name, &pdev->dev);
 196                platform_device_unregister(pdev);
 197                if (err) {
 198                        DRM_ERROR("Requesting EDID firmware \"%s\" failed (err=%d)\n",
 199                                  name, err);
 200                        return ERR_PTR(err);
 201                }
 202
 203                fwdata = fw->data;
 204                fwsize = fw->size;
 205        }
 206
 207        if (edid_size(fwdata, fwsize) != fwsize) {
 208                DRM_ERROR("Size of EDID firmware \"%s\" is invalid "
 209                          "(expected %d, got %d\n", name,
 210                          edid_size(fwdata, fwsize), (int)fwsize);
 211                edid = ERR_PTR(-EINVAL);
 212                goto out;
 213        }
 214
 215        edid = kmemdup(fwdata, fwsize, GFP_KERNEL);
 216        if (edid == NULL) {
 217                edid = ERR_PTR(-ENOMEM);
 218                goto out;
 219        }
 220
 221        if (!drm_edid_block_valid(edid, 0, print_bad_edid,
 222                                  &connector->edid_corrupt)) {
 223                connector->bad_edid_counter++;
 224                DRM_ERROR("Base block of EDID firmware \"%s\" is invalid ",
 225                    name);
 226                kfree(edid);
 227                edid = ERR_PTR(-EINVAL);
 228                goto out;
 229        }
 230
 231        for (i = 1; i <= edid[0x7e]; i++) {
 232                if (i != valid_extensions + 1)
 233                        memcpy(edid + (valid_extensions + 1) * EDID_LENGTH,
 234                            edid + i * EDID_LENGTH, EDID_LENGTH);
 235                if (drm_edid_block_valid(edid + i * EDID_LENGTH, i,
 236                                         print_bad_edid,
 237                                         NULL))
 238                        valid_extensions++;
 239        }
 240
 241        if (valid_extensions != edid[0x7e]) {
 242                u8 *new_edid;
 243
 244                edid[EDID_LENGTH-1] += edid[0x7e] - valid_extensions;
 245                DRM_INFO("Found %d valid extensions instead of %d in EDID data "
 246                    "\"%s\" for connector \"%s\"\n", valid_extensions,
 247                    edid[0x7e], name, connector_name);
 248                edid[0x7e] = valid_extensions;
 249
 250                new_edid = krealloc(edid, (valid_extensions + 1) * EDID_LENGTH,
 251                                    GFP_KERNEL);
 252                if (new_edid)
 253                        edid = new_edid;
 254        }
 255
 256        DRM_INFO("Got %s EDID base block and %d extension%s from "
 257            "\"%s\" for connector \"%s\"\n", (builtin >= 0) ? "built-in" :
 258            "external", valid_extensions, valid_extensions == 1 ? "" : "s",
 259            name, connector_name);
 260
 261out:
 262        release_firmware(fw);
 263        return edid;
 264}
 265
 266struct edid *drm_load_edid_firmware(struct drm_connector *connector)
 267{
 268        const char *connector_name = connector->name;
 269        char *edidname, *last, *colon, *fwstr, *edidstr, *fallback = NULL;
 270        struct edid *edid;
 271
 272        if (edid_firmware[0] == '\0')
 273                return ERR_PTR(-ENOENT);
 274
 275        /*
 276         * If there are multiple edid files specified and separated
 277         * by commas, search through the list looking for one that
 278         * matches the connector.
 279         *
 280         * If there's one or more that doesn't specify a connector, keep
 281         * the last one found one as a fallback.
 282         */
 283        fwstr = kstrdup(edid_firmware, GFP_KERNEL);
 284        if (!fwstr)
 285                return ERR_PTR(-ENOMEM);
 286        edidstr = fwstr;
 287
 288        while ((edidname = strsep(&edidstr, ","))) {
 289                colon = strchr(edidname, ':');
 290                if (colon != NULL) {
 291                        if (strncmp(connector_name, edidname, colon - edidname))
 292                                continue;
 293                        edidname = colon + 1;
 294                        break;
 295                }
 296
 297                if (*edidname != '\0') /* corner case: multiple ',' */
 298                        fallback = edidname;
 299        }
 300
 301        if (!edidname) {
 302                if (!fallback) {
 303                        kfree(fwstr);
 304                        return ERR_PTR(-ENOENT);
 305                }
 306                edidname = fallback;
 307        }
 308
 309        last = edidname + strlen(edidname) - 1;
 310        if (*last == '\n')
 311                *last = '\0';
 312
 313        edid = edid_load(connector, edidname, connector_name);
 314        kfree(fwstr);
 315
 316        return edid;
 317}
 318