86 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de>4 */5/*6 * generic EDID driver7 */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 } else42 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