#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "RegEdit.h" #include "ida.h" #include "x11.h" #include "man.h" #include "readers.h" #include "writers.h" #include "viewer.h" #include "op.h" #include "lut.h" #include "filter.h" #include "color.h" #include "icons.h" #include "browser.h" #include "filelist.h" #include "xdnd.h" #include "selections.h" #include "sane.h" #include "curl.h" #include "idaconfig.h" /* ---------------------------------------------------------------------- */ static void popup_ac(Widget, XEvent*, String*, Cardinal*); static void exit_ac(Widget, XEvent*, String*, Cardinal*); static void next_ac(Widget, XEvent*, String*, Cardinal*); static void prev_ac(Widget, XEvent*, String*, Cardinal*); static void next_page_ac(Widget, XEvent*, String*, Cardinal*); static void prev_page_ac(Widget, XEvent*, String*, Cardinal*); static void zoom_ac(Widget, XEvent*, String*, Cardinal*); static void scroll_ac(Widget, XEvent*, String*, Cardinal*); static void debug_ac(Widget, XEvent*, String*, Cardinal*); static void load_ac(Widget, XEvent*, String*, Cardinal*); static void save_ac(Widget, XEvent*, String*, Cardinal*); static void scan_ac(Widget, XEvent*, String*, Cardinal*); static void print_ac(Widget, XEvent*, String*, Cardinal*); static void undo_ac(Widget, XEvent*, String*, Cardinal*); static void filter_ac(Widget, XEvent*, String*, Cardinal*); static void gamma_ac(Widget, XEvent*, String*, Cardinal*); static void bright_ac(Widget, XEvent*, String*, Cardinal*); static void contrast_ac(Widget, XEvent*, String*, Cardinal*); static void color_ac(Widget, XEvent*, String*, Cardinal*); static void f3x3_ac(Widget, XEvent*, String*, Cardinal*); static void resize_ac(Widget, XEvent*, String*, Cardinal*); static void rotate_ac(Widget, XEvent*, String*, Cardinal*); static void sharpe_ac(Widget, XEvent*, String*, Cardinal*); static XtActionsRec actionTable[] = { { "Exit", exit_ac }, { "Next", next_ac }, { "Prev", prev_ac }, { "NextPage", next_page_ac }, { "PrevPage", prev_page_ac }, { "Zoom", zoom_ac }, { "Scroll", scroll_ac }, { "Debug", debug_ac }, { "Popup", popup_ac }, { "Man", man_action }, { "Load", load_ac }, { "Save", save_ac }, { "Scan", scan_ac }, { "Print", print_ac }, { "Browser", browser_ac }, { "Filelist", filelist_ac }, { "Undo", undo_ac }, { "Filter", filter_ac }, { "Gamma", gamma_ac }, { "Bright", bright_ac }, { "Contrast", contrast_ac }, { "Color", color_ac }, { "F3x3", f3x3_ac }, { "Resize", resize_ac }, { "Rotate", rotate_ac }, { "Sharpe", sharpe_ac }, { "Ipc", ipc_ac }, { "Xdnd", XdndAction }, }; /* ---------------------------------------------------------------------- */ XtAppContext app_context; Display *dpy; Widget app_shell; int gray=0; char *binary; struct ida_viewer *ida; /* ---------------------------------------------------------------------- */ struct ARGS args; unsigned int pcd_res; unsigned int sane_res; XtResource args_desc[] = { { "debug", XtCBoolean, XtRBoolean, sizeof(Boolean), XtOffset(struct ARGS*,debug), XtRString, "false" },{ "help", XtCBoolean, XtRBoolean, sizeof(Boolean), XtOffset(struct ARGS*,help), XtRString, "false" },{ "testload", XtCBoolean, XtRBoolean, sizeof(Boolean), XtOffset(struct ARGS*,testload), XtRString, "false" } }; const int args_count = XtNumber(args_desc); XrmOptionDescRec opt_desc[] = { { "-d", "debug", XrmoptionNoArg, "true" }, { "-debug", "debug", XrmoptionNoArg, "true" }, { "-testload", "testload", XrmoptionNoArg, "true" }, { "-h", "help", XrmoptionNoArg, "true" }, { "-help", "help", XrmoptionNoArg, "true" }, { "--help", "help", XrmoptionNoArg, "true" }, }; const int opt_count = (sizeof(opt_desc)/sizeof(XrmOptionDescRec)); static String fallback_ressources[] = { #include "Ida.ad.h" NULL }; /* ---------------------------------------------------------------------- */ static struct ida_writer *cwriter; static char *save_filename; static char *print_command; static Widget control_shell,status; static Atom wm_delete_window; static Widget view,loadbox,savebox,printbox; /* file list */ static Widget wlist; static char **files = NULL; static int cfile = -1; static int nfiles = 0; static int cpage = 0; static int npages = 1; /* filter controls */ static int gamma_val = 100; static int bright_val = 0; static int contrast_val = 0; static int rotate_val = 0; static int sharpe_val = 10; static struct MY_TOPLEVELS { char *name; Widget *shell; int mapped; } my_toplevels [] = { { "control", &control_shell }, }; #define TOPLEVELS (sizeof(my_toplevels)/sizeof(struct MY_TOPLEVELS)) /* ---------------------------------------------------------------------- */ static void popup_ac(Widget widget, XEvent *event, String *params, Cardinal *num_params) { unsigned int i; /* which window we are talking about ? */ if (*num_params > 0) { for (i = 0; i < TOPLEVELS; i++) { if (0 == strcasecmp(my_toplevels[i].name,params[0])) break; } if (i == TOPLEVELS) { fprintf(stderr,"PopupAction: oops: shell not found (name=%s)\n", params[0]); return; } } else { for (i = 0; i < TOPLEVELS; i++) { if (*(my_toplevels[i].shell) == widget) break; } if (i == TOPLEVELS) { fprintf(stderr,"PopupAction: oops: shell not found (%p:%s)\n", widget,XtName(widget)); return; } } /* popup/down window */ if (!my_toplevels[i].mapped) { XtPopup(*(my_toplevels[i].shell), XtGrabNone); my_toplevels[i].mapped = 1; } else { XtPopdown(*(my_toplevels[i].shell)); my_toplevels[i].mapped = 0; } } static void popupdown_cb(Widget widget, XtPointer clientdata, XtPointer call_data) { int i = 0; popup_ac(clientdata, NULL, NULL, &i); } /* ---------------------------------------------------------------------- */ void destroy_cb(Widget widget, XtPointer clientdata, XtPointer call_data) { XtDestroyWidget(clientdata); } void action_cb(Widget widget, XtPointer clientdata, XtPointer call_data) { char *calls, *action, *argv[16]; /* max: F3x3(9 args) */ int argc; calls = strdup(clientdata); action = strtok(calls,"(),"); for (argc = 0; NULL != (argv[argc] = strtok(NULL,"(),")); argc++) /* nothing */; XtCallActionProc(widget,action,NULL,argv,argc); free(calls); } static void about_cb(Widget widget, XtPointer clientdata, XtPointer call_data) { Widget msgbox; msgbox = XmCreateInformationDialog(app_shell,"aboutbox",NULL,0); XtUnmanageChild(XmMessageBoxGetChild(msgbox,XmDIALOG_HELP_BUTTON)); XtUnmanageChild(XmMessageBoxGetChild(msgbox,XmDIALOG_CANCEL_BUTTON)); XtAddCallback(msgbox,XmNokCallback,destroy_cb,msgbox); XtManageChild(msgbox); } #if 0 static void sorry_cb(Widget widget, XtPointer clientdata, XtPointer call_data) { Widget msgbox; msgbox = XmCreateErrorDialog(app_shell,"sorrybox",NULL,0); XtUnmanageChild(XmMessageBoxGetChild(msgbox,XmDIALOG_HELP_BUTTON)); XtUnmanageChild(XmMessageBoxGetChild(msgbox,XmDIALOG_CANCEL_BUTTON)); XtAddCallback(msgbox,XmNokCallback,destroy_cb,msgbox); XtManageChild(msgbox); } #endif void debug_ac(Widget widget, XEvent *event, String *params, Cardinal *num) { unsigned int i; fprintf(stderr,"Debug:"); for (i = 0; i < *num; i++) fprintf(stderr," %s",params[i]); fprintf(stderr,"\n"); } static void display_cb(Widget widget, XtPointer clientdata, XtPointer call_data) { XmDisplayCallbackStruct *arg = call_data; switch (arg->reason) { case XmCR_NO_RENDITION: fprintf(stderr,"display_cb: no rendition: \"%s\"\n",arg->tag); break; case XmCR_NO_FONT: fprintf(stderr,"display_cb: no font: \"%s\"\n",arg->font_name); break; default: /* should not happen */ fprintf(stderr,"display_cb: unknown reason [%d]\n",arg->reason); break; } } /* ---------------------------------------------------------------------- */ struct ptr_list { struct ptr_list *next; Widget widget; }; struct ptr_list *ptr_head; void ptr_register(Widget widget) { struct ptr_list *item; if (XtWindow(widget)) XDefineCursor(XtDisplay(widget), XtWindow(widget), ptrs[POINTER_NORMAL]); item = malloc(sizeof(*item)); memset(item,0,sizeof(*item)); item->widget = widget; item->next = ptr_head; ptr_head = item; } void ptr_unregister(Widget widget) { struct ptr_list *item,*fitem; if (ptr_head->widget == widget) { fitem = ptr_head; ptr_head = ptr_head->next; free(fitem); return; } for (item = ptr_head; NULL != item->next; item = item->next) { if (item->next->widget == widget) { fitem = item->next; item->next = fitem->next; free(fitem); return; } } /* shouldn't happen */ fprintf(stderr,"Oops: widget not found in list\n"); } void ptr_busy(void) { struct ptr_list *item; for (item = ptr_head; NULL != item; item = item->next) { if (!XtWindow(item->widget)) continue; XDefineCursor(XtDisplay(item->widget), XtWindow(item->widget), ptrs[POINTER_BUSY]); } XSync(dpy,False); } void ptr_idle(void) { struct ptr_list *item; for (item = ptr_head; NULL != item; item = item->next) { if (!XtWindow(item->widget)) continue; XDefineCursor(XtDisplay(item->widget), XtWindow(item->widget), ptrs[POINTER_NORMAL]); } } /* ---------------------------------------------------------------------- */ static Boolean exit_wp(XtPointer client_data) { exit(0); } static void exit_cb(Widget widget, XtPointer client_data, XtPointer calldata) { XtAppAddWorkProc(app_context,exit_wp, NULL); XtDestroyWidget(app_shell); } void exit_ac(Widget widget, XEvent *event, String *params, Cardinal *num) { exit_cb(widget,NULL,NULL); } /* ---------------------------------------------------------------------- */ static void list_update(void) { XmStringTable tab; int i; tab = malloc(nfiles * sizeof(XmString)); for (i = 0; i < nfiles; i++) tab[i] = XmStringGenerate(files[i], NULL, XmMULTIBYTE_TEXT, NULL); XtVaSetValues(wlist, XmNitems, tab, XmNitemCount, nfiles, NULL); for (i = 0; i < nfiles; i++) XmStringFree(tab[i]); free(tab); } static int list_append(char *filename) { int i; for (i = 0; i < nfiles; i++) if (0 == strcmp(files[i],filename)) return i; files = realloc(files,sizeof(char*)*(nfiles+1)); files[nfiles] = strdup(filename); nfiles++; return nfiles-1; } static void list_cb(Widget widget, XtPointer client_data, XtPointer calldata) { XmListCallbackStruct *list = calldata; if (0 == list->selected_item_count) return; cfile = list->selected_item_positions[0]-1; cpage = 0; npages = viewer_loadimage(ida,files[cfile],cpage); if (-1 == npages) return; resize_shell(); } static void pcd_set(Widget widget) { WidgetList items; Cardinal nitems; unsigned int i; int value; value = GET_PHOTOCD_RES(); XtVaGetValues(widget,XtNchildren,&items, XtNnumChildren,&nitems,NULL); for (i = 0; i < nitems; i++) XmToggleButtonSetState(items[i],value == i+1,False); pcd_res = value; } static void pcd_cb(Widget widget, XtPointer client_data, XtPointer calldata) { cfg_set_int(O_PHOTOCD_RES,(intptr_t)client_data); pcd_set(XtParent(widget)); } static void cfg_bool_cb(Widget widget, XtPointer client_data, XtPointer calldata) { char *option = XtName(widget); Boolean value = XmToggleButtonGetState(widget); cfg_set_bool(O_OPTIONS, option, value); } static void cfg_save_cb(Widget widget, XtPointer client_data, XtPointer calldata) { ida_write_config(); } static void create_control(void) { Widget form,menubar,tool,menu,smenu,push; control_shell = XtVaAppCreateShell("ctrl","Iv", topLevelShellWidgetClass, dpy, XtNclientLeader,app_shell, XmNdeleteResponse,XmDO_NOTHING, NULL); XmdRegisterEditres(control_shell); XmAddWMProtocolCallback(control_shell,wm_delete_window, popupdown_cb,control_shell); /* widgets */ form = XtVaCreateManagedWidget("form", xmFormWidgetClass, control_shell, NULL); menubar = XmCreateMenuBar(form,"bar",NULL,0); XtManageChild(menubar); tool = XtVaCreateManagedWidget("tool",xmRowColumnWidgetClass, form, NULL); status = XtVaCreateManagedWidget("status", xmLabelWidgetClass, form, NULL); wlist = XmCreateScrolledList(form,"list",NULL,0); XtManageChild(wlist); XtAddCallback(wlist,XmNdefaultActionCallback,list_cb,NULL); XtAddCallback(wlist,XmNdestinationCallback,selection_dest,NULL); dnd_add(wlist); /* menu - file */ menu = XmCreatePulldownMenu(menubar,"fileM",NULL,0); XtVaCreateManagedWidget("file",xmCascadeButtonWidgetClass,menubar, XmNsubMenuId,menu,NULL); push = XtVaCreateManagedWidget("load",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"Load()"); push = XtVaCreateManagedWidget("save",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"Save()"); push = XtVaCreateManagedWidget("browse",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"Browser()"); push = XtVaCreateManagedWidget("filelist",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"Filelist()"); XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL); #ifdef HAVE_LIBSANE sane_menu(menu); #endif push = XtVaCreateManagedWidget("print",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"Print()"); XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL); push = XtVaCreateManagedWidget("quit",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,exit_cb,NULL); /* menu - edit */ menu = XmCreatePulldownMenu(menubar,"editM",NULL,0); XtVaCreateManagedWidget("edit",xmCascadeButtonWidgetClass,menubar, XmNsubMenuId,menu,NULL); push = XtVaCreateManagedWidget("undo",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"Undo()"); XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL); push = XtVaCreateManagedWidget("copy",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"Ipc(copy)"); push = XtVaCreateManagedWidget("paste",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"Ipc(paste)"); XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL); push = XtVaCreateManagedWidget("flipv",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"Filter(flip-vert)"); push = XtVaCreateManagedWidget("fliph",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"Filter(flip-horz)"); push = XtVaCreateManagedWidget("rotcw",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"Filter(rotate-cw)"); push = XtVaCreateManagedWidget("rotccw",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"Filter(rotate-ccw)"); push = XtVaCreateManagedWidget("invert",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"Filter(invert)"); XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL); push = XtVaCreateManagedWidget("crop",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"Filter(crop)"); push = XtVaCreateManagedWidget("acrop",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"Filter(autocrop)"); push = XtVaCreateManagedWidget("scale",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"Resize()"); push = XtVaCreateManagedWidget("rotany",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"Rotate()"); /* menu - filters / operations */ menu = XmCreatePulldownMenu(menubar,"opM",NULL,0); XtVaCreateManagedWidget("op",xmCascadeButtonWidgetClass,menubar, XmNsubMenuId,menu,NULL); push = XtVaCreateManagedWidget("gamma",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"Gamma()"); push = XtVaCreateManagedWidget("bright",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"Bright()"); push = XtVaCreateManagedWidget("contr",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"Contrast()"); push = XtVaCreateManagedWidget("color",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"Color()"); push = XtVaCreateManagedWidget("gray",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"Filter(grayscale)"); XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL); push = XtVaCreateManagedWidget("blur",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,action_cb, "F3x3(1,1,1, 1,1,1, 1,1,1, 1,9,0)"); push = XtVaCreateManagedWidget("sharpe",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"Sharpe()"); push = XtVaCreateManagedWidget("edge",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,action_cb, "F3x3(-1,-1,-1, -1,8,-1, -1,-1,-1)"); push = XtVaCreateManagedWidget("emboss",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,action_cb, "F3x3(1,0,0, 0,0,0, 0,0,-1, 0,0,128)"); /* menu - view */ menu = XmCreatePulldownMenu(menubar,"viewM",NULL,0); XtVaCreateManagedWidget("view",xmCascadeButtonWidgetClass,menubar, XmNsubMenuId,menu,NULL); push = XtVaCreateManagedWidget("prev",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"Prev()"); push = XtVaCreateManagedWidget("next",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"Next()"); XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL); push = XtVaCreateManagedWidget("prevpage",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"PrevPage()"); push = XtVaCreateManagedWidget("nextpage",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"NextPage()"); XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL); push = XtVaCreateManagedWidget("zoomin",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"Zoom(inc)"); push = XtVaCreateManagedWidget("zoomout",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"Zoom(dec)"); /* menu - options */ menu = XmCreatePulldownMenu(menubar,"optM",NULL,0); push = XtVaCreateManagedWidget("opt",xmCascadeButtonWidgetClass,menubar, XmNsubMenuId,menu,NULL); smenu = XmCreatePulldownMenu(menu,"pcdM",NULL,0); XtVaCreateManagedWidget("pcd",xmCascadeButtonWidgetClass,menu, XmNsubMenuId,smenu,NULL); push = XtVaCreateManagedWidget("1",xmToggleButtonWidgetClass,smenu,NULL); XtAddCallback(push,XmNvalueChangedCallback,pcd_cb,(XtPointer)1); push = XtVaCreateManagedWidget("2",xmToggleButtonWidgetClass,smenu,NULL); XtAddCallback(push,XmNvalueChangedCallback,pcd_cb,(XtPointer)2); push = XtVaCreateManagedWidget("3",xmToggleButtonWidgetClass,smenu,NULL); XtAddCallback(push,XmNvalueChangedCallback,pcd_cb,(XtPointer)3); push = XtVaCreateManagedWidget("4",xmToggleButtonWidgetClass,smenu,NULL); XtAddCallback(push,XmNvalueChangedCallback,pcd_cb,(XtPointer)4); push = XtVaCreateManagedWidget("5",xmToggleButtonWidgetClass,smenu,NULL); XtAddCallback(push,XmNvalueChangedCallback,pcd_cb,(XtPointer)5); pcd_set(smenu); push = XtVaCreateManagedWidget("autozoom",xmToggleButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNvalueChangedCallback,cfg_bool_cb,NULL); XmToggleButtonSetState(push,GET_AUTOZOOM(),False); XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL); push = XtVaCreateManagedWidget("cfgsave",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,cfg_save_cb,NULL); /* menu - help */ menu = XmCreatePulldownMenu(menubar,"helpM",NULL,0); push = XtVaCreateManagedWidget("help",xmCascadeButtonWidgetClass,menubar, XmNsubMenuId,menu,NULL); XtVaSetValues(menubar,XmNmenuHelpWidget,push,NULL); push = XtVaCreateManagedWidget("man",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"Man(ida)"); XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL); push = XtVaCreateManagedWidget("about",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,about_cb,NULL); /* toolbar */ push = XtVaCreateManagedWidget("prev",xmPushButtonWidgetClass,tool,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"Prev()"); push = XtVaCreateManagedWidget("next",xmPushButtonWidgetClass,tool,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"Next()"); push = XtVaCreateManagedWidget("zoomin",xmPushButtonWidgetClass,tool,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"Zoom(inc)"); push = XtVaCreateManagedWidget("zoomout",xmPushButtonWidgetClass,tool,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"Zoom(dec)"); XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,tool,NULL); push = XtVaCreateManagedWidget("flipv",xmPushButtonWidgetClass,tool,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"Filter(flip-vert)"); push = XtVaCreateManagedWidget("fliph",xmPushButtonWidgetClass,tool,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"Filter(flip-horz)"); push = XtVaCreateManagedWidget("rotccw",xmPushButtonWidgetClass,tool,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"Filter(rotate-ccw)"); push = XtVaCreateManagedWidget("rotcw",xmPushButtonWidgetClass,tool,NULL); XtAddCallback(push,XmNactivateCallback,action_cb,"Filter(rotate-cw)"); XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,tool,NULL); push = XtVaCreateManagedWidget("exit",xmPushButtonWidgetClass,tool,NULL); XtAddCallback(push,XmNactivateCallback,exit_cb,NULL); } /* ---------------------------------------------------------------------- */ void resize_shell(void) { char *title,*base; Dimension x,y,w,h,sw,sh; XmString str; int len; XtVaGetValues(app_shell, XtNx,&x, XtNy,&y, NULL); /* resize shell + move shell size: image size + 2*shadowThickness */ w = ida->scrwidth+2; h = ida->scrheight+2; sw = XtScreen(ida->widget)->width; sh = XtScreen(ida->widget)->height; if (w > sw) w = sw; if (h > sh) h = sh; if (x+w > sw) x = sw-w; if (y+h > sh) y = sh-h; base = strrchr(ida->file,'/'); if (base) base++; else base = ida->file; title = malloc(strlen(base)+128); len = sprintf(title,"%s (%dx%d", base, ida->img.i.width, ida->img.i.height); if (ida->img.i.dpi) len += sprintf(title+len," | %d dpi", ida->img.i.dpi); if (ida->img.i.npages > 1) len += sprintf(title+len," | page %d/%d", cpage+1, ida->img.i.npages); len += sprintf(title+len," | %d%%)", viewer_i2s(ida->zoom,100)); XtVaSetValues(app_shell, XtNtitle,title, /* XtNx,x, XtNy,y, */ XtNwidth,w, XtNheight,h, NULL); str = XmStringGenerate(title,NULL,XmMULTIBYTE_TEXT,NULL); XtVaSetValues(status,XmNlabelString,str,NULL); XmStringFree(str); free(title); } static int load_file(int nr, int np) { if(nr < 0 || nr >= nfiles) return -1; npages = viewer_loadimage(ida,files[nr],np); if (-1 == npages) return -1; resize_shell(); #if 0 XmListSelectPos(wlist,nr+1,False); cfile = nr; #endif return npages; } char* load_tmpfile(char *base) { char *tmpdir; char *filename; tmpdir = getenv("TMPDIR"); if (NULL == tmpdir) tmpdir="/tmp"; filename = malloc(strlen(tmpdir)+strlen(base)+16); sprintf(filename,"%s/%s-XXXXXX",tmpdir,base); return filename; } static void load_logo(void) { static unsigned char logo[] = { #include "logo.h" }; char *filename = load_tmpfile("ida-logo"); int fd; fd = mkstemp(filename); write(fd,logo,sizeof(logo)); close(fd); cpage = 0; npages = 1; if (0 < viewer_loadimage(ida,filename,cpage)) { ida->file = "ida " VERSION; resize_shell(); } unlink(filename); free(filename); } static void load_stdin(void) { char *filename = load_tmpfile("ida-stdin"); char buf[4096]; int rc,fd; fd = mkstemp(filename); for (;;) { rc = read(0,buf,sizeof(buf)); if (rc <= 0) break; write(fd,buf,rc); } close(fd); cpage = 0; npages = 1; if (0 < viewer_loadimage(ida,filename,cpage)) { ida->file = "stdin"; resize_shell(); } unlink(filename); free(filename); } void next_ac(Widget widget, XEvent *event, String *params, Cardinal *num) { for (;;) { if (cfile >= nfiles-1) return; cfile++; cpage = 0; if (0 <= load_file(cfile,cpage)) break; } } void prev_ac(Widget widget, XEvent *event, String *params, Cardinal *num) { for (;;) { if (cfile < 1) return; cfile--; cpage = 0; if (0 <= load_file(cfile,cpage)) break; } } void next_page_ac(Widget widget, XEvent *event, String *params, Cardinal *num) { for (;;) { if (cpage >= npages-1) return; cpage++; if (0 <= load_file(cfile,cpage)) break; } } void prev_page_ac(Widget widget, XEvent *event, String *params, Cardinal *num) { for (;;) { if (cpage <= 0) return; cpage--; if (0 <= load_file(cfile,cpage)) break; } } void zoom_ac(Widget widget, XEvent *event, String *params, Cardinal *num) { int zoom; if (0 == *num) return; if (0 == strcasecmp(params[0],"auto")) { viewer_autozoom(ida); return; } if (0 == strcasecmp(params[0],"inc")) { zoom = ida->zoom+1; } else if (0 == strcasecmp(params[0],"dec")) { zoom = ida->zoom-1; } else { zoom = atoi(params[0]); } viewer_setzoom(ida,zoom); resize_shell(); } void scroll_ac(Widget widget, XEvent *event, String *params, Cardinal *num) { fprintf(stderr,"Scroll(): %s\n",XtName(widget)); } /* ---------------------------------------------------------------------- */ void new_file(char *name, int complain) { struct stat st; int n; if (curl_is_url(name)) goto load; if (0 == strncasecmp(name,"file:",5)) name += 5; if (-1 == stat(name,&st)) { if (complain) fprintf(stderr,"stat %s: %s\n",name,strerror(errno)); return; } switch (st.st_mode & S_IFMT) { case S_IFDIR: browser_window(name); break; case S_IFREG: goto load; break; } return; load: n = list_append(name); list_update(); cpage = 0; load_file(n,cpage); } static void load_done_cb(Widget widget, XtPointer clientdata, XtPointer call_data) { XmFileSelectionBoxCallbackStruct *cb = call_data; char *line; if (cb->reason == XmCR_OK) { line = XmStringUnparse(cb->value,NULL, XmMULTIBYTE_TEXT,XmMULTIBYTE_TEXT, NULL,0,0); new_file(line,1); } XtUnmanageChild(widget); } void load_ac(Widget widget, XEvent *event, String *params, Cardinal *num) { Widget help; if (NULL == loadbox) { loadbox = XmCreateFileSelectionDialog(app_shell,"load",NULL,0); help = XmFileSelectionBoxGetChild(loadbox,XmDIALOG_HELP_BUTTON); XtUnmanageChild(help); XtAddCallback(loadbox,XmNokCallback,load_done_cb,NULL); XtAddCallback(loadbox,XmNcancelCallback,load_done_cb,NULL); } else { XmFileSelectionDoSearch(loadbox,NULL); } XtManageChild(loadbox); } void scan_ac(Widget widget, XEvent *event, String *params, Cardinal *num) { #ifdef HAVE_LIBSANE cpage = 0; if (*num) npages = viewer_loader_start(ida, &sane_loader, NULL, params[0], 0); else npages = viewer_loader_start(ida, &sane_loader, NULL, "", 0); if (-1 == npages) return; ida->file = "scanned image"; resize_shell(); #endif } /* ---------------------------------------------------------------------- */ void do_save_print(void) { FILE *fp; if (save_filename) { XtUnmanageChild(savebox); ptr_busy(); if (NULL == (fp = fopen(save_filename,"wb"))) { fprintf(stderr,"save: can't open %s: %s\n", save_filename,strerror(errno)); } else if (-1 == cwriter->write(fp,&ida->img)) { fclose(fp); fprintf(stderr,"saving %s FAILED",save_filename); } else { fclose(fp); list_append(save_filename); list_update(); } ptr_idle(); } if (print_command) { XtUnmanageChild(printbox); ptr_busy(); if (NULL == (fp = popen(print_command,"w"))) { fprintf(stderr,"print: can't exec %s: %s\n", print_command,strerror(errno)); } else { if (-1 == cwriter->write(fp,&ida->img)) fprintf(stderr,"printing FAILED"); fclose(fp); } ptr_idle(); } } static void save_done_cb(Widget widget, XtPointer clientdata, XtPointer call_data) { XmFileSelectionBoxCallbackStruct *cb = call_data; if (cb->reason == XmCR_OK) { print_command = NULL; save_filename = XmStringUnparse(cb->value,NULL, XmMULTIBYTE_TEXT,XmMULTIBYTE_TEXT, NULL,0,0); if (cwriter->conf) { cwriter->conf(widget,&ida->img); } else { do_save_print(); } } else { XtUnmanageChild(widget); } } static void save_fmt_cb(Widget widget, XtPointer clientdata, XtPointer call_data) { cwriter = clientdata; } static void save_ext_cb(Widget widget, XtPointer clientdata, XtPointer call_data) { Widget option = clientdata; Widget menu; WidgetList children; Cardinal nchildren; struct ida_writer *wr = NULL; struct list_head *item; char *name,*ext; int i,j,pick; name = XmTextGetString(widget); ext = strrchr(name,'.'); if (NULL == ext) return; if (strchr(ext,'/')) return; ext++; i = 0; pick = -1; list_for_each(item,&writers) { wr = list_entry(item, struct ida_writer, list); for (j = 0; NULL != wr->ext[j]; j++) if (0 == strcasecmp(ext,wr->ext[j])) pick = i; if (-1 != pick) break; i++; } if (-1 == pick) return; XtVaGetValues(option,XmNsubMenuId,&menu,NULL); XtVaGetValues(menu,XtNchildren,&children, XtNnumChildren,&nchildren,NULL); XtVaSetValues(option,XmNmenuHistory,children[pick],NULL); cwriter = wr; } static void save_ac(Widget widget, XEvent *event, String *params, Cardinal *num) { Widget help,menu,option,push,text; Arg args[2]; struct ida_writer *wr = NULL; struct list_head *item; if (NULL == savebox) { savebox = XmCreateFileSelectionDialog(app_shell,"save",NULL,0); help = XmFileSelectionBoxGetChild(savebox,XmDIALOG_HELP_BUTTON); text = XmFileSelectionBoxGetChild(savebox,XmDIALOG_TEXT); XtUnmanageChild(help); menu = XmCreatePulldownMenu(savebox,"formatM",NULL,0); XtSetArg(args[0],XmNsubMenuId,menu); option = XmCreateOptionMenu(savebox,"format",args,1); XtManageChild(option); list_for_each(item,&writers) { wr = list_entry(item, struct ida_writer, list); push = XtVaCreateManagedWidget(wr->label, xmPushButtonWidgetClass,menu, NULL); XtAddCallback(push,XmNactivateCallback,save_fmt_cb,wr); } cwriter = list_entry(writers.next, struct ida_writer, list); XtAddCallback(text,XmNvalueChangedCallback,save_ext_cb,option); XtAddCallback(savebox,XmNokCallback,save_done_cb,NULL); XtAddCallback(savebox,XmNcancelCallback,save_done_cb,NULL); } else { XmFileSelectionDoSearch(savebox,NULL); } XtManageChild(savebox); } /* ---------------------------------------------------------------------- */ static void print_done_cb(Widget widget, XtPointer clientdata, XtPointer call_data) { XmSelectionBoxCallbackStruct *cb = call_data; if (cb->reason == XmCR_OK) { save_filename = NULL; print_command = XmStringUnparse(cb->value,NULL, XmMULTIBYTE_TEXT,XmMULTIBYTE_TEXT, NULL,0,0); cwriter = &ps_writer; cwriter->conf(widget,&ida->img); } else { XtUnmanageChild(widget); } } static void print_ac(Widget widget, XEvent *event, String *params, Cardinal *num) { if (NULL == printbox) { printbox = XmCreatePromptDialog(app_shell,"print",NULL,0); XtUnmanageChild(XmSelectionBoxGetChild(printbox,XmDIALOG_HELP_BUTTON)); XtAddCallback(printbox,XmNokCallback,print_done_cb,NULL); XtAddCallback(printbox,XmNcancelCallback,print_done_cb,NULL); } XtManageChild(printbox); } /* ---------------------------------------------------------------------- */ static struct ida_op *ops[] = { &desc_flip_vert, &desc_flip_horz, &desc_rotate_cw, &desc_rotate_ccw, &desc_invert, &desc_crop, &desc_autocrop, &desc_grayscale, NULL }; void filter_ac(Widget widget, XEvent *event, String *params, Cardinal *num) { struct ida_op *op = NULL; int i; if (*num < 1) return; for (i = 0; NULL != ops[i]; i++) { op = ops[i]; if (0 == strcasecmp(op->name,params[0])) break; } if (NULL == ops[i]) { fprintf(stderr,"Oops: unknown filter: %s\n",params[0]); return; } viewer_start_op(ida,op,NULL); if (ida->op_src.i.width != ida->img.i.width || ida->op_src.i.height != ida->img.i.height) resize_shell(); } void f3x3_ac(Widget widget, XEvent *event, String *params, Cardinal *num) { struct op_3x3_parm p; if (*num < 9) { fprintf(stderr,"F3x3: wrong number of args (%d, need 9)\n",*num); return; } memset(&p,0,sizeof(p)); p.f1[0] = atoi(params[0]); p.f1[1] = atoi(params[1]); p.f1[2] = atoi(params[2]); p.f2[0] = atoi(params[3]); p.f2[1] = atoi(params[4]); p.f2[2] = atoi(params[5]); p.f3[0] = atoi(params[6]); p.f3[1] = atoi(params[7]); p.f3[2] = atoi(params[8]); if (*num > 9) p.mul = atoi(params[ 9]); if (*num > 10) p.div = atoi(params[10]); if (*num > 11) p.add = atoi(params[11]); if (debug) { fprintf(stderr,"f3x3: -----------\n"); fprintf(stderr,"f3x3: %3d %3d %3d\n",p.f1[0],p.f1[1],p.f1[2]); fprintf(stderr,"f3x3: %3d %3d %3d\n",p.f2[0],p.f2[1],p.f2[2]); fprintf(stderr,"f3x3: %3d %3d %3d\n",p.f3[0],p.f3[1],p.f3[2]); fprintf(stderr,"f3x3: *%d/%d+%d\n",p.mul,p.div,p.add); } viewer_start_op(ida,&desc_3x3,&p); } void undo_ac(Widget widget, XEvent *event, String *params, Cardinal *num) { Widget msgbox; int resize; resize = (ida->undo.i.width != ida->img.i.width || ida->undo.i.height != ida->img.i.height); if (-1 == viewer_undo(ida)) { msgbox = XmCreateInformationDialog(app_shell,"noundobox",NULL,0); XtUnmanageChild(XmMessageBoxGetChild(msgbox,XmDIALOG_HELP_BUTTON)); XtUnmanageChild(XmMessageBoxGetChild(msgbox,XmDIALOG_CANCEL_BUTTON)); XtAddCallback(msgbox,XmNokCallback,destroy_cb,msgbox); XtManageChild(msgbox); } else { if (resize) resize_shell(); } } /* ---------------------------------------------------------------------- */ struct ida_prompt { Widget shell; Widget box; Widget scale; Widget text; int apply; int value; int decimal; int factor; /* 10^decimal */ void (*notify)(int value, int preview); }; static void prompt_setvalue(struct ida_prompt *me, int value, int scale, int text) { char str[32]; int min,max; if (me->value == value) return; XtVaGetValues(me->scale,XmNminimum,&min,XmNmaximum,&max,NULL); if (value < min || value > max) return; me->value = value; if (scale) XmScaleSetValue(me->scale,value); if (text) { if (me->decimal) { sprintf(str,"%*.*f",me->decimal+2,me->decimal, (float)value/me->factor); } else { sprintf(str,"%d",value); } XmTextSetString(me->text,str); } if (me->notify) me->notify(value,1); } static void prompt_scale_cb(Widget widget, XtPointer client_data, XtPointer calldata) { struct ida_prompt *me = client_data; XmScaleCallbackStruct *cd = calldata; prompt_setvalue(me,cd->value,0,1); } static void prompt_text_cb(Widget widget, XtPointer client_data, XtPointer calldata) { struct ida_prompt *me = client_data; float fvalue; int value; if (me->decimal) { fvalue = atof(XmTextGetString(me->text)); fvalue += 0.5/me->factor; value = (int)(fvalue * me->factor); } else { value = atoi(XmTextGetString(me->text)); } prompt_setvalue(me,value,1,0); } static void prompt_box_cb(Widget widget, XtPointer client_data, XtPointer calldata) { struct ida_prompt *me = client_data; XmSelectionBoxCallbackStruct *cd = calldata; if (XmCR_OK == cd->reason) me->apply = 1; XtDestroyWidget(me->shell); } static void prompt_shell_cb(Widget widget, XtPointer client_data, XtPointer calldata) { struct ida_prompt *me = client_data; if (me->apply) me->notify(me->value,0); else viewer_cancel_preview(ida); free(me); } static void prompt_init(char *name, int decimal, int value, void (*notify)(int value, int preview)) { struct ida_prompt *me; me = malloc(sizeof(*me)); memset(me,0,sizeof(*me)); if (decimal) { int i; me->decimal = decimal; me->factor = 1; for (i = 0; i < decimal; i++) me->factor *= 10; } me->notify = notify; me->box = XmCreatePromptDialog(app_shell,name,NULL,0); me->shell = XtParent(me->box); me->text = XmSelectionBoxGetChild(me->box,XmDIALOG_TEXT); XmdRegisterEditres(XtParent(me->box)); XtUnmanageChild(XmSelectionBoxGetChild(me->box,XmDIALOG_HELP_BUTTON)); me->scale = XtVaCreateManagedWidget("scale",xmScaleWidgetClass, me->box,NULL); XtAddCallback(me->scale,XmNdragCallback,prompt_scale_cb,me); XtAddCallback(me->scale,XmNvalueChangedCallback,prompt_scale_cb,me); XtAddCallback(me->text,XmNvalueChangedCallback,prompt_text_cb,me); XtAddCallback(me->box,XmNokCallback,prompt_box_cb,me); XtAddCallback(me->box,XmNcancelCallback,prompt_box_cb,me); XtAddCallback(me->shell,XmNdestroyCallback,prompt_shell_cb,me); XtManageChild(me->box); prompt_setvalue(me,value,1,1); } /* ---------------------------------------------------------------------- */ static void gamma_notify(int value, int preview) { struct op_map_parm param; float gamma = (float)value/100; param.red = op_map_nothing; param.red.gamma = gamma; param.green = param.red; param.blue = param.red; if (preview) { viewer_start_preview(ida,&desc_map,¶m); } else { gamma_val = value; viewer_start_op(ida,&desc_map,¶m); } } void gamma_ac(Widget widget, XEvent *event, String *params, Cardinal *num) { prompt_init("gamma",2,gamma_val,gamma_notify); } static void bright_notify(int value, int preview) { struct op_map_parm param; param.red = op_map_nothing; param.red.bottom += value; param.red.top += value; param.green = param.red; param.blue = param.red; if (preview) { viewer_start_preview(ida,&desc_map,¶m); } else { bright_val = value; viewer_start_op(ida,&desc_map,¶m); } } void bright_ac(Widget widget, XEvent *event, String *params, Cardinal *num) { prompt_init("bright",0,bright_val,bright_notify); } static void contrast_notify(int value, int preview) { struct op_map_parm param; param.red = op_map_nothing; param.red.bottom -= value; param.red.top += value; param.green = param.red; param.blue = param.red; if (preview) { viewer_start_preview(ida,&desc_map,¶m); } else { contrast_val = value; viewer_start_op(ida,&desc_map,¶m); } } void contrast_ac(Widget widget, XEvent *event, String *params, Cardinal *num) { prompt_init("contrast",0,contrast_val,contrast_notify); } static void rotate_notify(int value, int preview) { struct op_rotate_parm parm; parm.angle = value; if (preview) { viewer_start_preview(ida,&desc_rotate,&parm); } else { rotate_val = value; viewer_start_op(ida,&desc_rotate,&parm); } } void rotate_ac(Widget widget, XEvent *event, String *params, Cardinal *num) { prompt_init("rotate",0,rotate_val,rotate_notify); } static void sharpe_notify(int value, int preview) { struct op_sharpe_parm parm; parm.factor = value; if (preview) { viewer_start_preview(ida,&desc_sharpe,&parm); } else { sharpe_val = value; viewer_start_op(ida,&desc_sharpe,&parm); } } void sharpe_ac(Widget widget, XEvent *event, String *params, Cardinal *num) { prompt_init("sharpe",0,sharpe_val,sharpe_notify); } void color_ac(Widget widget, XEvent *event, String *params, Cardinal *num) { color_init(&ida->img); } /* ---------------------------------------------------------------------- */ struct ida_resize { Widget dlg,tx,ty,tr,lock,size,res,label; int yupdate,xupdate,rupdate; int apply; }; static void resize_phys_size(struct ida_resize *h) { char buf[128]; XmString str; int dpi; float x,y; dpi = atoi(XmTextGetString(h->tr)); if (dpi) { x = (float)atoi(XmTextGetString(h->tx)) / dpi; y = (float)atoi(XmTextGetString(h->ty)) / dpi; sprintf(buf,"%.2f x %.2f inch\n%.2f x %.2f cm", x,y, x*2.54, y*2.54); } else { strcpy(buf,"unknown"); } str = XmStringGenerate(buf, NULL, XmMULTIBYTE_TEXT,NULL); XtVaSetValues(h->label,XmNlabelString,str,NULL); } static void resize_sync_cb(Widget widget, XtPointer client_data, XtPointer calldata) { struct ida_resize *h = client_data; char buf[32]; int i,lock,res; lock = XmToggleButtonGetState(h->lock); res = XmToggleButtonGetState(h->res); /* update text fields */ if (h->tx == widget) { if (h->xupdate) { h->xupdate--; return; } i = atoi(XmTextGetString(h->tx)); if (lock) { sprintf(buf,"%d",i * ida->img.i.height / ida->img.i.width); h->yupdate++; XmTextSetString(h->ty,buf); if (res) { sprintf(buf,"%d", ida->img.i.dpi * i / ida->img.i.width); h->rupdate++; XmTextSetString(h->tr,buf); } } else { if (res) { h->rupdate++; XmTextSetString(h->tr,"0"); } } resize_phys_size(h); } if (h->ty == widget) { if (h->yupdate) { h->yupdate--; return; } i = atoi(XmTextGetString(h->ty)); if (lock) { sprintf(buf,"%d",i * ida->img.i.width / ida->img.i.height); h->xupdate++; XmTextSetString(h->tx,buf); if (res) { sprintf(buf,"%d", ida->img.i.dpi * i / ida->img.i.height); h->rupdate++; XmTextSetString(h->tr,buf); } } else { if (res) { h->rupdate++; XmTextSetString(h->tr,"0"); } } resize_phys_size(h); } if (h->tr == widget) { if (h->rupdate) { h->rupdate--; return; } i = atoi(XmTextGetString(h->tr)); sprintf(buf,"%d", ida->img.i.width * i / ida->img.i.dpi); h->xupdate++; XmTextSetString(h->tx,buf); sprintf(buf,"%d", ida->img.i.height * i / ida->img.i.dpi); h->yupdate++; XmTextSetString(h->ty,buf); resize_phys_size(h); } /* radio buttons pressed */ if (h->size == widget && XmToggleButtonGetState(h->size)) { XmToggleButtonSetState(h->res,0,False); sprintf(buf,"%d", ida->img.i.dpi); h->rupdate++; XmTextSetString(h->tr,buf); XtVaSetValues(h->tr,XmNsensitive,False,NULL); resize_phys_size(h); } if (h->res == widget && XmToggleButtonGetState(h->res)) { XmToggleButtonSetState(h->size,0,False); XtVaSetValues(h->tr,XmNsensitive,True,NULL); } } static void resize_button_cb(Widget widget, XtPointer client_data, XtPointer calldata) { struct ida_resize *h = client_data; XmSelectionBoxCallbackStruct *cb = calldata; if (cb->reason == XmCR_OK) h->apply = 1; XtDestroyWidget(XtParent(h->dlg)); } static void resize_destroy(Widget widget, XtPointer client_data, XtPointer calldata) { struct ida_resize *h = client_data; struct op_resize_parm param; if (!h->apply) return; param.width = atoi(XmTextGetString(h->tx)); param.height = atoi(XmTextGetString(h->ty)); param.dpi = atoi(XmTextGetString(h->tr)); if (0 == param.width || 0 == param.height) { fprintf(stderr,"resize: invalid argument\n"); return; } viewer_start_op(ida,&desc_resize,¶m); resize_shell(); free(h); } static void resize_ac(Widget widget, XEvent *event, String *params, Cardinal *num) { Widget rc,rc2; char buf[32]; struct ida_resize *h; h = malloc(sizeof(*h)); memset(h,0,sizeof(*h)); h->dlg = XmCreatePromptDialog(app_shell,"resize",NULL,0); XmdRegisterEditres(XtParent(h->dlg)); XtUnmanageChild(XmSelectionBoxGetChild(h->dlg,XmDIALOG_SELECTION_LABEL)); XtUnmanageChild(XmSelectionBoxGetChild(h->dlg,XmDIALOG_HELP_BUTTON)); XtUnmanageChild(XmSelectionBoxGetChild(h->dlg,XmDIALOG_TEXT)); rc = XtVaCreateManagedWidget("rc", xmRowColumnWidgetClass,h->dlg, NULL); XtVaCreateManagedWidget("lx", xmLabelWidgetClass,rc, NULL); h->tx = XtVaCreateManagedWidget("tx", xmTextWidgetClass,rc, NULL); XtVaCreateManagedWidget("ly", xmLabelWidgetClass,rc, NULL); h->ty = XtVaCreateManagedWidget("ty", xmTextWidgetClass,rc, NULL); XtVaCreateManagedWidget("lr", xmLabelWidgetClass,rc, NULL); h->tr = XtVaCreateManagedWidget("tr", xmTextWidgetClass,rc, NULL); h->lock = XtVaCreateManagedWidget("lock", xmToggleButtonWidgetClass, rc, NULL); rc2 = XtVaCreateManagedWidget("rc", xmRowColumnWidgetClass,rc, NULL); h->size = XtVaCreateManagedWidget("size", xmToggleButtonWidgetClass, rc2, NULL); h->res = XtVaCreateManagedWidget("res", xmToggleButtonWidgetClass, rc2, NULL); XtVaCreateManagedWidget("phys", xmLabelWidgetClass,rc,NULL); h->label = XtVaCreateManagedWidget("label", xmLabelWidgetClass, rc, NULL); sprintf(buf,"%d",ida->img.i.width); XmTextSetString(h->tx,buf); sprintf(buf,"%d",ida->img.i.height); XmTextSetString(h->ty,buf); sprintf(buf,"%d",ida->img.i.dpi); XmTextSetString(h->tr,buf); XtVaSetValues(h->tr,XmNsensitive,False,NULL); XmToggleButtonSetState(h->lock,1,False); XmToggleButtonSetState(h->size,1,False); XmToggleButtonSetState(h->res,0,False); if (!ida->img.i.dpi) { XtVaSetValues(h->size,XmNsensitive,False,NULL); XtVaSetValues(h->res, XmNsensitive,False,NULL); } resize_phys_size(h); XtAddCallback(XtParent(h->dlg),XmNdestroyCallback,resize_destroy,h); XtAddCallback(h->dlg, XmNokCallback, resize_button_cb, h); XtAddCallback(h->dlg, XmNcancelCallback, resize_button_cb, h); XtAddCallback(h->tx, XmNvalueChangedCallback, resize_sync_cb, h); XtAddCallback(h->ty, XmNvalueChangedCallback, resize_sync_cb, h); XtAddCallback(h->tr, XmNvalueChangedCallback, resize_sync_cb, h); XtAddCallback(h->size,XmNvalueChangedCallback, resize_sync_cb, h); XtAddCallback(h->res, XmNvalueChangedCallback, resize_sync_cb, h); XtManageChild(h->dlg); } /* ---------------------------------------------------------------------- */ struct stderr_handler { Widget box; XmString str; int pipe,err; XtInputId id; }; static void stderr_input(XtPointer clientdata, int *src, XtInputId *id) { struct stderr_handler *h = clientdata; XmString item; Widget label; char buf[1024]; int rc; rc = read(h->pipe,buf,sizeof(buf)-1); if (rc <= 0) { /* Oops */ XtRemoveInput(h->id); close(h->pipe); XtDestroyWidget(h->box); free(h); } buf[rc] = 0; write(h->err,buf,rc); item = XmStringGenerate(buf, NULL, XmMULTIBYTE_TEXT,NULL); h->str = XmStringConcatAndFree(h->str,item); label = XmMessageBoxGetChild(h->box,XmDIALOG_MESSAGE_LABEL); XtVaSetValues(label,XmNlabelString,h->str,NULL); XtManageChild(h->box); } static void stderr_ok_cb(Widget widget, XtPointer clientdata, XtPointer call_data) { struct stderr_handler *h = clientdata; XmStringFree(h->str); h->str = XmStringGenerate("", NULL, XmMULTIBYTE_TEXT,NULL); XtUnmanageChild(h->box); } static void stderr_close_cb(Widget widget, XtPointer clientdata, XtPointer call_data) { struct stderr_handler *h = clientdata; XmStringFree(h->str); h->str = XmStringGenerate("", NULL, XmMULTIBYTE_TEXT,NULL); } static void stderr_init(void) { struct stderr_handler *h; int p[2]; h = malloc(sizeof(*h)); memset(h,0,sizeof(*h)); h->str = XmStringGenerate("", NULL, XmMULTIBYTE_TEXT,NULL); h->box = XmCreateErrorDialog(app_shell,"errbox",NULL,0); XtUnmanageChild(XmMessageBoxGetChild(h->box,XmDIALOG_HELP_BUTTON)); XtUnmanageChild(XmMessageBoxGetChild(h->box,XmDIALOG_CANCEL_BUTTON)); XtAddCallback(h->box,XmNokCallback,stderr_ok_cb,h); XtAddCallback(XtParent(h->box),XmNpopdownCallback,stderr_close_cb,h); XSync(XtDisplay(app_shell),False); if (!debug) { pipe(p); h->err = dup(2); dup2(p[1],2); close(p[1]); h->pipe = p[0]; h->id = XtAppAddInput(app_context,h->pipe,(XtPointer)XtInputReadMask, stderr_input,h); } } /* ---------------------------------------------------------------------- */ static void create_mainwindow(void) { Widget img; XmdRegisterEditres(app_shell); view = XmCreateScrolledWindow(app_shell,"view",NULL,0); XtManageChild(view); img = XtVaCreateManagedWidget("image", xmDrawingAreaWidgetClass,view,NULL); XtAddCallback(img,XmNdestinationCallback,selection_dest,NULL); XtAddCallback(img,XmNconvertCallback,selection_convert,NULL); dnd_add(img); ida = viewer_init(img); XtInstallAllAccelerators(img,app_shell); } static void usage(void) { fprintf(stderr, "ida " VERSION " - image viewer & editor\n" "usage: ida [ options ] [ files ]\n" "options:\n" " -h, -help this text\n" " -pcd n pick PhotoCD size (n = 1 .. 5, default 3)\n" " -d, -debug enable debug messages\n"); exit(0); } int main(int argc, char *argv[]) { int i, files, zero = 0; struct stat st; Pixel background; #if 0 setlocale(LC_ALL,""); if (0 == strcasecmp("utf-8", nl_langinfo(CODESET))) { /* ### FIXME ### * for not-yet known reasons ida crashes somewhere deep in * the Motif libraries when running in utf-8 locale ... */ setenv("LC_ALL", "POSIX", 1); setlocale(LC_ALL,""); } #endif binary = argv[0]; ida_init_config(); ida_read_config(); XtSetLanguageProc(NULL,NULL,NULL); app_shell = XtAppInitialize(&app_context, "Ida", opt_desc, opt_count, &argc, argv, fallback_ressources, NULL, 0); dpy = XtDisplay(app_shell); XtGetApplicationResources(app_shell,&args, args_desc,args_count, NULL,0); pcd_res = GET_PHOTOCD_RES(); sane_res = GET_SANE_RES(); if (args.help) usage(); if (args.debug) { debug=1; xdnd_debug = 1; XSynchronize(dpy,1); } XtAppAddActions(app_context, actionTable, sizeof(actionTable) / sizeof(XtActionsRec)); if (0) { XtAddCallback(XmGetXmDisplay(dpy),XmNnoFontCallback, display_cb,NULL); XtAddCallback(XmGetXmDisplay(dpy),XmNnoRenditionCallback, display_cb,NULL); } XtVaGetValues(app_shell, XtNbackground,&background, NULL); x11_color_init(app_shell,&gray); x11_icons_init(dpy, background /* x11_gray */); stderr_init(); ipc_init(); wm_delete_window = XInternAtom(dpy,"WM_DELETE_WINDOW",False); create_mainwindow(); create_control(); XtRealizeWidget(app_shell); ptr_register(ida->widget); ptr_register(control_shell); /* handle cmd line args */ if (2 == argc && 0 == strcmp(argv[1],"-")) { load_stdin(); } else if (argc > 1) { for (files = 0, i = 1; i < argc; i++) { if (curl_is_url(argv[i])) { list_append(argv[i]); files++; continue; } if (-1 == stat(argv[i],&st)) { if (debug) fprintf(stderr,"stat %s: %s\n",argv[i],strerror(errno)); continue; } switch (st.st_mode & S_IFMT) { case S_IFDIR: browser_window(argv[i]); break; case S_IFREG: list_append(argv[i]); files++; break; } } if (files) { list_update(); next_ac(ida->widget,NULL,NULL,&zero); } } if (NULL == ida->file) load_logo(); XtAppMainLoop(app_context); return 0; /* keep compiler happy */ } n1432' href='#n1432'>1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903
/*
 * ARMv7 Cortex-A8 and Cortex-A9 Performance Events handling code.
 *
 * ARMv7 support: Jean Pihet <jpihet@mvista.com>
 * 2010 (c) MontaVista Software, LLC.
 *
 * Copied from ARMv6 code, with the low level code inspired
 *  by the ARMv7 Oprofile code.
 *
 * Cortex-A8 has up to 4 configurable performance counters and
 *  a single cycle counter.
 * Cortex-A9 has up to 31 configurable performance counters and
 *  a single cycle counter.
 *
 * All counters can be enabled/disabled and IRQ masked separately. The cycle
 *  counter and all 4 performance counters together can be reset separately.
 */

