162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de> 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci/* 662306a36Sopenharmony_ci * generic EDID driver 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/slab.h> 1062306a36Sopenharmony_ci#include <linux/fb.h> 1162306a36Sopenharmony_ci#include "via_aux.h" 1262306a36Sopenharmony_ci#include "../edid.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistatic const char *name = "EDID"; 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic void query_edid(struct via_aux_drv *drv) 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci struct fb_monspecs *spec = drv->data; 2162306a36Sopenharmony_ci unsigned char edid[EDID_LENGTH]; 2262306a36Sopenharmony_ci bool valid = false; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci if (spec) { 2562306a36Sopenharmony_ci fb_destroy_modedb(spec->modedb); 2662306a36Sopenharmony_ci } else { 2762306a36Sopenharmony_ci spec = kmalloc(sizeof(*spec), GFP_KERNEL); 2862306a36Sopenharmony_ci if (!spec) 2962306a36Sopenharmony_ci return; 3062306a36Sopenharmony_ci } 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci spec->version = spec->revision = 0; 3362306a36Sopenharmony_ci if (via_aux_read(drv, 0x00, edid, EDID_LENGTH)) { 3462306a36Sopenharmony_ci fb_edid_to_monspecs(edid, spec); 3562306a36Sopenharmony_ci valid = spec->version || spec->revision; 3662306a36Sopenharmony_ci } 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci if (!valid) { 3962306a36Sopenharmony_ci kfree(spec); 4062306a36Sopenharmony_ci spec = NULL; 4162306a36Sopenharmony_ci } else 4262306a36Sopenharmony_ci printk(KERN_DEBUG "EDID: %s %s\n", spec->manufacturer, spec->monitor); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci drv->data = spec; 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic const struct fb_videomode *get_preferred_mode(struct via_aux_drv *drv) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci struct fb_monspecs *spec = drv->data; 5062306a36Sopenharmony_ci int i; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci if (!spec || !spec->modedb || !(spec->misc & FB_MISC_1ST_DETAIL)) 5362306a36Sopenharmony_ci return NULL; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci for (i = 0; i < spec->modedb_len; i++) { 5662306a36Sopenharmony_ci if (spec->modedb[i].flag & FB_MODE_IS_FIRST && 5762306a36Sopenharmony_ci spec->modedb[i].flag & FB_MODE_IS_DETAILED) 5862306a36Sopenharmony_ci return &spec->modedb[i]; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci return NULL; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic void cleanup(struct via_aux_drv *drv) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci struct fb_monspecs *spec = drv->data; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci if (spec) 6962306a36Sopenharmony_ci fb_destroy_modedb(spec->modedb); 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_civoid via_aux_edid_probe(struct via_aux_bus *bus) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci struct via_aux_drv drv = { 7562306a36Sopenharmony_ci .bus = bus, 7662306a36Sopenharmony_ci .addr = 0x50, 7762306a36Sopenharmony_ci .name = name, 7862306a36Sopenharmony_ci .cleanup = cleanup, 7962306a36Sopenharmony_ci .get_preferred_mode = get_preferred_mode}; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci query_edid(&drv); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* as EDID devices can be connected/disconnected just add the driver */ 8462306a36Sopenharmony_ci via_aux_add(&drv); 8562306a36Sopenharmony_ci} 86