diff --git a/dtbtool/dtbtool.c b/dtbtool/dtbtool.c index 0dbee4d..1ef54d8 100644 --- a/dtbtool/dtbtool.c +++ b/dtbtool/dtbtool.c @@ -39,13 +39,15 @@ #include #include #include +#include #define QCDT_MAGIC "QCDT" /* Master DTB magic */ -#define QCDT_VERSION 1 /* QCDT version */ -#define QCDT_VERSION_NEW 2 /* QCDT version */ +#define QCDT_VERSION 3 /* QCDT version */ #define QCDT_DT_TAG "qcom,msm-id = <" #define QCDT_BOARD_TAG "qcom,board-id = <" +#define QCDT_PMIC_TAG "qcom,pmic-id = <" + #define PAGE_SIZE_DEF 2048 #define PAGE_SIZE_MAX (1024*1024) @@ -64,6 +66,7 @@ struct chipInfo_t { uint32_t platform; uint32_t subtype; uint32_t revNum; + uint32_t pmic_model[4]; uint32_t dtb_size; char *dtb_file; struct chipInfo_t *prev; @@ -90,14 +93,22 @@ struct chipSt_t { struct chipSt_t *t_next; }; +struct chipPt_t { + uint32_t pmic0; + uint32_t pmic1; + uint32_t pmic2; + uint32_t pmic3; + struct chipPt_t *next; + struct chipPt_t *t_next; +}; + char *input_dir; char *output_file; char *dtc_path; char *dt_tag = QCDT_DT_TAG; int verbose; int page_size = PAGE_SIZE_DEF; -int force_v2; - +int version_override = 0; void print_help() { @@ -109,7 +120,8 @@ void print_help() log_info(" --page-size/-s page size in bytes\n"); log_info(" --dt-tag/-d alternate QCDT_DT_TAG\n"); log_info(" --verbose/-v verbose\n"); - log_info(" --force-v2/-2 use dtb v2 format\n"); + log_info(" --force-v2/-2 output dtb v2 format\n"); + log_info(" --force-v3/-3 output dtb v3 format\n"); log_info(" --help/-h this help screen\n"); } @@ -122,13 +134,14 @@ int parse_commandline(int argc, char *const argv[]) {"dtc-path", 1, 0, 'p'}, {"page-size", 1, 0, 's'}, {"dt-tag", 1, 0, 'd'}, + {"force-v2", 0, 0, '2'}, + {"force-v3", 0, 0, '3'}, {"verbose", 0, 0, 'v'}, {"help", 0, 0, 'h'}, - {"force-v2", 0, 0, '2'}, {0, 0, 0, 0} }; - while ((c = getopt_long(argc, argv, "-o:p:s:d:vh2", long_options, NULL)) + while ((c = getopt_long(argc, argv, "-o:p:s:d:23vh", long_options, NULL)) != -1) { switch (c) { case 1: @@ -151,12 +164,17 @@ int parse_commandline(int argc, char *const argv[]) case 'd': dt_tag = optarg; break; + case '2': + case '3': + if (version_override != 0) { + log_err("A version output argument may only be passed once\n"); + return RC_ERROR; + } + version_override = c - '0'; + break; case 'v': verbose = 1; break; - case '2': - force_v2 = 1; - break; case 'h': default: return RC_ERROR; @@ -214,7 +232,11 @@ int chip_add(struct chipInfo_t *c) if ((c->chipset == x->chipset) && (c->platform == x->platform) && (c->subtype == x->subtype) && - (c->revNum == x->revNum)) { + (c->revNum == x->revNum) && + (c->pmic_model[0] == x->pmic_model[0]) && + (c->pmic_model[1] == x->pmic_model[1]) && + (c->pmic_model[2] == x->pmic_model[2]) && + (c->pmic_model[3] == x->pmic_model[3])) { return RC_ERROR; /* duplicate */ } if (!x->next) { @@ -260,15 +282,20 @@ struct chipInfo_t *getChipInfo(const char *filename, int *num, uint32_t msmversi size_t line_size; FILE *pfile; int llen; - struct chipInfo_t *chip = NULL, *tmp; + struct chipInfo_t *chip = NULL, *tmp, *chip_t; uint32_t data[3] = {0, 0, 0}; uint32_t data_st[2] = {0, 0}; + uint32_t data_pt[4] = {0, 0, 0, 0}; char *tok, *sptr = NULL; int i, entryValid, entryEnded; - int count = 0, count1 = 0, count2 =0; - int entryValidST, entryEndedST, entryValidDT, entryEndedDT; + int count = 0, count1 = 0, count2 = 0, count3 = 0; + int entryValidST, entryEndedST, entryValidDT, entryEndedDT, entryValidPT, entryEndedPT; struct chipId_t *chipId = NULL, *cId = NULL, *tmp_id = NULL; struct chipSt_t *chipSt = NULL, *cSt = NULL, *tmp_st = NULL; + struct chipPt_t *chipPt = NULL, *cPt = NULL, *tmp_pt = NULL; + struct chipId_t *chipId_tmp = NULL; + struct chipSt_t *chipSt_tmp = NULL; + struct chipPt_t *chipPt_tmp = NULL; line_size = 1024; line = (char *)malloc(line_size); @@ -348,6 +375,10 @@ struct chipInfo_t *getChipInfo(const char *filename, int *num, uint32_t msmversi tmp->platform = data[1]; tmp->subtype = 0; tmp->revNum = data[2]; + tmp->pmic_model[0] = 0; + tmp->pmic_model[1] = 0; + tmp->pmic_model[2] = 0; + tmp->pmic_model[3] = 0; tmp->dtb_size = 0; tmp->dtb_file = NULL; tmp->master = chip; @@ -360,7 +391,7 @@ struct chipInfo_t *getChipInfo(const char *filename, int *num, uint32_t msmversi log_err("... skip, incorrect '%s' format\n", dt_tag); break; } - } else if (msmversion == 2) { + } else if (msmversion == 2 || msmversion == 3) { if ((pos = strstr(line, dt_tag)) != NULL) { pos += strlen(dt_tag); @@ -450,6 +481,53 @@ struct chipInfo_t *getChipInfo(const char *filename, int *num, uint32_t msmversi } } } + + if ((pos = strstr(line,QCDT_PMIC_TAG)) != NULL) { + pos += strlen(QCDT_PMIC_TAG); + entryEndedPT = 0; + for (;entryEndedPT < 1;) { + entryValidPT = 1; + for (i = 0; i < 4; i++) { + tok = strtok_r(pos, " \t", &sptr); + pos = NULL; + if (tok != NULL) { + if (*tok == '>') { + entryEndedPT = 1; + entryValidPT = 0; + break; + } + data_pt[i] = strtoul(tok, NULL, 0); + } else { + data_pt[i] = 0; + entryValidPT = 0; + entryEndedPT = 1; + } + } + if (entryValidPT) { + tmp_pt = (struct chipPt_t *) + malloc(sizeof(struct chipPt_t)); + if (!tmp_pt) { + log_err("Out of memory\n"); + break; + } + + if (!chipPt) { + chipPt = tmp_pt; + cPt = tmp_pt; + chipPt->t_next = NULL; + } else { + tmp_pt->t_next = chipPt->t_next; + chipPt->t_next = tmp_pt; + } + + tmp_pt->pmic0 = data_pt[0]; + tmp_pt->pmic1 = data_pt[1]; + tmp_pt->pmic2 = data_pt[2]; + tmp_pt->pmic3 = data_pt[3]; + count3++; + } + } + } } } } @@ -457,8 +535,6 @@ struct chipInfo_t *getChipInfo(const char *filename, int *num, uint32_t msmversi if (line) free(line); - if (force_v2 || msmversion == 2) { - if (count1 == 0) { log_err("... skip, incorrect '%s' format\n", dt_tag); return NULL; @@ -467,58 +543,121 @@ struct chipInfo_t *getChipInfo(const char *filename, int *num, uint32_t msmversi log_err("... skip, incorrect '%s' format\n", QCDT_BOARD_TAG); return NULL; } + if (count3 == 0 && msmversion == 3) { + log_err("... skip, incorrect '%s' format\n", QCDT_PMIC_TAG); + return NULL; + } tmp_st = cSt; + tmp_pt = cPt; while (cId != NULL) { while (cSt != NULL) { - tmp = (struct chipInfo_t *) - malloc(sizeof(struct chipInfo_t)); - if (!tmp) { - log_err("Out of memory\n"); - break; - } - if (!chip) { - chip = tmp; - chip->t_next = NULL; + if (msmversion == 3) { + while (cPt != NULL) { + tmp = (struct chipInfo_t *) + malloc(sizeof(struct chipInfo_t)); + if (!tmp) { + log_err("Out of memory\n"); + break; + } + if (!chip) { + chip = tmp; + chip->t_next = NULL; + } else { + tmp->t_next = chip->t_next; + chip->t_next = tmp; + } + + tmp->chipset = cId->chipset; + tmp->platform = cSt->platform; + tmp->revNum = cId->revNum; + tmp->subtype = cSt->subtype; + tmp->pmic_model[0] = cPt->pmic0; + tmp->pmic_model[1] = cPt->pmic1; + tmp->pmic_model[2] = cPt->pmic2; + tmp->pmic_model[3] = cPt->pmic3; + tmp->dtb_size = 0; + tmp->dtb_file = NULL; + tmp->master = chip; + tmp->wroteDtb = 0; + tmp->master_offset = 0; + cPt = cPt->t_next; + } + cPt = tmp_pt; } else { - tmp->t_next = chip->t_next; - chip->t_next = tmp; + tmp = (struct chipInfo_t *) + malloc(sizeof(struct chipInfo_t)); + if (!tmp) { + log_err("Out of memory\n"); + break; + } + if (!chip) { + chip = tmp; + chip->t_next = NULL; + } else { + tmp->t_next = chip->t_next; + chip->t_next = tmp; + } + tmp->chipset = cId->chipset; + tmp->platform = cSt->platform; + tmp->revNum = cId->revNum; + tmp->subtype = cSt->subtype; + tmp->pmic_model[0] = 0; + tmp->pmic_model[1] = 0; + tmp->pmic_model[2] = 0; + tmp->pmic_model[3] = 0; + tmp->dtb_size = 0; + tmp->dtb_file = NULL; + tmp->master = chip; + tmp->wroteDtb = 0; + tmp->master_offset = 0; } - - tmp->chipset = cId->chipset; - tmp->platform = cSt->platform; - tmp->revNum = cId->revNum; - tmp->subtype = cSt->subtype; - tmp->dtb_size = 0; - tmp->dtb_file = NULL; - tmp->master = chip; - tmp->wroteDtb = 0; - tmp->master_offset = 0; - cSt = cSt->t_next; - } cSt = tmp_st; cId = cId->t_next; } - if (entryEndedST == 1 && entryEndedDT == 1) { - pclose(pfile); + if (msmversion == 2) + entryEndedPT = 1; + + /* clear memory*/ + pclose(pfile); + while (chipId) { + chipId_tmp = chipId; + chipId = chipId->t_next; + free(chipId_tmp); + } + while (chipSt) { + chipSt_tmp= chipSt; + chipSt = chipSt->t_next; + free(chipSt_tmp); + } + + while (chipPt) { + chipPt_tmp= chipPt; + chipPt = chipPt->t_next; + free(chipPt_tmp); + } + + if (entryEndedST == 1 && entryEndedDT == 1 && entryEndedPT == 1) { *num = count1; - free(chipSt); - free(chipId); return chip; } - } else { - pclose(pfile); + /* clear memory*/ + while (chip) { + chip_t = chip; + chip = chip->next; + if (chip_t->dtb_file) + free(chip_t->dtb_file); + free(chip_t); } - return NULL; } /* Get the version-id based on dtb files */ -int GetVersionInfo(const char *filename) +uint32_t GetVersionInfo(const char *filename) { const char str1[] = "dtc -I dtb -O dts \""; const char str2[] = "\" 2>&1"; @@ -527,7 +666,7 @@ int GetVersionInfo(const char *filename) size_t line_size; FILE *pfile; int llen; - int v = 1; + uint32_t v = 1; line_size = 1024; line = (char *)malloc(line_size); @@ -562,17 +701,165 @@ int GetVersionInfo(const char *filename) while ((llen = getline(&line, &line_size, pfile)) != -1) { if ((pos = strstr(line,QCDT_BOARD_TAG)) != NULL) { v = 2; + } + if ((pos = strstr(line,QCDT_PMIC_TAG)) != NULL) { + v = 3; break; } } } - if (v == 1) - log_info(" Old Version:%d\n", v); + free(line); + log_info("Version:%d\n", v); return v; } +static int find_dtb(const char *path, uint32_t *version) +{ + struct dirent *dp; + int flen; + char *filename; + struct chipInfo_t *chip, *t_chip; + struct stat st; + int num; + int rc = RC_SUCCESS; + uint32_t msmversion = 0; + int dtb_count = 0; + + DIR *dir = opendir(path); + if (!dir) { + log_err("Failed to open input directory '%s'\n", path); + return RC_ERROR; + } + + /* Open the .dtb files in the specified path, decompile and + extract "qcom,msm-id" parameter + */ + while ((dp = readdir(dir)) != NULL) { + if (dp->d_type == DT_UNKNOWN) { + struct stat statbuf; + char name[PATH_MAX]; + snprintf(name, sizeof(name), "%s%s%s", + path, + (path[strlen(path) - 1] == '/' ? "" : "/"), + dp->d_name); + if (!stat(name, &statbuf)) { + if (S_ISREG(statbuf.st_mode)) { + dp->d_type = DT_REG; + } else if (S_ISDIR(statbuf.st_mode)) { + dp->d_type = DT_DIR; + } + } + } + + if (dp->d_type == DT_DIR) { + char name[PATH_MAX]; + if (dp->d_name[0] == '.') { + continue; + } + snprintf(name, sizeof(name), "%s%s%s%s", + path, + (path[strlen(path) - 1] == '/' ? "" : "/"), + dp->d_name, + "/"); + log_info("Searching subdir: %s ... \n", name); + dtb_count += find_dtb(name, version); + } else if (dp->d_type == DT_REG) { + flen = strlen(dp->d_name); + if ((flen > 4) && + (strncmp(&dp->d_name[flen-4], ".dtb", 4) == 0)) { + log_info("Found file: %s ... \n", dp->d_name); + + flen = strlen(path) + strlen(dp->d_name) + 1; + filename = (char *)malloc(flen); + if (!filename) { + log_err("Out of memory\n"); + rc = RC_ERROR; + break; + } + strncpy(filename, path, flen); + strncat(filename, dp->d_name, flen); + + /* To identify the version number */ + msmversion = GetVersionInfo(filename); + if (*version < msmversion) { + *version = msmversion; + } + + num = 1; + chip = getChipInfo(filename, &num, msmversion); + + if (msmversion == 1) { + if (!chip) { + log_err("skip, failed to scan for '%s' tag\n", dt_tag); + free(filename); + continue; + } + } + if (msmversion == 2) { + if (!chip) { + log_err("skip, failed to scan for '%s' or '%s' tag\n", + dt_tag, QCDT_BOARD_TAG); + free(filename); + continue; + } + } + if (msmversion == 3) { + if (!chip) { + log_err("skip, failed to scan for '%s', '%s' or '%s' tag\n", + dt_tag, QCDT_BOARD_TAG, QCDT_PMIC_TAG); + free(filename); + continue; + } + } + + if ((stat(filename, &st) != 0) || + (st.st_size == 0)) { + log_err("skip, failed to get DTB size\n"); + free(filename); + continue; + } + + log_info("chipset: %u, rev: %u, platform: %u, subtype: %u, pmic0: %u, pmic1: %u, pmic2: %u, pmic3: %u\n", + chip->chipset, chip->revNum, chip->platform, chip->subtype, + chip->pmic_model[0], chip->pmic_model[1], chip->pmic_model[2], chip->pmic_model[3]); + + for (t_chip = chip->t_next; t_chip; t_chip = t_chip->t_next) { + log_info("additional chipset: %u, rev: %u, platform: %u, subtype: %u, pmic0: %u, pmic1: %u, pmic2: %u, pmic3: %u\n", + t_chip->chipset, t_chip->revNum, t_chip->platform, t_chip->subtype, + t_chip->pmic_model[0], t_chip->pmic_model[1], t_chip->pmic_model[2], t_chip->pmic_model[3]); + } + + rc = chip_add(chip); + if (rc != RC_SUCCESS) { + log_err("... duplicate info, skipped\n"); + free(filename); + continue; + } + + dtb_count++; + + chip->dtb_size = st.st_size + + (page_size - (st.st_size % page_size)); + chip->dtb_file = filename; + + for (t_chip = chip->t_next; t_chip; t_chip = t_chip->t_next) { + rc = chip_add(t_chip); + if (rc != RC_SUCCESS) { + log_err("... duplicate info, skipped (chipset %u, rev: %u, platform: %u, subtype: %u\n", + t_chip->chipset, t_chip->revNum, t_chip->platform, t_chip->subtype); + continue; + } + dtb_count++; + } + } + } + } + closedir(dir); + return dtb_count; +} + /* Extract 'qcom,msm-id' 'qcom,board-id' parameter from DTB v1 format: qcom,msm-id = [, ...]; @@ -588,24 +875,19 @@ int GetVersionInfo(const char *filename) int main(int argc, char **argv) { char buf[COPY_BLK]; - struct chipInfo_t *chip, *t_chip; - struct dirent *dp; + struct chipInfo_t *chip; FILE *pInputFile; - char *filename; int padding; uint8_t *filler = NULL; int numBytesRead = 0; int totBytesRead = 0; int out_fd; - int flen; int rc = RC_SUCCESS; - int dtb_count = 0, dtb_offset = 0; + int dtb_count = 0, dtb_offset = 0, entry_size; size_t wrote = 0, expected = 0; - struct stat st; - uint32_t version = QCDT_VERSION; - int num; uint32_t dtb_size; - int msmversion = 0; + uint32_t version = 0; + char *filename; log_info("DTB combiner:\n"); @@ -617,109 +899,16 @@ int main(int argc, char **argv) log_info(" Input directory: '%s'\n", input_dir); log_info(" Output file: '%s'\n", output_file); - DIR *dir = opendir(input_dir); - if (!dir) { - log_err("Failed to open input directory '%s'\n", input_dir); - return RC_ERROR; - } filler = (uint8_t *)malloc(page_size); if (!filler) { log_err("Out of memory\n"); - closedir(dir); return RC_ERROR; } memset(filler, 0, page_size); - /* Open the .dtb files in the specified path, decompile and - extract "qcom,msm-id" parameter - */ - while ((dp = readdir(dir)) != NULL) { + dtb_count = find_dtb(input_dir, &version); - flen = strlen(input_dir) + strlen(dp->d_name) + 1; - filename = (char *)malloc(flen); - if (!filename) { - log_err("Out of memory\n"); - rc = RC_ERROR; - break; - } - strncpy(filename, input_dir, flen); - strncat(filename, dp->d_name, flen); - - if (stat(filename, &st) != 0 || !S_ISREG(st.st_mode)) { - free(filename); - continue; - } - - flen = strlen(dp->d_name); - if ((flen <= 4) || (strncmp(&dp->d_name[flen-4], ".dtb", 4) != 0)) { - free(filename); - continue; - } - - log_info("Found file: %s ... ", dp->d_name); - - /* To identify the version number */ - msmversion = force_v2 ? GetVersionInfo(filename) : 1; - - num = 1; - chip = getChipInfo(filename, &num, msmversion); - - if (msmversion == 1) { - if (!chip) { - log_err("skip, failed to scan for '%s' tag\n", - dt_tag); - free(filename); - continue; - } - } - if (msmversion == 2) { - if (!chip) { - log_err("skip, failed to scan for '%s' or '%s' tag\n", - dt_tag, QCDT_BOARD_TAG); - free(filename); - continue; - } - } - - if (st.st_size == 0) { - log_err("skip, failed to get DTB size\n"); - free(filename); - continue; - } - - log_info("chipset: %u, rev: %u, platform: %u, subtype: %u\n", - chip->chipset, chip->revNum, chip->platform, chip->subtype); - - for (t_chip = chip->t_next; t_chip; t_chip = t_chip->t_next) { - log_info(" additional chipset: %u, rev: %u, platform: %u, subtype: %u\n", - t_chip->chipset, t_chip->revNum, t_chip->platform, t_chip->subtype); - } - - rc = chip_add(chip); - if (rc != RC_SUCCESS) { - log_err("... duplicate info, skipped\n"); - free(filename); - continue; - } - - dtb_count++; - - chip->dtb_size = st.st_size + - (page_size - (st.st_size % page_size)); - chip->dtb_file = filename; - - for (t_chip = chip->t_next; t_chip; t_chip = t_chip->t_next) { - rc = chip_add(t_chip); - if (rc != RC_SUCCESS) { - log_err("... duplicate info, skipped (chipset %u, rev: %u, platform: %u, subtype %u:\n", - t_chip->chipset, t_chip->revNum, t_chip->platform, t_chip->subtype); - continue; - } - dtb_count++; - } - } - closedir(dir); log_info("=> Found %d unique DTB(s)\n", dtb_count); if (!dtb_count) @@ -735,14 +924,22 @@ int main(int argc, char **argv) log_info("\nGenerating master DTB... "); out_fd = open(output_file, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR); - if (!out_fd < 0) { + if (out_fd == -1) { log_err("Cannot create '%s'\n", output_file); rc = RC_ERROR; goto cleanup; } - if (force_v2) { - version = QCDT_VERSION_NEW; + if (version_override != 0) { + version = version_override; + } + + if (version == 1) { + entry_size = 20; + } else if (version == 2) { + entry_size = 24; + } else { + entry_size = 40; } /* Write header info */ @@ -752,9 +949,10 @@ int main(int argc, char **argv) /* #DTB */ /* Calculate offset of first DTB block */ - dtb_offset = 12 + /* header */ - ((force_v2 ? 24 : 20) * dtb_count) + /* DTB table entries */ - 4; /* end of table indicator */ + dtb_offset = 12 + /* header */ + (entry_size * dtb_count) + /* DTB table entries */ + 4; /* end of table indicator */ + /* Round up to page size */ padding = page_size - (dtb_offset % page_size); dtb_offset += padding; @@ -763,17 +961,28 @@ int main(int argc, char **argv) /* Write index table: chipset platform - subtype + subtype (v2/v3 only) soc rev + pmic model0 (v3 only) + pmic model1 (v3 only) + pmic model2 (v3 only) + pmic model3 (v3 only) dtb offset dtb size */ for (chip = chip_list; chip; chip = chip->next) { wrote += write(out_fd, &chip->chipset, sizeof(uint32_t)); wrote += write(out_fd, &chip->platform, sizeof(uint32_t)); - if (force_v2) + if (version >= 2) { wrote += write(out_fd, &chip->subtype, sizeof(uint32_t)); + } wrote += write(out_fd, &chip->revNum, sizeof(uint32_t)); + if (version >= 3) { + wrote += write(out_fd, &chip->pmic_model[0], sizeof(uint32_t)); + wrote += write(out_fd, &chip->pmic_model[1], sizeof(uint32_t)); + wrote += write(out_fd, &chip->pmic_model[2], sizeof(uint32_t)); + wrote += write(out_fd, &chip->pmic_model[3], sizeof(uint32_t)); + } if (chip->master->master_offset != 0) { wrote += write(out_fd, &chip->master->master_offset, sizeof(uint32_t)); } else { diff --git a/dtbtool/dtbtool.txt b/dtbtool/dtbtool.txt index e738ef8..1aa0be4 100644 --- a/dtbtool/dtbtool.txt +++ b/dtbtool/dtbtool.txt @@ -1,4 +1,4 @@ -Copyright (c) 2012, The Linux Foundation. All rights reserved. +Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. Redistribution and use in source form and compiled forms (SGML, HTML, PDF, PostScript, RTF and so forth) with or without modification, are @@ -33,6 +33,7 @@ Android - Table of Device Tree 0) Document revision v1.0 - Initial version (dng) + v1.1 - Add v2 format to allow subtype (dng) 1) Android boot image: ---------------------- @@ -72,16 +73,26 @@ Android - Table of Device Tree x +------------------+ | | MAGIC ("QCDT") | 4B | +------------------+ - header | VERSION | uint32 (initial version 1) + header | VERSION | uint32 (version 3) | +------------------+ | | num of DTBs | uint32 (number of DTB entries) x +------------------+ | | platform id #1 | uint32 (e.g. ID for MSM8974) | +------------------+ | | variant id #1 | uint32 (e.g. ID for CDP, MTP) + | +------------------+ + | | subtype id #1 | uint32 (e.g. ID for subtype) (QCDT v2) device +------------------+ #1 | soc rev #1 | uint32 (e.g. MSM8974 v2) entry +------------------+ + | | pmic0 #1 | uint32 (pmic0-> first smallest SID of existing pmic) + | +------------------+ + | | pmic1 #1 | uint32 (pmic1-> secondary smallest SID of existing pmic) + | +------------------+ + | | pmic2 #1 | uint32 (pmic2-> third smallest SID of existing pmic) + | +------------------+ + | | pmic3 #1 | uint32 (pmic3-> fourth smallest SID of existing pmic) + | +------------------+ | | offset #1 | uint32 (byte offset from start/before MAGIC | +------------------+ to DTB entry) | | size #1 | uint32 (size in bytes of DTB blob) @@ -95,8 +106,18 @@ Android - Table of Device Tree | +------------------+ device | variant id #Z | uint32 (e.g. ID for CDP, MTP) #Z +------------------+ - entry | soc rev #Z | uint32 (e.g. MSM8974 v2) + entry | subtype id #Z | uint32 (e.g. ID for subtype) (QCDT v2) (last) +------------------+ + | | soc rev #Z | uint32 (e.g. MSM8974 v2) + | +------------------+ + | | pmic0 #1 | uint32 (pmic0-> first smallest SID of existing pmic) + | +------------------+ + | | pmic1 #1 | uint32 (pmic1-> secondary smallest SID of existing pmic) + | +------------------+ + | | pmic2 #1 | uint32 (pmic2-> third smallest SID of existing pmic) + | +------------------+ + | | pmic3 #1 | uint32 (pmic3-> fourth smallest SID of existing pmic) + | +------------------+ | | offset #Z | uint32 (byte offset from start/before MAGIC x +------------------+ to DTB entry) | 0 ("zero") | uint32 (end of list delimiter) @@ -122,25 +143,63 @@ Android - Table of Device Tree 3) Operations ------------- 3.1) Build-time: - 1) Each DTS per device will add a "qcom,msm-id" triplet + 1) Each DTS per device will add a "qcom,msm-id" entry e.g. for msm8974-sim.dts, add - qcom,msm-id = ; - x = ID for msm8974 - y = ID for CDP, MTP, etc. - z = ID for soc revision - The triplet can optionally be an array of triplets: - qcom,msm-id = , , ...; + qcom,msm-id = ; + or + qcom,msm-id = ; + qcom,board-id = ; + or + qcom,msm-id = ; + qcom,board-id = ; + qcom,pmic-id = ; + x = ID for msm8974 + y = ID for CDP, MTP, etc. + y' = ID for subtype (assumed zero if absent) + z = ID for soc revision + a = pmic0 + b = pmic1 + c = pmic2 + d = pmic3 + SBL populates the pmic entries always in ascending order of SID, so + pmic0-> SID0, pmic1-> SID1, pmic2-> SID2, pmic3-> SID3. + e.g. for qcom,pmic-id = + Board X = MSM8994 + PM8994 + PMI8994 (Existing boards [ROW]) + Board Y = MSM8994 + PM8994 + PMI8994 + PM8004 (Internal SS board variant) + Board Z = MSM8994 + PM8994 + PM8004 (Boards that SS will be making) + + For all boards X, Y, and Z, PMICs have the following SIDs and REVID SUBTYPEs + (i.e. PMIC Model): + PM8994 - SID 0 and 1; subtype = 9 + PMI8994 - SID 2 and 3; subtype = 10 + PM8004 - SID 4 and 5; subtype = 12 + + LK using SMEM PMIC info(1 as major and 0 as minor version for example): + Board X: qcom,pmic-id = <0x0109 0x010A 0x0 0x0>; + Board Y: qcom,pmic-id = <0x0109 0x010A 0x010C 0x0>; + Board Z: qcom,pmic-id = <0x0109 0x010C 0x0 0x0>; + + The entry can optionally be an array: + qcom,msm-id = , , ...; + or + qcom,msm-id = , , ...; + qcom,board-id = , ...; + or + qcom,msm-id = , , ...; + qcom,board-id = , ...; + qcom,pmic-id = , ...; + Note that qcom,msm-id, qcom,board-id and qcom,pmic-id are not matched pairs. 2) Kernel compile will generate the DTB 3) Android build will run a new tool (dtbTool) a) scan the DTB output directory for all compiled DTB - b) decompile the DTB for "qcom,msm-id" + b) decompile the DTB for "qcom,msm-id"/"qcom,board-id"/"qcom,pmic-id" c) generate the QC table of device tree in sorted - order (platform, variant, soc rev) + order (platform, variant, subtype, soc rev, pmic0, pmic1, pmic2, pmic3) d) modified mkbootimg will merge new table of DT 3.2) Run-time: - 1) LK bootloader will obtain MSM id/variant/soc rev info - either from early bootloaders or via other means + 1) LK bootloader will obtain platform id/variant/subtype/soc rev/major ver/minor ver + /pmic0/pmic1/pmic2/pmic3 info either from early bootloaders or via other means 2) LK bootloader will check entries #10 for non-zero value (set to zero for standard boot.img). If the value is non-zero, refer to page section after @@ -150,9 +209,27 @@ Android - Table of Device Tree QCDT version) 5) LK scans through the QCDT table to look for matching entry. Search order is: - 1) platform ID exact match - 2) variant ID exact match - 3) select the highest soc rev in QCDT that is + 1) msm ID exact match + 2) Platform type exact match + 3) subtype ID exact match + 4) HLOS subtype exact match + 5) Pmic0 model ID exact match + 6) Pmic1 model ID exact match + 7) Pmic2 model ID exact match + 8) Pmic3 model ID exact match + 9) foundry ID, look for exact match, if not found choose + device tree with foundry-id(0x0) + 10) select the highest soc rev in QCDT that is equal to or lower than the runtime detected soc rev + 11) select the highest major&minor ver in QCDT that is + equal to or lower than the runtime detected major ver + 12) select the highest pmic0 major&minor in QCDT that is + equal to or lower than the runtime detected pmic0 + 13) select the highest pmic1 major&minor in QCDT that is + equal to or lower than the runtime detected pmic1 + 14) select the highest pmic2 major&minor in QCDT that is + equal to or lower than the runtime detected pmic2 + 15) select the highest pmic3 major&minor in QCDT that is + equal to or lower than the runtime detected pmic3 6) Load the matching DTB blob to the tags addr 7) LK pass the correct DTB to the kernel diff --git a/init/init_msm8952.cpp b/init/init_msm8952.cpp new file mode 100644 index 0000000..a6ae854 --- /dev/null +++ b/init/init_msm8952.cpp @@ -0,0 +1,119 @@ +/* + Copyright (c) 2015, The Linux Foundation. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "vendor_init.h" +#include "property_service.h" +#include "log.h" +#include "util.h" + +#include "init_msm.h" + +#define VIRTUAL_SIZE "/sys/class/graphics/fb0/virtual_size" +#define VERSION "/sys/devices/soc.0/1d00000.qcom,vidc/version" +#define BUF_SIZE 64 + +int get_version(int *version) +{ + char buf[16]; + int pos = 0, rv = 0, fd = -1; + long value = 0; + char *endptr = NULL; + int bytes_to_read = sizeof(buf); + + fd = open(VERSION, O_RDONLY, 0660); + if (fd < 0) { + return -1; + } + + do { + pos += rv; + if ((bytes_to_read - pos) <= 0) + break; + rv += read(fd, buf + pos, bytes_to_read - pos); + } while (rv > 0); + + if (rv < 0) { + return -1; + } + buf[pos - 1] = '\0'; + value = strtol(buf, &endptr, sizeof(long)); + if (endptr == buf) { + return -1; + } + *version = (int)value; + + return 1; +} + +void init_msm_properties(unsigned long msm_id, unsigned long msm_ver, char *board_type) +{ + char platform[PROP_VALUE_MAX]; + int rc; + int version = 0; + unsigned long virtual_size = 0; + char str[BUF_SIZE]; + + UNUSED(msm_id); + UNUSED(msm_ver); + UNUSED(board_type); + + rc = property_get("ro.board.platform", platform); + if (!rc || !ISMATCH(platform, ANDROID_TARGET)){ + return; + } + + rc = read_file2(VIRTUAL_SIZE, str, sizeof(str)); + if (rc) { + virtual_size = strtoul(str, NULL, 0); + } + + if(virtual_size >= 1080) { + property_set(PROP_LCDDENSITY, "480"); + } else if (virtual_size >= 720) { + // For 720x1280 resolution + property_set(PROP_LCDDENSITY, "320"); + } else if (virtual_size >= 480) { + // For 480x854 resolution QRD. + property_set(PROP_LCDDENSITY, "240"); + } else + property_set(PROP_LCDDENSITY, "320"); + + if (msm_id == 266 || msm_id == 278 || msm_id == 277 || msm_id == 274) { + property_set("media.msm8956hw", "1"); + property_set("media.settings.xml", "/etc/media_profiles_8956.xml"); + get_version(&version); + if (version == 1) + property_set("media.msm8956.version", "1"); + else + property_set("media.msm8956.version", "0"); + } +} diff --git a/power/Android.mk b/power/Android.mk index 5c727ad..ac40171 100644 --- a/power/Android.mk +++ b/power/Android.mk @@ -41,6 +41,10 @@ ifeq ($(call is-board-platform-in-list, msm8916), true) LOCAL_SRC_FILES += power-8916.c endif +ifeq ($(call is-board-platform-in-list, msm8952), true) +LOCAL_SRC_FILES += power-8952.c +endif + ifeq ($(call is-board-platform-in-list, apq8084), true) LOCAL_SRC_FILES += power-8084.c endif @@ -58,6 +62,16 @@ LOCAL_CFLAGS += -DSET_INTERACTIVE_EXT LOCAL_SRC_FILES += ../../../../$(TARGET_POWERHAL_SET_INTERACTIVE_EXT) endif +ifneq ($(TARGET_TAP_TO_WAKE_NODE),) + LOCAL_CFLAGS += -DTAP_TO_WAKE_NODE=\"$(TARGET_TAP_TO_WAKE_NODE)\" +endif + +ifeq ($(TARGET_POWER_SET_FEATURE_LIB),) + LOCAL_SRC_FILES += power-feature-default.c +else + LOCAL_STATIC_LIBRARIES += $(TARGET_POWER_SET_FEATURE_LIB) +endif + ifneq ($(CM_POWERHAL_EXTENSION),) LOCAL_MODULE := power.$(CM_POWERHAL_EXTENSION) else diff --git a/power/power-8084.c b/power/power-8084.c index e39bc08..8c65f10 100644 --- a/power/power-8084.c +++ b/power/power-8084.c @@ -52,11 +52,9 @@ static int display_hint2_sent; static int first_display_off_hint; extern int display_boost; -enum { - PROFILE_POWER_SAVE = 0, - PROFILE_BALANCED, - PROFILE_HIGH_PERFORMANCE -}; +int get_number_of_profiles() { + return 3; +} static int current_power_profile = PROFILE_BALANCED; @@ -98,25 +96,17 @@ int power_hint_override(__attribute__((unused)) struct power_module *module, power_hint_t hint, void *data) { if (hint == POWER_HINT_SET_PROFILE) { - set_power_profile((int)data); + set_power_profile(*(int32_t *)data); return HINT_HANDLED; } - if (hint == POWER_HINT_LOW_POWER) { - if (current_power_profile == PROFILE_POWER_SAVE) { - set_power_profile(PROFILE_BALANCED); - } else { - set_power_profile(PROFILE_POWER_SAVE); - } - } - // Skip other hints in custom power modes if (current_power_profile != PROFILE_BALANCED) { return HINT_HANDLED; } if (hint == POWER_HINT_CPU_BOOST) { - int duration = (int)data / 1000; + int duration = *(int32_t *)data / 1000; int resources[] = { CPUS_ONLINE_MIN_2, 0x20B, 0x30B, 0x1C00}; if (duration > 0) diff --git a/power/power-8226.c b/power/power-8226.c index 6c69881..2cbfad6 100644 --- a/power/power-8226.c +++ b/power/power-8226.c @@ -50,11 +50,9 @@ static int display_hint_sent; -enum { - PROFILE_POWER_SAVE = 0, - PROFILE_BALANCED, - PROFILE_HIGH_PERFORMANCE -}; +int get_number_of_profiles() { + return 3; +} static int current_power_profile = PROFILE_BALANCED; @@ -95,25 +93,17 @@ int power_hint_override(__attribute__((unused)) struct power_module *module, power_hint_t hint, void *data) { if (hint == POWER_HINT_SET_PROFILE) { - set_power_profile((int)data); + set_power_profile(*(int32_t *)data); return HINT_HANDLED; } - if (hint == POWER_HINT_LOW_POWER) { - if (current_power_profile == PROFILE_POWER_SAVE) { - set_power_profile(PROFILE_BALANCED); - } else { - set_power_profile(PROFILE_POWER_SAVE); - } - } - // Skip other hints in custom power modes if (current_power_profile != PROFILE_BALANCED) { return HINT_HANDLED; } if (hint == POWER_HINT_CPU_BOOST) { - int duration = (int)data / 1000; + int duration = *(int32_t *)data / 1000; int resources[] = { CPUS_ONLINE_MIN_2, 0x20F, 0x30F}; if (duration > 0) diff --git a/power/power-8610.c b/power/power-8610.c index 4afc1d9..2e64083 100644 --- a/power/power-8610.c +++ b/power/power-8610.c @@ -50,11 +50,9 @@ static int display_hint_sent; -enum { - PROFILE_POWER_SAVE = 0, - PROFILE_BALANCED, - PROFILE_HIGH_PERFORMANCE -}; +int get_number_of_profiles() { + return 3; +} static int current_power_profile = PROFILE_BALANCED; @@ -93,25 +91,17 @@ int power_hint_override(__attribute__((unused)) struct power_module *module, power_hint_t hint, void *data) { if (hint == POWER_HINT_SET_PROFILE) { - set_power_profile((int)data); + set_power_profile(*(int32_t *)data); return HINT_HANDLED; } - if (hint == POWER_HINT_LOW_POWER) { - if (current_power_profile == PROFILE_POWER_SAVE) { - set_power_profile(PROFILE_BALANCED); - } else { - set_power_profile(PROFILE_POWER_SAVE); - } - } - // Skip other hints in custom power modes if (current_power_profile != PROFILE_BALANCED) { return HINT_HANDLED; } if (hint == POWER_HINT_CPU_BOOST) { - int duration = (int)data / 1000; + int duration = *(int32_t *)data / 1000; int resources[] = { CPUS_ONLINE_MIN_2, 0x20F, 0x30F}; if (duration > 0) diff --git a/power/power-8916.c b/power/power-8916.c index 7b1ad71..55fdf3d 100644 --- a/power/power-8916.c +++ b/power/power-8916.c @@ -65,11 +65,9 @@ int display_boost; static int saved_interactive_mode = -1; static int slack_node_rw_failed = 0; -enum { - PROFILE_POWER_SAVE = 0, - PROFILE_BALANCED, - PROFILE_HIGH_PERFORMANCE -}; +int get_number_of_profiles() { + return 3; +} static int current_power_profile = PROFILE_BALANCED; @@ -381,15 +379,7 @@ int set_interactive_override(struct power_module *module __unused, int on) int power_hint_override(struct power_module *module __unused, power_hint_t hint, void *data) { if (hint == POWER_HINT_SET_PROFILE) { - set_power_profile((hintdata)data); - } - - if (hint == POWER_HINT_LOW_POWER) { - if (current_power_profile == PROFILE_POWER_SAVE) { - set_power_profile(PROFILE_BALANCED); - } else { - set_power_profile(PROFILE_POWER_SAVE); - } + set_power_profile(*(int32_t *)data); } // Skip other hints in custom power modes @@ -407,7 +397,7 @@ int power_hint_override(struct power_module *module __unused, power_hint_t hint, } if (hint == POWER_HINT_CPU_BOOST) { - int duration = (hintdata)data / 1000; + int duration = *(int32_t *)data / 1000; int resources[] = { SCHED_BOOST_ON, 0x20D, 0x3E01, 0x101 }; if (duration > 0) diff --git a/power/power-8952.c b/power/power-8952.c new file mode 100644 index 0000000..a38218f --- /dev/null +++ b/power/power-8952.c @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_NIDEBUG 0 + +#include +#include +#include +#include +#include +#include +#include + +#define LOG_TAG "QTI PowerHAL" +#include +#include +#include + +#include "utils.h" +#include "metadata-defs.h" +#include "hint-data.h" +#include "performance.h" +#include "power-common.h" + +#define ARRAY_SIZE(arr) (sizeof((arr)) / sizeof((arr)[0])) + +static int display_hint_sent; +static int video_encode_hint_sent; +static int current_power_profile = PROFILE_BALANCED; + +static void process_video_encode_hint(void *metadata); + +extern void interaction(int duration, int num_args, int opt_list[]); + +static int profile_high_performance_8952[11] = { + SCHED_BOOST_ON, + 0x704, 0x4d04, /* Enable all CPUs */ + CPU0_MIN_FREQ_TURBO_MAX, CPU1_MIN_FREQ_TURBO_MAX, + CPU2_MIN_FREQ_TURBO_MAX, CPU3_MIN_FREQ_TURBO_MAX, + CPU4_MIN_FREQ_TURBO_MAX, CPU5_MIN_FREQ_TURBO_MAX, + CPU6_MIN_FREQ_TURBO_MAX, CPU7_MIN_FREQ_TURBO_MAX, +}; + +static int profile_power_save_8952[5] = { + 0x8fe, 0x3dfd, /* 1 big core, 2 little cores*/ + CPUS_ONLINE_MAX_LIMIT_2, + CPU0_MAX_FREQ_NONTURBO_MAX, CPU1_MAX_FREQ_NONTURBO_MAX, + CPU2_MAX_FREQ_NONTURBO_MAX, CPU3_MAX_FREQ_NONTURBO_MAX, +}; + +int get_number_of_profiles() { + return 3; +} + +static void set_power_profile(int profile) { + + if (profile == current_power_profile) + return; + + ALOGV("%s: profile=%d", __func__, profile); + + if (current_power_profile != PROFILE_BALANCED) { + undo_hint_action(DEFAULT_PROFILE_HINT_ID); + ALOGV("%s: hint undone", __func__); + } + + if (profile == PROFILE_HIGH_PERFORMANCE) { + int *resource_values = profile_high_performance_8952; + perform_hint_action(DEFAULT_PROFILE_HINT_ID, resource_values, + ARRAY_SIZE(resource_values)); + ALOGD("%s: set performance mode", __func__); + + } else if (profile == PROFILE_POWER_SAVE) { + int *resource_values = profile_power_save_8952; + perform_hint_action(DEFAULT_PROFILE_HINT_ID, resource_values, + ARRAY_SIZE(resource_values)); + ALOGD("%s: set powersave", __func__); + } + + current_power_profile = profile; +} + +int power_hint_override(struct power_module *module, power_hint_t hint, + void *data) +{ + int duration; + int resources_launch_boost[] = { + SCHED_BOOST_ON, + 0x20f, + 0x101, + 0x3e01, + 0x4001, + 0x4101, + 0x4201, + }; + int resources_cpu_boost[] = { + SCHED_BOOST_ON, + 0x20d, + 0x3e01, + 0x101, + }; + + if (hint == POWER_HINT_SET_PROFILE) { + set_power_profile(*(int32_t *)data); + return HINT_HANDLED; + } + + // Skip other hints in custom power modes + if (current_power_profile != PROFILE_BALANCED) { + return HINT_HANDLED; + } + + switch (hint) { + case POWER_HINT_LAUNCH_BOOST: + duration = 2000; + interaction(duration, ARRAY_SIZE(resources_launch_boost), + resources_launch_boost); + return HINT_HANDLED; + case POWER_HINT_CPU_BOOST: + duration = *(int32_t *)data / 1000; + if (duration > 0) { + interaction(duration, ARRAY_SIZE(resources_cpu_boost), + resources_cpu_boost); + } + return HINT_HANDLED; + case POWER_HINT_VIDEO_ENCODE: + process_video_encode_hint(data); + return HINT_HANDLED; + } + return HINT_NONE; +} + +int set_interactive_override(struct power_module *module, int on) +{ + char governor[80]; + + ALOGI("Got set_interactive hint"); + + if (get_scaling_governor_check_cores(governor, sizeof(governor),CPU0) == -1) { + if (get_scaling_governor_check_cores(governor, sizeof(governor),CPU1) == -1) { + if (get_scaling_governor_check_cores(governor, sizeof(governor),CPU2) == -1) { + if (get_scaling_governor_check_cores(governor, sizeof(governor),CPU3) == -1) { + ALOGE("Can't obtain scaling governor."); + return HINT_HANDLED; + } + } + } + } + + if (!on) { + /* Display off. */ + if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) && + (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) { + int resource_values[] = {TR_MS_CPU0_50, TR_MS_CPU4_50}; + + if (!display_hint_sent) { + perform_hint_action(DISPLAY_STATE_HINT_ID, + resource_values, ARRAY_SIZE(resource_values)); + display_hint_sent = 1; + } + } /* Perf time rate set for CORE0,CORE4 8952 target*/ + + } else { + /* Display on. */ + if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) && + (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) { + + undo_hint_action(DISPLAY_STATE_HINT_ID); + display_hint_sent = 0; + } + } + return HINT_HANDLED; +} + +/* Video Encode Hint */ +static void process_video_encode_hint(void *metadata) +{ + char governor[80]; + struct video_encode_metadata_t video_encode_metadata; + + ALOGI("Got process_video_encode_hint"); + + if (get_scaling_governor_check_cores(governor, + sizeof(governor),CPU0) == -1) { + if (get_scaling_governor_check_cores(governor, + sizeof(governor),CPU1) == -1) { + if (get_scaling_governor_check_cores(governor, + sizeof(governor),CPU2) == -1) { + if (get_scaling_governor_check_cores(governor, + sizeof(governor),CPU3) == -1) { + ALOGE("Can't obtain scaling governor."); + return HINT_HANDLED; + } + } + } + } + + /* Initialize encode metadata struct fields. */ + memset(&video_encode_metadata, 0, sizeof(struct video_encode_metadata_t)); + video_encode_metadata.state = -1; + video_encode_metadata.hint_id = DEFAULT_VIDEO_ENCODE_HINT_ID; + + if (metadata) { + if (parse_video_encode_metadata((char *)metadata, + &video_encode_metadata) == -1) { + ALOGE("Error occurred while parsing metadata."); + return; + } + } else { + return; + } + + if (video_encode_metadata.state == 1) { + if ((strncmp(governor, INTERACTIVE_GOVERNOR, + strlen(INTERACTIVE_GOVERNOR)) == 0) && + (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) { + int resource_values[] = {TR_MS_CPU0_30, TR_MS_CPU4_30}; + if (!video_encode_hint_sent) { + perform_hint_action(video_encode_metadata.hint_id, + resource_values, + ARRAY_SIZE(resource_values)); + video_encode_hint_sent = 1; + } + } + } else if (video_encode_metadata.state == 0) { + if ((strncmp(governor, INTERACTIVE_GOVERNOR, + strlen(INTERACTIVE_GOVERNOR)) == 0) && + (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) { + undo_hint_action(video_encode_metadata.hint_id); + video_encode_hint_sent = 0; + return ; + } + } + return; +} diff --git a/power/power-8960.c b/power/power-8960.c index 045eb12..bfff734 100644 --- a/power/power-8960.c +++ b/power/power-8960.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Copyright (c) 2015, The CyanogenMod Project * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -47,41 +48,101 @@ #include "performance.h" #include "power-common.h" -static int display_hint_sent; +static int current_power_profile = PROFILE_BALANCED; -int set_interactive_override(struct power_module *module, int on) +static int is_8064 = -1; + +int get_number_of_profiles() { + return 3; +} + +static int is_target_8064() /* Returns value=8064 if target is 8064 else value 0 */ { - char governor[80]; + int fd; + char buf[10] = {0}; - if (get_scaling_governor(governor, sizeof(governor)) == -1) { - ALOGE("Can't obtain scaling governor."); + if (is_8064 >= 0) + return is_8064; - return HINT_NONE; + fd = open("/sys/devices/system/soc/soc0/id", O_RDONLY); + if (fd >= 0) { + if (read(fd, buf, sizeof(buf) - 1) == -1) { + ALOGW("Unable to read soc_id"); + is_8064 = 0; + } else { + int soc_id = atoi(buf); + if (soc_id == 153) { + is_8064 = 8064; + } + } } + close(fd); + return is_8064; +} - if (!on) { - /* Display off. */ - if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) && - (strlen(governor) == strlen(ONDEMAND_GOVERNOR))) { - int resource_values[] = {MS_500, THREAD_MIGRATION_SYNC_OFF}; +static int profile_high_performance_8960[3] = { + CPUS_ONLINE_MIN_2, + CPU0_MIN_FREQ_TURBO_MAX, CPU1_MIN_FREQ_TURBO_MAX +}; - if (!display_hint_sent) { - perform_hint_action(DISPLAY_STATE_HINT_ID, - resource_values, sizeof(resource_values)/sizeof(resource_values[0])); - display_hint_sent = 1; - } +static int profile_high_performance_8064[5] = { + CPUS_ONLINE_MIN_4, + CPU0_MIN_FREQ_TURBO_MAX, CPU1_MIN_FREQ_TURBO_MAX, + CPU2_MIN_FREQ_TURBO_MAX, CPU3_MIN_FREQ_TURBO_MAX +}; - return HINT_HANDLED; - } - } else { - /* Display on */ - if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) && - (strlen(governor) == strlen(ONDEMAND_GOVERNOR))) { - undo_hint_action(DISPLAY_STATE_HINT_ID); - display_hint_sent = 0; - - return HINT_HANDLED; - } +static int profile_power_save_8960[2] = { + CPU0_MAX_FREQ_NONTURBO_MAX, CPU1_MAX_FREQ_NONTURBO_MAX +}; + +static int profile_power_save_8064[5] = { + CPUS_ONLINE_MAX_LIMIT_2, + CPU0_MAX_FREQ_NONTURBO_MAX, CPU1_MAX_FREQ_NONTURBO_MAX, + CPU2_MAX_FREQ_NONTURBO_MAX, CPU3_MAX_FREQ_NONTURBO_MAX +}; + +static void set_power_profile(int profile) { + + if (profile == current_power_profile) + return; + + ALOGV("%s: profile=%d", __func__, profile); + + if (current_power_profile != PROFILE_BALANCED) { + undo_hint_action(DEFAULT_PROFILE_HINT_ID); + ALOGV("%s: hint undone", __func__); + } + + if (profile == PROFILE_HIGH_PERFORMANCE) { + int *resource_values = is_target_8064() ? + profile_high_performance_8064 : profile_high_performance_8960; + + perform_hint_action(DEFAULT_PROFILE_HINT_ID, + resource_values, sizeof(resource_values)/sizeof(resource_values[0])); + ALOGD("%s: set performance mode", __func__); + } else if (profile == PROFILE_POWER_SAVE) { + int* resource_values = is_target_8064() ? + profile_power_save_8064 : profile_power_save_8960; + + perform_hint_action(DEFAULT_PROFILE_HINT_ID, + resource_values, sizeof(resource_values)/sizeof(resource_values[0])); + ALOGD("%s: set powersave", __func__); + } + + current_power_profile = profile; +} + +int power_hint_override(__attribute__((unused)) struct power_module *module, + power_hint_t hint, void *data) +{ + if (hint == POWER_HINT_SET_PROFILE) { + set_power_profile(*(int32_t *)data); + return HINT_HANDLED; + } + + // Skip other hints in custom power modes + if (current_power_profile != PROFILE_BALANCED) { + return HINT_HANDLED; } return HINT_NONE; diff --git a/power/power-8974.c b/power/power-8974.c index 9e561c0..b84b80b 100644 --- a/power/power-8974.c +++ b/power/power-8974.c @@ -53,14 +53,12 @@ static int display_hint2_sent; static int first_display_off_hint; extern int display_boost; -enum { - PROFILE_POWER_SAVE = 0, - PROFILE_BALANCED, - PROFILE_HIGH_PERFORMANCE -}; - static int current_power_profile = PROFILE_BALANCED; +int get_number_of_profiles() { + return 5; +} + static void set_power_profile(int profile) { if (profile == current_power_profile) @@ -74,15 +72,28 @@ static void set_power_profile(int profile) { } if (profile == PROFILE_HIGH_PERFORMANCE) { - int resource_values[] = { CPUS_ONLINE_MIN_4, + int resource_values[] = { CPUS_ONLINE_MIN_4, 0x0901, CPU0_MIN_FREQ_TURBO_MAX, CPU1_MIN_FREQ_TURBO_MAX, CPU2_MIN_FREQ_TURBO_MAX, CPU3_MIN_FREQ_TURBO_MAX }; perform_hint_action(DEFAULT_PROFILE_HINT_ID, resource_values, sizeof(resource_values)/sizeof(resource_values[0])); ALOGD("%s: set performance mode", __func__); - + } else if (profile == PROFILE_BIAS_PERFORMANCE) { + int resource_values[] = { + CPU0_MIN_FREQ_NONTURBO_MAX + 1, CPU1_MIN_FREQ_NONTURBO_MAX + 1, + CPU2_MIN_FREQ_NONTURBO_MAX + 1, CPU2_MIN_FREQ_NONTURBO_MAX + 1 }; + perform_hint_action(DEFAULT_PROFILE_HINT_ID, + resource_values, sizeof(resource_values)/sizeof(resource_values[0])); + ALOGD("%s: set bias perf mode", __func__); + } else if (profile == PROFILE_BIAS_POWER) { + int resource_values[] = { 0x0A03, + CPU0_MAX_FREQ_NONTURBO_MAX, CPU1_MAX_FREQ_NONTURBO_MAX, + CPU1_MAX_FREQ_NONTURBO_MAX, CPU2_MAX_FREQ_NONTURBO_MAX }; + perform_hint_action(DEFAULT_PROFILE_HINT_ID, + resource_values, sizeof(resource_values)/sizeof(resource_values[0])); + ALOGD("%s: set bias power mode", __func__); } else if (profile == PROFILE_POWER_SAVE) { - int resource_values[] = { CPUS_ONLINE_MAX_LIMIT_2, + int resource_values[] = { 0x0A03, CPUS_ONLINE_MAX_LIMIT_2, CPU0_MAX_FREQ_NONTURBO_MAX, CPU1_MAX_FREQ_NONTURBO_MAX, CPU2_MAX_FREQ_NONTURBO_MAX, CPU3_MAX_FREQ_NONTURBO_MAX }; perform_hint_action(DEFAULT_PROFILE_HINT_ID, @@ -99,20 +110,13 @@ int power_hint_override(__attribute__((unused)) struct power_module *module, power_hint_t hint, void *data) { if (hint == POWER_HINT_SET_PROFILE) { - set_power_profile((int)data); + set_power_profile(*(int32_t *)data); return HINT_HANDLED; } - if (hint == POWER_HINT_LOW_POWER) { - if (current_power_profile == PROFILE_POWER_SAVE) { - set_power_profile(PROFILE_BALANCED); - } else { - set_power_profile(PROFILE_POWER_SAVE); - } - } - - // Skip other hints in custom power modes - if (current_power_profile != PROFILE_BALANCED) { + // Skip other hints in high/low power modes + if (current_power_profile == PROFILE_POWER_SAVE || + current_power_profile == PROFILE_HIGH_PERFORMANCE) { return HINT_HANDLED; } @@ -128,8 +132,9 @@ int power_hint_override(__attribute__((unused)) struct power_module *module, } if (hint == POWER_HINT_CPU_BOOST) { - int duration = (int)data / 1000; - int resources[] = { CPUS_ONLINE_MIN_2, 0x20F, 0x30F }; + int duration = *(int32_t *)data / 1000; + int resources[] = { CPUS_ONLINE_MIN_2, + 0x20F, 0x30F, 0x40F, 0x50F }; if (duration) interaction(duration, sizeof(resources)/sizeof(resources[0]), resources); diff --git a/power/power-8992.c b/power/power-8992.c index d272841..8f855be 100644 --- a/power/power-8992.c +++ b/power/power-8992.c @@ -47,8 +47,12 @@ #include "performance.h" #include "power-common.h" +#define ARRAY_SIZE(arr) (sizeof((arr)) / sizeof((arr)[0])) + static int display_hint_sent; +extern void interaction(int duration, int num_args, int opt_list[]); + static int process_video_encode_hint(void *metadata) { char governor[80]; @@ -100,10 +104,24 @@ static int process_video_encode_hint(void *metadata) return HINT_NONE; } -int power_hint_override(struct power_module *module, power_hint_t hint, void *data) +int power_hint_override(__attribute__((unused)) struct power_module *module, + power_hint_t hint, void *data) { int ret_val = HINT_NONE; + int duration; + int resources_launch_boost[] = { + SCHED_BOOST_ON, + 0x20D, + }; + switch(hint) { + case POWER_HINT_LAUNCH_BOOST: + duration = 2000; + interaction(duration, ARRAY_SIZE(resources_launch_boost), + resources_launch_boost); + + ret_val = HINT_HANDLED; + break; case POWER_HINT_VIDEO_ENCODE: ret_val = process_video_encode_hint(data); break; @@ -113,7 +131,7 @@ int power_hint_override(struct power_module *module, power_hint_t hint, void *da return ret_val; } -int set_interactive_override(struct power_module *module, int on) +int set_interactive_override(__attribute__((unused)) struct power_module *module, int on) { char governor[80]; diff --git a/power/power-8994.c b/power/power-8994.c index ed38f1e..f98d6c6 100644 --- a/power/power-8994.c +++ b/power/power-8994.c @@ -49,14 +49,11 @@ static int display_hint_sent; -enum { - PROFILE_POWER_SAVE, - PROFILE_BALANCED, - PROFILE_HIGH_PERFORMANCE -}; +int get_number_of_profiles() { + return 5; +} static int current_power_profile = PROFILE_BALANCED; -static int low_power_mode = 0; static void set_power_profile(int profile) { @@ -71,7 +68,7 @@ static void set_power_profile(int profile) { } if (profile == PROFILE_POWER_SAVE) { - int resource_values[] = { CPUS_ONLINE_MAX_LIMIT_2, + int resource_values[] = { CPUS_ONLINE_MPD_OVERRIDE, 0x0A03, CPU0_MAX_FREQ_NONTURBO_MAX - 2, CPU1_MAX_FREQ_NONTURBO_MAX - 2, CPU2_MAX_FREQ_NONTURBO_MAX - 2, CPU3_MAX_FREQ_NONTURBO_MAX - 2, CPU4_MAX_FREQ_NONTURBO_MAX - 2, CPU5_MAX_FREQ_NONTURBO_MAX - 2, @@ -88,6 +85,22 @@ static void set_power_profile(int profile) { perform_hint_action(DEFAULT_PROFILE_HINT_ID, resource_values, sizeof(resource_values)/sizeof(resource_values[0])); ALOGD("%s: set performance mode", __func__); + } else if (profile == PROFILE_BIAS_POWER) { + int resource_values[] = { 0x0A03, 0x0902, + CPU0_MAX_FREQ_NONTURBO_MAX - 2, CPU1_MAX_FREQ_NONTURBO_MAX - 2, + CPU1_MAX_FREQ_NONTURBO_MAX - 2, CPU2_MAX_FREQ_NONTURBO_MAX - 2, + CPU4_MAX_FREQ_NONTURBO_MAX, CPU5_MAX_FREQ_NONTURBO_MAX, + CPU6_MAX_FREQ_NONTURBO_MAX, CPU7_MAX_FREQ_NONTURBO_MAX }; + perform_hint_action(DEFAULT_PROFILE_HINT_ID, + resource_values, sizeof(resource_values)/sizeof(resource_values[0])); + ALOGD("%s: set bias power mode", __func__); + } else if (profile == PROFILE_BIAS_PERFORMANCE) { + int resource_values[] = { CPUS_ONLINE_MAX_LIMIT_MAX, + CPU4_MIN_FREQ_NONTURBO_MAX + 1, CPU5_MIN_FREQ_NONTURBO_MAX + 1, + CPU6_MIN_FREQ_NONTURBO_MAX + 1, CPU7_MIN_FREQ_NONTURBO_MAX + 1 }; + perform_hint_action(DEFAULT_PROFILE_HINT_ID, + resource_values, sizeof(resource_values)/sizeof(resource_values[0])); + ALOGD("%s: set bias perf mode", __func__); } current_power_profile = profile; @@ -155,24 +168,14 @@ static int process_video_encode_hint(void *metadata) int power_hint_override(__attribute__((unused)) struct power_module *module, power_hint_t hint, void *data) { - if (hint == POWER_HINT_SET_PROFILE && !low_power_mode) { - set_power_profile((hintdata)data); - return HINT_HANDLED; - } - - if (hint == POWER_HINT_LOW_POWER) { - if (low_power_mode) { - set_power_profile(PROFILE_BALANCED); - low_power_mode = 0; - } else { - set_power_profile(PROFILE_POWER_SAVE); - low_power_mode = 1; - } + if (hint == POWER_HINT_SET_PROFILE) { + set_power_profile(*(int32_t *)data); return HINT_HANDLED; } // Skip other hints in custom power modes - if (current_power_profile != PROFILE_BALANCED) { + if (current_power_profile == PROFILE_POWER_SAVE || + current_power_profile == PROFILE_HIGH_PERFORMANCE) { return HINT_HANDLED; } @@ -194,7 +197,7 @@ int power_hint_override(__attribute__((unused)) struct power_module *module, } if (hint == POWER_HINT_CPU_BOOST) { - int duration = (hintdata)data / 1000; + int duration = *(int32_t *)data / 1000; int resources[] = { SCHED_BOOST_ON }; if (duration > 0) diff --git a/power/power-common.h b/power/power-common.h index 49c1f4b..967d7b0 100644 --- a/power/power-common.h +++ b/power/power-common.h @@ -48,3 +48,11 @@ enum CPU_GOV_CHECK { CPU2 = 2, CPU3 = 3 }; + +enum { + PROFILE_POWER_SAVE = 0, + PROFILE_BALANCED, + PROFILE_HIGH_PERFORMANCE, + PROFILE_BIAS_POWER, + PROFILE_BIAS_PERFORMANCE +}; diff --git a/power/power-feature-default.c b/power/power-feature-default.c new file mode 100644 index 0000000..c432aa8 --- /dev/null +++ b/power/power-feature-default.c @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2015 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "power-feature.h" + +void set_device_specific_feature(struct power_module *module __unused, + feature_t feature __unused, int state __unused) +{ +} + diff --git a/power/power-feature.h b/power/power-feature.h new file mode 100644 index 0000000..595e3dd --- /dev/null +++ b/power/power-feature.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2015 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _QCOM_POWER_FEATURE_H +#define _QCOM_POWER_FEATURE_H + +#include + +void set_device_specific_feature(struct power_module *module, feature_t feature, int state); + +#endif diff --git a/power/power.c b/power/power.c index c489dc2..1c2b84f 100644 --- a/power/power.c +++ b/power/power.c @@ -49,6 +49,7 @@ #include "hint-data.h" #include "performance.h" #include "power-common.h" +#include "power-feature.h" static int saved_dcvs_cpu0_slack_max = -1; static int saved_dcvs_cpu0_slack_min = -1; @@ -228,6 +229,7 @@ static void power_hint(__attribute__((unused)) struct power_module *module, powe case POWER_HINT_VIDEO_DECODE: process_video_decode_hint(data); break; + default: break; } @@ -242,6 +244,11 @@ int __attribute__ ((weak)) set_interactive_override( return HINT_NONE; } +int __attribute__ ((weak)) get_number_of_profiles() +{ + return 0; +} + #ifdef SET_INTERACTIVE_EXT extern void cm_power_set_interactive_ext(int on); #endif @@ -251,7 +258,7 @@ void set_interactive(struct power_module *module, int on) char governor[80]; char tmp_str[NODE_MAX]; struct video_encode_metadata_t video_encode_metadata; - int rc; + int rc = 0; pthread_mutex_lock(&hint_mutex); @@ -283,7 +290,7 @@ void set_interactive(struct power_module *module, int on) } } else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) && (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) { - int resource_values[] = {THREAD_MIGRATION_SYNC_OFF}; + int resource_values[] = {TR_MS_50, THREAD_MIGRATION_SYNC_OFF}; if (!display_hint_sent) { perform_hint_action(DISPLAY_STATE_HINT_ID, @@ -459,10 +466,31 @@ void set_interactive(struct power_module *module, int on) pthread_mutex_unlock(&hint_mutex); } +void set_feature(struct power_module *module, feature_t feature, int state) +{ +#ifdef TAP_TO_WAKE_NODE + char tmp_str[NODE_MAX]; + if (feature == POWER_FEATURE_DOUBLE_TAP_TO_WAKE) { + snprintf(tmp_str, NODE_MAX, "%d", state); + sysfs_write(TAP_TO_WAKE_NODE, tmp_str); + return; + } +#endif + set_device_specific_feature(module, feature, state); +} + +int get_feature(struct power_module *module __unused, feature_t feature) +{ + if (feature == POWER_FEATURE_SUPPORTED_PROFILES) { + return get_number_of_profiles(); + } + return -1; +} + struct power_module HAL_MODULE_INFO_SYM = { .common = { .tag = HARDWARE_MODULE_TAG, - .module_api_version = POWER_MODULE_API_VERSION_0_2, + .module_api_version = POWER_MODULE_API_VERSION_0_3, .hal_api_version = HARDWARE_HAL_API_VERSION, .id = POWER_HARDWARE_MODULE_ID, .name = "QCOM Power HAL", @@ -473,4 +501,6 @@ struct power_module HAL_MODULE_INFO_SYM = { .init = power_init, .powerHint = power_hint, .setInteractive = set_interactive, + .setFeature = set_feature, + .getFeature = get_feature };