blob: 2a5ec361b07f5f9980df6d705c792e9f01fe4a3f [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
21#define EXT_OBSERVER_DEBUG 0
22#include <ctype.h>
23#include <binder/IPCThreadState.h>
24#include <binder/IServiceManager.h>
25#include <media/IAudioPolicyService.h>
26#include <media/AudioSystem.h>
27#include <utils/threads.h>
28#include <utils/Errors.h>
29#include <utils/Log.h>
30
31#include <linux/msm_mdp.h>
32#include <linux/fb.h>
33#include <sys/ioctl.h>
34#include <sys/poll.h>
35
36#include <hardware_legacy/uevent.h>
37#include <cutils/properties.h>
38#include "hwc_utils.h"
39#include "hwc_ext_observer.h"
40
41namespace qhwc {
42
43
44#define DEVICE_ROOT "/sys/devices/virtual/graphics"
45#define DEVICE_NODE "fb1"
46
47#define SYSFS_CONNECTED DEVICE_ROOT "/" DEVICE_NODE "/connected"
48#define SYSFS_EDID_MODES DEVICE_ROOT "/" DEVICE_NODE "/edid_modes"
49#define SYSFS_HPD DEVICE_ROOT "/" DEVICE_NODE "/hpd"
50
51
52android::sp<ExtDisplayObserver> ExtDisplayObserver::
53 sExtDisplayObserverInstance(0);
54
55ExtDisplayObserver::ExtDisplayObserver() : Thread(false),
56 fd(-1), mCurrentID(-1), mHwcContext(NULL)
57{
58 //Enable HPD for HDMI
59 writeHPDOption(1);
60}
61
62ExtDisplayObserver::~ExtDisplayObserver() {
63 if (fd > 0)
64 close(fd);
65}
66
67ExtDisplayObserver *ExtDisplayObserver::getInstance() {
68 ALOGD_IF(EXT_OBSERVER_DEBUG, "%s ", __FUNCTION__);
69 if(sExtDisplayObserverInstance.get() == NULL)
70 sExtDisplayObserverInstance = new ExtDisplayObserver();
71 return sExtDisplayObserverInstance.get();
72}
73
74void ExtDisplayObserver::setHwcContext(hwc_context_t* hwcCtx) {
75 ALOGD_IF(EXT_OBSERVER_DEBUG, "%s", __FUNCTION__);
76 if(hwcCtx) {
77 mHwcContext = hwcCtx;
78 }
79 return;
80}
81void ExtDisplayObserver::onFirstRef() {
82 ALOGD_IF(EXT_OBSERVER_DEBUG, "%s", __FUNCTION__);
83 run("ExtDisplayObserver", ANDROID_PRIORITY_DISPLAY);
84}
85
86int ExtDisplayObserver::readyToRun() {
87 //Initialize the uevent
88 uevent_init();
89 ALOGD_IF(EXT_OBSERVER_DEBUG, "%s: success", __FUNCTION__);
90 return android::NO_ERROR;
91}
92
93void ExtDisplayObserver::handleUEvent(char* str){
94 int connected = 0;
95 // TODO: check for fb2(WFD) driver also
96 if(!strcasestr(str, DEVICE_NODE))
97 {
98 ALOGD_IF(EXT_OBSERVER_DEBUG, "%s: Not Ext Disp Event ", __FUNCTION__);
99 return;
100 }
101 // Event will be of the form:
102 // change@/devices/virtual/graphics/fb1 ACTION=change
103 // DEVPATH=/devices/virtual/graphics/fb1
104 // SUBSYSTEM=graphics HDCP_STATE=FAIL MAJOR=29
105 // for now just parse the online or offline are important for us.
106 if(!(strncmp(str,"online@",strlen("online@")))) {
107 ALOGD_IF(EXT_OBSERVER_DEBUG, "%s: external disp online", __FUNCTION__);
108 connected = 1;
109 readResolution();
110 //Get the best mode and set
111 // TODO: DO NOT call this for WFD
112 setResolution(getBestMode());
113 } else if(!(strncmp(str,"offline@",strlen("offline@")))) {
114 ALOGD_IF(EXT_OBSERVER_DEBUG, "%s: external disp online", __FUNCTION__);
115 connected = 0;
116 close(fd);
117 }
118 setExternalDisplayStatus(connected);
119}
120
121bool ExtDisplayObserver::threadLoop()
122{
123 static char uEventString[1024];
124 memset(uEventString, 0, sizeof(uEventString));
125 int count = uevent_next_event(uEventString, sizeof(uEventString));
126 if(count) {
127 ALOGD_IF(EXT_OBSERVER_DEBUG, "%s: UeventString: %s len = %d",
128 __FUNCTION__, uEventString, count);
129 handleUEvent(uEventString);
130 }
131 return true;
132}
133
134struct disp_mode_timing_type {
135 int video_format;
136
137 int active_h;
138 int active_v;
139
140 int front_porch_h;
141 int pulse_width_h;
142 int back_porch_h;
143
144 int front_porch_v;
145 int pulse_width_v;
146 int back_porch_v;
147
148 int pixel_freq;
149 bool interlaced;
150
151 void set_info(struct fb_var_screeninfo &info) const;
152};
153
154void disp_mode_timing_type::set_info(struct fb_var_screeninfo &info) const
155{
156 info.reserved[0] = 0;
157 info.reserved[1] = 0;
158 info.reserved[2] = 0;
159 info.reserved[3] = video_format;
160
161 info.xoffset = 0;
162 info.yoffset = 0;
163 info.xres = active_h;
164 info.yres = active_v;
165
166 info.pixclock = pixel_freq*1000;
167 info.vmode = interlaced ? FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED;
168
169 info.right_margin = front_porch_h;
170 info.hsync_len = pulse_width_h;
171 info.left_margin = back_porch_h;
172 info.lower_margin = front_porch_v;
173 info.vsync_len = pulse_width_v;
174 info.upper_margin = back_porch_v;
175}
176
177/* Video formates supported by the HDMI Standard */
178/* Indicates the resolution, pix clock and the aspect ratio */
179#define m640x480p60_4_3 1
180#define m720x480p60_4_3 2
181#define m720x480p60_16_9 3
182#define m1280x720p60_16_9 4
183#define m1920x1080i60_16_9 5
184#define m1440x480i60_4_3 6
185#define m1440x480i60_16_9 7
186#define m1920x1080p60_16_9 16
187#define m720x576p50_4_3 17
188#define m720x576p50_16_9 18
189#define m1280x720p50_16_9 19
190#define m1440x576i50_4_3 21
191#define m1440x576i50_16_9 22
192#define m1920x1080p50_16_9 31
193#define m1920x1080p24_16_9 32
194#define m1920x1080p25_16_9 33
195#define m1920x1080p30_16_9 34
196
197static struct disp_mode_timing_type supported_video_mode_lut[] = {
198 {m640x480p60_4_3, 640, 480, 16, 96, 48, 10, 2, 33, 25200, false},
199 {m720x480p60_4_3, 720, 480, 16, 62, 60, 9, 6, 30, 27030, false},
200 {m720x480p60_16_9, 720, 480, 16, 62, 60, 9, 6, 30, 27030, false},
201 {m1280x720p60_16_9, 1280, 720, 110, 40, 220, 5, 5, 20, 74250, false},
202 {m1920x1080i60_16_9, 1920, 540, 88, 44, 148, 2, 5, 5, 74250, false},
203 {m1440x480i60_4_3, 1440, 240, 38, 124, 114, 4, 3, 15, 27000, true},
204 {m1440x480i60_16_9, 1440, 240, 38, 124, 114, 4, 3, 15, 27000, true},
205 {m1920x1080p60_16_9, 1920, 1080, 88, 44, 148, 4, 5, 36, 148500, false},
206 {m720x576p50_4_3, 720, 576, 12, 64, 68, 5, 5, 39, 27000, false},
207 {m720x576p50_16_9, 720, 576, 12, 64, 68, 5, 5, 39, 27000, false},
208 {m1280x720p50_16_9, 1280, 720, 440, 40, 220, 5, 5, 20, 74250, false},
209 {m1440x576i50_4_3, 1440, 288, 24, 126, 138, 2, 3, 19, 27000, true},
210 {m1440x576i50_16_9, 1440, 288, 24, 126, 138, 2, 3, 19, 27000, true},
211 {m1920x1080p50_16_9, 1920, 1080, 528, 44, 148, 4, 5, 36, 148500, false},
212 {m1920x1080p24_16_9, 1920, 1080, 638, 44, 148, 4, 5, 36, 74250, false},
213 {m1920x1080p25_16_9, 1920, 1080, 528, 44, 148, 4, 5, 36, 74250, false},
214 {m1920x1080p30_16_9, 1920, 1080, 88, 44, 148, 4, 5, 36, 74250, false},
215};
216int ExtDisplayObserver::parseResolution(char* edidStr, int* edidModes, int len)
217{
218 char delim = ',';
219 int count = 0;
220 char *start, *end;
221 // EDIDs are string delimited by ','
222 // Ex: 16,4,5,3,32,34,1
223 // Parse this string to get mode(int)
224 start = (char*) edidStr;
225 for(int i=0; i<len; i++) {
226 edidModes[i] = (int) strtol(start, &end, 10);
227 if(*end != delim) {
228 // return as we reached end of string
229 return count;
230 }
231 start = end+1;
232 count++;
233 }
234 return count;
235}
236bool ExtDisplayObserver::readResolution()
237{
238 int hdmiEDIDFile = open(SYSFS_EDID_MODES, O_RDONLY, 0);
239 int len = -1;
240
241 memset(mEDIDs, 0, sizeof(mEDIDs));
242 memset(mEDIDModes, 0, sizeof(mEDIDModes));
243 mModeCount = 0;
244 if (hdmiEDIDFile < 0) {
245 ALOGD_IF(EXT_OBSERVER_DEBUG, "%s: edid_modes file '%s' not found",
246 __FUNCTION__, SYSFS_EDID_MODES);
247 return false;
248 } else {
249 len = read(hdmiEDIDFile, mEDIDs, sizeof(mEDIDs)-1);
250 ALOGD_IF(EXT_OBSERVER_DEBUG, "%s: EDID string: %s length = %d",
251 __FUNCTION__, mEDIDs, len);
252 if ( len <= 0) {
253 ALOGD_IF(EXT_OBSERVER_DEBUG, "%s: edid_modes file empty '%s'",
254 __FUNCTION__, SYSFS_EDID_MODES);
255 }
256 else {
257 while (len > 1 && isspace(mEDIDs[len-1]))
258 --len;
259 mEDIDs[len] = 0;
260 }
261 }
262 close(hdmiEDIDFile);
263 if(len > 0) {
264 // GEt EDID modes from the EDID strings
265 mModeCount = parseResolution(mEDIDs, mEDIDModes, len);
266 ALOGD_IF(EXT_OBSERVER_DEBUG, "%s: mModeCount = %d", __FUNCTION__,
267 mModeCount);
268 }
269
270 return (strlen(mEDIDs) > 0);
271}
272
273bool ExtDisplayObserver::openFramebuffer()
274{
275 if (fd == -1) {
276 fd = open("/dev/graphics/fb1", O_RDWR);
277 if (fd < 0)
278 ALOGD_IF(EXT_OBSERVER_DEBUG, "%s: /dev/graphics/fb1 not available"
279 "\n", __FUNCTION__);
280 }
281 return (fd > 0);
282}
283
284
285int ExtDisplayObserver::getModeOrder(int mode)
286{
287 switch (mode) {
288 default:
289 case m1440x480i60_4_3:
290 return 1; // 480i 4:3
291 case m1440x480i60_16_9:
292 return 2; // 480i 16:9
293 case m1440x576i50_4_3:
294 return 3; // i576i 4:3
295 case m1440x576i50_16_9:
296 return 4; // 576i 16:9
297 case m640x480p60_4_3:
298 return 5; // 640x480 4:3
299 case m720x480p60_4_3:
300 return 6; // 480p 4:3
301 case m720x480p60_16_9:
302 return 7; // 480p 16:9
303 case m720x576p50_4_3:
304 return 8; // 576p 4:3
305 case m720x576p50_16_9:
306 return 9; // 576p 16:9
307 case m1920x1080i60_16_9:
308 return 10; // 1080i 16:9
309 case m1280x720p50_16_9:
310 return 11; // 720p@50Hz
311 case m1280x720p60_16_9:
312 return 12; // 720p@60Hz
313 case m1920x1080p24_16_9:
314 return 13; //1080p@24Hz
315 case m1920x1080p25_16_9:
316 return 14; //108-p@25Hz
317 case m1920x1080p30_16_9:
318 return 15; //1080p@30Hz
319 case m1920x1080p50_16_9:
320 return 16; //1080p@50Hz
321 case m1920x1080p60_16_9:
322 return 17; //1080p@60Hz
323 }
324}
325
326// Get the best mode for the current HD TV
327int ExtDisplayObserver::getBestMode() {
328 int bestOrder = 0;
329 int bestMode = m640x480p60_4_3;
330
331 // for all the edid read, get the best mode
332 for(int i = 0; i < mModeCount; i++) {
333 int mode = mEDIDModes[i];
334 int order = getModeOrder(mode);
335 if (order > bestOrder) {
336 bestOrder = order;
337 bestMode = mode;
338 }
339 }
340 return bestMode;
341 }
342
343inline bool ExtDisplayObserver::isValidMode(int ID)
344{
345 return ((ID >= m640x480p60_4_3) && (ID <= m1920x1080p30_16_9));
346}
347
348void ExtDisplayObserver::setResolution(int ID)
349{
350 struct fb_var_screeninfo info;
351 if (!openFramebuffer())
352 return;
353 //If its a valid mode and its a new ID - update var_screeninfo
354 if ((isValidMode(ID)) && mCurrentID != ID) {
355 const struct disp_mode_timing_type *mode =
356 &supported_video_mode_lut[0];
357 unsigned count = sizeof(supported_video_mode_lut)/sizeof
358 (*supported_video_mode_lut);
359 for (unsigned int i = 0; i < count; ++i) {
360 const struct disp_mode_timing_type *cur =
361 &supported_video_mode_lut[i];
362 if (cur->video_format == ID)
363 mode = cur;
364 }
365 ioctl(fd, FBIOGET_VSCREENINFO, &info);
366 ALOGD_IF(EXT_OBSERVER_DEBUG, "%s: GET Info<ID=%d %dx%d (%d,%d,%d),"
367 "(%d,%d,%d) %dMHz>", __FUNCTION__,
368 info.reserved[3], info.xres, info.yres,
369 info.right_margin, info.hsync_len, info.left_margin,
370 info.lower_margin, info.vsync_len, info.upper_margin,
371 info.pixclock/1000/1000);
372 mode->set_info(info);
373 ALOGD_IF(EXT_OBSERVER_DEBUG, "%s: SET Info<ID=%d => Info<ID=%d %dx%d"
374 "(%d,%d,%d), (%d,%d,%d) %dMHz>", __FUNCTION__, ID,
375 info.reserved[3], info.xres, info.yres,
376 info.right_margin, info.hsync_len, info.left_margin,
377 info.lower_margin, info.vsync_len, info.upper_margin,
378 info.pixclock/1000/1000);
379 info.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_ALL | FB_ACTIVATE_FORCE;
380 ioctl(fd, FBIOPUT_VSCREENINFO, &info);
381 mCurrentID = ID;
382 }
383 //Powerup
384 ioctl(fd, FBIOBLANK, FB_BLANK_UNBLANK);
385 ioctl(fd, FBIOGET_VSCREENINFO, &info);
386 //Pan_Display
387 ioctl(fd, FBIOPAN_DISPLAY, &info);
388 property_set("hw.hdmiON", "1");
389}
390
391
392int ExtDisplayObserver::getExternalDisplay() const
393{
394 return mExternalDisplay;
395}
396
397void ExtDisplayObserver::setExternalDisplayStatus(int connected)
398{
399
400 hwc_context_t* ctx = mHwcContext;
401 if(ctx) {
402 ALOGD_IF(EXT_OBSERVER_DEBUG, "%s: status = %d", __FUNCTION__,
403 connected);
404 // Store the external display
405 mExternalDisplay = connected;//(external_display_type)value;
406 //Invalidate
407 hwc_procs* proc = (hwc_procs*)ctx->device.reserved_proc[0];
408 if(!proc) {
409 ALOGD_IF(EXT_OBSERVER_DEBUG, "%s: HWC proc not registered",
410 __FUNCTION__);
411 } else {
412 /* Trigger redraw */
413 ALOGD_IF(EXT_OBSERVER_DEBUG, "%s: Invalidate !!", __FUNCTION__);
414 proc->invalidate(proc);
415 }
416 }
417 return;
418}
419
420bool ExtDisplayObserver::writeHPDOption(int userOption) const
421{
422 bool ret = true;
423 int hdmiHPDFile = open(SYSFS_HPD,O_RDWR, 0);
424 if (hdmiHPDFile < 0) {
425 ALOGD_IF(EXT_OBSERVER_DEBUG, "%s: state file '%s' not found : ret%d"
426 "err str: %s", __FUNCTION__, SYSFS_HPD, hdmiHPDFile, strerror(errno));
427 ret = false;
428 } else {
429 int err = -1;
430 ALOGD_IF(EXT_OBSERVER_DEBUG, "%s: option = %d", __FUNCTION__,
431 userOption);
432 if(userOption)
433 err = write(hdmiHPDFile, "1", 2);
434 else
435 err = write(hdmiHPDFile, "0" , 2);
436 if (err <= 0) {
437 ALOGD_IF(EXT_OBSERVER_DEBUG, "%s: file write failed '%s'",
438 __FUNCTION__, SYSFS_HPD);
439 ret = false;
440 }
441 close(hdmiHPDFile);
442 }
443 return ret;
444}
445};
446