#ifdef CONFIG_CPU_V7

#include <asm/cp15.h>
#include <asm/cputype.h>
#include <asm/irq_regs.h>
#include <asm/pmu.h>
#include <asm/vfp.h>
#include "../vfp/vfpinstr.h"

#include <linux/of.h>
#include <linux/platform_device.h>

/*
 * Common ARMv7 event types
 *
 * Note: An implementation may not be able to count all of these events
 * but the encodings are considered to be `reserved' in the case that
 * they are not available.
 */
enum armv7_perf_types {
	ARMV7_PERFCTR_PMNC_SW_INCR			= 0x00,
	ARMV7_PERFCTR_L1_ICACHE_REFILL			= 0x01,
	ARMV7_PERFCTR_ITLB_REFILL			= 0x02,
	ARMV7_PERFCTR_L1_DCACHE_REFILL			= 0x03,
	ARMV7_PERFCTR_L1_DCACHE_ACCESS			= 0x04,
	ARMV7_PERFCTR_DTLB_REFILL			= 0x05,
	ARMV7_PERFCTR_MEM_READ				= 0x06,
	ARMV7_PERFCTR_MEM_WRITE				= 0x07,
	ARMV7_PERFCTR_INSTR_EXECUTED			= 0x08,
	ARMV7_PERFCTR_EXC_TAKEN				= 0x09,
	ARMV7_PERFCTR_EXC_EXECUTED			= 0x0A,
	ARMV7_PERFCTR_CID_WRITE				= 0x0B,

