Commit | Line | Data |
---|---|---|
e7589271 FTS |
1 | /* |
2 | * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de> | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or | |
5 | * modify it under the terms of the GNU General Public | |
6 | * License as published by the Free Software Foundation; | |
7 | * either version 2, or (at your option) any later version. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; without even | |
11 | * the implied warranty of MERCHANTABILITY or FITNESS FOR | |
12 | * A PARTICULAR PURPOSE.See the GNU General Public License | |
13 | * for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program; if not, write to the Free Software | |
17 | * Foundation, Inc., | |
18 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
19 | */ | |
20 | /* | |
21 | * generic EDID driver | |
22 | */ | |
23 | ||
24 | #include <linux/slab.h> | |
5dc5f618 | 25 | #include <linux/fb.h> |
e7589271 | 26 | #include "via_aux.h" |
5dc5f618 | 27 | #include "../edid.h" |
e7589271 FTS |
28 | |
29 | ||
30 | static const char *name = "EDID"; | |
31 | ||
32 | ||
5dc5f618 FTS |
33 | static void query_edid(struct via_aux_drv *drv) |
34 | { | |
35 | struct fb_monspecs *spec = drv->data; | |
36 | unsigned char edid[EDID_LENGTH]; | |
37 | bool valid = false; | |
38 | ||
c572c8bb | 39 | if (spec) { |
5dc5f618 | 40 | fb_destroy_modedb(spec->modedb); |
c572c8bb | 41 | } else { |
5dc5f618 | 42 | spec = kmalloc(sizeof(*spec), GFP_KERNEL); |
c572c8bb DC |
43 | if (!spec) |
44 | return; | |
45 | } | |
5dc5f618 FTS |
46 | |
47 | spec->version = spec->revision = 0; | |
48 | if (via_aux_read(drv, 0x00, edid, EDID_LENGTH)) { | |
49 | fb_edid_to_monspecs(edid, spec); | |
50 | valid = spec->version || spec->revision; | |
51 | } | |
52 | ||
53 | if (!valid) { | |
54 | kfree(spec); | |
55 | spec = NULL; | |
56 | } else | |
57 | printk(KERN_DEBUG "EDID: %s %s\n", spec->manufacturer, spec->monitor); | |
58 | ||
59 | drv->data = spec; | |
60 | } | |
61 | ||
62 | static const struct fb_videomode *get_preferred_mode(struct via_aux_drv *drv) | |
63 | { | |
64 | struct fb_monspecs *spec = drv->data; | |
65 | int i; | |
66 | ||
67 | if (!spec || !spec->modedb || !(spec->misc & FB_MISC_1ST_DETAIL)) | |
68 | return NULL; | |
69 | ||
70 | for (i = 0; i < spec->modedb_len; i++) { | |
71 | if (spec->modedb[i].flag & FB_MODE_IS_FIRST && | |
72 | spec->modedb[i].flag & FB_MODE_IS_DETAILED) | |
73 | return &spec->modedb[i]; | |
74 | } | |
75 | ||
76 | return NULL; | |
77 | } | |
78 | ||
79 | static void cleanup(struct via_aux_drv *drv) | |
80 | { | |
81 | struct fb_monspecs *spec = drv->data; | |
82 | ||
83 | if (spec) | |
84 | fb_destroy_modedb(spec->modedb); | |
85 | } | |
86 | ||
e7589271 FTS |
87 | void via_aux_edid_probe(struct via_aux_bus *bus) |
88 | { | |
89 | struct via_aux_drv drv = { | |
90 | .bus = bus, | |
91 | .addr = 0x50, | |
5dc5f618 FTS |
92 | .name = name, |
93 | .cleanup = cleanup, | |
94 | .get_preferred_mode = get_preferred_mode}; | |
95 | ||
96 | query_edid(&drv); | |
e7589271 FTS |
97 | |
98 | /* as EDID devices can be connected/disconnected just add the driver */ | |
99 | via_aux_add(&drv); | |
100 | } |