diff --git a/README.md b/README.md
index 23c81e430..16562ca0f 100644
--- a/README.md
+++ b/README.md
@@ -79,6 +79,7 @@ Supported Cars
| Honda | CR-V 2015-16 | Touring | Yes | Yes | 25mph1| 12mph | Nidec |
| Honda | CR-V 2017-19 | Honda Sensing | Yes | Stock | 0mph | 12mph | Bosch |
| Honda | CR-V Hybrid 2017-2019 | Honda Sensing | Yes | Stock | 0mph | 12mph | Bosch |
+| Honda | Fit 2018-19 | Honda Sensing | Yes | Yes | 25mph1| 12mph | Inverted Nidec |
| Honda | Odyssey 2018-19 | Honda Sensing | Yes | Yes | 25mph1| 0mph | Inverted Nidec |
| Honda | Passport 2019 | All | Yes | Yes | 25mph1| 12mph | Inverted Nidec |
| Honda | Pilot 2016-18 | Honda Sensing | Yes | Yes | 25mph1| 12mph | Nidec |
@@ -111,7 +112,7 @@ Supported Cars
| Toyota | Highlander Hybrid 2017-19 | All | Yes | Yes2| 0mph | 0mph | Toyota |
| Toyota | Prius 2016 | TSS-P | Yes | Yes2| 0mph | 0mph | Toyota |
| Toyota | Prius 2017-19 | All | Yes | Yes2| 0mph | 0mph | Toyota |
-| Toyota | Prius Prime 2017-19 | All | Yes | Yes2| 0mph | 0mph | Toyota |
+| Toyota | Prius Prime 2017-20 | All | Yes | Yes2| 0mph | 0mph | Toyota |
| Toyota | Rav4 2016 | TSS-P | Yes | Yes2| 20mph1| 0mph | Toyota |
| Toyota | Rav4 2017-18 | All | Yes | Yes2| 20mph1| 0mph | Toyota |
| Toyota | Rav4 2019 | All | Yes | Yes | 0mph | 0mph | Toyota |
diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py
index 94881dd66..dad8123c1 100644
--- a/selfdrive/car/toyota/values.py
+++ b/selfdrive/car/toyota/values.py
@@ -104,6 +104,10 @@ FINGERPRINTS = {
#2019 LE
{
36: 8, 37: 8, 166: 8, 170: 8, 180: 8, 295: 8, 296: 8, 426: 6, 452: 8, 466: 8, 467: 8, 550: 8, 552: 4, 560: 7, 562: 6, 581: 5, 608: 8, 610: 8, 614: 8, 643: 7, 658: 8, 713: 8, 740: 5, 742: 8, 743: 8, 800: 8, 810: 2, 814: 8, 829: 2, 830: 7, 835: 8, 836: 8, 863: 8, 865: 8, 869: 7, 870: 7, 871: 2, 896: 8, 898: 8, 900: 6, 902: 6, 905: 8, 918: 8, 921: 8, 933: 8, 944: 8, 945: 8, 950: 8, 951: 8, 953: 8, 955: 8, 956: 8, 971: 7, 975: 5, 993: 8, 998: 5, 999: 7, 1000: 8, 1001: 8, 1002: 8, 1014: 8, 1017: 8, 1020: 8, 1041: 8, 1042: 8, 1044: 8, 1056: 8, 1057: 8, 1059: 1, 1071: 8, 1076: 8, 1077: 8, 1082: 8, 1083: 8, 1084: 8, 1085: 8, 1086: 8, 1114: 8, 1132: 8, 1161: 8, 1162: 8, 1163: 8, 1175: 8, 1227: 8, 1228: 8, 1235: 8, 1237: 8, 1279: 8, 1552: 8, 1553: 8, 1556: 8, 1557: 8, 1568: 8, 1570: 8, 1571: 8, 1572: 8, 1592: 8, 1595: 8, 1777: 8, 1779: 8, 1904: 8, 1912: 8, 1990: 8, 1998: 8
+ },
+ #2020 Prius Prime Limited
+ {
+ 36: 8, 37: 8, 166: 8, 170: 8, 180: 8, 295: 8, 296: 8, 426: 6, 452: 8, 466: 8, 467: 8, 550: 8, 552: 4, 560: 7, 562: 6, 581: 5, 608: 8, 610: 8, 614: 8, 643: 7, 658: 8, 713: 8, 740: 5, 742: 8, 743: 8, 800: 8, 810: 2, 814: 8, 824: 2, 829: 2, 830: 7, 835: 8, 836: 8, 863: 8, 865: 8, 869: 7, 870: 7, 871: 2, 896: 8, 898: 8, 900: 6, 902: 6, 905: 8, 913: 8, 918: 8, 921: 8, 933: 8, 944: 8, 945: 8, 950: 8, 951: 8, 953: 8, 955: 8, 956: 8, 971: 7, 974: 8, 975: 5, 993: 8, 998: 5, 999: 7, 1000: 8, 1001: 8, 1014: 8, 1017: 8, 1020: 8, 1041: 8, 1042: 8, 1044: 8, 1056: 8, 1057: 8, 1059: 1, 1071: 8, 1076: 8, 1077: 8, 1082: 8, 1083: 8, 1084: 8, 1085: 8, 1086: 8, 1114: 8, 1132: 8, 1161: 8, 1162: 8, 1163: 8, 1164: 8, 1165: 8, 1166: 8, 1167: 8, 1175: 8, 1227: 8, 1228: 8, 1235: 8, 1237: 8, 1279: 8, 1541: 8, 1552: 8, 1553: 8, 1556: 8, 1557: 8, 1568: 8, 1570: 8, 1571: 8, 1572: 8, 1592: 8, 1595: 8, 1649: 8, 1777: 8, 1779: 8, 1904: 8, 1912: 8, 1990: 8, 1998: 8, 2015: 8, 2024: 8, 2026: 8, 2027: 8, 2029: 8, 2030: 8, 2031: 8
}],
#Corolla w/ added Pedal Support (512L and 513L)
CAR.COROLLA: [{
diff --git a/selfdrive/common/spinner.c b/selfdrive/common/spinner.c
new file mode 100644
index 000000000..c8bfde8c7
--- /dev/null
+++ b/selfdrive/common/spinner.c
@@ -0,0 +1,113 @@
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#include "nanovg.h"
+#define NANOVG_GLES3_IMPLEMENTATION
+#include "nanovg_gl.h"
+#include "nanovg_gl_utils.h"
+
+#include "framebuffer.h"
+#include "spinner.h"
+
+// external resources linked in
+extern const unsigned char _binary_opensans_semibold_ttf_start[];
+extern const unsigned char _binary_opensans_semibold_ttf_end[];
+
+extern const unsigned char _binary_img_spinner_track_png_start[];
+extern const unsigned char _binary_img_spinner_track_png_end[];
+
+extern const unsigned char _binary_img_spinner_comma_png_start[];
+extern const unsigned char _binary_img_spinner_comma_png_end[];
+
+int spin(int argc, char** argv) {
+ int err;
+
+ const char* spintext = NULL;
+ if (argc == 1) {
+ spintext = argv[0];
+ } else if (argc >= 2) {
+ spintext = argv[1];
+ }
+
+ // spinner
+ int fb_w, fb_h;
+ EGLDisplay display;
+ EGLSurface surface;
+ FramebufferState *fb = framebuffer_init("spinner", 0x00001000, false,
+ &display, &surface, &fb_w, &fb_h);
+ assert(fb);
+
+ NVGcontext *vg = nvgCreateGLES3(NVG_ANTIALIAS | NVG_STENCIL_STROKES);
+ assert(vg);
+
+ int font = nvgCreateFontMem(vg, "Bold", (unsigned char*)_binary_opensans_semibold_ttf_start, _binary_opensans_semibold_ttf_end-_binary_opensans_semibold_ttf_start, 0);
+ assert(font >= 0);
+
+ int spinner_img = nvgCreateImageMem(vg, 0, (unsigned char*)_binary_img_spinner_track_png_start, _binary_img_spinner_track_png_end - _binary_img_spinner_track_png_start);
+ assert(spinner_img >= 0);
+ int spinner_img_s = 360;
+ int spinner_img_x = ((fb_w/2)-(spinner_img_s/2));
+ int spinner_img_y = 260;
+ int spinner_img_xc = (fb_w/2);
+ int spinner_img_yc = (fb_h/2)-100;
+ int spinner_comma_img = nvgCreateImageMem(vg, 0, (unsigned char*)_binary_img_spinner_comma_png_start, _binary_img_spinner_comma_png_end - _binary_img_spinner_comma_png_start);
+ assert(spinner_comma_img >= 0);
+
+ for (int cnt = 0; ; cnt++) {
+ glClearColor(0.1, 0.1, 0.1, 1.0);
+ glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ nvgBeginFrame(vg, fb_w, fb_h, 1.0f);
+
+ // background
+ nvgBeginPath(vg);
+ NVGpaint bg = nvgLinearGradient(vg, fb_w, 0, fb_w, fb_h,
+ nvgRGBA(0, 0, 0, 175), nvgRGBA(0, 0, 0, 255));
+ nvgFillPaint(vg, bg);
+ nvgRect(vg, 0, 0, fb_w, fb_h);
+ nvgFill(vg);
+
+ // spin track
+ nvgSave(vg);
+ nvgTranslate(vg, spinner_img_xc, spinner_img_yc);
+ nvgRotate(vg, (3.75*M_PI * cnt/120.0));
+ nvgTranslate(vg, -spinner_img_xc, -spinner_img_yc);
+ NVGpaint spinner_imgPaint = nvgImagePattern(vg, spinner_img_x, spinner_img_y,
+ spinner_img_s, spinner_img_s, 0, spinner_img, 0.6f);
+ nvgBeginPath(vg);
+ nvgFillPaint(vg, spinner_imgPaint);
+ nvgRect(vg, spinner_img_x, spinner_img_y, spinner_img_s, spinner_img_s);
+ nvgFill(vg);
+ nvgRestore(vg);
+
+ // comma
+ NVGpaint comma_imgPaint = nvgImagePattern(vg, spinner_img_x, spinner_img_y,
+ spinner_img_s, spinner_img_s, 0, spinner_comma_img, 1.0f);
+ nvgBeginPath(vg);
+ nvgFillPaint(vg, comma_imgPaint);
+ nvgRect(vg, spinner_img_x, spinner_img_y, spinner_img_s, spinner_img_s);
+ nvgFill(vg);
+
+ // message
+ if (spintext) {
+ nvgTextAlign(vg, NVG_ALIGN_CENTER | NVG_ALIGN_TOP);
+ nvgFontSize(vg, 96.0f);
+ nvgText(vg, fb_w/2, (fb_h*2/3)+24, spintext, NULL);
+ }
+
+ nvgEndFrame(vg);
+ eglSwapBuffers(display, surface);
+ assert(glGetError() == GL_NO_ERROR);
+ }
+
+ return 0;
+}
diff --git a/selfdrive/common/spinner.h b/selfdrive/common/spinner.h
new file mode 100644
index 000000000..fd35dcc7d
--- /dev/null
+++ b/selfdrive/common/spinner.h
@@ -0,0 +1,14 @@
+#ifndef COMMON_SPINNER_H
+#define COMMON_SPINNER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int spin(int argc, char** argv);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif