linux/drivers/gpu/drm/drm_displayid.c
<<
>>
Prefs
   1// SPDX-License-Identifier: MIT
   2/*
   3 * Copyright © 2021 Intel Corporation
   4 */
   5
   6#include <drm/drm_displayid.h>
   7#include <drm/drm_edid.h>
   8#include <drm/drm_print.h>
   9
  10static int validate_displayid(const u8 *displayid, int length, int idx)
  11{
  12        int i, dispid_length;
  13        u8 csum = 0;
  14        const struct displayid_header *base;
  15
  16        base = (const struct displayid_header *)&displayid[idx];
  17
  18        DRM_DEBUG_KMS("base revision 0x%x, length %d, %d %d\n",
  19                      base->rev, base->bytes, base->prod_id, base->ext_count);
  20
  21        /* +1 for DispID checksum */
  22        dispid_length = sizeof(*base) + base->bytes + 1;
  23        if (dispid_length > length - idx)
  24                return -EINVAL;
  25
  26        for (i = 0; i < dispid_length; i++)
  27                csum += displayid[idx + i];
  28        if (csum) {
  29                DRM_NOTE("DisplayID checksum invalid, remainder is %d\n", csum);
  30                return -EINVAL;
  31        }
  32
  33        return 0;
  34}
  35
  36static const u8 *drm_find_displayid_extension(const struct edid *edid,
  37                                              int *length, int *idx,
  38                                              int *ext_index)
  39{
  40        const u8 *displayid = drm_find_edid_extension(edid, DISPLAYID_EXT, ext_index);
  41        const struct displayid_header *base;
  42        int ret;
  43
  44        if (!displayid)
  45                return NULL;
  46
  47        /* EDID extensions block checksum isn't for us */
  48        *length = EDID_LENGTH - 1;
  49        *idx = 1;
  50
  51        ret = validate_displayid(displayid, *length, *idx);
  52        if (ret)
  53                return NULL;
  54
  55        base = (const struct displayid_header *)&displayid[*idx];
  56        *length = *idx + sizeof(*base) + base->bytes;
  57
  58        return displayid;
  59}
  60
  61void displayid_iter_edid_begin(const struct edid *edid,
  62                               struct displayid_iter *iter)
  63{
  64        memset(iter, 0, sizeof(*iter));
  65
  66        iter->edid = edid;
  67}
  68
  69static const struct displayid_block *
  70displayid_iter_block(const struct displayid_iter *iter)
  71{
  72        const struct displayid_block *block;
  73
  74        if (!iter->section)
  75                return NULL;
  76
  77        block = (const struct displayid_block *)&iter->section[iter->idx];
  78
  79        if (iter->idx + sizeof(*block) <= iter->length &&
  80            iter->idx + sizeof(*block) + block->num_bytes <= iter->length)
  81                return block;
  82
  83        return NULL;
  84}
  85
  86const struct displayid_block *
  87__displayid_iter_next(struct displayid_iter *iter)
  88{
  89        const struct displayid_block *block;
  90
  91        if (!iter->edid)
  92                return NULL;
  93
  94        if (iter->section) {
  95                /* current block should always be valid */
  96                block = displayid_iter_block(iter);
  97                if (WARN_ON(!block)) {
  98                        iter->section = NULL;
  99                        iter->edid = NULL;
 100                        return NULL;
 101                }
 102
 103                /* next block in section */
 104                iter->idx += sizeof(*block) + block->num_bytes;
 105
 106                block = displayid_iter_block(iter);
 107                if (block)
 108                        return block;
 109        }
 110
 111        for (;;) {
 112                iter->section = drm_find_displayid_extension(iter->edid,
 113                                                             &iter->length,
 114                                                             &iter->idx,
 115                                                             &iter->ext_index);
 116                if (!iter->section) {
 117                        iter->edid = NULL;
 118                        return NULL;
 119                }
 120
 121                iter->idx += sizeof(struct displayid_header);
 122
 123                block = displayid_iter_block(iter);
 124                if (block)
 125                        return block;
 126        }
 127}
 128
 129void displayid_iter_end(struct displayid_iter *iter)
 130{
 131        memset(iter, 0, sizeof(*iter));
 132}
 133