diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp
index 59c75042c7..7409534b6f 100644
--- a/src/video_core/command_processor.cpp
+++ b/src/video_core/command_processor.cpp
@@ -464,6 +464,21 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
             break;
         }
 
+        case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[0], 0x1c8):
+        case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[1], 0x1c9):
+        case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[2], 0x1ca):
+        case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[3], 0x1cb):
+        case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[4], 0x1cc):
+        case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[5], 0x1cd):
+        case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[6], 0x1ce):
+        case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[7], 0x1cf):
+        {
+            auto& lut_config = regs.lighting.lut_config;
+            g_state.lighting.luts[lut_config.type][lut_config.index].raw = value;
+            lut_config.index = lut_config.index + 1;
+            break;
+        }
+
         default:
             break;
     }
diff --git a/src/video_core/pica.h b/src/video_core/pica.h
index 81a568e885..b09484de43 100644
--- a/src/video_core/pica.h
+++ b/src/video_core/pica.h
@@ -1156,6 +1156,25 @@ struct State {
     ShaderSetup vs;
     ShaderSetup gs;
 
+    struct {
+        union LutEntry {
+            // Used for raw access
+            u32 raw;
+
+            // LUT value, encoded as 12-bit fixed point, with 12 fraction bits
+            BitField< 0, 12, u32> value;
+
+            // Used by HW for efficient interpolation, Citra does not use these
+            BitField<12, 12, u32> difference;
+
+            float ToFloat() {
+                return static_cast<float>(value) / 4095.f;
+            }
+        };
+
+        std::array<LutEntry, 256> luts[24];
+    } lighting;
+
     /// Current Pica command list
     struct {
         const u32* head_ptr;