diff --git a/test/cpu_speed_test.cc b/test/cpu_speed_test.cc
index c206285944d6eb29d71e0b6a850fd5d3e08c33c5..0620f56c8a56ccd6327e67a4c5a66560ffa43942 100644
--- a/test/cpu_speed_test.cc
+++ b/test/cpu_speed_test.cc
@@ -17,9 +17,6 @@
 namespace {
 
 const int kMaxPSNR = 100;
-#if CONFIG_AOM_QM
-const int kMaxPSNR_QM = 35;
-#endif
 
 class CpuSpeedTest
     : public ::libvpx_test::EncoderTest,
@@ -83,11 +80,7 @@ TEST_P(CpuSpeedTest, TestQ0) {
   init_flags_ = VPX_CODEC_USE_PSNR;
 
   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
-#if CONFIG_AOM_QM
-  EXPECT_GE(min_psnr_, kMaxPSNR_QM);
-#else
   EXPECT_GE(min_psnr_, kMaxPSNR);
-#endif
 }
 
 TEST_P(CpuSpeedTest, TestScreencastQ0) {
@@ -102,11 +95,7 @@ TEST_P(CpuSpeedTest, TestScreencastQ0) {
   init_flags_ = VPX_CODEC_USE_PSNR;
 
   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
-#if CONFIG_AOM_QM
-  EXPECT_GE(min_psnr_, kMaxPSNR_QM);
-#else
   EXPECT_GE(min_psnr_, kMaxPSNR);
-#endif
 }
 
 TEST_P(CpuSpeedTest, TestEncodeHighBitrate) {
diff --git a/test/end_to_end_test.cc b/test/end_to_end_test.cc
index b436e983d12287dec36053533e186279fde87de8..96621dbc27888319b54ba81e36e76de0158bb7f5 100644
--- a/test/end_to_end_test.cc
+++ b/test/end_to_end_test.cc
@@ -132,11 +132,7 @@ class EndToEndTestLarge
   }
 
   double GetPsnrThreshold() {
-#if CONFIG_AOM_QM
-    return kPsnrThreshold[cpu_used_][encoding_mode_] - 3.0;
-#else
     return kPsnrThreshold[cpu_used_][encoding_mode_];
-#endif
   }
 
   TestVideoParam test_video_param_;
diff --git a/vp10/common/onyxc_int.h b/vp10/common/onyxc_int.h
index 7c126e114ad7652878ccbe6c618f13b72bec128f..c17de2c60c9bbed350fb7dd18a10d93ef7756484 100644
--- a/vp10/common/onyxc_int.h
+++ b/vp10/common/onyxc_int.h
@@ -211,6 +211,7 @@ typedef struct VP10Common {
   qm_val_t *y_qmatrix[MAX_SEGMENTS][2][TX_SIZES];
   qm_val_t *uv_qmatrix[MAX_SEGMENTS][2][TX_SIZES];
 
+  int using_qmatrix;
 #endif
 
   /* We allocate a MODE_INFO struct for each macroblock, together with
diff --git a/vp10/decoder/decodeframe.c b/vp10/decoder/decodeframe.c
index 3f7f098fac37305ac08d59ffa85cc6fceee10cdd..706914ce7a94f4edbcc84ac063eab830a7eeffee 100644
--- a/vp10/decoder/decodeframe.c
+++ b/vp10/decoder/decodeframe.c
@@ -1116,6 +1116,9 @@ static void setup_quantization(VP10_COMMON *const cm,
   cm->uv_dc_delta_q = read_delta_q(rb);
   cm->uv_ac_delta_q = read_delta_q(rb);
   cm->dequant_bit_depth = cm->bit_depth;
+#if CONFIG_AOM_QM
+  cm->using_qmatrix = vpx_rb_read_bit(rb);
+#endif
 }
 
 static void setup_segmentation_dequant(VP10_COMMON *const cm) {
@@ -1125,6 +1128,7 @@ static void setup_segmentation_dequant(VP10_COMMON *const cm) {
   int lossless;
   int j = 0;
   int qmindex;
+  int using_qm = cm->using_qmatrix;
 #endif
   if (cm->seg.enabled) {
     for (i = 0; i < MAX_SEGMENTS; ++i) {
@@ -1140,8 +1144,9 @@ static void setup_segmentation_dequant(VP10_COMMON *const cm) {
       lossless = qindex == 0 && cm->y_dc_delta_q == 0 &&
                  cm->uv_dc_delta_q == 0 && cm->uv_ac_delta_q == 0;
       // NB: depends on base index so there is only 1 set per frame
-      // No quant weighting when lossless
-      qmindex = lossless ? QINDEX_RANGE - 1 : cm->base_qindex;
+      // No quant weighting when lossless or signalled not using QM
+      qmindex = (lossless || using_qm == 0) ?
+        QINDEX_RANGE - 1 : cm->base_qindex;
       for (j = 0; j < TX_SIZES; ++j) {
         cm->y_iqmatrix[i][1][j] = aom_iqmatrix(cm, qmindex, 0, j, 1);
         cm->y_iqmatrix[i][0][j] = aom_iqmatrix(cm, qmindex, 0, j, 0);
@@ -1164,8 +1169,9 @@ static void setup_segmentation_dequant(VP10_COMMON *const cm) {
 #if CONFIG_AOM_QM
     lossless = qindex == 0 && cm->y_dc_delta_q == 0 && cm->uv_dc_delta_q == 0 &&
                cm->uv_ac_delta_q == 0;
-    // No quant weighting when lossless
-    qmindex = lossless ? QINDEX_RANGE - 1 : cm->base_qindex;
+    // No quant weighting when lossless or signalled not using QM
+    qmindex = (lossless || using_qm == 0) ?
+        QINDEX_RANGE - 1 : cm->base_qindex;
     for (j = 0; j < TX_SIZES; ++j) {
       cm->y_iqmatrix[i][1][j] = aom_iqmatrix(cm, qmindex, 0, j, 1);
       cm->y_iqmatrix[i][0][j] = aom_iqmatrix(cm, qmindex, 0, j, 0);
diff --git a/vp10/encoder/bitstream.c b/vp10/encoder/bitstream.c
index 9f7b6b39977a651b34060c31effb0604f6768740..d4fd08ca146d594c66cbbe6f36950374fe5097ec 100644
--- a/vp10/encoder/bitstream.c
+++ b/vp10/encoder/bitstream.c
@@ -863,6 +863,9 @@ static void encode_quantization(const VP10_COMMON *const cm,
   write_delta_q(wb, cm->y_dc_delta_q);
   write_delta_q(wb, cm->uv_dc_delta_q);
   write_delta_q(wb, cm->uv_ac_delta_q);
+#if CONFIG_AOM_QM
+  vpx_wb_write_bit(wb, cm->using_qmatrix);
+#endif
 }
 
 static void encode_segmentation(VP10_COMMON *cm, MACROBLOCKD *xd,
diff --git a/vp10/encoder/encoder.c b/vp10/encoder/encoder.c
index b7a18cc7651ff4f4fdcb1e86e2b828c611bee38f..881ec99d2dc140c80246f3f1c8e305b578076c01 100644
--- a/vp10/encoder/encoder.c
+++ b/vp10/encoder/encoder.c
@@ -3826,6 +3826,10 @@ int vp10_get_compressed_data(VP10_COMP *cpi, unsigned int *frame_flags,
     for (i = 0; i < MAX_REF_FRAMES; ++i) cpi->scaled_ref_idx[i] = INVALID_IDX;
   }
 
+#if CONFIG_AOM_QM
+  cm->using_qmatrix = cpi->oxcf.using_qm;
+#endif
+
   if (oxcf->pass == 1) {
     cpi->td.mb.e_mbd.lossless[0] = is_lossless_requested(oxcf);
     vp10_first_pass(cpi, source);
diff --git a/vp10/encoder/encoder.h b/vp10/encoder/encoder.h
index 3547cbae3e77893925e367a2a7552cc17a537a79..697da51169b861b031cc2d89c3771bca25bbbbf7 100644
--- a/vp10/encoder/encoder.h
+++ b/vp10/encoder/encoder.h
@@ -168,6 +168,7 @@ typedef struct VP10EncoderConfig {
   int best_allowed_q;
   int cq_level;
   AQ_MODE aq_mode;  // Adaptive Quantization mode
+  int using_qm;
 
   // Internal frame size scaling.
   RESIZE_TYPE resize_mode;
diff --git a/vp10/encoder/quantize.c b/vp10/encoder/quantize.c
index 820dc4a0200eec2c0e53d29a81ed68ca799912cd..ae5c97931e67d1c8b8eb8fb72cd2494c011b0dd1 100644
--- a/vp10/encoder/quantize.c
+++ b/vp10/encoder/quantize.c
@@ -392,7 +392,8 @@ void vp10_init_plane_quantizers(VP10_COMP *cpi, MACROBLOCK *x) {
 #if CONFIG_AOM_QM
   const int lossless = xd->lossless[segment_id];
   // Quant matrix only depends on the base QP so there is only one set per frame
-  int qmlevel = lossless ? NUM_QM_LEVELS - 1 : aom_get_qmlevel(cm->base_qindex);
+  int qmlevel = (lossless || cm->using_qmatrix == 0) ?
+    NUM_QM_LEVELS - 1 : aom_get_qmlevel(cm->base_qindex);
 #endif
 
   // Y
diff --git a/vp10/vp10_cx_iface.c b/vp10/vp10_cx_iface.c
index 0e737881ea2befe931d355c800966b7be6a113f3..840c4a7f5b3a68842e5c200919727f58ed393d93 100644
--- a/vp10/vp10_cx_iface.c
+++ b/vp10/vp10_cx_iface.c
@@ -39,6 +39,9 @@ struct vp10_extracfg {
   unsigned int rc_max_inter_bitrate_pct;
   unsigned int gf_cbr_boost_pct;
   unsigned int lossless;
+#if CONFIG_AOM_QM
+  unsigned int enable_qm;
+#endif
   unsigned int frame_parallel_decoding_mode;
   AQ_MODE aq_mode;
   unsigned int frame_periodic_boost;
@@ -68,6 +71,9 @@ static struct vp10_extracfg default_extra_cfg = {
   0,                    // rc_max_inter_bitrate_pct
   0,                    // gf_cbr_boost_pct
   0,                    // lossless
+#if CONFIG_AOM_QM
+  0,                    // enable_qm
+#endif
   1,                    // frame_parallel_decoding_mode
   NO_AQ,                // aq_mode
   0,                    // frame_periodic_delta_q
@@ -357,6 +363,10 @@ static vpx_codec_err_t set_encoder_config(
   oxcf->cq_level = vp10_quantizer_to_qindex(extra_cfg->cq_level);
   oxcf->fixed_q = -1;
 
+#if CONFIG_AOM_QM
+  oxcf->using_qm = extra_cfg->enable_qm;
+#endif
+
   oxcf->under_shoot_pct = cfg->rc_undershoot_pct;
   oxcf->over_shoot_pct = cfg->rc_overshoot_pct;
 
@@ -632,6 +642,15 @@ static vpx_codec_err_t ctrl_set_lossless(vpx_codec_alg_priv_t *ctx,
   return update_extra_cfg(ctx, &extra_cfg);
 }
 
+#if CONFIG_AOM_QM
+static vpx_codec_err_t ctrl_set_enable_qm(vpx_codec_alg_priv_t *ctx,
+                                         va_list args) {
+  struct vp10_extracfg extra_cfg = ctx->extra_cfg;
+  extra_cfg.enable_qm = CAST(VP9E_SET_ENABLE_QM, args);
+  return update_extra_cfg(ctx, &extra_cfg);
+}
+#endif
+
 static vpx_codec_err_t ctrl_set_frame_parallel_decoding_mode(
     vpx_codec_alg_priv_t *ctx, va_list args) {
   struct vp10_extracfg extra_cfg = ctx->extra_cfg;
@@ -1228,6 +1247,9 @@ static vpx_codec_ctrl_fn_map_t encoder_ctrl_maps[] = {
   { VP9E_SET_MAX_INTER_BITRATE_PCT, ctrl_set_rc_max_inter_bitrate_pct },
   { VP9E_SET_GF_CBR_BOOST_PCT, ctrl_set_rc_gf_cbr_boost_pct },
   { VP9E_SET_LOSSLESS, ctrl_set_lossless },
+#if CONFIG_AOM_QM
+  { VP9E_SET_ENABLE_QM, ctrl_set_enable_qm },
+#endif
   { VP9E_SET_FRAME_PARALLEL_DECODING, ctrl_set_frame_parallel_decoding_mode },
   { VP9E_SET_AQ_MODE, ctrl_set_aq_mode },
   { VP9E_SET_FRAME_PERIODIC_BOOST, ctrl_set_frame_periodic_boost },
diff --git a/vpx/vp8cx.h b/vpx/vp8cx.h
index 835ea78dee329d8df94cccdbb8098bcb043681e8..0f033ffb7917c5154919332fc86cd42bdc70e5b9 100644
--- a/vpx/vp8cx.h
+++ b/vpx/vp8cx.h
@@ -310,6 +310,21 @@ enum vp8e_enc_control_id {
    * Supported in codecs: VP9
    */
   VP9E_SET_LOSSLESS,
+#if CONFIG_AOM_QM
+  /*!\brief Codec control function to encode with quantisation matrices.
+   *
+   * AOM can operate with default quantisation matrices dependent on
+   * quantisation level and block type.
+   *                          0 = do not use quantisation matrices
+   *                          1 = use quantisation matrices
+   *
+   *  By default, the encoder operates without quantisation matrices.
+   *
+   * Supported in codecs: AOM
+   */
+
+  VP9E_SET_ENABLE_QM,
+#endif
 
   /*!\brief Codec control function to set number of tile columns.
    *
@@ -745,6 +760,11 @@ VPX_CTRL_USE_TYPE(VP9E_SET_GF_CBR_BOOST_PCT, unsigned int)
 VPX_CTRL_USE_TYPE(VP9E_SET_LOSSLESS, unsigned int)
 #define VPX_CTRL_VP9E_SET_LOSSLESS
 
+#if CONFIG_AOM_QM
+VPX_CTRL_USE_TYPE(VP9E_SET_ENABLE_QM, unsigned int)
+#define VPX_CTRL_VP9E_SET_ENABLE_QM
+#endif
+
 VPX_CTRL_USE_TYPE(VP9E_SET_FRAME_PARALLEL_DECODING, unsigned int)
 #define VPX_CTRL_VP9E_SET_FRAME_PARALLEL_DECODING
 
diff --git a/vpxenc.c b/vpxenc.c
index f9ff31c1960d372f66ad089648df4a0457323b9d..8fbeb3659a5b0c625d7acca6d57a717f11ddf74a 100644
--- a/vpxenc.c
+++ b/vpxenc.c
@@ -365,6 +365,11 @@ static const arg_def_t tile_rows =
     ARG_DEF(NULL, "tile-rows", 1, "Number of tile rows to use, log2");
 static const arg_def_t lossless =
     ARG_DEF(NULL, "lossless", 1, "Lossless mode (0: false (default), 1: true)");
+#if CONFIG_AOM_QM
+static const arg_def_t enable_qm =
+    ARG_DEF(NULL, "enable_qm", 1,
+        "Enable quantisation matrices (0: false (default), 1: true)");
+#endif
 static const arg_def_t frame_parallel_decoding = ARG_DEF(
     NULL, "frame-parallel", 1, "Enable frame parallel decodability features");
 static const arg_def_t aq_mode = ARG_DEF(
@@ -431,6 +436,9 @@ static const arg_def_t *vp10_args[] = {
   &tile_cols, &tile_rows, &arnr_maxframes, &arnr_strength, &arnr_type,
   &tune_ssim, &cq_level, &max_intra_rate_pct, &max_inter_rate_pct,
   &gf_cbr_boost_pct, &lossless,
+#if CONFIG_AOM_QM
+  &enable_qm,
+#endif
   &frame_parallel_decoding, &aq_mode, &frame_periodic_boost,
   &noise_sens, &tune_content, &input_color_space,
   &min_gf_interval, &max_gf_interval,
@@ -443,7 +451,11 @@ static const int vp10_arg_ctrl_map[] = {
   VP8E_SET_ARNR_MAXFRAMES, VP8E_SET_ARNR_STRENGTH, VP8E_SET_ARNR_TYPE,
   VP8E_SET_TUNING, VP8E_SET_CQ_LEVEL, VP8E_SET_MAX_INTRA_BITRATE_PCT,
   VP9E_SET_MAX_INTER_BITRATE_PCT, VP9E_SET_GF_CBR_BOOST_PCT,
-  VP9E_SET_LOSSLESS, VP9E_SET_FRAME_PARALLEL_DECODING, VP9E_SET_AQ_MODE,
+  VP9E_SET_LOSSLESS,
+#if CONFIG_AOM_QM
+  VP9E_SET_ENABLE_QM,
+#endif
+  VP9E_SET_FRAME_PARALLEL_DECODING, VP9E_SET_AQ_MODE,
   VP9E_SET_FRAME_PERIODIC_BOOST, VP9E_SET_NOISE_SENSITIVITY,
   VP9E_SET_TUNE_CONTENT, VP9E_SET_COLOR_SPACE,
   VP9E_SET_MIN_GF_INTERVAL, VP9E_SET_MAX_GF_INTERVAL,