	/*
	 * ARMV7_PERFCTR_PC_WRITE is equivalent to HW_BRANCH_INSTRUCTIONS.
	 * It counts:
	 *  - all (taken) branch instructions,
	 *  - instructions that explicitly write the PC,
	 *  - exception generating instructions.
	 */
	ARMV7_PERFCTR_PC_WRITE				= 0x0C,
	ARMV7_PERFCTR_PC_IMM_BRANCH			= 0x0D,
	ARMV7_PERFCTR_PC_PROC_RETURN			= 0x0E,
	ARMV7_PERFCTR_MEM_UNALIGNED_ACCESS		= 0x0F,
	ARMV7_PERFCTR_PC_BRANCH_MIS_PRED		= 0x10,
	ARMV7_PERFCTR_CLOCK_CYCLES			= 0x11,
	ARMV7_PERFCTR_PC_BRANCH_PRED			= 0x12,

	/* These events are defined by the PMUv2 supplement (ARM DDI 0457A). */
	ARMV7_PERFCTR_MEM_ACCESS			= 0x13,
	ARMV7_PERFCTR_L1_ICACHE_ACCESS			= 0x14,
	ARMV7_PERFCTR_L1_DCACHE_WB			= 0x15,
	ARMV7_PERFCTR_L2_CACHE_ACCESS			= 0x16,
	ARMV7_PERFCTR_L2_CACHE_REFILL			= 0x17,
	ARMV7_PERFCTR_L2_CACHE_WB			= 0x18,
	ARMV7_PERFCTR_BUS_ACCESS			= 0x19,
	ARMV7_PERFCTR_MEM_ERROR				= 0x1A,
	ARMV7_PERFCTR_INSTR_SPEC			= 0x1B,
	ARMV7_PERFCTR_TTBR_WRITE			= 0x1C,
	ARMV7_PERFCTR_BUS_CYCLES			= 0x1D,

	ARMV7_PERFCTR_CPU_CYCLES			= 0xFF
};

/* ARMv7 Cortex-A8 specific event types */
enum armv7_a8_perf_types {
	ARMV7_A8_PERFCTR_L2_CACHE_ACCESS		= 0x43,
	ARMV7_A8_PERFCTR_L2_CACHE_REFILL		= 0x44,
	ARMV7_A8_PERFCTR_L1_ICACHE_ACCESS		= 0x50,
	ARMV7_A8_PERFCTR_STALL_ISIDE			= 0x56,
};

/* ARMv7 Cortex-A9 specific event types */
enum armv7_a9_perf_types {
	ARMV7_A9_PERFCTR_INSTR_CORE_RENAME		= 0x68,
	ARMV7_A9_PERFCTR_STALL_ICACHE			= 0x60,
	ARMV7_A9_PERFCTR_STALL_DISPATCH			= 0x66,
};

/* ARMv7 Cortex-A5 specific event types */
enum armv7_a5_perf_types {
	ARMV7_A5_PERFCTR_PREFETCH_LINEFILL		= 0xc2,
	ARMV7_A5_PERFCTR_PREFETCH_LINEFILL_DROP		= 0xc3,
};

/* ARMv7 Cortex-A15 specific event types */
enum armv7_a15_perf_types {
	ARMV7_A15_PERFCTR_L1_DCACHE_ACCESS_READ		= 0x40,
	ARMV7_A15_PERFCTR_L1_DCACHE_ACCESS_WRITE	= 0x41,
	ARMV7_A15_PERFCTR_L1_DCACHE_REFILL_READ		= 0x42,
	ARMV7_A15_PERFCTR_L1_DCACHE_REFILL_WRITE	= 0x43,

	ARMV7_A15_PERFCTR_DTLB_REFILL_L1_READ		= 0x4C,
	ARMV7_A15_PERFCTR_DTLB_REFILL_L1_WRITE		= 0x4D,

	ARMV7_A15_PERFCTR_L2_CACHE_ACCESS_READ		= 0x50,
	ARMV7_A15_PERFCTR_L2_CACHE_ACCESS_WRITE		= 0x51,
	ARMV7_A15_PERFCTR_L2_CACHE_REFILL_READ		= 0x52,
	ARMV7_A15_PERFCTR_L2_CACHE_REFILL_WRITE		= 0x53,

	ARMV7_A15_PERFCTR_PC_WRITE_SPEC			= 0x76,
};

/* ARMv7 Cortex-A12 specific event types */
enum armv7_a12_perf_types {
	ARMV7_A12_PERFCTR_L1_DCACHE_ACCESS_READ		= 0x40,
	ARMV7_A12_PERFCTR_L1_DCACHE_ACCESS_WRITE	= 0x41,

	ARMV7_A12_PERFCTR_L2_CACHE_ACCESS_READ		= 0x50,
	ARMV7_A12_PERFCTR_L2_CACHE_ACCESS_WRITE		= 0x51,

	ARMV7_A12_PERFCTR_PC_WRITE_SPEC			= 0x76,

	ARMV7_A12_PERFCTR_PF_TLB_REFILL			= 0xe7,
};

/* ARMv7 Krait specific event types */
enum krait_perf_types {
	KRAIT_PMRESR0_GROUP0				= 0xcc,
	KRAIT_PMRESR1_GROUP0				= 0xd0,
	KRAIT_PMRESR2_GROUP0				= 0xd4,
	KRAIT_VPMRESR0_GROUP0				= 0xd8,

	KRAIT_PERFCTR_L1_ICACHE_ACCESS			= 0x10011,
	KRAIT_PERFCTR_L1_ICACHE_MISS			= 0x10010,

	KRAIT_PERFCTR_L1_ITLB_ACCESS			= 0x12222,
	KRAIT_PERFCTR_L1_DTLB_ACCESS			= 0x12210,
};

/* ARMv7 Scorpion specific event types */
enum scorpion_perf_types {
	SCORPION_LPM0_GROUP0				= 0x4c,
	SCORPION_LPM1_GROUP0				= 0x50,
	SCORPION_LPM2_GROUP0				= 0x54,
	SCORPION_L2LPM_GROUP0				= 0x58,
	SCORPION_VLPM_GROUP0				= 0x5c,

	SCORPION_ICACHE_ACCESS				= 0x10053,
	SCORPION_ICACHE_MISS				= 0x10052,

	SCORPION_DTLB_ACCESS				= 0x12013,
	SCORPION_DTLB_MISS				= 0x12012,

	SCORPION_ITLB_MISS				= 0x12021,
};

/*
 * Cortex-A8 HW events mapping
 *
 * The hardware events that we support. We do support cache operations but
 * we have harvard caches and no way to combine instruction and data
 * accesses/misses in hardware.
 */
static const unsigned armv7_a8_perf_map[PERF_COUNT_HW_MAX] = {
	PERF_MAP_ALL_UNSUPPORTED,
	[PERF_COUNT_HW_CPU_CYCLES]		= ARMV7_PERFCTR_CPU_CYCLES,
	[PERF_COUNT_HW_INSTRUCTIONS]		= ARMV7_PERFCTR_INSTR_EXECUTED,
	[PERF_COUNT_HW_CACHE_REFERENCES]	= ARMV7_PERFCTR_L1_DCACHE_ACCESS,
	[PERF_COUNT_HW_CACHE_MISSES]		= ARMV7_PERFCTR_L1_DCACHE_REFILL,
	[PERF_COUNT_HW_BRANCH_INSTRUCTIONS]	= ARMV7_PERFCTR_PC_WRITE,
	[PERF_COUNT_HW_BRANCH_MISSES]		= ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
	[PERF_COUNT_HW_STALLED_CYCLES_FRONTEND]	= ARMV7_A8_PERFCTR_STALL_ISIDE,
};

static const unsigned armv7_a8_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
					  [PERF_COUNT_HW_CACHE_OP_MAX]
					  [PERF_COUNT_HW_CACHE_RESULT_MAX] = {
	PERF_CACHE_MAP_ALL_UNSUPPORTED,

	/*
	 * The performance counters don't differentiate between read and write
	 * accesses/misses so this isn't strictly correct, but it's the best we
	 * can do. Writes and reads get combined.
	 */
	[C(L1D)][C(OP_READ)][C(RESULT_ACCESS)]	= ARMV7_PERFCTR_L1_DCACHE_ACCESS,
	[C(L1D)][C(OP_READ)][C(RESULT_MISS)]	= ARMV7_PERFCTR_L1_DCACHE_REFILL,
	[C(L1D)][C(OP_WRITE)][C(RESULT_ACCESS)]	= ARMV7_PERFCTR_L1_DCACHE_ACCESS,
	[C(L1D)][C(OP_WRITE)][C(RESULT_MISS)]	= ARMV7_PERFCTR_L1_DCACHE_REFILL,

	[C(L1I)][C(OP_READ)][C(RESULT_ACCESS)]	= ARMV7_A8_PERFCTR_L1_ICACHE_ACCESS,
	[C(L1I)][C(OP_READ)][C(RESULT_MISS)]	= ARMV7_PERFCTR_L1_ICACHE_REFILL,

	[C(LL)][C(OP_READ)][C(RESULT_ACCESS)]	= ARMV7_A8_PERFCTR_L2_CACHE_ACCESS,
	[C(LL)][C(OP_READ)][C(RESULT_MISS)]	= ARMV7_A8_PERFCTR_L2_CACHE_REFILL,
	[C(LL)][C(OP_WRITE)][C(RESULT_ACCESS)]	= ARMV7_A8_PERFCTR_L2_CACHE_ACCESS,
	[C(LL)][C(OP_WRITE)][C(RESULT_MISS)]	= ARMV7_A8_PERFCTR_L2_CACHE_REFILL,

	[C(DTLB)][C(OP_READ)][C(RESULT_MISS)]	= ARMV7_PERFCTR_DTLB_REFILL,
	[C(DTLB)][C(OP_WRITE)][C(RESULT_MISS)]	= ARMV7_PERFCTR_DTLB_REFILL,

	[C(ITLB)][C(OP_READ)][C(RESULT_MISS)]	= ARMV7_PERFCTR_ITLB_REFILL,
	[C(ITLB)][C(OP_WRITE)][C(RESULT_MISS)]	= ARMV7_PERFCTR_ITLB_REFILL,

	[C(BPU)][C(OP_READ)][C(RESULT_ACCESS)]	= ARMV7_PERFCTR_PC_BRANCH_PRED,
	[C(BPU)][C(OP_READ)][C(RESULT_MISS)]	= ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
	[C(BPU)][C(OP_WRITE)][C(RESULT_ACCESS)]	= ARMV7_PERFCTR_PC_BRANCH_PRED,
	[C(BPU)][C(OP_WRITE)][C(RESULT_MISS)]	= ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
};

/*
 * Cortex-A9 HW events mapping
 */
static const unsigned armv7_a9_perf_map[PERF_COUNT_HW_MAX] = {
	PERF_MAP_ALL_UNSUPPORTED,
	[PERF_COUNT_HW_CPU_CYCLES]		= ARMV7_PERFCTR_CPU_CYCLES,
	[PERF_COUNT_HW_INSTRUCTIONS]		= ARMV7_A9_PERFCTR_INSTR_CORE_RENAME,
	[PERF_COUNT_HW_CACHE_REFERENCES]	= ARMV7_PERFCTR_L1_DCACHE_ACCESS,
	[PERF_COUNT_HW_CACHE_MISSES]		= ARMV7_PERFCTR_L1_DCACHE_REFILL,
	[PERF_COUNT_HW_BRANCH_INSTRUCTIONS]	= ARMV7_PERFCTR_PC_WRITE,
	[PERF_COUNT_HW_BRANCH_MISSES]		= ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
	[PERF_COUNT_HW_STALLED_CYCLES_FRONTEND]	= ARMV7_A9_PERFCTR_STALL_ICACHE,
	[PERF_COUNT_HW_STALLED_CYCLES_BACKEND]	= ARMV7_A9_PERFCTR_STALL_DISPATCH,
};

static const unsigned armv7_a9_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
					  [PERF_COUNT_HW_CACHE_OP_MAX]
					  [PERF_COUNT_HW_CACHE_RESULT_MAX] = {
	PERF_CACHE_MAP_ALL_UNSUPPORTED,

	/*
	 * The performance counters don't differentiate between read and write
	 * accesses/misses so this isn't strictly correct, but it's the best we
	 * can do. Writes and reads get combined.
	 */
	[C(L1D)][C(OP_READ)][C(RESULT_ACCESS)]	= ARMV7_PERFCTR_L1_DCACHE_ACCESS,
	[C(L1D)][C(OP_READ)][C(RESULT_MISS)]	= ARMV7_PERFCTR_L1_DCACHE_REFILL,
	[C(L1D)][C(OP_WRITE)][C(RESULT_ACCESS)]	= ARMV7_PERFCTR_L1_DCACHE_ACCESS,
	[C(L1D)][C(OP_WRITE)][C(RESULT_MISS)]	= ARMV7_PERFCTR_L1_DCACHE_REFILL,

	[C(L1I)][C(OP_READ)][C(RESULT_MISS)]	= ARMV7_PERFCTR_L1_ICACHE_REFILL,

	[C(DTLB)][C(OP_READ)][C(RESULT_MISS)]	= ARMV7_PERFCTR_DTLB_REFILL,
	[C(DTLB)][C(OP_WRITE)][C(RESULT_MISS)]	= ARMV7_PERFCTR_DTLB_REFILL,

	[C(ITLB)][C(OP_READ)][C(RESULT_MISS)]	= ARMV7_PERFCTR_ITLB_REFILL,
	[C(ITLB)][C(OP_WRITE)][C(RESULT_MISS)]	= ARMV7_PERFCTR_ITLB_REFILL,

	[C(BPU)][C(OP_READ)][C(RESULT_ACCESS)]	= ARMV7_PERFCTR_PC_BRANCH_PRED,
	[C(BPU)][C(OP_READ)][C(RESULT_MISS)]	= ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
	[C(BPU)][C(OP_WRITE)][C(RESULT_ACCESS)]	= ARMV7_PERFCTR_PC_BRANCH_PRED,
	[C(BPU)][C(OP_WRITE)][C(RESULT_MISS)]	= ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
};

/*
 * Cortex-A5 HW events mapping
 */
static const unsigned armv7_a5_perf_map[PERF_COUNT_HW_MAX] = {
	PERF_MAP_ALL_UNSUPPORTED,
	[PERF_COUNT_HW_CPU_CYCLES]		= ARMV7_PERFCTR_CPU_CYCLES,
	[PERF_COUNT_HW_INSTRUCTIONS]		= ARMV7_PERFCTR_INSTR_EXECUTED,
	[PERF_COUNT_HW_CACHE_REFERENCES]	= ARMV7_PERFCTR_L1_DCACHE_ACCESS,
	[PERF_COUNT_HW_CACHE_MISSES]		= ARMV7_PERFCTR_L1_DCACHE_REFILL,
	[PERF_COUNT_HW_BRANCH_INSTRUCTIONS]	= ARMV7_PERFCTR_PC_WRITE,
	[PERF_COUNT_HW_BRANCH_MISSES]		= ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
};

static const unsigned armv7_a5_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
					[PERF_COUNT_HW_CACHE_OP_MAX]
					[PERF_COUNT_HW_CACHE_RESULT_MAX] = {
	PERF_CACHE_MAP_ALL_UNSUPPORTED,

	[C(L1D)][C(OP_READ)][C(RESULT_ACCESS)]	= ARMV7_PERFCTR_L1_DCACHE_ACCESS,
	[C(L1D)][C(OP_READ)][C(RESULT_MISS)]	= ARMV7_PERFCTR_L1_DCACHE_REFILL,
	[C(L1D)][C(OP_WRITE)][C(RESULT_ACCESS)]	= ARMV7_PERFCTR_L1_DCACHE_ACCESS,
	[C(L1D)][C(OP_WRITE)][C(RESULT_MISS)]	= ARMV7_PERFCTR_L1_DCACHE_REFILL,
	[C(L1D)][C(OP_PREFETCH)][C(RESULT_ACCESS)]	= ARMV7_A5_PERFCTR_PREFETCH_LINEFILL,
	[C(L1D)][C(OP_PREFETCH)][C(RESULT_MISS)]	= ARMV7_A5_PERFCTR_PREFETCH_LINEFILL_DROP,

	[C(L1I)][C(OP_READ)][C(RESULT_ACCESS)]	= ARMV7_PERFCTR_L1_ICACHE_ACCESS,
	[C(L1I)][C(OP_READ)][C(RESULT_MISS)]	= ARMV7_PERFCTR_L1_ICACHE_REFILL,
	/*
	 * The prefetch counters don't differentiate between the I side and the
	 * D side.
	 */
	[C(L1I)][C(OP_PREFETCH)][C(RESULT_ACCESS)]	= ARMV7_A5_PERFCTR_PREFETCH_LINEFILL,
	[C(L1I)][C(OP_PREFETCH)][C(RESULT_MISS)]	= ARMV7_A5_PERFCTR_PREFETCH_LINEFILL_DROP,

	[C(DTLB)][C(OP_READ)][C(RESULT_MISS)]	= ARMV7_PERFCTR_DTLB_REFILL,
	[C(DTLB)][C(OP_WRITE)][C(RESULT_MISS)]	= ARMV7_PERFCTR_DTLB_REFILL,

	[C(ITLB)][C(OP_READ)][C(RESULT_MISS)]	= ARMV7_PERFCTR_ITLB_REFILL,
	[C(ITLB)][C(OP_WRITE)][C(RESULT_MISS)]	= ARMV7_PERFCTR_ITLB_REFILL,

	[C(BPU)][C(OP_READ)][C(RESULT_ACCESS)]	= ARMV7_PERFCTR_PC_BRANCH_PRED,
	[C(BPU)][C(OP_READ)][C(RESULT_MISS)]	= ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
	[C(BPU)][C(OP_WRITE)][C(RESULT_ACCESS)]	= ARMV7_PERFCTR_PC_BRANCH_PRED,
	[C(BPU)][C(OP_WRITE)][C(RESULT_MISS)]	= ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
};

/*
 * Cortex-A15 HW events mapping
 */
static const unsigned armv7_a15_perf_map[PERF_COUNT_HW_MAX] = {
	PERF_MAP_ALL_UNSUPPORTED,
	[PERF_COUNT_HW_CPU_CYCLES]		= ARMV7_PERFCTR_CPU_CYCLES,
	[PERF_COUNT_HW_INSTRUCTIONS]		= ARMV7_PERFCTR_INSTR_EXECUTED,
	[PERF_COUNT_HW_CACHE_REFERENCES]	= ARMV7_PERFCTR_L1_DCACHE_ACCESS,
	[PERF_COUNT_HW_CACHE_MISSES]		= ARMV7_PERFCTR_L1_DCACHE_REFILL,
	[PERF_COUNT_HW_BRANCH_INSTRUCTIONS]	= ARMV7_A15_PERFCTR_PC_WRITE_SPEC,
	[PERF_COUNT_HW_BRANCH_MISSES]		= ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
	[PERF_COUNT_HW_BUS_CYCLES]		= ARMV7_PERFCTR_BUS_CYCLES,
};

static const unsigned armv7_a15_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
					[PERF_COUNT_HW_CACHE_OP_MAX]
					[PERF_COUNT_HW_CACHE_RESULT_MAX] = {
	PERF_CACHE_MAP_ALL_UNSUPPORTED,

	[C(L1D)][C(OP_READ)][C(RESULT_ACCESS)]	= ARMV7_A15_PERFCTR_L1_DCACHE_ACCESS_READ,
	[C(L1D)][C(OP_READ)][C(RESULT_MISS)]	= ARMV7_A15_PERFCTR_L1_DCACHE_REFILL_READ,
	[C(L1D)][C(OP_WRITE)][C(RESULT_ACCESS)]	= ARMV7_A15_PERFCTR_L1_DCACHE_ACCESS_WRITE,
	[C(L1D)][C(OP_WRITE)][C(RESULT_MISS)]	= ARMV7_A15_PERFCTR_L1_DCACHE_REFILL_WRITE,

	/*
	 * Not all performance counters differentiate between read and write
	 * accesses/misses so we're not always strictly correct, but it's the
	 * best we can do. Writes and reads get combined in these cases.
	 */
	[C(L1I)][C(OP_READ)][C(RESULT_ACCESS)]	= ARMV7_PERFCTR_L1_ICACHE_ACCESS,
	[C(L1I)][C(OP_READ)][C(RESULT_MISS)]	= ARMV7_PERFCTR_L1_ICACHE_REFILL,

	[C(LL)][C(OP_READ)][C(RESULT_ACCESS)]	= ARMV7_A15_PERFCTR_L2_CACHE_ACCESS_READ,
	[C(LL)][C(OP_READ)][C(RESULT_MISS)]	= ARMV7_A15_PERFCTR_L2_CACHE_REFILL_READ,
	[C(LL)][C(OP_WRITE)][C(RESULT_ACCESS)]	= ARMV7_A15_PERFCTR_L2_CACHE_ACCESS_WRITE,
	[C(LL)][C(OP_WRITE)][C(RESULT_MISS)]	= ARMV7_A15_PERFCTR_L2_CACHE_REFILL_WRITE,

	[C(DTLB)][C(OP_READ)][C(RESULT_MISS)]	= ARMV7_A15_PERFCTR_DTLB_REFILL_L1_READ,
	[C(DTLB)][C(OP_WRITE)][C(RESULT_MISS)]	= ARMV7_A15_PERFCTR_DTLB_REFILL_L1_WRITE,

	[C(ITLB)][C(OP_READ)][C(RESULT_MISS)]	= ARMV7_PERFCTR_ITLB_REFILL,
	[C(ITLB)][C(OP_WRITE)][C(RESULT_MISS)]	= ARMV7_PERFCTR_ITLB_REFILL,

	[C(BPU)][C(OP_READ)][C(RESULT_ACCESS)]	= ARMV7_PERFCTR_PC_BRANCH_PRED,
	[C(BPU)][C(OP_READ)][C(RESULT_MISS)]	= ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
	[C(BPU)][C(OP_WRITE)][C(RESULT_ACCESS)]	= ARMV7_PERFCTR_PC_BRANCH_PRED,
	[C(BPU)][C(OP_WRITE)][C(RESULT_MISS)]	= ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
};

/*
 * Cortex-A7 HW events mapping
 */
static const unsigned armv7_a7_perf_map[PERF_COUNT_HW_MAX] = {
	PERF_MAP_ALL_UNSUPPORTED,
	[PERF_COUNT_HW_CPU_CYCLES]		= ARMV7_PERFCTR_CPU_CYCLES,
	[PERF_COUNT_HW_INSTRUCTIONS]		= ARMV7_PERFCTR_INSTR_EXECUTED,
	[PERF_COUNT_HW_CACHE_REFERENCES]	= ARMV7_PERFCTR_L1_DCACHE_ACCESS,
	[PERF_COUNT_HW_CACHE_MISSES]		= ARMV7_PERFCTR_L1_DCACHE_REFILL,
	[PERF_COUNT_HW_BRANCH_INSTRUCTIONS]	= ARMV7_PERFCTR_PC_WRITE,
	[PERF_COUNT_HW_BRANCH_MISSES]		= ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
	[PERF_COUNT_HW_BUS_CYCLES]		= ARMV7_PERFCTR_BUS_CYCLES,
};

static const unsigned armv7_a7_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
					[PERF_COUNT_HW_CACHE_OP_MAX]
					[PERF_COUNT_HW_CACHE_RESULT_MAX] = {
	PERF_CACHE_MAP_ALL_UNSUPPORTED,

	/*
	 * The performance counters don't differentiate between read and write
	 * accesses/misses so this isn't strictly correct, but it's the best we
	 * can do. Writes and reads get combined.
	 */
	[C(L1D)][C(OP_READ)][C(RESULT_ACCESS)]	= ARMV7_PERFCTR_L1_DCACHE_ACCESS,
	[C(L1D)][C(OP_READ)][C(RESULT_MISS)]	= ARMV7_PERFCTR_L1_DCACHE_REFILL,
	[C(L1D)][C(OP_WRITE)][C(RESULT_ACCESS)]	= ARMV7_PERFCTR_L1_DCACHE_ACCESS,
	[C(L1D)][C(OP_WRITE)][C(RESULT_MISS)]	= ARMV7_PERFCTR_L1_DCACHE_REFILL,

	[C(L1I)][C(OP_READ)][C(RESULT_ACCESS)]	= ARMV7_PERFCTR_L1_ICACHE_ACCESS,
	[C(L1I)][C(OP_READ)][C(RESULT_MISS)]	= ARMV7_PERFCTR_L1_ICACHE_REFILL,

	[C(LL)][C(OP_READ)][C(RESULT_ACCESS)]	= ARMV7_PERFCTR_L2_CACHE_ACCESS,
	[C(LL)][C(OP_READ)][C(RESULT_MISS)]	= ARMV7_PERFCTR_L2_CACHE_REFILL,
	[C(LL)][C(OP_WRITE)][C(RESULT_ACCESS)]	= ARMV7_PERFCTR_L2_CACHE_ACCESS,
	[C(LL)][C(OP_WRITE)][C(RESULT_MISS)]	= ARMV7_PERFCTR_L2_CACHE_REFILL,

	[C(DTLB)][C(OP_READ)][C(RESULT_MISS)]	= ARMV7_PERFCTR_DTLB_REFILL,
	[C(DTLB)][C(OP_WRITE)][C(RESULT_MISS)]	= ARMV7_PERFCTR_DTLB_REFILL,

	[C(ITLB)][C(OP_READ)][C(RESULT_MISS)]	= ARMV7_PERFCTR_ITLB_REFILL,
	[C(ITLB)][C(OP_WRITE)][C(RESULT_MISS)]	= ARMV7_PERFCTR_ITLB_REFILL,

	[C(BPU)][C(OP_READ)][C(RESULT_ACCESS)]	= ARMV7_PERFCTR_PC_BRANCH_PRED,
	[C(BPU)][C(OP_READ)][C(RESULT_MISS)]	= ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
	[C(BPU)][C(OP_WRITE)][C(RESULT_ACCESS)]	= ARMV7_PERFCTR_PC_BRANCH_PRED,
	[C(BPU)][C(OP_WRITE)][C(RESULT_MISS)]	= ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
};

/*
 * Cortex-A12 HW events mapping
 */
static const unsigned armv7_a12_perf_map[PERF_COUNT_HW_MAX] = {
	PERF_MAP_ALL_UNSUPPORTED,
	[PERF_COUNT_HW_CPU_CYCLES]		= ARMV7_PERFCTR_CPU_CYCLES,
	[PERF_COUNT_HW_INSTRUCTIONS]		= ARMV7_PERFCTR_INSTR_EXECUTED,
	[PERF_COUNT_HW_CACHE_REFERENCES]	= ARMV7_PERFCTR_L1_DCACHE_ACCESS,
	[PERF_COUNT_HW_CACHE_MISSES]		= ARMV7_PERFCTR_L1_DCACHE_REFILL,
	[PERF_COUNT_HW_BRANCH_INSTRUCTIONS]	= ARMV7_A12_PERFCTR_PC_WRITE_SPEC,
	[PERF_COUNT_HW_BRANCH_MISSES]		= ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
	[PERF_COUNT_HW_BUS_CYCLES]		= ARMV7_PERFCTR_BUS_CYCLES,
};

static const unsigned armv7_a12_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
					[PERF_COUNT_HW_CACHE_OP_MAX]
					[PERF_COUNT_HW_CACHE_RESULT_MAX] = {
	PERF_CACHE_MAP_ALL_UNSUPPORTED,

	[C(L1D)][C(OP_READ)][C(RESULT_ACCESS)]	= ARMV7_A12_PERFCTR_L1_DCACHE_ACCESS_READ,
	[C(L1D)][C(OP_READ)][C(RESULT_MISS)]	= ARMV7_PERFCTR_L1_DCACHE_REFILL,
	[C(L1D)][C(OP_WRITE)][C(RESULT_ACCESS)]	= ARMV7_A12_PERFCTR_L1_DCACHE_ACCESS_WRITE,
	[C(L1D)][C(OP_WRITE)][C(RESULT_MISS)]	= ARMV7_PERFCTR_L1_DCACHE_REFILL,

	/*
	 * Not all performance counters differentiate between read and write
	 * accesses/misses so we're not always strictly correct, but it's the
	 * best we can do. Writes and reads get combined in these cases.
	 */
	[C(L1I)][C(OP_READ)][C(RESULT_ACCESS)]	= ARMV7_PERFCTR_L1_ICACHE_ACCESS,
	[C(L1I)][C(OP_READ)][C(RESULT_MISS)]	= ARMV7_PERFCTR_L1_ICACHE_REFILL,

	[C(LL)][C(OP_READ)][C(RESULT_ACCESS)]	= ARMV7_A12_PERFCTR_L2_CACHE_ACCESS_READ,
	[C(LL)][C(OP_READ)][C(RESULT_MISS)]	= ARMV7_PERFCTR_L2_CACHE_REFILL,
	[C(LL)][C(OP_WRITE)][C(RESULT_ACCESS)]	= ARMV7_A12_PERFCTR_L2_CACHE_ACCESS_WRITE,
	[C(LL)][C(OP_WRITE)][C(RESULT_MISS)]	= ARMV7_PERFCTR_L2_CACHE_REFILL,

	[C(DTLB)][C(OP_READ)][C(RESULT_MISS)]	= ARMV7_PERFCTR_DTLB_REFILL,
	[C(DTLB)][C(OP_WRITE)][C(RESULT_MISS)]	= ARMV7_PERFCTR_DTLB_REFILL,
	[C(DTLB)][C(OP_PREFETCH)][C(RESULT_MISS)]	= ARMV7_A12_PERFCTR_PF_TLB_REFILL,

	[C(ITLB)][C(OP_READ)][C(RESULT_MISS)]	= ARMV7_PERFCTR_ITLB_REFILL,
	[C(ITLB)][C(OP_WRITE)][C(RESULT_MISS)]	= ARMV7_PERFCTR_ITLB_REFILL,

	[C(BPU)][C(OP_READ)][C(RESULT_ACCESS)]	= ARMV7_PERFCTR_PC_BRANCH_PRED,
	[C(BPU)][C(OP_READ)][C(RESULT_MISS)]	= ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
	[C(BPU)][C(OP_WRITE)][C(RESULT_ACCESS)]	= ARMV7_PERFCTR_PC_BRANCH_PRED,
	[C(BPU)][C(OP_WRITE)][C(RESULT_MISS)]	= ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
};

/*
 * Krait HW events mapping
 */
