1 /*
2 * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
3
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above
10 * copyright notice, this list of conditions and the following
11 * disclaimer in the documentation and/or other materials provided
12 * with the distribution.
13 * * Neither the name of The Linux Foundation nor the names of its
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29 #include <cutils/log.h>
30 #include <linux/msm_mdp.h>
31 #include "mdp_version.h"
32 #include "qd_utils.h"
33
34 #define DEBUG 0
35
36 ANDROID_SINGLETON_STATIC_INSTANCE(qdutils::MDPVersion);
37 namespace qdutils {
38
39 #define TOKEN_PARAMS_DELIM "="
40
41 // chip variants have same major number and minor numbers usually vary
42 // for e.g., MDSS_MDP_HW_REV_101 is 0x10010000
43 // 1001 - major number
44 // 0000 - minor number
45 // 8x26 v1 minor number is 0000
46 // v2 minor number is 0001 etc..
47 #ifndef MDSS_MDP_HW_REV_100
48 #define MDSS_MDP_HW_REV_100 0x10000000 //8974 v1
49 #endif
50 #ifndef MDSS_MDP_HW_REV_101
51 #define MDSS_MDP_HW_REV_101 0x10010000 //8x26
52 #endif
53 #ifndef MDSS_MDP_HW_REV_102
54 #define MDSS_MDP_HW_REV_102 0x10020000 //8974 v2
55 #endif
56 #ifndef MDSS_MDP_HW_REV_103
57 #define MDSS_MDP_HW_REV_103 0x10030000 //8084
58 #endif
59 #ifndef MDSS_MDP_HW_REV_104
60 #define MDSS_MDP_HW_REV_104 0x10040000 //Unused
61 #endif
62 #ifndef MDSS_MDP_HW_REV_105
63 #define MDSS_MDP_HW_REV_105 0x10050000 //8994
64 #endif
65 #ifndef MDSS_MDP_HW_REV_106
66 #define MDSS_MDP_HW_REV_106 0x10060000 //8x16
67 #endif
68 #ifndef MDSS_MDP_HW_REV_107
69 #define MDSS_MDP_HW_REV_107 0x10070000 //Unused
70 #endif
71 #ifndef MDSS_MDP_HW_REV_108
72 #define MDSS_MDP_HW_REV_108 0x10080000 //8x39 & 8x36
73 #endif
74 #ifndef MDSS_MDP_HW_REV_109
75 #define MDSS_MDP_HW_REV_109 0x10090000 //8994 v2
76 #endif
77 #ifndef MDSS_MDP_HW_REV_110
78 #define MDSS_MDP_HW_REV_110 0x100a0000 //8992
79 #endif
80 #ifndef MDSS_MDP_HW_REV_200
81 #define MDSS_MDP_HW_REV_200 0x20000000 //8092
82 #endif
83 #ifndef MDSS_MDP_HW_REV_206
84 #define MDSS_MDP_HW_REV_206 0x20060000 //Future
85 #endif
86
MDPVersion()87 MDPVersion::MDPVersion()
88 {
89 mMDPVersion = MDSS_V5;
90 mMdpRev = 0;
91 mRGBPipes = 0;
92 mVGPipes = 0;
93 mDMAPipes = 0;
94 mFeatures = 0;
95 mMDPUpscale = 1;
96 mMDPDownscale = 1;
97 mMacroTileEnabled = false;
98 mLowBw = 0;
99 mHighBw = 0;
100 mSourceSplit = false;
101 mSourceSplitAlways = false;
102 mRGBHasNoScalar = false;
103 mRotDownscale = false;
104 mBlendStages = 4; //min no. of stages supported by MDP.
105
106 // this is the default limit of mixer unless driver reports it.
107 // For resolutions beyond this, we use dual mixer/ping pong split.
108 mMaxMixerWidth = 2048;
109
110 // Default width of MDSS SSPP. For layer resolutions beyond this, we drive
111 // using two SSPP's.
112 mMaxPipeWidth = 2048;
113
114 updatePanelInfo();
115
116 if(!updateSysFsInfo()) {
117 ALOGE("Unable to read display sysfs node");
118 }
119 if (mMdpRev == MDP_V3_0_4){
120 mMDPVersion = MDP_V3_0_4;
121 }
122 else if (mMdpRev == MDP_V3_0_5){
123 mMDPVersion = MDP_V3_0_5;
124 }
125
126 mHasOverlay = false;
127 if((mMDPVersion >= MDP_V4_0) ||
128 (mMDPVersion == MDP_V_UNKNOWN) ||
129 (mMDPVersion == MDP_V3_0_4) ||
130 (mMDPVersion == MDP_V3_0_5))
131 mHasOverlay = true;
132 if(!updateSplitInfo()) {
133 ALOGE("Unable to read display split node");
134 }
135 }
136
~MDPVersion()137 MDPVersion::~MDPVersion() {
138 close(mFd);
139 }
140
tokenizeParams(char * inputParams,const char * delim,char * tokenStr[],int * idx)141 int MDPVersion::tokenizeParams(char *inputParams, const char *delim,
142 char* tokenStr[], int *idx) {
143 char *tmp_token = NULL;
144 char *temp_ptr;
145 int index = 0;
146 if (!inputParams) {
147 return -1;
148 }
149 tmp_token = strtok_r(inputParams, delim, &temp_ptr);
150 while (tmp_token != NULL) {
151 tokenStr[index++] = tmp_token;
152 tmp_token = strtok_r(NULL, " ", &temp_ptr);
153 }
154 *idx = index;
155 return 0;
156 }
157 // This function reads the sysfs node to read the primary panel type
158 // and updates information accordingly
updatePanelInfo()159 void MDPVersion::updatePanelInfo() {
160 FILE *displayDeviceFP = NULL;
161 FILE *panelInfoNodeFP = NULL;
162 char fbType[MAX_FRAME_BUFFER_NAME_SIZE];
163 const char *strCmdPanel = "mipi dsi cmd panel";
164 const char *strVideoPanel = "mipi dsi video panel";
165 const char *strLVDSPanel = "lvds panel";
166 const char *strEDPPanel = "edp panel";
167
168 displayDeviceFP = fopen("/sys/class/graphics/fb0/msm_fb_type", "r");
169 if(displayDeviceFP){
170 fread(fbType, sizeof(char), MAX_FRAME_BUFFER_NAME_SIZE,
171 displayDeviceFP);
172 if(strncmp(fbType, strCmdPanel, strlen(strCmdPanel)) == 0) {
173 mPanelInfo.mType = MIPI_CMD_PANEL;
174 }
175 else if(strncmp(fbType, strVideoPanel, strlen(strVideoPanel)) == 0) {
176 mPanelInfo.mType = MIPI_VIDEO_PANEL;
177 }
178 else if(strncmp(fbType, strLVDSPanel, strlen(strLVDSPanel)) == 0) {
179 mPanelInfo.mType = LVDS_PANEL;
180 }
181 else if(strncmp(fbType, strEDPPanel, strlen(strEDPPanel)) == 0) {
182 mPanelInfo.mType = EDP_PANEL;
183 }
184 fclose(displayDeviceFP);
185 } else {
186 ALOGE("Unable to read Primary Panel Information");
187 }
188
189 panelInfoNodeFP = fopen("/sys/class/graphics/fb0/msm_fb_panel_info", "r");
190 if(panelInfoNodeFP){
191 size_t len = PAGE_SIZE;
192 ssize_t read;
193 char *readLine = (char *) malloc (len);
194 char property[PROPERTY_VALUE_MAX];
195 while((read = getline((char **)&readLine, &len,
196 panelInfoNodeFP)) != -1) {
197 int token_ct=0;
198 char *tokens[10];
199 memset(tokens, 0, sizeof(tokens));
200
201 if(!tokenizeParams(readLine, TOKEN_PARAMS_DELIM, tokens,
202 &token_ct)) {
203 if(!strncmp(tokens[0], "pu_en", strlen("pu_en"))) {
204 mPanelInfo.mPartialUpdateEnable = atoi(tokens[1]);
205 ALOGI("PartialUpdate status: %s",
206 mPanelInfo.mPartialUpdateEnable? "Enabled" :
207 "Disabled");
208 }
209 if(!strncmp(tokens[0], "xstart", strlen("xstart"))) {
210 mPanelInfo.mLeftAlign = atoi(tokens[1]);
211 ALOGI("Left Align: %d", mPanelInfo.mLeftAlign);
212 }
213 if(!strncmp(tokens[0], "walign", strlen("walign"))) {
214 mPanelInfo.mWidthAlign = atoi(tokens[1]);
215 ALOGI("Width Align: %d", mPanelInfo.mWidthAlign);
216 }
217 if(!strncmp(tokens[0], "ystart", strlen("ystart"))) {
218 mPanelInfo.mTopAlign = atoi(tokens[1]);
219 ALOGI("Top Align: %d", mPanelInfo.mTopAlign);
220 }
221 if(!strncmp(tokens[0], "halign", strlen("halign"))) {
222 mPanelInfo.mHeightAlign = atoi(tokens[1]);
223 ALOGI("Height Align: %d", mPanelInfo.mHeightAlign);
224 }
225 if(!strncmp(tokens[0], "min_w", strlen("min_w"))) {
226 mPanelInfo.mMinROIWidth = atoi(tokens[1]);
227 ALOGI("Min ROI Width: %d", mPanelInfo.mMinROIWidth);
228 }
229 if(!strncmp(tokens[0], "min_h", strlen("min_h"))) {
230 mPanelInfo.mMinROIHeight = atoi(tokens[1]);
231 ALOGI("Min ROI Height: %d", mPanelInfo.mMinROIHeight);
232 }
233 if(!strncmp(tokens[0], "roi_merge", strlen("roi_merge"))) {
234 mPanelInfo.mNeedsROIMerge = atoi(tokens[1]);
235 ALOGI("Needs ROI Merge: %d", mPanelInfo.mNeedsROIMerge);
236 }
237 if(!strncmp(tokens[0], "dyn_fps_en", strlen("dyn_fps_en"))) {
238 mPanelInfo.mDynFpsSupported = atoi(tokens[1]);
239 ALOGI("Dynamic Fps: %s", mPanelInfo.mDynFpsSupported ?
240 "Enabled" : "Disabled");
241 }
242 if(!strncmp(tokens[0], "min_fps", strlen("min_fps"))) {
243 mPanelInfo.mMinFps = atoi(tokens[1]);
244 ALOGI("Min Panel fps: %d", mPanelInfo.mMinFps);
245 }
246 if(!strncmp(tokens[0], "max_fps", strlen("max_fps"))) {
247 mPanelInfo.mMaxFps = atoi(tokens[1]);
248 ALOGI("Max Panel fps: %d", mPanelInfo.mMaxFps);
249 }
250 }
251 }
252 if((property_get("persist.hwc.pubypass", property, 0) > 0) &&
253 (!strncmp(property, "1", PROPERTY_VALUE_MAX ) ||
254 (!strncasecmp(property,"true", PROPERTY_VALUE_MAX )))) {
255 mPanelInfo.mPartialUpdateEnable = 0;
256 ALOGI("PartialUpdate disabled by property");
257 }
258 fclose(panelInfoNodeFP);
259 free(readLine);
260 } else {
261 ALOGE("Failed to open msm_fb_panel_info node");
262 }
263 }
264
265 // This function reads the sysfs node to read MDP capabilities
266 // and parses and updates information accordingly.
updateSysFsInfo()267 bool MDPVersion::updateSysFsInfo() {
268 FILE *sysfsFd;
269 size_t len = PAGE_SIZE;
270 ssize_t read;
271 char *line = NULL;
272 char sysfsPath[255];
273 memset(sysfsPath, 0, sizeof(sysfsPath));
274 snprintf(sysfsPath , sizeof(sysfsPath),
275 "/sys/class/graphics/fb0/mdp/caps");
276 char property[PROPERTY_VALUE_MAX];
277 bool enableMacroTile = false;
278
279 if((property_get("persist.hwc.macro_tile_enable", property, NULL) > 0) &&
280 (!strncmp(property, "1", PROPERTY_VALUE_MAX ) ||
281 (!strncasecmp(property,"true", PROPERTY_VALUE_MAX )))) {
282 enableMacroTile = true;
283 }
284
285 sysfsFd = fopen(sysfsPath, "rb");
286
287 if (sysfsFd == NULL) {
288 ALOGE("%s: sysFsFile file '%s' not found",
289 __FUNCTION__, sysfsPath);
290 return false;
291 } else {
292 line = (char *) malloc(len);
293 while((read = getline(&line, &len, sysfsFd)) != -1) {
294 int index=0;
295 char *tokens[10];
296 memset(tokens, 0, sizeof(tokens));
297
298 // parse the line and update information accordingly
299 if(!tokenizeParams(line, TOKEN_PARAMS_DELIM, tokens, &index)) {
300 if(!strncmp(tokens[0], "hw_rev", strlen("hw_rev"))) {
301 mMdpRev = atoi(tokens[1]);
302 }
303 else if(!strncmp(tokens[0], "rgb_pipes", strlen("rgb_pipes"))) {
304 mRGBPipes = (uint8_t)atoi(tokens[1]);
305 }
306 else if(!strncmp(tokens[0], "vig_pipes", strlen("vig_pipes"))) {
307 mVGPipes = (uint8_t)atoi(tokens[1]);
308 }
309 else if(!strncmp(tokens[0], "dma_pipes", strlen("dma_pipes"))) {
310 mDMAPipes = (uint8_t)atoi(tokens[1]);
311 }
312 else if(!strncmp(tokens[0], "blending_stages",
313 strlen("blending_stages"))) {
314 mBlendStages = (uint8_t)atoi(tokens[1]);
315 }
316 else if(!strncmp(tokens[0], "max_downscale_ratio",
317 strlen("max_downscale_ratio"))) {
318 mMDPDownscale = atoi(tokens[1]);
319 }
320 else if(!strncmp(tokens[0], "max_upscale_ratio",
321 strlen("max_upscale_ratio"))) {
322 mMDPUpscale = atoi(tokens[1]);
323 } else if(!strncmp(tokens[0], "max_bandwidth_low",
324 strlen("max_bandwidth_low"))) {
325 mLowBw = atol(tokens[1]);
326 } else if(!strncmp(tokens[0], "max_bandwidth_high",
327 strlen("max_bandwidth_high"))) {
328 mHighBw = atol(tokens[1]);
329 } else if(!strncmp(tokens[0], "max_mixer_width",
330 strlen("max_mixer_width"))) {
331 mMaxMixerWidth = atoi(tokens[1]);
332 } else if(!strncmp(tokens[0], "max_pipe_width",
333 strlen("max_pipe_width"))) {
334 mMaxPipeWidth = atoi(tokens[1]);
335 } else if(!strncmp(tokens[0], "features", strlen("features"))) {
336 for(int i=1; i<index;i++) {
337 if(!strncmp(tokens[i], "bwc", strlen("bwc"))) {
338 mFeatures |= MDP_BWC_EN;
339 } else if(!strncmp(tokens[i], "decimation",
340 strlen("decimation"))) {
341 mFeatures |= MDP_DECIMATION_EN;
342 } else if(!strncmp(tokens[i], "tile_format",
343 strlen("tile_format"))) {
344 if(enableMacroTile)
345 mMacroTileEnabled = true;
346 } else if(!strncmp(tokens[i], "src_split",
347 strlen("src_split"))) {
348 mSourceSplit = true;
349 } else if(!strncmp(tokens[i], "non_scalar_rgb",
350 strlen("non_scalar_rgb"))) {
351 mRGBHasNoScalar = true;
352 } else if(!strncmp(tokens[i], "rotator_downscale",
353 strlen("rotator_downscale"))) {
354 mRotDownscale = true;
355 }
356 }
357 }
358 }
359 }
360 free(line);
361 fclose(sysfsFd);
362 }
363
364 if(mMDPVersion >= qdutils::MDP_V4_2 and mMDPVersion < qdutils::MDSS_V5) {
365 mRotDownscale = true;
366 }
367
368 if(mSourceSplit) {
369 memset(sysfsPath, 0, sizeof(sysfsPath));
370 snprintf(sysfsPath , sizeof(sysfsPath),
371 "/sys/class/graphics/fb0/msm_fb_src_split_info");
372
373 sysfsFd = fopen(sysfsPath, "rb");
374 if (sysfsFd == NULL) {
375 ALOGE("%s: Opening file %s failed with error %s", __FUNCTION__,
376 sysfsPath, strerror(errno));
377 return false;
378 } else {
379 line = (char *) malloc(len);
380 if((read = getline(&line, &len, sysfsFd)) != -1) {
381 if(!strncmp(line, "src_split_always",
382 strlen("src_split_always"))) {
383 mSourceSplitAlways = true;
384 }
385 }
386 free(line);
387 fclose(sysfsFd);
388 }
389 }
390
391 ALOGD_IF(DEBUG, "%s: mMDPVersion: %d mMdpRev: %x mRGBPipes:%d,"
392 "mVGPipes:%d", __FUNCTION__, mMDPVersion, mMdpRev,
393 mRGBPipes, mVGPipes);
394 ALOGD_IF(DEBUG, "%s:mDMAPipes:%d \t mMDPDownscale:%d, mFeatures:%d",
395 __FUNCTION__, mDMAPipes, mMDPDownscale, mFeatures);
396 ALOGD_IF(DEBUG, "%s:mLowBw: %lu mHighBw: %lu", __FUNCTION__, mLowBw,
397 mHighBw);
398
399 return true;
400 }
401
402 // This function reads the sysfs node to read MDP capabilities
403 // and parses and updates information accordingly.
updateSplitInfo()404 bool MDPVersion::updateSplitInfo() {
405 if(mMDPVersion >= MDSS_V5) {
406 char split[64] = {0};
407 FILE* fp = fopen("/sys/class/graphics/fb0/msm_fb_split", "r");
408 if(fp){
409 //Format "left right" space as delimiter
410 if(fread(split, sizeof(char), 64, fp)) {
411 split[sizeof(split) - 1] = '\0';
412 mSplit.mLeft = atoi(split);
413 ALOGI_IF(mSplit.mLeft, "Left Split=%d", mSplit.mLeft);
414 char *rght = strpbrk(split, " ");
415 if(rght)
416 mSplit.mRight = atoi(rght + 1);
417 ALOGI_IF(mSplit.mRight, "Right Split=%d", mSplit.mRight);
418 }
419 } else {
420 ALOGE("Failed to open mdss_fb_split node");
421 return false;
422 }
423 if(fp)
424 fclose(fp);
425 }
426 return true;
427 }
428
429
hasMinCropWidthLimitation() const430 bool MDPVersion::hasMinCropWidthLimitation() const {
431 return mMdpRev <= MDSS_MDP_HW_REV_102;
432 }
433
supportsDecimation()434 bool MDPVersion::supportsDecimation() {
435 return mFeatures & MDP_DECIMATION_EN;
436 }
437
getMaxMDPDownscale()438 uint32_t MDPVersion::getMaxMDPDownscale() {
439 return mMDPDownscale;
440 }
441
getMaxMDPUpscale()442 uint32_t MDPVersion::getMaxMDPUpscale() {
443 return mMDPUpscale;
444 }
445
supportsBWC()446 bool MDPVersion::supportsBWC() {
447 // BWC - Bandwidth Compression
448 return (mFeatures & MDP_BWC_EN);
449 }
450
supportsMacroTile()451 bool MDPVersion::supportsMacroTile() {
452 // MACRO TILE support
453 return mMacroTileEnabled;
454 }
455
isSrcSplit() const456 bool MDPVersion::isSrcSplit() const {
457 return mSourceSplit;
458 }
459
isSrcSplitAlways() const460 bool MDPVersion::isSrcSplitAlways() const {
461 return mSourceSplitAlways;
462 }
463
isRGBScalarSupported() const464 bool MDPVersion::isRGBScalarSupported() const {
465 return (!mRGBHasNoScalar);
466 }
467
is8x26()468 bool MDPVersion::is8x26() {
469 return (mMdpRev >= MDSS_MDP_HW_REV_101 and
470 mMdpRev < MDSS_MDP_HW_REV_102);
471 }
472
is8x74v2()473 bool MDPVersion::is8x74v2() {
474 return (mMdpRev >= MDSS_MDP_HW_REV_102 and
475 mMdpRev < MDSS_MDP_HW_REV_103);
476 }
477
is8084()478 bool MDPVersion::is8084() {
479 return (mMdpRev >= MDSS_MDP_HW_REV_103 and
480 mMdpRev < MDSS_MDP_HW_REV_104);
481 }
482
is8092()483 bool MDPVersion::is8092() {
484 return (mMdpRev >= MDSS_MDP_HW_REV_200 and
485 mMdpRev < MDSS_MDP_HW_REV_206);
486 }
487
is8994()488 bool MDPVersion::is8994() {
489 return ((mMdpRev >= MDSS_MDP_HW_REV_105 and
490 mMdpRev < MDSS_MDP_HW_REV_106) or
491 (mMdpRev >= MDSS_MDP_HW_REV_109 and
492 mMdpRev < MDSS_MDP_HW_REV_110));
493 }
494
is8x16()495 bool MDPVersion::is8x16() {
496 return (mMdpRev >= MDSS_MDP_HW_REV_106 and
497 mMdpRev < MDSS_MDP_HW_REV_107);
498 }
499
is8x39()500 bool MDPVersion::is8x39() {
501 return (mMdpRev >= MDSS_MDP_HW_REV_108 and
502 mMdpRev < MDSS_MDP_HW_REV_109);
503 }
504
505
506 }; //namespace qdutils
507
508