linux/drivers/video/fbdev/via/via_aux_edid.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
   4 */
   5/*
   6 * generic EDID driver
   7 */
   8
   9#include <linux/slab.h>
  10#include <linux/fb.h>
  11#include "via_aux.h"
  12#include "../edid.h"
  13
  14
  15static const char *name = "EDID";
  16
  17
  18static void query_edid(struct via_aux_drv *drv)
  19{
  20        struct fb_monspecs *spec = drv->data;
  21        unsigned char edid[EDID_LENGTH];
  22        bool valid = false;
  23
  24        if (spec) {
  25                fb_destroy_modedb(spec->modedb);
  26        } else {
  27                spec = kmalloc(sizeof(*spec), GFP_KERNEL);
  28                if (!spec)
  29                        return;
  30        }
  31
  32        spec->version = spec->revision = 0;
  33        if (via_aux_read(drv, 0x00, edid, EDID_LENGTH)) {
  34                fb_edid_to_monspecs(edid, spec);
  35                valid = spec->version || spec->revision;
  36        }
  37
  38        if (!valid) {
  39                kfree(spec);
  40                spec = NULL;
  41        } else
  42                printk(KERN_DEBUG "EDID: %s %s\n", spec->manufacturer, spec->monitor);
  43
  44        drv->data = spec;
  45}
  46
  47static const struct fb_videomode *get_preferred_mode(struct via_aux_drv *drv)
  48{
  49        struct fb_monspecs *spec = drv->data;
  50        int i;
  51
  52        if (!spec || !spec->modedb || !(spec->misc & FB_MISC_1ST_DETAIL))
  53                return NULL;
  54
  55        for (i = 0; i < spec->modedb_len; i++) {
  56                if (spec->modedb[i].flag & FB_MODE_IS_FIRST &&
  57                        spec->modedb[i].flag & FB_MODE_IS_DETAILED)
  58                        return &spec->modedb[i];
  59        }
  60
  61        return NULL;
  62}
  63
  64static void cleanup(struct via_aux_drv *drv)
  65{
  66        struct fb_monspecs *spec = drv->data;
  67
  68        if (spec)
  69                fb_destroy_modedb(spec->modedb);
  70}
  71
  72void via_aux_edid_probe(struct via_aux_bus *bus)
  73{
  74        struct via_aux_drv drv = {
  75                .bus    =       bus,
  76                .addr   =       0x50,
  77                .name   =       name,
  78                .cleanup        =       cleanup,
  79                .get_preferred_mode     =       get_preferred_mode};
  80
  81        query_edid(&drv);
  82
  83        /* as EDID devices can be connected/disconnected just add the driver */
  84        via_aux_add(&drv);
  85}
  86