From 9cb47cf6cbe763d094ad1de8839d4a7aee95f131 Mon Sep 17 00:00:00 2001
From: Mateusz Kwiatkowski <kfyatek+publicgit@gmail.com>
Date: Thu, 15 Jul 2021 01:08:01 +0200
Subject: [PATCH] drm/vc4: Allow setting the TV norm via module parameter

Similar to the ch7006 and nouveau drivers, introduce a "tv_mode" module
parameter that allow setting the TV norm by specifying vc4.tv_norm= on
the kernel command line.

If that is not specified, try inferring one of the most popular norms
(PAL or NTSC) from the video mode specified on the command line. On
Raspberry Pis, this causes the most common cases of the sdtv_mode
setting in config.txt to be respected.

Signed-off-by: Mateusz Kwiatkowski <kfyatek+publicgit@gmail.com>

drm/vc4: Do not reset tv mode as this is already handled by framework

In vc4_vec_connector_reset, the tv mode is already reset to the
property default by drm_atomic_helper_connector_tv_reset, so there
is no need for a local fixup to potentially some other default.

Fixes: 96922af14473 ("drm/vc4: Allow setting the TV norm via module parameter")
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
---
 drivers/gpu/drm/vc4/vc4_vec.c | 107 +++++++++++++++++++++++++---------
 1 file changed, 78 insertions(+), 29 deletions(-)

--- a/drivers/gpu/drm/vc4/vc4_vec.c
+++ b/drivers/gpu/drm/vc4/vc4_vec.c
@@ -67,7 +67,7 @@
 #define VEC_CONFIG0_YCDELAY		BIT(4)
 #define VEC_CONFIG0_RAMPEN		BIT(2)
 #define VEC_CONFIG0_YCDIS		BIT(2)
-#define VEC_CONFIG0_STD_MASK		GENMASK(1, 0)
+#define VEC_CONFIG0_STD_MASK		(VEC_CONFIG0_SECAM_STD | GENMASK(1, 0))
 #define VEC_CONFIG0_NTSC_STD		0
 #define VEC_CONFIG0_PAL_BDGHI_STD	1
 #define VEC_CONFIG0_PAL_M_STD		2
@@ -186,6 +186,8 @@
 #define VEC_DAC_MISC_DAC_RST_N		BIT(0)
 
 
+static char *vc4_vec_tv_norm;
+
 struct vc4_vec_variant {
 	u32 dac_config;
 };
@@ -371,6 +373,33 @@ static const struct drm_prop_enum_list l
 	{ VC4_VEC_TV_MODE_MONOCHROME, "Mono", },
 };
 
