GNU Linux-libre 5.19-rc6-gnu
[releases.git] / drivers / video / fbdev / via / via_aux_edid.c
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
15 static const char *name = "EDID";
16
17
18 static 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
47 static 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
64 static 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
72 void 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 }