diff --git a/include/libcamera/control_ids.h.in b/include/libcamera/control_ids.h.in
index 0718a8886f6c..410cfb643a0b 100644
--- a/include/libcamera/control_ids.h.in
+++ b/include/libcamera/control_ids.h.in
@@ -2,7 +2,7 @@
 /*
  * Copyright (C) 2019, Google Inc.
  *
- * control_ids.h - Control ID list
+ * ${include_prefix}control_ids.h - Control ID list
  *
  * This file is auto-generated. Do not edit.
  */
@@ -16,7 +16,7 @@
 
 namespace libcamera {
 
-namespace controls {
+namespace controls${namespace_extension} {
 
 enum {
 ${ids}
@@ -32,6 +32,6 @@ ${draft_controls}
 
 } /* namespace draft */
 
-} /* namespace controls */
+} /* namespace controls${namespace_extension} */
 
 } /* namespace libcamera */
diff --git a/src/libcamera/control_ids.cpp.in b/src/libcamera/control_ids.cpp.in
index 5fb1c2c30558..e7e1080e9324 100644
--- a/src/libcamera/control_ids.cpp.in
+++ b/src/libcamera/control_ids.cpp.in
@@ -2,17 +2,17 @@
 /*
  * Copyright (C) 2019, Google Inc.
  *
- * control_ids.cpp : Control ID list
+ * ${include_prefix}control_ids.cpp : Control ID list
  *
  * This file is auto-generated. Do not edit.
  */
 
-#include <libcamera/control_ids.h>
+#include <libcamera/${include_prefix}control_ids.h>
 #include <libcamera/controls.h>
 
 /**
- * \file control_ids.h
- * \brief Camera control identifiers
+ * \file libcamera/${include_prefix}control_ids.h
+ * \brief Control identifiers
  */
 
 namespace libcamera {
@@ -20,7 +20,7 @@ namespace libcamera {
 /**
  * \brief Namespace for libcamera controls
  */
-namespace controls {
+namespace controls${namespace_extension} {
 
 ${controls_doc}
 
@@ -57,6 +57,6 @@ extern const ControlIdMap controls {
 ${controls_map}
 };
 
-} /* namespace controls */
+} /* namespace controls${namespace_extension} */
 
 } /* namespace libcamera */
diff --git a/utils/gen-controls.py b/utils/gen-controls.py
index 3f99b5e2ba7d..978179f63858 100755
--- a/utils/gen-controls.py
+++ b/utils/gen-controls.py
@@ -188,6 +188,17 @@ def fill_template(template, data):
     return template.substitute(data)
 
 
+def handle_internal_arg(internal, data):
+
+    if internal:
+        data["namespace_extension"] = "::internal"
+        data["include_prefix"] = "internal/"
+        return
+
+    data["namespace_extension"] = ""
+    data["include_prefix"] = ""
+
+
 def main(argv):
 
     # Parse command line arguments
@@ -198,6 +209,7 @@ def main(argv):
                         help='Input file name.')
     parser.add_argument('template', type=str,
                         help='Template file name.')
+    parser.add_argument('--internal', type=bool, default=False)
     args = parser.parse_args(argv[1:])
 
     data = open(args.input, 'rb').read()
@@ -210,6 +222,8 @@ def main(argv):
     else:
         raise RuntimeError('Unknown template type')
 
+    handle_internal_arg(args.internal, data)
+
     data = fill_template(args.template, data)
 
     if args.output:
