blob: 35071341574e4270adcb880ecb171b15f514b7a3 [file] [log] [blame]
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -07001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 * Copyright (C) 2012, Code Aurora Forum. All rights reserved.
4 *
5 * Not a Contribution, Apache license notifications and license are
6 * retained for attribution purposes only.
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
Naseer Ahmed72cf9762012-07-21 12:17:13 -070021#define DEBUG 0
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -070022#include <ctype.h>
Naseer Ahmed72cf9762012-07-21 12:17:13 -070023#include <fcntl.h>
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -070024#include <media/IAudioPolicyService.h>
25#include <media/AudioSystem.h>
26#include <utils/threads.h>
27#include <utils/Errors.h>
28#include <utils/Log.h>
29
30#include <linux/msm_mdp.h>
31#include <linux/fb.h>
32#include <sys/ioctl.h>
33#include <sys/poll.h>
Naseer Ahmed72cf9762012-07-21 12:17:13 -070034#include <sys/resource.h>
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -070035#include <cutils/properties.h>
36#include "hwc_utils.h"
Naseer Ahmed72cf9762012-07-21 12:17:13 -070037#include "hwc_external.h"
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -070038
39namespace qhwc {
40
41
42#define DEVICE_ROOT "/sys/devices/virtual/graphics"
43#define DEVICE_NODE "fb1"
44
45#define SYSFS_CONNECTED DEVICE_ROOT "/" DEVICE_NODE "/connected"
46#define SYSFS_EDID_MODES DEVICE_ROOT "/" DEVICE_NODE "/edid_modes"
47#define SYSFS_HPD DEVICE_ROOT "/" DEVICE_NODE "/hpd"
48
49
Naseer Ahmed72cf9762012-07-21 12:17:13 -070050ExternalDisplay::ExternalDisplay(hwc_context_t* ctx):fd(-1),
51 mCurrentID(-1), mHwcContext(ctx)
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -070052{
53 //Enable HPD for HDMI
54 writeHPDOption(1);
55}
56
Naseer Ahmed72cf9762012-07-21 12:17:13 -070057ExternalDisplay::~ExternalDisplay()
58{
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -070059 if (fd > 0)
60 close(fd);
61}
62
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -070063struct disp_mode_timing_type {
64 int video_format;
65
66 int active_h;
67 int active_v;
68
69 int front_porch_h;
70 int pulse_width_h;
71 int back_porch_h;
72
73 int front_porch_v;
74 int pulse_width_v;
75 int back_porch_v;
76
77 int pixel_freq;
78 bool interlaced;
79
80 void set_info(struct fb_var_screeninfo &info) const;
81};
82
83void disp_mode_timing_type::set_info(struct fb_var_screeninfo &info) const
84{
85 info.reserved[0] = 0;
86 info.reserved[1] = 0;
87 info.reserved[2] = 0;
88 info.reserved[3] = video_format;
89
90 info.xoffset = 0;
91 info.yoffset = 0;
92 info.xres = active_h;
93 info.yres = active_v;
94
95 info.pixclock = pixel_freq*1000;
96 info.vmode = interlaced ? FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED;
97
98 info.right_margin = front_porch_h;
99 info.hsync_len = pulse_width_h;
100 info.left_margin = back_porch_h;
101 info.lower_margin = front_porch_v;
102 info.vsync_len = pulse_width_v;
103 info.upper_margin = back_porch_v;
104}
105
106/* Video formates supported by the HDMI Standard */
107/* Indicates the resolution, pix clock and the aspect ratio */
108#define m640x480p60_4_3 1
109#define m720x480p60_4_3 2
110#define m720x480p60_16_9 3
111#define m1280x720p60_16_9 4
112#define m1920x1080i60_16_9 5
113#define m1440x480i60_4_3 6
114#define m1440x480i60_16_9 7
115#define m1920x1080p60_16_9 16
116#define m720x576p50_4_3 17
117#define m720x576p50_16_9 18
118#define m1280x720p50_16_9 19
119#define m1440x576i50_4_3 21
120#define m1440x576i50_16_9 22
121#define m1920x1080p50_16_9 31
122#define m1920x1080p24_16_9 32
123#define m1920x1080p25_16_9 33
124#define m1920x1080p30_16_9 34
125
126static struct disp_mode_timing_type supported_video_mode_lut[] = {
127 {m640x480p60_4_3, 640, 480, 16, 96, 48, 10, 2, 33, 25200, false},
128 {m720x480p60_4_3, 720, 480, 16, 62, 60, 9, 6, 30, 27030, false},
129 {m720x480p60_16_9, 720, 480, 16, 62, 60, 9, 6, 30, 27030, false},
130 {m1280x720p60_16_9, 1280, 720, 110, 40, 220, 5, 5, 20, 74250, false},
131 {m1920x1080i60_16_9, 1920, 540, 88, 44, 148, 2, 5, 5, 74250, false},
132 {m1440x480i60_4_3, 1440, 240, 38, 124, 114, 4, 3, 15, 27000, true},
133 {m1440x480i60_16_9, 1440, 240, 38, 124, 114, 4, 3, 15, 27000, true},
134 {m1920x1080p60_16_9, 1920, 1080, 88, 44, 148, 4, 5, 36, 148500, false},
135 {m720x576p50_4_3, 720, 576, 12, 64, 68, 5, 5, 39, 27000, false},
136 {m720x576p50_16_9, 720, 576, 12, 64, 68, 5, 5, 39, 27000, false},
137 {m1280x720p50_16_9, 1280, 720, 440, 40, 220, 5, 5, 20, 74250, false},
138 {m1440x576i50_4_3, 1440, 288, 24, 126, 138, 2, 3, 19, 27000, true},
139 {m1440x576i50_16_9, 1440, 288, 24, 126, 138, 2, 3, 19, 27000, true},
140 {m1920x1080p50_16_9, 1920, 1080, 528, 44, 148, 4, 5, 36, 148500, false},
141 {m1920x1080p24_16_9, 1920, 1080, 638, 44, 148, 4, 5, 36, 74250, false},
142 {m1920x1080p25_16_9, 1920, 1080, 528, 44, 148, 4, 5, 36, 74250, false},
143 {m1920x1080p30_16_9, 1920, 1080, 88, 44, 148, 4, 5, 36, 74250, false},
144};
Naseer Ahmed72cf9762012-07-21 12:17:13 -0700145
146int ExternalDisplay::parseResolution(char* edidStr, int* edidModes, int len)
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -0700147{
148 char delim = ',';
149 int count = 0;
150 char *start, *end;
151 // EDIDs are string delimited by ','
152 // Ex: 16,4,5,3,32,34,1
153 // Parse this string to get mode(int)
154 start = (char*) edidStr;
155 for(int i=0; i<len; i++) {
156 edidModes[i] = (int) strtol(start, &end, 10);
157 if(*end != delim) {
158 // return as we reached end of string
159 return count;
160 }
161 start = end+1;
162 count++;
163 }
164 return count;
165}
Naseer Ahmed72cf9762012-07-21 12:17:13 -0700166bool ExternalDisplay::readResolution()
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -0700167{
168 int hdmiEDIDFile = open(SYSFS_EDID_MODES, O_RDONLY, 0);
169 int len = -1;
170
171 memset(mEDIDs, 0, sizeof(mEDIDs));
172 memset(mEDIDModes, 0, sizeof(mEDIDModes));
173 mModeCount = 0;
174 if (hdmiEDIDFile < 0) {
Naseer Ahmed72cf9762012-07-21 12:17:13 -0700175 ALOGD_IF(DEBUG, "%s: edid_modes file '%s' not found",
176 __FUNCTION__, SYSFS_EDID_MODES);
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -0700177 return false;
178 } else {
179 len = read(hdmiEDIDFile, mEDIDs, sizeof(mEDIDs)-1);
Naseer Ahmed72cf9762012-07-21 12:17:13 -0700180 ALOGD_IF(DEBUG, "%s: EDID string: %s length = %d",
181 __FUNCTION__, mEDIDs, len);
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -0700182 if ( len <= 0) {
Naseer Ahmed72cf9762012-07-21 12:17:13 -0700183 ALOGD_IF(DEBUG, "%s: edid_modes file empty '%s'",
184 __FUNCTION__, SYSFS_EDID_MODES);
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -0700185 }
186 else {
187 while (len > 1 && isspace(mEDIDs[len-1]))
188 --len;
189 mEDIDs[len] = 0;
190 }
191 }
192 close(hdmiEDIDFile);
193 if(len > 0) {
194 // GEt EDID modes from the EDID strings
195 mModeCount = parseResolution(mEDIDs, mEDIDModes, len);
Naseer Ahmed72cf9762012-07-21 12:17:13 -0700196 ALOGD_IF(DEBUG, "%s: mModeCount = %d", __FUNCTION__,
197 mModeCount);
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -0700198 }
199
200 return (strlen(mEDIDs) > 0);
201}
202
Naseer Ahmed72cf9762012-07-21 12:17:13 -0700203bool ExternalDisplay::openFramebuffer()
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -0700204{
205 if (fd == -1) {
206 fd = open("/dev/graphics/fb1", O_RDWR);
207 if (fd < 0)
Naseer Ahmed72cf9762012-07-21 12:17:13 -0700208 ALOGD_IF(DEBUG, "%s: /dev/graphics/fb1 not available"
209 "\n", __FUNCTION__);
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -0700210 }
211 return (fd > 0);
212}
213
214
Naseer Ahmed72cf9762012-07-21 12:17:13 -0700215int ExternalDisplay::getModeOrder(int mode)
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -0700216{
217 switch (mode) {
218 default:
219 case m1440x480i60_4_3:
220 return 1; // 480i 4:3
221 case m1440x480i60_16_9:
222 return 2; // 480i 16:9
223 case m1440x576i50_4_3:
224 return 3; // i576i 4:3
225 case m1440x576i50_16_9:
226 return 4; // 576i 16:9
227 case m640x480p60_4_3:
228 return 5; // 640x480 4:3
229 case m720x480p60_4_3:
230 return 6; // 480p 4:3
231 case m720x480p60_16_9:
232 return 7; // 480p 16:9
233 case m720x576p50_4_3:
234 return 8; // 576p 4:3
235 case m720x576p50_16_9:
236 return 9; // 576p 16:9
237 case m1920x1080i60_16_9:
238 return 10; // 1080i 16:9
239 case m1280x720p50_16_9:
240 return 11; // 720p@50Hz
241 case m1280x720p60_16_9:
242 return 12; // 720p@60Hz
243 case m1920x1080p24_16_9:
244 return 13; //1080p@24Hz
245 case m1920x1080p25_16_9:
246 return 14; //108-p@25Hz
247 case m1920x1080p30_16_9:
248 return 15; //1080p@30Hz
249 case m1920x1080p50_16_9:
250 return 16; //1080p@50Hz
251 case m1920x1080p60_16_9:
252 return 17; //1080p@60Hz
Naseer Ahmed72cf9762012-07-21 12:17:13 -0700253 }
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -0700254}
255
256// Get the best mode for the current HD TV
Naseer Ahmed72cf9762012-07-21 12:17:13 -0700257int ExternalDisplay::getBestMode() {
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -0700258 int bestOrder = 0;
259 int bestMode = m640x480p60_4_3;
260
261 // for all the edid read, get the best mode
262 for(int i = 0; i < mModeCount; i++) {
263 int mode = mEDIDModes[i];
264 int order = getModeOrder(mode);
265 if (order > bestOrder) {
266 bestOrder = order;
267 bestMode = mode;
268 }
269 }
270 return bestMode;
Naseer Ahmed72cf9762012-07-21 12:17:13 -0700271}
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -0700272
Naseer Ahmed72cf9762012-07-21 12:17:13 -0700273inline bool ExternalDisplay::isValidMode(int ID)
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -0700274{
275 return ((ID >= m640x480p60_4_3) && (ID <= m1920x1080p30_16_9));
276}
277
Naseer Ahmed72cf9762012-07-21 12:17:13 -0700278void ExternalDisplay::setResolution(int ID)
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -0700279{
280 struct fb_var_screeninfo info;
281 if (!openFramebuffer())
282 return;
283 //If its a valid mode and its a new ID - update var_screeninfo
284 if ((isValidMode(ID)) && mCurrentID != ID) {
285 const struct disp_mode_timing_type *mode =
Naseer Ahmed72cf9762012-07-21 12:17:13 -0700286 &supported_video_mode_lut[0];
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -0700287 unsigned count = sizeof(supported_video_mode_lut)/sizeof
Naseer Ahmed72cf9762012-07-21 12:17:13 -0700288 (*supported_video_mode_lut);
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -0700289 for (unsigned int i = 0; i < count; ++i) {
290 const struct disp_mode_timing_type *cur =
Naseer Ahmed72cf9762012-07-21 12:17:13 -0700291 &supported_video_mode_lut[i];
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -0700292 if (cur->video_format == ID)
293 mode = cur;
294 }
295 ioctl(fd, FBIOGET_VSCREENINFO, &info);
Naseer Ahmed72cf9762012-07-21 12:17:13 -0700296 ALOGD_IF(DEBUG, "%s: GET Info<ID=%d %dx%d (%d,%d,%d),"
297 "(%d,%d,%d) %dMHz>", __FUNCTION__,
298 info.reserved[3], info.xres, info.yres,
299 info.right_margin, info.hsync_len, info.left_margin,
300 info.lower_margin, info.vsync_len, info.upper_margin,
301 info.pixclock/1000/1000);
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -0700302 mode->set_info(info);
Naseer Ahmed72cf9762012-07-21 12:17:13 -0700303 ALOGD_IF(DEBUG, "%s: SET Info<ID=%d => Info<ID=%d %dx%d"
304 "(%d,%d,%d), (%d,%d,%d) %dMHz>", __FUNCTION__, ID,
305 info.reserved[3], info.xres, info.yres,
306 info.right_margin, info.hsync_len, info.left_margin,
307 info.lower_margin, info.vsync_len, info.upper_margin,
308 info.pixclock/1000/1000);
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -0700309 info.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_ALL | FB_ACTIVATE_FORCE;
310 ioctl(fd, FBIOPUT_VSCREENINFO, &info);
311 mCurrentID = ID;
312 }
313 //Powerup
314 ioctl(fd, FBIOBLANK, FB_BLANK_UNBLANK);
315 ioctl(fd, FBIOGET_VSCREENINFO, &info);
316 //Pan_Display
317 ioctl(fd, FBIOPAN_DISPLAY, &info);
318 property_set("hw.hdmiON", "1");
319}
320
321
Naseer Ahmed72cf9762012-07-21 12:17:13 -0700322int ExternalDisplay::getExternalDisplay() const
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -0700323{
Naseer Ahmed72cf9762012-07-21 12:17:13 -0700324 return mExternalDisplay;
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -0700325}
326
Naseer Ahmed72cf9762012-07-21 12:17:13 -0700327void ExternalDisplay::setExternalDisplayStatus(int connected)
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -0700328{
329
330 hwc_context_t* ctx = mHwcContext;
331 if(ctx) {
Naseer Ahmed72cf9762012-07-21 12:17:13 -0700332 ALOGD_IF(DEBUG, "%s: status = %d", __FUNCTION__,
333 connected);
334 if(connected) {
335 readResolution();
336 //Get the best mode and set
337 // TODO: DO NOT call this for WFD
338 setResolution(getBestMode());
339 } else {
340 close(fd);
341 }
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -0700342 // Store the external display
Naseer Ahmed72cf9762012-07-21 12:17:13 -0700343 mExternalDisplay = connected;
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -0700344 //Invalidate
345 hwc_procs* proc = (hwc_procs*)ctx->device.reserved_proc[0];
346 if(!proc) {
Naseer Ahmed72cf9762012-07-21 12:17:13 -0700347 ALOGD_IF(DEBUG, "%s: HWC proc not registered",
348 __FUNCTION__);
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -0700349 } else {
350 /* Trigger redraw */
Naseer Ahmed72cf9762012-07-21 12:17:13 -0700351 ALOGD_IF(DEBUG, "%s: Invalidate !!", __FUNCTION__);
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -0700352 proc->invalidate(proc);
353 }
354 }
355 return;
356}
357
Naseer Ahmed72cf9762012-07-21 12:17:13 -0700358bool ExternalDisplay::writeHPDOption(int userOption) const
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -0700359{
360 bool ret = true;
361 int hdmiHPDFile = open(SYSFS_HPD,O_RDWR, 0);
362 if (hdmiHPDFile < 0) {
Naseer Ahmed72cf9762012-07-21 12:17:13 -0700363 ALOGD_IF(DEBUG, "%s: state file '%s' not found : ret%d"
364 "err str: %s", __FUNCTION__, SYSFS_HPD, hdmiHPDFile, strerror(errno));
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -0700365 ret = false;
366 } else {
367 int err = -1;
Naseer Ahmed72cf9762012-07-21 12:17:13 -0700368 ALOGD_IF(DEBUG, "%s: option = %d", __FUNCTION__,
369 userOption);
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -0700370 if(userOption)
371 err = write(hdmiHPDFile, "1", 2);
372 else
373 err = write(hdmiHPDFile, "0" , 2);
374 if (err <= 0) {
Naseer Ahmed72cf9762012-07-21 12:17:13 -0700375 ALOGD_IF(DEBUG, "%s: file write failed '%s'",
376 __FUNCTION__, SYSFS_HPD);
Naseer Ahmed0c8b7b52012-07-20 09:06:13 -0700377 ret = false;
378 }
379 close(hdmiHPDFile);
380 }
381 return ret;
382}
383};
384