static const unsigned krait_perf_map[PERF_COUNT_HW_MAX] = {
	PERF_MAP_ALL_UNSUPPORTED,
	[PERF_COUNT_HW_CPU_CYCLES]	    = ARMV7_PERFCTR_CPU_CYCLES,
	[PERF_COUNT_HW_INSTRUCTIONS]	    = ARMV7_PERFCTR_INSTR_EXECUTED,
	[PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = ARMV7_PERFCTR_PC_WRITE,
	[PERF_COUNT_HW_BRANCH_MISSES]	    = ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
	[PERF_COUNT_HW_BUS_CYCLES]	    = ARMV7_PERFCTR_CLOCK_CYCLES,
};

static const unsigned krait_perf_map_no_branch[PERF_COUNT_HW_MAX] = {
	PERF_MAP_ALL_UNSUPPORTED,
	[PERF_COUNT_HW_CPU_CYCLES]	    = ARMV7_PERFCTR_CPU_CYCLES,
	[PERF_COUNT_HW_INSTRUCTIONS]	    = ARMV7_PERFCTR_INSTR_EXECUTED,
	[PERF_COUNT_HW_BRANCH_MISSES]	    = ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
	[PERF_COUNT_HW_BUS_CYCLES]	    = ARMV7_PERFCTR_CLOCK_CYCLES,
};

static const unsigned krait_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
					  [PERF_COUNT_HW_CACHE_OP_MAX]
					  [PERF_COUNT_HW_CACHE_RESULT_MAX] = {
	PERF_CACHE_MAP_ALL_UNSUPPORTED,

	/*
	 * The performance counters don't differentiate between read and write
	 * accesses/misses so this isn't strictly correct, but it's the best we
	 * can do. Writes and reads get combined.
	 */
	[C(L1D)][C(OP_READ)][C(RESULT_ACCESS)]	= ARMV7_PERFCTR_L1_DCACHE_ACCESS,
	[C(L1D)][C(OP_READ)][C(RESULT_MISS)]	= ARMV7_PERFCTR_L1_DCACHE_REFILL,
	[C(L1D)][C(OP_WRITE)][C(RESULT_ACCESS)]	= ARMV7_PERFCTR_L1_DCACHE_ACCESS,
	[C(L1D)][C(OP_WRITE)][C(RESULT_MISS)]	= ARMV7_PERFCTR_L1_DCACHE_REFILL,

	[C(L1I)][C(OP_READ)][C(RESULT_ACCESS)]	= KRAIT_PERFCTR_L1_ICACHE_ACCESS,
	[C(L1I)][C(OP_READ)][C(RESULT_MISS)]	= KRAIT_PERFCTR_L1_ICACHE_MISS,

	[C(DTLB)][C(OP_READ)][C(RESULT_ACCESS)]	= KRAIT_PERFCTR_L1_DTLB_ACCESS,
	[C(DTLB)][C(OP_WRITE)][C(RESULT_ACCESS)]	= KRAIT_PERFCTR_L1_DTLB_ACCESS,

	[C(ITLB)][C(OP_READ)][C(RESULT_ACCESS)]	= KRAIT_PERFCTR_L1_ITLB_ACCESS,
	[C(ITLB)][C(OP_WRITE)][C(RESULT_ACCESS)]	= KRAIT_PERFCTR_L1_ITLB_ACCESS,

	[C(BPU)][C(OP_READ)][C(RESULT_ACCESS)]	= ARMV7_PERFCTR_PC_BRANCH_PRED,
	[C(BPU)][C(OP_READ)][C(RESULT_MISS)]	= ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
	[C(BPU)][C(OP_WRITE)][C(RESULT_ACCESS)]	= ARMV7_PERFCTR_PC_BRANCH_PRED,
	[C(BPU)][C(OP_WRITE)][C(RESULT_MISS)]	= ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
};

/*
 * Scorpion HW events mapping
 */
static const unsigned scorpion_perf_map[PERF_COUNT_HW_MAX] = {
	PERF_MAP_ALL_UNSUPPORTED,
	[PERF_COUNT_HW_CPU_CYCLES]	    = ARMV7_PERFCTR_CPU_CYCLES,
	[PERF_COUNT_HW_INSTRUCTIONS]	    = ARMV7_PERFCTR_INSTR_EXECUTED,
	[PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = ARMV7_PERFCTR_PC_WRITE,
	[PERF_COUNT_HW_BRANCH_MISSES]	    = ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
	[PERF_COUNT_HW_BUS_CYCLES]	    = ARMV7_PERFCTR_CLOCK_CYCLES,
};

static const unsigned scorpion_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
					    [PERF_COUNT_HW_CACHE_OP_MAX]
					    [PERF_COUNT_HW_CACHE_RESULT_MAX] = {
	PERF_CACHE_MAP_ALL_UNSUPPORTED,
	/*
	 * The performance counters don't differentiate between read and write
	 * accesses/misses so this isn't strictly correct, but it's the best we
	 * can do. Writes and reads get combined.
	 */
	[C(L1D)][C(OP_READ)][C(RESULT_ACCESS)] = ARMV7_PERFCTR_L1_DCACHE_ACCESS,
	[C(L1D)][C(OP_READ)][C(RESULT_MISS)] = ARMV7_PERFCTR_L1_DCACHE_REFILL,
	[C(L1D)][C(OP_WRITE)][C(RESULT_ACCESS)] = ARMV7_PERFCTR_L1_DCACHE_ACCESS,
	[C(L1D)][C(OP_WRITE)][C(RESULT_MISS)] = ARMV7_PERFCTR_L1_DCACHE_REFILL,
	[C(L1I)][C(OP_READ)][C(RESULT_ACCESS)] = SCORPION_ICACHE_ACCESS,
	[C(L1I)][C(OP_READ)][C(RESULT_MISS)] = SCORPION_ICACHE_MISS,
	/*
	 * Only ITLB misses and DTLB refills are supported.  If users want the
	 * DTLB refills misses a raw counter must be used.
	 */
	[C(DTLB)][C(OP_READ)][C(RESULT_ACCESS)] = SCORPION_DTLB_ACCESS,
	[C(DTLB)][C(OP_READ)][C(RESULT_MISS)] = SCORPION_DTLB_MISS,
	[C(DTLB)][C(OP_WRITE)][C(RESULT_ACCESS)] = SCORPION_DTLB_ACCESS,
	[C(DTLB)][C(OP_WRITE)][C(RESULT_MISS)] = SCORPION_DTLB_MISS,
	[C(ITLB)][C(OP_READ)][C(RESULT_MISS)] = SCORPION_ITLB_MISS,
	[C(ITLB)][C(OP_WRITE)][C(RESULT_MISS)] = SCORPION_ITLB_MISS,
	[C(BPU)][C(OP_READ)][C(RESULT_ACCESS)] = ARMV7_PERFCTR_PC_BRANCH_PRED,
	[C(BPU)][C(OP_READ)][C(RESULT_MISS)] = ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
	[C(BPU)][C(OP_WRITE)][C(RESULT_ACCESS)] = ARMV7_PERFCTR_PC_BRANCH_PRED,
	[C(BPU)][C(OP_WRITE)][C(RESULT_MISS)] = ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
};

/*
 * Perf Events' indices
 */
#define	ARMV7_IDX_CYCLE_COUNTER	0
#define	ARMV7_IDX_COUNTER0	1
#define	ARMV7_IDX_COUNTER_LAST(cpu_pmu) \
	(ARMV7_IDX_CYCLE_COUNTER + cpu_pmu->num_events - 1)

#define	ARMV7_MAX_COUNTERS	32
#define	ARMV7_COUNTER_MASK	(ARMV7_MAX_COUNTERS - 1)

/*
 * ARMv7 low level PMNC access
 */

/*
 * Perf Event to low level counters mapping
 */
#define	ARMV7_IDX_TO_COUNTER(x)	\
	(((x) - ARMV7_IDX_COUNTER0) & ARMV7_COUNTER_MASK)

/*
 * Per-CPU PMNC: config reg
 */
#define ARMV7_PMNC_E		(1 << 0) /* Enable all counters */
#define ARMV7_PMNC_P		(1 << 1) /* Reset all counters */
#define ARMV7_PMNC_C		(1 << 2) /* Cycle counter reset */
#define ARMV7_PMNC_D		(1 << 3) /* CCNT counts every 64th cpu cycle */
#define ARMV7_PMNC_X		(1 << 4) /* Export to ETM */
#define ARMV7_PMNC_DP		(1 << 5) /* Disable CCNT if non-invasive debug*/
#define	ARMV7_PMNC_N_SHIFT	11	 /* Number of counters supported */
#define	ARMV7_PMNC_N_MASK	0x1f
#define	ARMV7_PMNC_MASK		0x3f	 /* Mask for writable bits */

/*
 * FLAG: counters overflow flag status reg
 */
#define	ARMV7_FLAG_MASK		0xffffffff	/* Mask for writable bits */
#define	ARMV7_OVERFLOWED_MASK	ARMV7_FLAG_MASK

/*
 * PMXEVTYPER: Event selection reg
 */
#define	ARMV7_EVTYPE_MASK	0xc80000ff	/* Mask for writable bits */
#define	ARMV7_EVTYPE_EVENT	0xff		/* Mask for EVENT bits */

/*
 * Event filters for PMUv2
 */
#define	ARMV7_EXCLUDE_PL1	(1 << 31)
#define	ARMV7_EXCLUDE_USER	(1 << 30)
#define	ARMV7_INCLUDE_HYP	(1 << 27)

static inline u32 armv7_pmnc_read(void)
{
	u32 val;
	asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r"(val));
	return val;
}

static inline void armv7_pmnc_write(u32 val)
{
	val &= ARMV7_PMNC_MASK;
	isb();
	asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r"(val));
}

static inline int armv7_pmnc_has_overflowed(u32 pmnc)
{
	return pmnc & ARMV7_OVERFLOWED_MASK;
}

static inline int armv7_pmnc_counter_valid(struct arm_pmu *cpu_pmu, int idx)
{
	return idx >= ARMV7_IDX_CYCLE_COUNTER &&
		idx <= ARMV7_IDX_COUNTER_LAST(cpu_pmu);
}

static inline int armv7_pmnc_counter_has_overflowed(u32 pmnc, int idx)
{
	return pmnc & BIT(ARMV7_IDX_TO_COUNTER(idx));
}

static inline void armv7_pmnc_select_counter(int idx)
{
	u32 counter = ARMV7_IDX_TO_COUNTER(idx);
	asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (counter));
	isb();
}

static inline u32 armv7pmu_read_counter(struct perf_event *event)
{
	struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
	struct hw_perf_event *hwc = &event->hw;
	int idx = hwc->idx;
	u32 value = 0;

	if (!armv7_pmnc_counter_valid(cpu_pmu, idx)) {
		pr_err("CPU%u reading wrong counter %d\n",
			smp_processor_id(), idx);
	} else if (idx == ARMV7_IDX_CYCLE_COUNTER) {
		asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (value));
	} else {
		armv7_pmnc_select_counter(idx);
		asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (value));
	}

	return value;
}

static inline void armv7pmu_write_counter(struct perf_event *event, u32 value)
{
	struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
	struct hw_perf_event *hwc = &event->hw;
	int idx = hwc->idx;

	if (!armv7_pmnc_counter_valid(cpu_pmu, idx)) {
		pr_err("CPU%u writing wrong counter %d\n",
			smp_processor_id(), idx);
	} else if (idx == ARMV7_IDX_CYCLE_COUNTER) {
		asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (value));
	} else {
		armv7_pmnc_select_counter(idx);
		asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (value));
	}
}

static inline void armv7_pmnc_write_evtsel(int idx, u32 val)
{
	armv7_pmnc_select_counter(idx);
	val &= ARMV7_EVTYPE_MASK;
	asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val));
}

static inline void armv7_pmnc_enable_counter(int idx)
{
	u32 counter = ARMV7_IDX_TO_COUNTER(idx);
	asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (BIT(counter)));
}

static inline void armv7_pmnc_disable_counter(int idx)
{
	u32 counter = ARMV7_IDX_TO_COUNTER(idx);
	asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (BIT(counter)));
}

static inline void armv7_pmnc_enable_intens(int idx)
{
	u32 counter = ARMV7_IDX_TO_COUNTER(idx);
	asm volatile("mcr p15, 0, %0, c9, c14, 1" : : "r" (BIT(counter)));
}

static inline void armv7_pmnc_disable_intens(int idx)
{
	u32 counter = ARMV7_IDX_TO_COUNTER(idx);
	asm volatile("mcr p15, 0, %0, c9, c14, 2" : : "r" (BIT(counter)));
	isb();
	/* Clear the overflow flag in case an interrupt is pending. */
	asm volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (BIT(counter)));
	isb();
}

static inline u32 armv7_pmnc_getreset_flags(void)
{
	u32 val;

	/* Read */
	asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (val));

	/* Write to clear flags */
	val &= ARMV7_FLAG_MASK;
	asm volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (val));

	return val;
}

#ifdef DEBUG
static void armv7_pmnc_dump_regs(struct arm_pmu *cpu_pmu)
{
	u32 val;
	unsigned int cnt;

	pr_info("PMNC registers dump:\n");

	asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val));
	pr_info("PMNC  =0x%08x\n", val);

	asm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r" (val));
	pr_info("CNTENS=0x%08x\n", val);

	asm volatile("mrc p15, 0, %0, c9, c14, 1" : "=r" (val));
	pr_info("INTENS=0x%08x\n", val);

	asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (val));
	pr_info("FLAGS =0x%08x\n", val);

	asm volatile("mrc p15, 0, %0, c9, c12, 5" : "=r" (val));
	pr_info("SELECT=0x%08x\n", val);

	asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val));
	pr_info("CCNT  =0x%08x\n", val);

	for (cnt = ARMV7_IDX_COUNTER0;
			cnt <= ARMV7_IDX_COUNTER_LAST(cpu_pmu); cnt++) {
		armv7_pmnc_select_counter(cnt);
		asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (val));
		pr_info("CNT[%d] count =0x%08x\n",
			ARMV7_IDX_TO_COUNTER(cnt), val);
		asm volatile("mrc p15, 0, %0, c9, c13, 1" : "=r" (val));
		pr_info("CNT[%d] evtsel=0x%08x\n",
			ARMV7_IDX_TO_COUNTER(cnt), val);
	}
}
#endif

static void armv7pmu_enable_event(struct perf_event *event)
{
	unsigned long flags;
	struct hw_perf_event *hwc = &event->hw;
	struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
	struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
	int idx = hwc->idx;

	if (!armv7_pmnc_counter_valid(cpu_pmu, idx)) {
		pr_err("CPU%u enabling wrong PMNC counter IRQ enable %d\n",
			smp_processor_id(), idx);
		return;
	}

	/*
	 * Enable counter and interrupt, and set the counter to count
	 * the event that we're interested in.
	 */
	raw_spin_lock_irqsave(&events->pmu_lock, flags);

	/*
	 * Disable counter
	 */
	armv7_pmnc_disable_counter(idx);

	/*
	 * Set event (if destined for PMNx counters)
	 * We only need to set the event for the cycle counter if we
	 * have the ability to perform event filtering.
	 */
	if (cpu_pmu->set_event_filter || idx != ARMV7_IDX_CYCLE_COUNTER)
		armv7_pmnc_write_evtsel(idx, hwc->config_base);

	/*
	 * Enable interrupt for this counter
	 */
	armv7_pmnc_enable_intens(idx);

	/*
	 * Enable counter
	 */
	armv7_pmnc_enable_counter(idx);

	raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
}

static void armv7pmu_disable_event(struct perf_event *event)
{
	unsigned long flags;
	struct hw_perf_event *hwc = &event->hw;
	struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
	struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
	int idx = hwc->idx;

	if (!armv7_pmnc_counter_valid(cpu_pmu, idx)) {
		pr_err("CPU%u disabling wrong PMNC counter IRQ enable %d\n",
			smp_processor_id(), idx);
		return;
	}

	/*
	 * Disable counter and interrupt
	 */
	raw_spin_lock_irqsave(&events->pmu_lock, flags);

	/*
	 * Disable counter
	 */
	armv7_pmnc_disable_counter(idx);

	/*
	 * Disable interrupt for this counter
	 */
	armv7_pmnc_disable_intens(idx);

	raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
}

static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev)
{
	u32 pmnc;
	struct perf_sample_data data;
	struct arm_pmu *cpu_pmu = (struct arm_pmu *)dev;
	struct pmu_hw_events *cpuc = this_cpu_ptr(cpu_pmu->hw_events);
	struct pt_regs *regs;
	int idx;

	/*
	 * Get and reset the IRQ flags
	 */
	pmnc = armv7_pmnc_getreset_flags();

	/*
	 * Did an overflow occur?
	 */
	if (!armv7_pmnc_has_overflowed(pmnc))
		return IRQ_NONE;

	/*
	 * Handle the counter(s) overflow(s)
	 */
	regs = get_irq_regs();

	for (idx = 0; idx < cpu_pmu->num_events; ++idx) {
		struct perf_event *event = cpuc->events[idx];
		struct hw_perf_event *hwc;

		/* Ignore if we don't have an event. */
		if (!event)
			continue;

		/*
		 * We have a single interrupt for all counters. Check that
		 * each counter has overflowed before we process it.
		 */
		if (!armv7_pmnc_counter_has_overflowed(pmnc, idx))
			continue;

		hwc = &event->hw;
		armpmu_event_update(event);
		perf_sample_data_init(&data, 0, hwc->last_period);
		if (!armpmu_event_set_period(event))
			continue;

		if (perf_event_overflow(event, &data, regs))
			cpu_pmu->disable(event);
	}

	/*
	 * Handle the pending perf events.
	 *
	 * Note: this call *must* be run with interrupts disabled. For
	 * platforms that can have the PMU interrupts raised as an NMI, this
	 * will not work.
	 */
	irq_work_run();

	return IRQ_HANDLED;
}

