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(modedb: spec->modedb); |
26 | } else { |
27 | spec = kmalloc(size: sizeof(*spec), GFP_KERNEL); |
28 | if (!spec) |
29 | return; |
30 | } |
31 | |
32 | spec->version = spec->revision = 0; |
33 | if (via_aux_read(drv, start: 0x00, buf: edid, EDID_LENGTH)) { |
34 | fb_edid_to_monspecs(edid, specs: spec); |
35 | valid = spec->version || spec->revision; |
36 | } |
37 | |
38 | if (!valid) { |
39 | kfree(objp: 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(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: &drv); |
82 | |
83 | /* as EDID devices can be connected/disconnected just add the driver */ |
84 | via_aux_add(drv: &drv); |
85 | } |
86 | |