+static enum drm_connector_tv_mode
+vc4_vec_get_default_mode(struct drm_connector *connector)
+{
+	if (vc4_vec_tv_norm) {
+		int ret;
+
+		ret = drm_get_tv_mode_from_name(vc4_vec_tv_norm, strlen(vc4_vec_tv_norm));
+		if (ret >= 0)
+			return ret;
+	} else if (connector->cmdline_mode.specified &&
+		   ((connector->cmdline_mode.refresh_specified &&
+		     (connector->cmdline_mode.refresh == 25 ||
+		      connector->cmdline_mode.refresh == 50)) ||
+		    (!connector->cmdline_mode.refresh_specified &&
+		     (connector->cmdline_mode.yres == 288 ||
+		      connector->cmdline_mode.yres == 576)))) {
+		/*
+		 * no explicitly specified TV norm; use PAL if a mode that
+		 * looks like PAL has been specified on the command line
+		 */
+		return DRM_MODE_TV_MODE_PAL;
+	}
+
+	/* in all other cases, default to NTSC */
+	return DRM_MODE_TV_MODE_NTSC;
+}
+
 static enum drm_connector_status
 vc4_vec_connector_detect(struct drm_connector *connector, bool force)
 {
@@ -436,52 +465,55 @@ vc4_vec_connector_set_property(struct dr
 }
 
 static int
-vc4_vec_connector_get_property(struct drm_connector *connector,
-			       const struct drm_connector_state *state,
-			       struct drm_property *property,
-			       uint64_t *val)
+vc4_vec_generic_tv_mode_to_legacy(enum drm_connector_tv_mode tv_mode)
 {
-	struct vc4_vec *vec = connector_to_vc4_vec(connector);
-
-	if (property != vec->legacy_tv_mode_property)
-		return -EINVAL;
-
-	switch (state->tv.mode) {
+	switch (tv_mode) {
 	case DRM_MODE_TV_MODE_NTSC:
-		*val = VC4_VEC_TV_MODE_NTSC;
-		break;
+		return VC4_VEC_TV_MODE_NTSC;
 
 	case DRM_MODE_TV_MODE_NTSC_443:
-		*val = VC4_VEC_TV_MODE_NTSC_443;
-		break;
+		return VC4_VEC_TV_MODE_NTSC_443;
 
 	case DRM_MODE_TV_MODE_NTSC_J:
-		*val = VC4_VEC_TV_MODE_NTSC_J;
-		break;
+		return VC4_VEC_TV_MODE_NTSC_J;
 
 	case DRM_MODE_TV_MODE_PAL:
-		*val = VC4_VEC_TV_MODE_PAL;
-		break;
+		return VC4_VEC_TV_MODE_PAL;
 
 	case DRM_MODE_TV_MODE_PAL_M:
-		*val = VC4_VEC_TV_MODE_PAL_M;
-		break;
+		return VC4_VEC_TV_MODE_PAL_M;
 
 	case DRM_MODE_TV_MODE_PAL_N:
-		*val = VC4_VEC_TV_MODE_PAL_N;
-		break;
+		return VC4_VEC_TV_MODE_PAL_N;
 
 	case DRM_MODE_TV_MODE_SECAM:
-		*val = VC4_VEC_TV_MODE_SECAM;
-		break;
+		return VC4_VEC_TV_MODE_SECAM;
 
 	case DRM_MODE_TV_MODE_MONOCHROME:
-		*val = VC4_VEC_TV_MODE_MONOCHROME;
-		break;
+		return VC4_VEC_TV_MODE_MONOCHROME;
 
 	default:
 		return -EINVAL;
 	}
+}
+
+static int
+vc4_vec_connector_get_property(struct drm_connector *connector,
+			       const struct drm_connector_state *state,
+			       struct drm_property *property,
+			       uint64_t *val)
+{
+	struct vc4_vec *vec = connector_to_vc4_vec(connector);
+	enum vc4_vec_tv_mode_id legacy_mode;
+
+	if (property != vec->legacy_tv_mode_property)
+		return -EINVAL;
+
+	legacy_mode = vc4_vec_generic_tv_mode_to_legacy(state->tv.mode);
+	if (legacy_mode < 0)
+		return legacy_mode;
+
+	*val = legacy_mode;
 
 	return 0;
 }
@@ -504,6 +536,8 @@ static const struct drm_connector_helper
 static int vc4_vec_connector_init(struct drm_device *dev, struct vc4_vec *vec)
 {
 	struct drm_connector *connector = &vec->connector;
+	enum vc4_vec_tv_mode_id legacy_default_mode;
+	enum drm_connector_tv_mode default_mode;
 	struct drm_property *prop;
 	int ret;
 
@@ -516,9 +550,17 @@ static int vc4_vec_connector_init(struct
 
 	drm_connector_helper_add(connector, &vc4_vec_connector_helper_funcs);
 
+	default_mode = vc4_vec_get_default_mode(connector);
+	if (default_mode < 0)
+		return default_mode;
+
 	drm_object_attach_property(&connector->base,
 				   dev->mode_config.tv_mode_property,
-				   DRM_MODE_TV_MODE_NTSC);
+				   default_mode);
+
+	legacy_default_mode = vc4_vec_generic_tv_mode_to_legacy(default_mode);
+	if (legacy_default_mode < 0)
+		return legacy_default_mode;
 
 	prop = drm_property_create_enum(dev, 0, "mode",
 					legacy_tv_mode_names,
@@ -527,7 +569,7 @@ static int vc4_vec_connector_init(struct
 		return -ENOMEM;
 	vec->legacy_tv_mode_property = prop;
 
-	drm_object_attach_property(&connector->base, prop, VC4_VEC_TV_MODE_NTSC);
+	drm_object_attach_property(&connector->base, prop, legacy_default_mode);
 
 	drm_connector_attach_tv_margin_properties(connector);
 
@@ -854,3 +896,10 @@ struct platform_driver vc4_vec_driver =
 		.of_match_table = vc4_vec_dt_match,
 	},
 };
+
+module_param_named(tv_norm, vc4_vec_tv_norm, charp, 0600);
+MODULE_PARM_DESC(tv_norm, "Default TV norm.\n"
+		 "\t\tSupported: NTSC, NTSC-J, NTSC-443, PAL, PAL-M, PAL-N,\n"
+		 "\t\t\tPAL60, SECAM.\n"
+		 "\t\tDefault: PAL if a 50 Hz mode has been set via video=,\n"
+		 "\t\t\tNTSC otherwise");