static void armv7pmu_start(struct arm_pmu *cpu_pmu)
{
	unsigned long flags;
	struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);

	raw_spin_lock_irqsave(&events->pmu_lock, flags);
	/* Enable all counters */
	armv7_pmnc_write(armv7_pmnc_read() | ARMV7_PMNC_E);
	raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
}

static void armv7pmu_stop(struct arm_pmu *cpu_pmu)
{
	unsigned long flags;
	struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);

	raw_spin_lock_irqsave(&events->pmu_lock, flags);
	/* Disable all counters */
	armv7_pmnc_write(armv7_pmnc_read() & ~ARMV7_PMNC_E);
	raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
}

static int armv7pmu_get_event_idx(struct pmu_hw_events *cpuc,
				  struct perf_event *event)
{
	int idx;
	struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
	struct hw_perf_event *hwc = &event->hw;
	unsigned long evtype = hwc->config_base & ARMV7_EVTYPE_EVENT;

	/* Always place a cycle counter into the cycle counter. */
	if (evtype == ARMV7_PERFCTR_CPU_CYCLES) {
		if (test_and_set_bit(ARMV7_IDX_CYCLE_COUNTER, cpuc->used_mask))
			return -EAGAIN;

		return ARMV7_IDX_CYCLE_COUNTER;
	}

	/*
	 * For anything other than a cycle counter, try and use
	 * the events counters
	 */
	for (idx = ARMV7_IDX_COUNTER0; idx < cpu_pmu->num_events; ++idx) {
		if (!test_and_set_bit(idx, cpuc->used_mask))
			return idx;
	}

	/* The counters are all in use. */
	return -EAGAIN;
}

/*
 * Add an event filter to a given event. This will only work for PMUv2 PMUs.
 */
static int armv7pmu_set_event_filter(struct hw_perf_event *event,
				     struct perf_event_attr *attr)
{
	unsigned long config_base = 0;

	if (attr->exclude_idle)
		return -EPERM;
	if (attr->exclude_user)
		config_base |= ARMV7_EXCLUDE_USER;
	if (attr->exclude_kernel)
		config_base |= ARMV7_EXCLUDE_PL1;
	if (!attr->exclude_hv)
		config_base |= ARMV7_INCLUDE_HYP;

	/*
	 * Install the filter into config_base as this is used to
	 * construct the event type.
	 */
	event->config_base = config_base;

	return 0;
}

static void armv7pmu_reset(void *info)
{
	struct arm_pmu *cpu_pmu = (struct arm_pmu *)info;
	u32 idx, nb_cnt = cpu_pmu->num_events;

	/* The counter and interrupt enable registers are unknown at reset. */
	for (idx = ARMV7_IDX_CYCLE_COUNTER; idx < nb_cnt; ++idx) {
		armv7_pmnc_disable_counter(idx);
		armv7_pmnc_disable_intens(idx);
	}

	/* Initialize & Reset PMNC: C and P bits */
	armv7_pmnc_write(ARMV7_PMNC_P | ARMV7_PMNC_C);
}

static int armv7_a8_map_event(struct perf_event *event)
{
	return armpmu_map_event(event, &armv7_a8_perf_map,
				&armv7_a8_perf_cache_map, 0xFF);
}

static int armv7_a9_map_event(struct perf_event *event)
{
	return armpmu_map_event(event, &armv7_a9_perf_map,
				&armv7_a9_perf_cache_map, 0xFF);
}

static int armv7_a5_map_event(struct perf_event *event)
{
	return armpmu_map_event(event, &armv7_a5_perf_map,
				&armv7_a5_perf_cache_map, 0xFF);
}

static int armv7_a15_map_event(struct perf_event *event)
{
	return armpmu_map_event(event, &armv7_a15_perf_map,
				&armv7_a15_perf_cache_map, 0xFF);
}

static int armv7_a7_map_event(struct perf_event *event)
{
	return armpmu_map_event(event, &armv7_a7_perf_map,
				&armv7_a7_perf_cache_map, 0xFF);
}

static int armv7_a12_map_event(struct perf_event *event)
{
	return armpmu_map_event(event, &armv7_a12_perf_map,
				&armv7_a12_perf_cache_map, 0xFF);
}

static int krait_map_event(struct perf_event *event)
{
	return armpmu_map_event(event, &krait_perf_map,
				&krait_perf_cache_map, 0xFFFFF);
}

static int krait_map_event_no_branch(struct perf_event *event)
{
	return armpmu_map_event(event, &krait_perf_map_no_branch,
				&krait_perf_cache_map, 0xFFFFF);
}

static int scorpion_map_event(struct perf_event *event)
{
	return armpmu_map_event(event, &scorpion_perf_map,
				&scorpion_perf_cache_map, 0xFFFFF);
}

static void armv7pmu_init(struct arm_pmu *cpu_pmu)
{
	cpu_pmu->handle_irq	= armv7pmu_handle_irq;
	cpu_pmu->enable		= armv7pmu_enable_event;
	cpu_pmu->disable	= armv7pmu_disable_event;
	cpu_pmu->read_counter	= armv7pmu_read_counter;
	cpu_pmu->write_counter	= armv7pmu_write_counter;
	cpu_pmu->get_event_idx	= armv7pmu_get_event_idx;
	cpu_pmu->start		= armv7pmu_start;
	cpu_pmu->stop		= armv7pmu_stop;
	cpu_pmu->reset		= armv7pmu_reset;
	cpu_pmu->max_period	= (1LLU << 32) - 1;
};

static void armv7_read_num_pmnc_events(void *info)
{
	int *nb_cnt = info;

	/* Read the nb of CNTx counters supported from PMNC */
	*nb_cnt = (armv7_pmnc_read() >> ARMV7_PMNC_N_SHIFT) & ARMV7_PMNC_N_MASK;

	/* Add the CPU cycles counter */
	*nb_cnt += 1;
}

static int armv7_probe_num_events(struct arm_pmu *arm_pmu)
{
	return smp_call_function_any(&arm_pmu->supported_cpus,
				     armv7_read_num_pmnc_events,
				     &arm_pmu->num_events, 1);
}

static int armv7_a8_pmu_init(struct arm_pmu *cpu_pmu)
{
	armv7pmu_init(cpu_pmu);
	cpu_pmu->name		= "armv7_cortex_a8";
	cpu_pmu->map_event	= armv7_a8_map_event;
	return armv7_probe_num_events(cpu_pmu);
}

static int armv7_a9_pmu_init(struct arm_pmu *cpu_pmu)
{
	armv7pmu_init(cpu_pmu);
	cpu_pmu->name		= "armv7_cortex_a9";
	cpu_pmu->map_event	= armv7_a9_map_event;
	return armv7_probe_num_events(cpu_pmu);
}

static int armv7_a5_pmu_init(struct arm_pmu *cpu_pmu)
{
	armv7pmu_init(cpu_pmu);
	cpu_pmu->name		= "armv7_cortex_a5";
	cpu_pmu->map_event	= armv7_a5_map_event;
	return armv7_probe_num_events(cpu_pmu);
}

static int armv7_a15_pmu_init(struct arm_pmu *cpu_pmu)
{
	armv7pmu_init(cpu_pmu);
	cpu_pmu->name		= "armv7_cortex_a15";
	cpu_pmu->map_event	= armv7_a15_map_event;
	cpu_pmu->set_event_filter = armv7pmu_set_event_filter;
	return armv7_probe_num_events(cpu_pmu);
}

static int armv7_a7_pmu_init(struct arm_pmu *cpu_pmu)
{
	armv7pmu_init(cpu_pmu);
	cpu_pmu->name		= "armv7_cortex_a7";
	cpu_pmu->map_event	= armv7_a7_map_event;
	cpu_pmu->set_event_filter = armv7pmu_set_event_filter;
	return armv7_probe_num_events(cpu_pmu);
}

static int armv7_a12_pmu_init(struct arm_pmu *cpu_pmu)
{
	armv7pmu_init(cpu_pmu);
	cpu_pmu->name		= "armv7_cortex_a12";
	cpu_pmu->map_event	= armv7_a12_map_event;
	cpu_pmu->set_event_filter = armv7pmu_set_event_filter;
	return armv7_probe_num_events(cpu_pmu);
}

static int armv7_a17_pmu_init(struct arm_pmu *cpu_pmu)
{
	int ret = armv7_a12_pmu_init(cpu_pmu);
	cpu_pmu->name = "armv7_cortex_a17";
	return ret;
}

/*
 * Krait Performance Monitor Region Event Selection Register (PMRESRn)
 *
 *            31   30     24     16     8      0
 *            +--------------------------------+
 *  PMRESR0   | EN |  CC  |  CC  |  CC  |  CC  |   N = 1, R = 0
 *            +--------------------------------+
 *  PMRESR1   | EN |  CC  |  CC  |  CC  |  CC  |   N = 1, R = 1
 *            +--------------------------------+
 *  PMRESR2   | EN |  CC  |  CC  |  CC  |  CC  |   N = 1, R = 2
 *            +--------------------------------+
 *  VPMRESR0  | EN |  CC  |  CC  |  CC  |  CC  |   N = 2, R = ?
 *            +--------------------------------+
 *              EN | G=3  | G=2  | G=1  | G=0
 *
 *  Event Encoding:
 *
 *      hwc->config_base = 0xNRCCG
 *
 *      N  = prefix, 1 for Krait CPU (PMRESRn), 2 for Venum VFP (VPMRESR)
 *      R  = region register
 *      CC = class of events the group G is choosing from
 *      G  = group or particular event
 *
 *  Example: 0x12021 is a Krait CPU event in PMRESR2's group 1 with code 2
 *
 *  A region (R) corresponds to a piece of the CPU (execution unit, instruction
 *  unit, etc.) while the event code (CC) corresponds to a particular class of
 *  events (interrupts for example). An event code is broken down into
 *  groups (G) that can be mapped into the PMU (irq, fiqs, and irq+fiqs for
 *  example).
 */

#define KRAIT_EVENT		(1 << 16)
#define VENUM_EVENT		(2 << 16)
#define KRAIT_EVENT_MASK	(KRAIT_EVENT | VENUM_EVENT)
#define PMRESRn_EN		BIT(31)

#define EVENT_REGION(event)	(((event) >> 12) & 0xf)		/* R */
#define EVENT_GROUP(event)	((event) & 0xf)			/* G */
#define EVENT_CODE(event)	(((event) >> 4) & 0xff)		/* CC */
#define EVENT_VENUM(event)	(!!(event & VENUM_EVENT))	/* N=2 */
#define EVENT_CPU(event)	(!!(event & KRAIT_EVENT))	/* N=1 */

static u32 krait_read_pmresrn(int n)
{
	u32 val;

	switch (n) {
	case 0:
		asm volatile("mrc p15, 1, %0, c9, c15, 0" : "=r" (val));
		break;
	case 1:
		asm volatile("mrc p15, 1, %0, c9, c15, 1" : "=r" (val));
		break;
	case 2:
		asm volatile("mrc p15, 1, %0, c9, c15, 2" : "=r" (val));
		break;
	default:
		BUG(); /* Should be validated in krait_pmu_get_event_idx() */
	}

	return val;
}

static void krait_write_pmresrn(int n, u32 val)
{
	switch (n) {
	case 0:
		asm volatile("mcr p15, 1, %0, c9, c15, 0" : : "r" (val));
		break;
	case 1:
		asm volatile("mcr p15, 1, %0, c9, c15, 1" : : "r" (val));
		break;
	case 2:
		asm volatile("mcr p15, 1, %0, c9, c15, 2" : : "r" (val));
		break;
	default:
		BUG(); /* Should be validated in krait_pmu_get_event_idx() */
	}
}

static u32 venum_read_pmresr(void)
{
	u32 val;
	asm volatile("mrc p10, 7, %0, c11, c0, 0" : "=r" (val));
	return val;
}

static void venum_write_pmresr(u32 val)
{
	asm volatile("mcr p10, 7, %0, c11, c0, 0" : : "r" (val));
}

static void venum_pre_pmresr(u32 *venum_orig_val, u32 *fp_orig_val)
{
	u32 venum_new_val;
	u32 fp_new_val;

	BUG_ON(preemptible());
	/* CPACR Enable CP10 and CP11 access */
	*venum_orig_val = get_copro_access();
	venum_new_val = *venum_orig_val | CPACC_SVC(10) | CPACC_SVC(11);
	set_copro_access(venum_new_val);

	/* Enable FPEXC */
	*fp_orig_val = fmrx(FPEXC);
	fp_new_val = *fp_orig_val | FPEXC_EN;
	fmxr(FPEXC, fp_new_val);
}

static void venum_post_pmresr(u32 venum_orig_val, u32 fp_orig_val)
{
	BUG_ON(preemptible());
	/* Restore FPEXC */
	fmxr(FPEXC, fp_orig_val);
	isb();
	/* Restore CPACR */
	set_copro_access(venum_orig_val);
}

static u32 krait_get_pmresrn_event(unsigned int region)
{
	static const u32 pmresrn_table[] = { KRAIT_PMRESR0_GROUP0,
					     KRAIT_PMRESR1_GROUP0,
					     KRAIT_PMRESR2_GROUP0 };
	return pmresrn_table[region];
}

static void krait_evt_setup(int idx, u32 config_base)
{
	u32 val;
	u32 mask;
	u32 vval, fval;
	unsigned int region = EVENT_REGION(config_base);
	unsigned int group = EVENT_GROUP(config_base);
	unsigned int code = EVENT_CODE(config_base);
	unsigned int group_shift;
	bool venum_event = EVENT_VENUM(config_base);

	group_shift = group * 8;
	mask = 0xff << group_shift;

	/* Configure evtsel for the region and group */
	if (venum_event)
		val = KRAIT_VPMRESR0_GROUP0;
	else
		val = krait_get_pmresrn_event(region);
	val += group;
	/* Mix in mode-exclusion bits */
	val |= config_base & (ARMV7_EXCLUDE_USER | ARMV7_EXCLUDE_PL1);
	armv7_pmnc_write_evtsel(idx, val);

	if (venum_event) {
		venum_pre_pmresr(&vval, &fval);
		val = venum_read_pmresr();
		val &= ~mask;
		val |= code << group_shift;
		val |= PMRESRn_EN;
		venum_write_pmresr(val);
		venum_post_pmresr(vval, fval);
	} else {
		val = krait_read_pmresrn(region);
		val &= ~mask;
		val |= code << group_shift;
		val |= PMRESRn_EN;
		krait_write_pmresrn(region, val);
	}
}

static u32 clear_pmresrn_group(u32 val, int group)
{
	u32 mask;
	int group_shift;

	group_shift = group * 8;
	mask = 0xff << group_shift;
	val &= ~mask;

	/* Don't clear enable bit if entire region isn't disabled */
	if (val & ~PMRESRn_EN)
		return val |= PMRESRn_EN;

	return 0;
}

static void krait_clearpmu(u32 config_base)
{
	u32 val;
	u32 vval, fval;
	unsigned int region = EVENT_REGION(config_base);
	unsigned int group = EVENT_GROUP(config_base);
	bool venum_event = EVENT_VENUM(config_base);

	if (venum_event) {
		venum_pre_pmresr(&vval, &fval);
		val = venum_read_pmresr();
		val = clear_pmresrn_group(val, group);
		venum_write_pmresr(val);
		venum_post_pmresr(vval, fval);
	} else {
		val = krait_read_pmresrn(region);
		val = clear_pmresrn_group(val, group);
		krait_write_pmresrn(region, val);
	}
}

static void krait_pmu_disable_event(struct perf_event *event)
{
	unsigned long flags;
	struct hw_perf_event *hwc = &event->hw;
	int idx = hwc->idx;
	struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
	struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);

	/* Disable counter and interrupt */
	raw_spin_lock_irqsave(&events->pmu_lock, flags);

	/* Disable counter */
	armv7_pmnc_disable_counter(idx);

	/*
	 * Clear pmresr code (if destined for PMNx counters)
	 */
	if (hwc->config_base & KRAIT_EVENT_MASK)
		krait_clearpmu(hwc->config_base);

	/* Disable interrupt for this counter */
	armv7_pmnc_disable_intens(idx);

	raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
}

static void krait_pmu_enable_event(struct perf_event *event)
{
	unsigned long flags;
	struct hw_perf_event *hwc = &event->hw;
	int idx = hwc->idx;
	struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
	struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);

	/*
	 * Enable counter and interrupt, and set the counter to count
	 * the event that we're interested in.
	 */
	raw_spin_lock_irqsave(&events->pmu_lock, flags);

	/* Disable counter */
	armv7_pmnc_disable_counter(idx);

	/*
	 * Set event (if destined for PMNx counters)
	 * We set the event for the cycle counter because we
	 * have the ability to perform event filtering.
	 */
	if (hwc->config_base & KRAIT_EVENT_MASK)
		krait_evt_setup(idx, hwc->config_base);
	else
		armv7_pmnc_write_evtsel(idx, hwc->config_base);

	/* Enable interrupt for this counter */
	armv7_pmnc_enable_intens(idx);

	/* Enable counter */
	armv7_pmnc_enable_counter(idx);

	raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
}

static void krait_pmu_reset(void *info)
{
	u32 vval, fval;
	struct arm_pmu *cpu_pmu = info;
	u32 idx, nb_cnt = cpu_pmu->num_events;

	armv7pmu_reset(info);

	/* Clear all pmresrs */
	krait_write_pmresrn(0, 0);
	krait_write_pmresrn(1, 0);
	krait_write_pmresrn(2, 0);

	venum_pre_pmresr(&vval, &fval);
	venum_write_pmresr(0);
	venum_post_pmresr(vval, fval);

	/* Reset PMxEVNCTCR to sane default */
	for (idx = ARMV7_IDX_CYCLE_COUNTER; idx < nb_cnt; ++idx) {
		armv7_pmnc_select_counter(idx);
		asm volatile("mcr p15, 0, %0, c9, c15, 0" : : "r" (0));
	}

}

