1 // Copyright 2017 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "edid.h"
6 
7 #include <ddk/binding.h>
8 #include <ddk/debug.h>
9 #include <ddk/platform-defs.h>
10 #include <ddk/protocol/platform/device.h>
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <unistd.h>
15 #include <hw/reg.h>
16 
edid_has_extension(const uint8_t * edid_buf)17 bool edid_has_extension(const uint8_t* edid_buf) {
18     const edid_t* edid = (edid_t *) edid_buf;
19 
20     return (edid->ext_flag == 1);
21 }
22 
edid_get_num_dtd(const uint8_t * edid_buf,uint8_t * num_dtd)23 zx_status_t edid_get_num_dtd(const uint8_t* edid_buf, uint8_t* num_dtd) {
24     const uint8_t* start_ext;
25     const uint8_t* start_dtd;
26     *num_dtd = 0;
27     int i;
28 
29     if (!edid_has_extension(edid_buf)) {
30         *num_dtd = 0;
31         return ZX_OK;
32     }
33 
34     // It has extension. Read from start of DTD until you hit 00 00
35     start_ext = &edid_buf[128];
36 
37     if (start_ext[0] != 0x2) {
38         zxlogf(ERROR, "%s: Unknown tag! %d\n", __FUNCTION__, start_ext[0]);
39         return ZX_ERR_WRONG_TYPE;
40     }
41 
42     if (start_ext[2] == 0) {
43         zxlogf(ERROR, "%s: Invalid DTD pointer! 0x%x\n", __FUNCTION__, start_ext[2]);
44         return ZX_ERR_WRONG_TYPE;
45     }
46 
47     start_dtd = &start_ext[0] + start_ext[2];
48     i = 0;
49     while (start_dtd[i] != 0x0 && start_dtd[i + 1] != 0x0) {
50         *num_dtd += 1;
51         i += 18;
52     }
53 
54     return ZX_OK;
55 }
56 
edid_dump_disp_timing(const disp_timing_t * d)57 void edid_dump_disp_timing(const disp_timing_t* d) {
58     zxlogf(INFO, "%s\n", __FUNCTION__);
59 
60     zxlogf(INFO, "pixel_clk = 0x%x\n", d->pixel_clk);
61     zxlogf(INFO, "HActive = 0x%x\n", d->HActive);
62     zxlogf(INFO, "HBlanking = 0x%x\n", d->HBlanking);
63     zxlogf(INFO, "VActive = 0x%x\n", d->VActive);
64     zxlogf(INFO, "VBlanking = 0x%x\n", d->VBlanking);
65     zxlogf(INFO, "HSyncOffset = 0x%x\n", d->HSyncOffset);
66     zxlogf(INFO, "HSyncPulseWidth = 0x%x\n", d->HSyncPulseWidth);
67     zxlogf(INFO, "VSyncOffset = 0x%x\n", d->VSyncOffset);
68     zxlogf(INFO, "VSyncPulseWidth = 0x%x\n", d->VSyncPulseWidth);
69     zxlogf(INFO, "HImageSize = 0x%x\n", d->HImageSize);
70     zxlogf(INFO, "VImageSize = 0x%x\n", d->VImageSize);
71     zxlogf(INFO, "HBorder = 0x%x\n", d->HBorder);
72     zxlogf(INFO, "VBorder = 0x%x\n", d->VBorder);
73     zxlogf(INFO, "Flags = 0x%x\n", d->Flags);
74 }
75 
edid_parse_std_display_timing(const uint8_t * edid_buf,detailed_timing_t * raw_dtd,disp_timing_t * disp_timing)76 zx_status_t edid_parse_std_display_timing(const uint8_t* edid_buf, detailed_timing_t* raw_dtd,
77                                             disp_timing_t* disp_timing) {
78     const uint8_t* start_dtd;
79     uint8_t* s_r_dtd = (uint8_t*) raw_dtd;
80 
81     start_dtd = &edid_buf[0x36];
82 
83     // populate raw structure first
84     memcpy(s_r_dtd, start_dtd, 18);
85 
86     disp_timing->pixel_clk = raw_dtd->raw_pixel_clk[1] << 8 |
87                                                         raw_dtd->raw_pixel_clk[0];
88     disp_timing->HActive = (((raw_dtd->raw_Hact_HBlank & 0xf0)>>4) << 8) |
89                                                         raw_dtd->raw_Hact;
90     disp_timing->HBlanking = ((raw_dtd->raw_Hact_HBlank & 0x0f) << 8) |
91                                                         raw_dtd->raw_HBlank;
92     disp_timing->VActive = (((raw_dtd->raw_Vact_VBlank & 0xf0)>>4) << 8) |
93                                                         raw_dtd->raw_Vact;
94     disp_timing->VBlanking = ((raw_dtd->raw_Vact_VBlank & 0x0f) << 8) |
95                                                         raw_dtd->raw_VBlank;
96     disp_timing->HSyncOffset = (((raw_dtd->raw_HSync_VSync_OFF_PW & 0xc0)>>6) << 8) |
97                                                         raw_dtd->raw_HSyncOff;
98     disp_timing->HSyncPulseWidth = (((raw_dtd->raw_HSync_VSync_OFF_PW & 0x30)>>4) << 8) |
99                                                         raw_dtd->raw_HSyncPW;
100     disp_timing->VSyncOffset = (((raw_dtd->raw_HSync_VSync_OFF_PW & 0x0c)>>2) << 4) |
101                                                         (raw_dtd->raw_VSyncOff_VSyncPW & 0xf0)>>4;
102     disp_timing->VSyncPulseWidth = ((raw_dtd->raw_HSync_VSync_OFF_PW & 0x03) << 4) |
103                                                         (raw_dtd->raw_VSyncOff_VSyncPW & 0x0f);
104     disp_timing->HImageSize = (((raw_dtd->raw_H_V_ImageSize & 0xf0)>>4)<<8) |
105                                                         raw_dtd->raw_HImageSize;
106     disp_timing->VImageSize = ((raw_dtd->raw_H_V_ImageSize & 0x0f)<<8) |
107                                                         raw_dtd->raw_VImageSize;
108     disp_timing->HBorder = raw_dtd->raw_HBorder;
109     disp_timing->VBorder = raw_dtd->raw_VBorder;
110     disp_timing->Flags = raw_dtd->raw_Flags;
111     return ZX_OK;
112 }
113 
edid_parse_display_timing(const uint8_t * edid_buf,detailed_timing_t * raw_dtd,disp_timing_t * disp_timing,uint8_t num_dtd)114 zx_status_t edid_parse_display_timing(const uint8_t* edid_buf, detailed_timing_t* raw_dtd,
115                                                     disp_timing_t* disp_timing, uint8_t num_dtd) {
116     const uint8_t* start_ext;
117     const uint8_t* start_dtd;
118     uint8_t* s_r_dtd = (uint8_t*) raw_dtd;
119 
120     if (!edid_has_extension(edid_buf)) {
121         return ZX_ERR_INVALID_ARGS;
122     }
123 
124     // It has extension. Read from start of DTD until you hit 00 00
125     start_ext = &edid_buf[128];
126 
127     if (start_ext[0] != 0x2) {
128         zxlogf(ERROR, "%s: Unknown tag! %d\n", __FUNCTION__, start_ext[0]);
129         return ZX_ERR_WRONG_TYPE;
130     }
131 
132     if (start_ext[2] == 0) {
133         zxlogf(ERROR, "%s: Invalid DTD pointer! 0x%x\n", __FUNCTION__, start_ext[2]);
134         return ZX_ERR_WRONG_TYPE;
135     }
136 
137     start_dtd = &start_ext[0] + start_ext[2];
138 
139 
140     for (int i = 0; i < num_dtd; i++) {
141         // populate raw structure first
142         memcpy(s_r_dtd, start_dtd, 18);
143 
144         disp_timing[i].pixel_clk = raw_dtd[i].raw_pixel_clk[1] << 8 |
145                                                         raw_dtd[i].raw_pixel_clk[0];
146         disp_timing[i].HActive = (((raw_dtd[i].raw_Hact_HBlank & 0xf0)>>4) << 8) |
147                                                         raw_dtd[i].raw_Hact;
148         disp_timing[i].HBlanking = ((raw_dtd[i].raw_Hact_HBlank & 0x0f) << 8) |
149                                                         raw_dtd[i].raw_HBlank;
150         disp_timing[i].VActive = (((raw_dtd[i].raw_Vact_VBlank & 0xf0)>>4) << 8) |
151                                                         raw_dtd[i].raw_Vact;
152         disp_timing[i].VBlanking = ((raw_dtd[i].raw_Vact_VBlank & 0x0f) << 8) |
153                                                         raw_dtd[i].raw_VBlank;
154         disp_timing[i].HSyncOffset = (((raw_dtd[i].raw_HSync_VSync_OFF_PW & 0xc0)>>6) << 8) |
155                                                         raw_dtd[i].raw_HSyncOff;
156         disp_timing[i].HSyncPulseWidth = (((raw_dtd[i].raw_HSync_VSync_OFF_PW & 0x30)>>4) << 8) |
157                                                         raw_dtd[i].raw_HSyncPW;
158         disp_timing[i].VSyncOffset = (((raw_dtd[i].raw_HSync_VSync_OFF_PW & 0x0c)>>2) << 4) |
159                                                         (raw_dtd[i].raw_VSyncOff_VSyncPW & 0xf0)>>4;
160         disp_timing[i].VSyncPulseWidth = ((raw_dtd[i].raw_HSync_VSync_OFF_PW & 0x03) << 4) |
161                                                         (raw_dtd[i].raw_VSyncOff_VSyncPW & 0x0f);
162         disp_timing[i].HImageSize = (((raw_dtd[i].raw_H_V_ImageSize & 0xf0)>>4)<<8) |
163                                                         raw_dtd[i].raw_HImageSize;
164         disp_timing[i].VImageSize = ((raw_dtd[i].raw_H_V_ImageSize & 0x0f)<<8) |
165                                                         raw_dtd[i].raw_VImageSize;
166         disp_timing[i].HBorder = raw_dtd[i].raw_HBorder;
167         disp_timing[i].VBorder = raw_dtd[i].raw_VBorder;
168         disp_timing[i].Flags = raw_dtd[i].raw_Flags;
169         s_r_dtd += 18;
170         start_dtd += 18;
171     }
172     return ZX_OK;
173 }
174