static int krait_event_to_bit(struct perf_event *event, unsigned int region,
			      unsigned int group)
{
	int bit;
	struct hw_perf_event *hwc = &event->hw;
	struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);

	if (hwc->config_base & VENUM_EVENT)
		bit = KRAIT_VPMRESR0_GROUP0;
	else
		bit = krait_get_pmresrn_event(region);
	bit -= krait_get_pmresrn_event(0);
	bit += group;
	/*
	 * Lower bits are reserved for use by the counters (see
	 * armv7pmu_get_event_idx() for more info)
	 */
	bit += ARMV7_IDX_COUNTER_LAST(cpu_pmu) + 1;

	return bit;
}

/*
 * We check for column exclusion constraints here.
 * Two events cant use the same group within a pmresr register.
 */
static int krait_pmu_get_event_idx(struct pmu_hw_events *cpuc,
				   struct perf_event *event)
{
	int idx;
	int bit = -1;
	struct hw_perf_event *hwc = &event->hw;
	unsigned int region = EVENT_REGION(hwc->config_base);
	unsigned int code = EVENT_CODE(hwc->config_base);
	unsigned int group = EVENT_GROUP(hwc->config_base);
	bool venum_event = EVENT_VENUM(hwc->config_base);
	bool krait_event = EVENT_CPU(hwc->config_base);

	if (venum_event || krait_event) {
		/* Ignore invalid events */
		if (group > 3 || region > 2)
			return -EINVAL;
		if (venum_event && (code & 0xe0))
			return -EINVAL;

		bit = krait_event_to_bit(event, region, group);
		if (test_and_set_bit(bit, cpuc->used_mask))
			return -EAGAIN;
	}

	idx = armv7pmu_get_event_idx(cpuc, event);
	if (idx < 0 && bit >= 0)
		clear_bit(bit, cpuc->used_mask);

	return idx;
}

static void krait_pmu_clear_event_idx(struct pmu_hw_events *cpuc,
				      struct perf_event *event)
{
	int bit;
	struct hw_perf_event *hwc = &event->hw;
	unsigned int region = EVENT_REGION(hwc->config_base);
	unsigned int group = EVENT_GROUP(hwc->config_base);
	bool venum_event = EVENT_VENUM(hwc->config_base);
	bool krait_event = EVENT_CPU(hwc->config_base);

	if (venum_event || krait_event) {
		bit = krait_event_to_bit(event, region, group);
		clear_bit(bit, cpuc->used_mask);
	}
}

static int krait_pmu_init(struct arm_pmu *cpu_pmu)
{
	armv7pmu_init(cpu_pmu);
	cpu_pmu->name		= "armv7_krait";
	/* Some early versions of Krait don't support PC write events */
	if (of_property_read_bool(cpu_pmu->plat_device->dev.of_node,
				  "qcom,no-pc-write"))
		cpu_pmu->map_event = krait_map_event_no_branch;
	else
		cpu_pmu->map_event = krait_map_event;
	cpu_pmu->set_event_filter = armv7pmu_set_event_filter;
	cpu_pmu->reset		= krait_pmu_reset;
	cpu_pmu->enable		= krait_pmu_enable_event;
	cpu_pmu->disable	= krait_pmu_disable_event;
	cpu_pmu->get_event_idx	= krait_pmu_get_event_idx;
	cpu_pmu->clear_event_idx = krait_pmu_clear_event_idx;
	return armv7_probe_num_events(cpu_pmu);
}

/*
 * Scorpion Local Performance Monitor Register (LPMn)
 *
 *            31   30     24     16     8      0
 *            +--------------------------------+
 *  LPM0      | EN |  CC  |  CC  |  CC  |  CC  |   N = 1, R = 0
 *            +--------------------------------+
 *  LPM1      | EN |  CC  |  CC  |  CC  |  CC  |   N = 1, R = 1
 *            +--------------------------------+
 *  LPM2      | EN |  CC  |  CC  |  CC  |  CC  |   N = 1, R = 2
 *            +--------------------------------+
 *  L2LPM     | EN |  CC  |  CC  |  CC  |  CC  |   N = 1, R = 3
 *            +--------------------------------+
 *  VLPM      | EN |  CC  |  CC  |  CC  |  CC  |   N = 2, R = ?
 *            +--------------------------------+
 *              EN | G=3  | G=2  | G=1  | G=0
 *
 *
 *  Event Encoding:
 *
 *      hwc->config_base = 0xNRCCG
 *
 *      N  = prefix, 1 for Scorpion CPU (LPMn/L2LPM), 2 for Venum VFP (VLPM)
 *      R  = region register
 *      CC = class of events the group G is choosing from
 *      G  = group or particular event
 *
 *  Example: 0x12021 is a Scorpion CPU event in LPM2's group 1 with code 2
 *
 *  A region (R) corresponds to a piece of the CPU (execution unit, instruction
 *  unit, etc.) while the event code (CC) corresponds to a particular class of
 *  events (interrupts for example). An event code is broken down into
 *  groups (G) that can be mapped into the PMU (irq, fiqs, and irq+fiqs for
 *  example).
 */

static u32 scorpion_read_pmresrn(int n)
{
	u32 val;

	switch (n) {
	case 0:
		asm volatile("mrc p15, 0, %0, c15, c0, 0" : "=r" (val));
		break;
	case 1:
		asm volatile("mrc p15, 1, %0, c15, c0, 0" : "=r" (val));
		break;
	case 2:
		asm volatile("mrc p15, 2, %0, c15, c0, 0" : "=r" (val));
		break;
	case 3:
		asm volatile("mrc p15, 3, %0, c15, c2, 0" : "=r" (val));
		break;
	default:
		BUG(); /* Should be validated in scorpion_pmu_get_event_idx() */
	}

	return val;
}

static void scorpion_write_pmresrn(int n, u32 val)
{
	switch (n) {
	case 0:
		asm volatile("mcr p15, 0, %0, c15, c0, 0" : : "r" (val));
		break;
	case 1:
		asm volatile("mcr p15, 1, %0, c15, c0, 0" : : "r" (val));
		break;
	case 2:
		asm volatile("mcr p15, 2, %0, c15, c0, 0" : : "r" (val));
		break;
	case 3:
		asm volatile("mcr p15, 3, %0, c15, c2, 0" : : "r" (val));
		break;
	default:
		BUG(); /* Should be validated in scorpion_pmu_get_event_idx() */
	}
}

static u32 scorpion_get_pmresrn_event(unsigned int region)
{
	static const u32 pmresrn_table[] = { SCORPION_LPM0_GROUP0,
					     SCORPION_LPM1_GROUP0,
					     SCORPION_LPM2_GROUP0,
					     SCORPION_L2LPM_GROUP0 };
	return pmresrn_table[region];
}

static void scorpion_evt_setup(int idx, u32 config_base)
{
	u32 val;
	u32 mask;
	u32 vval, fval;
	unsigned int region = EVENT_REGION(config_base);
	unsigned int group = EVENT_GROUP(config_base);
	unsigned int code = EVENT_CODE(config_base);
	unsigned int group_shift;
	bool venum_event = EVENT_VENUM(config_base);

	group_shift = group * 8;
	mask = 0xff << group_shift;

	/* Configure evtsel for the region and group */
	if (venum_event)
		val = SCORPION_VLPM_GROUP0;
	else
		val = scorpion_get_pmresrn_event(region);
	val += group;
	/* Mix in mode-exclusion bits */
	val |= config_base & (ARMV7_EXCLUDE_USER | ARMV7_EXCLUDE_PL1);
	armv7_pmnc_write_evtsel(idx, val);

	asm volatile("mcr p15, 0, %0, c9, c15, 0" : : "r" (0));

	if (venum_event) {
		venum_pre_pmresr(&vval, &fval);
		val = venum_read_pmresr();
		val &= ~mask;
		val |= code << group_shift;
		val |= PMRESRn_EN;
		venum_write_pmresr(val);
		venum_post_pmresr(vval, fval);
	} else {
		val = scorpion_read_pmresrn(region);
		val &= ~mask;
		val |= code << group_shift;
		val |= PMRESRn_EN;
		scorpion_write_pmresrn(region, val);
	}
}

static void scorpion_clearpmu(u32 config_base)
{
	u32 val;
	u32 vval, fval;
	unsigned int region = EVENT_REGION(config_base);
	unsigned int group = EVENT_GROUP(config_base);
	bool venum_event = EVENT_VENUM(config_base);

	if (venum_event) {
		venum_pre_pmresr(&vval, &fval);
		val = venum_read_pmresr();
		val = clear_pmresrn_group(val, group);
		venum_write_pmresr(val);
		venum_post_pmresr(vval, fval);
	} else {
		val = scorpion_read_pmresrn(region);
		val = clear_pmresrn_group(val, group);
		scorpion_write_pmresrn(region, val);
	}
}

static void scorpion_pmu_disable_event(struct perf_event *event)
{
	unsigned long flags;
	struct hw_perf_event *hwc = &event->hw;
	int idx = hwc->idx;
	struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
	struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);

	/* Disable counter and interrupt */
	raw_spin_lock_irqsave(&events->pmu_lock, flags);

	/* Disable counter */
	armv7_pmnc_disable_counter(idx);

	/*
	 * Clear pmresr code (if destined for PMNx counters)
	 */
	if (hwc->config_base & KRAIT_EVENT_MASK)
		scorpion_clearpmu(hwc->config_base);

	/* Disable interrupt for this counter */
	armv7_pmnc_disable_intens(idx);

	raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
}

static void scorpion_pmu_enable_event(struct perf_event *event)
{
	unsigned long flags;
	struct hw_perf_event *hwc = &event->hw;
	int idx = hwc->idx;
	struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
	struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);

	/*
	 * Enable counter and interrupt, and set the counter to count
	 * the event that we're interested in.
	 */
	raw_spin_lock_irqsave(&events->pmu_lock, flags);

	/* Disable counter */
	armv7_pmnc_disable_counter(idx);

	/*
	 * Set event (if destined for PMNx counters)
	 * We don't set the event for the cycle counter because we
	 * don't have the ability to perform event filtering.
	 */
	if (hwc->config_base & KRAIT_EVENT_MASK)
		scorpion_evt_setup(idx, hwc->config_base);
	else if (idx != ARMV7_IDX_CYCLE_COUNTER)
		armv7_pmnc_write_evtsel(idx, hwc->config_base);

	/* Enable interrupt for this counter */
	armv7_pmnc_enable_intens(idx);

	/* Enable counter */
	armv7_pmnc_enable_counter(idx);

	raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
}

static void scorpion_pmu_reset(void *info)
{
	u32 vval, fval;
	struct arm_pmu *cpu_pmu = info;
	u32 idx, nb_cnt = cpu_pmu->num_events;

	armv7pmu_reset(info);

	/* Clear all pmresrs */
	scorpion_write_pmresrn(0, 0);
	scorpion_write_pmresrn(1, 0);
	scorpion_write_pmresrn(2, 0);
	scorpion_write_pmresrn(3, 0);

	venum_pre_pmresr(&vval, &fval);
	venum_write_pmresr(0);
	venum_post_pmresr(vval, fval);

	/* Reset PMxEVNCTCR to sane default */
	for (idx = ARMV7_IDX_CYCLE_COUNTER; idx < nb_cnt; ++idx) {
		armv7_pmnc_select_counter(idx);
		asm volatile("mcr p15, 0, %0, c9, c15, 0" : : "r" (0));
	}
}

static int scorpion_event_to_bit(struct perf_event *event, unsigned int region,
			      unsigned int group)
{
	int bit;
	struct hw_perf_event *hwc = &event->hw;
	struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);

	if (hwc->config_base & VENUM_EVENT)
		bit = SCORPION_VLPM_GROUP0;
	else
		bit = scorpion_get_pmresrn_event(region);
	bit -= scorpion_get_pmresrn_event(0);
	bit += group;
	/*
	 * Lower bits are reserved for use by the counters (see
	 * armv7pmu_get_event_idx() for more info)
	 */
	bit += ARMV7_IDX_COUNTER_LAST(cpu_pmu) + 1;

	return bit;
}

/*
 * We check for column exclusion constraints here.
 * Two events cant use the same group within a pmresr register.
 */
static int scorpion_pmu_get_event_idx(struct pmu_hw_events *cpuc,
				   struct perf_event *event)
{
	int idx;
	int bit = -1;
	struct hw_perf_event *hwc = &event->hw;
	unsigned int region = EVENT_REGION(hwc->config_base);
	unsigned int group = EVENT_GROUP(hwc->config_base);
	bool venum_event = EVENT_VENUM(hwc->config_base);
	bool scorpion_event = EVENT_CPU(hwc->config_base);

	if (venum_event || scorpion_event) {
		/* Ignore invalid events */
		if (group > 3 || region > 3)
			return -EINVAL;

		bit = scorpion_event_to_bit(event, region, group);
		if (test_and_set_bit(bit, cpuc->used_mask))
			return -EAGAIN;
	}

	idx = armv7pmu_get_event_idx(cpuc, event);
	if (idx < 0 && bit >= 0)
		clear_bit(bit, cpuc->used_mask);

	return idx;
}

static void scorpion_pmu_clear_event_idx(struct pmu_hw_events *cpuc,
				      struct perf_event *event)
{
	int bit;
	struct hw_perf_event *hwc = &event->hw;
	unsigned int region = EVENT_REGION(hwc->config_base);
	unsigned int group = EVENT_GROUP(hwc->config_base);
	bool venum_event = EVENT_VENUM(hwc->config_base);
	bool scorpion_event = EVENT_CPU(hwc->config_base);

	if (venum_event || scorpion_event) {
		bit = scorpion_event_to_bit(event, region, group);
		clear_bit(bit, cpuc->used_mask);
	}
}

static int scorpion_pmu_init(struct arm_pmu *cpu_pmu)
{
	armv7pmu_init(cpu_pmu);
	cpu_pmu->name		= "armv7_scorpion";
	cpu_pmu->map_event	= scorpion_map_event;
	cpu_pmu->reset		= scorpion_pmu_reset;
	cpu_pmu->enable		= scorpion_pmu_enable_event;
	cpu_pmu->disable	= scorpion_pmu_disable_event;
	cpu_pmu->get_event_idx	= scorpion_pmu_get_event_idx;
	cpu_pmu->clear_event_idx = scorpion_pmu_clear_event_idx;
	return armv7_probe_num_events(cpu_pmu);
}

static int scorpion_mp_pmu_init(struct arm_pmu *cpu_pmu)
{
	armv7pmu_init(cpu_pmu);
	cpu_pmu->name		= "armv7_scorpion_mp";
	cpu_pmu->map_event	= scorpion_map_event;
	cpu_pmu->reset		= scorpion_pmu_reset;
	cpu_pmu->enable		= scorpion_pmu_enable_event;
	cpu_pmu->disable	= scorpion_pmu_disable_event;
	cpu_pmu->get_event_idx	= scorpion_pmu_get_event_idx;
	cpu_pmu->clear_event_idx = scorpion_pmu_clear_event_idx;
	return armv7_probe_num_events(cpu_pmu);
}

static const struct of_device_id armv7_pmu_of_device_ids[] = {
	{.compatible = "arm,cortex-a17-pmu",	.data = armv7_a17_pmu_init},
	{.compatible = "arm,cortex-a15-pmu",	.data = armv7_a15_pmu_init},
	{.compatible = "arm,cortex-a12-pmu",	.data = armv7_a12_pmu_init},
	{.compatible = "arm,cortex-a9-pmu",	.data = armv7_a9_pmu_init},
	{.compatible = "arm,cortex-a8-pmu",	.data = armv7_a8_pmu_init},
	{.compatible = "arm,cortex-a7-pmu",	.data = armv7_a7_pmu_init},
	{.compatible = "arm,cortex-a5-pmu",	.data = armv7_a5_pmu_init},
	{.compatible = "qcom,krait-pmu",	.data = krait_pmu_init},
	{.compatible = "qcom,scorpion-pmu",	.data = scorpion_pmu_init},
	{.compatible = "qcom,scorpion-mp-pmu",	.data = scorpion_mp_pmu_init},
	{},
};

static const struct pmu_probe_info armv7_pmu_probe_table[] = {
	ARM_PMU_PROBE(ARM_CPU_PART_CORTEX_A8, armv7_a8_pmu_init),
	ARM_PMU_PROBE(ARM_CPU_PART_CORTEX_A9, armv7_a9_pmu_init),
	{ /* sentinel value */ }
};


static int armv7_pmu_device_probe(struct platform_device *pdev)
{
	return arm_pmu_device_probe(pdev, armv7_pmu_of_device_ids,
				    armv7_pmu_probe_table);
}

static struct platform_driver armv7_pmu_driver = {
	.driver		= {
		.name	= "armv7-pmu",
		.of_match_table = armv7_pmu_of_device_ids,
	},
	.probe		= armv7_pmu_device_probe,
};

static int __init register_armv7_pmu_driver(void)
{
	return platform_driver_register(&armv7_pmu_driver);
}
device_initcall(register_armv7_pmu_driver);
#endif	/* CONFIG_CPU_V7 */