2022-08-29 13:36:49

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH v2 00/41] drm: Analog TV Improvements

Hi,



Here's a series aiming at improving the command line named modes support,

and more importantly how we deal with all the analog TV variants.



The named modes support were initially introduced to allow to specify the

analog TV mode to be used.



However, this was causing multiple issues:



* The mode name parsed on the command line was passed directly to the

driver, which had to figure out which mode it was suppose to match;



* Figuring that out wasn't really easy, since the video= argument or what

the userspace might not even have a name in the first place, but

instead could have passed a mode with the same timings;



* The fallback to matching on the timings was mostly working as long as

we were supporting one 525 lines (most likely NSTC) and one 625 lines

(PAL), but couldn't differentiate between two modes with the same

timings (NTSC vs PAL-M vs NSTC-J for example);



* There was also some overlap with the tv mode property registered by

drm_mode_create_tv_properties(), but named modes weren't interacting

with that property at all.



* Even though that property was generic, its possible values were

specific to each drivers, which made some generic support difficult.



Thus, I chose to tackle in multiple steps:



* A new TV norm property was introduced, with generic values, each driver

reporting through a bitmask what standard it supports to the userspace;



* This option was added to the command line parsing code to be able to

specify it on the kernel command line, and new atomic_check and reset

helpers were created to integrate properly into atomic KMS;



* The named mode parsing code is now creating a proper display mode for

the given named mode, and the TV standard will thus be part of the

connector state;



* Two drivers were converted and tested for now (vc4 and sun4i), with

some backward compatibility code to translate the old TV mode to the

new TV mode;



Unit tests were created along the way.



One can switch from NTSC to PAL now using (on vc4)



modetest -M vc4 -s 53:720x480i -w 53:'tv norm':0



modetest -M vc4 -s 53:720x480i -w 53:'tv norm':4



Let me know what you think,

Maxime



Changes from v1 (https://lore.kernel.org/dri-devel/[email protected]/):

- Kept the older TV mode property as legacy so we can keep the old drivers functional

- Renamed the tv_norm property to tv_mode

- Added a function to create PAL and NTSC compatible display modes

- Added some helpers to instantiate a mock DRM device in Kunit

- More Kunit tests

- Removed the HD analog TV modes

- Renamed some of the tests

- Renamed some of the named modes

- Fixed typos in commit logs

- Added the various tags



Cc: Geert Uytterhoeven <[email protected]>

Cc: Mateusz Kwiatkowski <[email protected]>

Cc: "Noralf Trønnes" <[email protected]>

Cc: Dave Stevenson <[email protected]>

Cc: Dom Cobley <[email protected]>

Cc: Phil Elwell <[email protected]>

Cc: <[email protected]>



---

Geert Uytterhoeven (1):

drm/modes: parse_cmdline: Add support for named modes containing dashes



Mateusz Kwiatkowski (5):

drm/vc4: vec: Refactor VEC TV mode setting

drm/vc4: vec: Remove redundant atomic_mode_set

drm/vc4: vec: Fix timings for VEC modes

drm/vc4: vec: Fix definition of PAL-M mode

drm/vc4: vec: Add support for more analog TV standards



Maxime Ripard (35):

drm/tests: Order Kunit tests in Makefile

drm/tests: Add Kunit Helpers

drm/atomic-helper: Rename drm_atomic_helper_connector_tv_reset to avoid ambiguity

drm/connector: Rename subconnector state variable

drm/atomic: Add TV subconnector property to get/set_property

drm/connector: Rename legacy TV property

drm/connector: Only register TV mode property if present

drm/connector: Rename drm_mode_create_tv_properties

drm/connector: Add TV standard property

drm/modes: Add a function to generate analog display modes

drm/modes: Only consider bpp and refresh before options

drm/client: Add some tests for drm_connector_pick_cmdline_mode()

drm/modes: Move named modes parsing to a separate function

drm/modes: Switch to named mode descriptors

drm/modes: Fill drm_cmdline mode from named modes

drm/connector: Add pixel clock to cmdline mode

drm/connector: Add a function to lookup a TV mode by its name

drm/modes: Introduce the tv_mode property as a command-line option

drm/modes: Properly generate a drm_display_mode from a named mode

drm/modes: Introduce more named modes

drm/atomic-helper: Add a TV properties reset helper

drm/atomic-helper: Add an analog TV atomic_check implementation

drm/vc4: vec: Remove empty mode_fixup

drm/vc4: vec: Convert to atomic helpers

drm/vc4: vec: Switch for common modes

drm/vc4: vec: Use TV Reset implementation

drm/vc4: vec: Convert to the new TV mode property

drm/sun4i: tv: Remove unused mode_valid

drm/sun4i: tv: Convert to atomic hooks

drm/sun4i: tv: Merge mode_set into atomic_enable

drm/sun4i: tv: Remove useless function

drm/sun4i: tv: Remove useless destroy function

drm/sun4i: tv: Rename error label

drm/sun4i: tv: Add missing reset assertion

drm/sun4i: tv: Convert to the new TV mode property



drivers/gpu/drm/drm_atomic_state_helper.c | 115 ++++-

drivers/gpu/drm/drm_atomic_uapi.c | 8 +

drivers/gpu/drm/drm_client_modeset.c | 4 +

drivers/gpu/drm/drm_connector.c | 119 ++++-

drivers/gpu/drm/drm_modes.c | 638 +++++++++++++++++++++++-

drivers/gpu/drm/gud/gud_connector.c | 8 +-

drivers/gpu/drm/i2c/ch7006_drv.c | 6 +-

drivers/gpu/drm/i915/display/intel_tv.c | 2 +-

drivers/gpu/drm/nouveau/dispnv04/tvnv17.c | 6 +-

drivers/gpu/drm/sun4i/sun4i_tv.c | 198 +++-----

drivers/gpu/drm/tests/Makefile | 16 +-

drivers/gpu/drm/tests/drm_client_modeset_test.c | 239 +++++++++

drivers/gpu/drm/tests/drm_cmdline_parser_test.c | 216 ++++++++

drivers/gpu/drm/tests/drm_kunit_helpers.c | 54 ++

drivers/gpu/drm/tests/drm_kunit_helpers.h | 9 +

drivers/gpu/drm/tests/drm_modes_test.c | 131 +++++

drivers/gpu/drm/vc4/vc4_hdmi.c | 2 +-

drivers/gpu/drm/vc4/vc4_vec.c | 422 ++++++++++------

include/drm/drm_atomic_state_helper.h | 4 +

include/drm/drm_connector.h | 165 +++++-

include/drm/drm_mode_config.h | 12 +-

include/drm/drm_modes.h | 17 +

22 files changed, 2057 insertions(+), 334 deletions(-)

---

base-commit: 8869fa666a9e6782c3c896c1fa57d65adca23249

change-id: 20220728-rpi-analog-tv-properties-0914dfcee460



Best regards,

--

Maxime Ripard <[email protected]>


2022-08-29 13:38:56

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH v2 33/41] drm/vc4: vec: Add support for more analog TV standards

From: Mateusz Kwiatkowski <[email protected]>



Add support for the following composite output modes (all of them are

somewhat more obscure than the previously defined ones):



- NTSC_443 - NTSC-style signal with the chroma subcarrier shifted to

4.43361875 MHz (the PAL subcarrier frequency). Never used for

broadcasting, but sometimes used as a hack to play NTSC content in PAL

regions (e.g. on VCRs).

- PAL_N - PAL with alternative chroma subcarrier frequency,

3.58205625 MHz. Used as a broadcast standard in Argentina, Paraguay

and Uruguay to fit 576i50 with colour in 6 MHz channel raster.

- PAL60 - 480i60 signal with PAL-style color at normal European PAL

frequency. Another non-standard, non-broadcast mode, used in similar

contexts as NTSC_443. Some displays support one but not the other.

- SECAM - French frequency-modulated analog color standard; also have

been broadcast in Eastern Europe and various parts of Africa and Asia.

Uses the same 576i50 timings as PAL.



Also added some comments explaining color subcarrier frequency

registers.



Signed-off-by: Mateusz Kwiatkowski <[email protected]>

Signed-off-by: Maxime Ripard <[email protected]>



diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c

index 58286acf4b9e..55f6f490877c 100644

--- a/drivers/gpu/drm/vc4/vc4_vec.c

+++ b/drivers/gpu/drm/vc4/vc4_vec.c

@@ -46,6 +46,7 @@

#define VEC_CONFIG0_YDEL(x) ((x) << 26)

#define VEC_CONFIG0_CDEL_MASK GENMASK(25, 24)

#define VEC_CONFIG0_CDEL(x) ((x) << 24)

+#define VEC_CONFIG0_SECAM_STD BIT(21)

#define VEC_CONFIG0_PBPR_FIL BIT(18)

#define VEC_CONFIG0_CHROMA_GAIN_MASK GENMASK(17, 16)

#define VEC_CONFIG0_CHROMA_GAIN_UNITY (0 << 16)

@@ -76,6 +77,27 @@

#define VEC_SOFT_RESET 0x10c

#define VEC_CLMP0_START 0x144

#define VEC_CLMP0_END 0x148

+

+/*

+ * These set the color subcarrier frequency

+ * if VEC_CONFIG1_CUSTOM_FREQ is enabled.

+ *

+ * VEC_FREQ1_0 contains the most significant 16-bit half-word,

+ * VEC_FREQ3_2 contains the least significant 16-bit half-word.

+ * 0x80000000 seems to be equivalent to the pixel clock

+ * (which itself is the VEC clock divided by 8).

+ *

+ * Reference values (with the default pixel clock of 13.5 MHz):

+ *

+ * NTSC (3579545.[45] Hz) - 0x21F07C1F

+ * PAL (4433618.75 Hz) - 0x2A098ACB

+ * PAL-M (3575611.[888111] Hz) - 0x21E6EFE3

+ * PAL-N (3582056.25 Hz) - 0x21F69446

+ *

+ * NOTE: For SECAM, it is used as the Dr center frequency,

+ * regardless of whether VEC_CONFIG1_CUSTOM_FREQ is enabled or not;

+ * that is specified as 4406250 Hz, which corresponds to 0x29C71C72.

+ */

#define VEC_FREQ3_2 0x180

#define VEC_FREQ1_0 0x184



@@ -118,6 +140,14 @@



#define VEC_INTERRUPT_CONTROL 0x190

#define VEC_INTERRUPT_STATUS 0x194

+

+/*

+ * Db center frequency for SECAM; the clock for this is the same as for

+ * VEC_FREQ3_2/VEC_FREQ1_0, which is used for Dr center frequency.

+ *

+ * This is specified as 4250000 Hz, which corresponds to 0x284BDA13.

+ * That is also the default value, so no need to set it explicitly.

+ */

#define VEC_FCW_SECAM_B 0x198

#define VEC_SECAM_GAIN_VAL 0x19c



@@ -194,9 +224,13 @@ connector_to_vc4_vec(struct drm_connector *connector)



enum vc4_vec_tv_mode_id {

VC4_VEC_TV_MODE_NTSC,

+ VC4_VEC_TV_MODE_NTSC_443,

VC4_VEC_TV_MODE_NTSC_J,

VC4_VEC_TV_MODE_PAL,

+ VC4_VEC_TV_MODE_PAL_60,

VC4_VEC_TV_MODE_PAL_M,

+ VC4_VEC_TV_MODE_PAL_N,

+ VC4_VEC_TV_MODE_SECAM,

};



struct vc4_vec_tv_mode {

@@ -234,6 +268,12 @@ static const struct debugfs_reg32 vec_regs[] = {

};



static const struct vc4_vec_tv_mode vc4_vec_tv_modes[] = {

+ {

+ .mode = DRM_MODE_TV_MODE_NTSC_443,

+ .config0 = VEC_CONFIG0_NTSC_STD,

+ .config1 = VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ,

+ .custom_freq = 0x2a098acb,

+ },

{

.mode = DRM_MODE_TV_MODE_NTSC_M,

.config0 = VEC_CONFIG0_NTSC_STD | VEC_CONFIG0_PDEN,

@@ -244,6 +284,12 @@ static const struct vc4_vec_tv_mode vc4_vec_tv_modes[] = {

.config0 = VEC_CONFIG0_NTSC_STD,

.config1 = VEC_CONFIG1_C_CVBS_CVBS,

},

+ {

+ .mode = DRM_MODE_TV_MODE_PAL_60,

+ .config0 = VEC_CONFIG0_PAL_M_STD,

+ .config1 = VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ,

+ .custom_freq = 0x2a098acb,

+ },

{

.mode = DRM_MODE_TV_MODE_PAL_B,

.config0 = VEC_CONFIG0_PAL_BDGHI_STD,

@@ -254,6 +300,17 @@ static const struct vc4_vec_tv_mode vc4_vec_tv_modes[] = {

.config0 = VEC_CONFIG0_PAL_M_STD,

.config1 = VEC_CONFIG1_C_CVBS_CVBS,

},

+ {

+ .mode = DRM_MODE_TV_MODE_PAL_N,

+ .config0 = VEC_CONFIG0_PAL_N_STD,

+ .config1 = VEC_CONFIG1_C_CVBS_CVBS,

+ },

+ {

+ .mode = DRM_MODE_TV_MODE_SECAM_B,

+ .config0 = VEC_CONFIG0_SECAM_STD,

+ .config1 = VEC_CONFIG1_C_CVBS_CVBS,

+ .custom_freq = 0x29c71c72,

+ },

};



static inline const struct vc4_vec_tv_mode *

@@ -273,9 +330,13 @@ vc4_vec_tv_mode_lookup(unsigned int mode)



static const struct drm_prop_enum_list tv_mode_names[] = {

{ VC4_VEC_TV_MODE_NTSC, "NTSC", },

+ { VC4_VEC_TV_MODE_NTSC_443, "NTSC-443", },

{ VC4_VEC_TV_MODE_NTSC_J, "NTSC-J", },

{ VC4_VEC_TV_MODE_PAL, "PAL", },

+ { VC4_VEC_TV_MODE_PAL_60, "PAL-60", },

{ VC4_VEC_TV_MODE_PAL_M, "PAL-M", },

+ { VC4_VEC_TV_MODE_PAL_N, "PAL-N", },

+ { VC4_VEC_TV_MODE_SECAM, "SECAM", },

};



static enum drm_connector_status

@@ -332,6 +393,10 @@ vc4_vec_connector_set_property(struct drm_connector *connector,

state->tv.mode = DRM_MODE_TV_MODE_NTSC_M;

break;



+ case VC4_VEC_TV_MODE_NTSC_443:

+ state->tv.mode = DRM_MODE_TV_MODE_NTSC_443;

+ break;

+

case VC4_VEC_TV_MODE_NTSC_J:

state->tv.mode = DRM_MODE_TV_MODE_NTSC_J;

break;

@@ -340,10 +405,22 @@ vc4_vec_connector_set_property(struct drm_connector *connector,

state->tv.mode = DRM_MODE_TV_MODE_PAL_B;

break;



+ case VC4_VEC_TV_MODE_PAL_60:

+ state->tv.mode = DRM_MODE_TV_MODE_PAL_60;

+ break;

+

case VC4_VEC_TV_MODE_PAL_M:

state->tv.mode = DRM_MODE_TV_MODE_PAL_M;

break;



+ case VC4_VEC_TV_MODE_PAL_N:

+ state->tv.mode = DRM_MODE_TV_MODE_PAL_N;

+ break;

+

+ case VC4_VEC_TV_MODE_SECAM:

+ state->tv.mode = DRM_MODE_TV_MODE_SECAM_B;

+ break;

+

default:

return -EINVAL;

}

@@ -363,6 +440,10 @@ vc4_vec_connector_get_property(struct drm_connector *connector,

return -EINVAL;



switch (state->tv.mode) {

+ case DRM_MODE_TV_MODE_NTSC_443:

+ *val = VC4_VEC_TV_MODE_NTSC_443;

+ break;

+

case DRM_MODE_TV_MODE_NTSC_J:

*val = VC4_VEC_TV_MODE_NTSC_J;

break;

@@ -371,6 +452,10 @@ vc4_vec_connector_get_property(struct drm_connector *connector,

*val = VC4_VEC_TV_MODE_NTSC;

break;



+ case DRM_MODE_TV_MODE_PAL_60:

+ *val = VC4_VEC_TV_MODE_PAL_60;

+ break;

+

case DRM_MODE_TV_MODE_PAL_B:

*val = VC4_VEC_TV_MODE_PAL;

break;

@@ -379,6 +464,14 @@ vc4_vec_connector_get_property(struct drm_connector *connector,

*val = VC4_VEC_TV_MODE_PAL_M;

break;



+ case DRM_MODE_TV_MODE_PAL_N:

+ *val = VC4_VEC_TV_MODE_PAL_N;

+ break;

+

+ case DRM_MODE_TV_MODE_SECAM_B:

+ *val = VC4_VEC_TV_MODE_SECAM;

+ break;

+

default:

return -EINVAL;

}

@@ -608,10 +701,14 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data)

int ret;



ret = drm_mode_create_tv_properties(drm,

+ BIT(DRM_MODE_TV_MODE_NTSC_443) |

BIT(DRM_MODE_TV_MODE_NTSC_J) |

BIT(DRM_MODE_TV_MODE_NTSC_M) |

+ BIT(DRM_MODE_TV_MODE_PAL_60) |

BIT(DRM_MODE_TV_MODE_PAL_B) |

- BIT(DRM_MODE_TV_MODE_PAL_M));

+ BIT(DRM_MODE_TV_MODE_PAL_M) |

+ BIT(DRM_MODE_TV_MODE_PAL_N) |

+ BIT(DRM_MODE_TV_MODE_SECAM_B));

if (ret)

return ret;





--

b4 0.10.0-dev-65ba7

2022-08-29 13:39:08

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH v2 34/41] drm/sun4i: tv: Remove unused mode_valid

The mode_valid implementation is pretty much a nop, let's remove it.



Signed-off-by: Maxime Ripard <[email protected]>

Reviewed-by: Jernej Skrabec <[email protected]>



diff --git a/drivers/gpu/drm/sun4i/sun4i_tv.c b/drivers/gpu/drm/sun4i/sun4i_tv.c

index 94883abe0dfd..53152d77c392 100644

--- a/drivers/gpu/drm/sun4i/sun4i_tv.c

+++ b/drivers/gpu/drm/sun4i/sun4i_tv.c

@@ -497,16 +497,8 @@ static int sun4i_tv_comp_get_modes(struct drm_connector *connector)

return i;

}



-static int sun4i_tv_comp_mode_valid(struct drm_connector *connector,

- struct drm_display_mode *mode)

-{

- /* TODO */

- return MODE_OK;

-}

-

static const struct drm_connector_helper_funcs sun4i_tv_comp_connector_helper_funcs = {

.get_modes = sun4i_tv_comp_get_modes,

- .mode_valid = sun4i_tv_comp_mode_valid,

};



static void



--

b4 0.10.0-dev-65ba7

2022-08-29 13:48:15

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH v2 28/41] drm/vc4: vec: Fix timings for VEC modes

From: Mateusz Kwiatkowski <[email protected]>



This commit fixes vertical timings of the VEC (composite output) modes

to accurately represent the 525-line ("NTSC") and 625-line ("PAL") ITU-R

standards.



Previous timings were actually defined as 502 and 601 lines, resulting

in non-standard 62.69 Hz and 52 Hz signals being generated,

respectively.



Signed-off-by: Mateusz Kwiatkowski <[email protected]>

Signed-off-by: Maxime Ripard <[email protected]>



diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c

index 4d7bc7c20704..d1d40b69279e 100644

--- a/drivers/gpu/drm/vc4/vc4_vec.c

+++ b/drivers/gpu/drm/vc4/vc4_vec.c

@@ -227,14 +227,14 @@ static const struct debugfs_reg32 vec_regs[] = {

static const struct drm_display_mode ntsc_mode = {

DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 13500,

720, 720 + 14, 720 + 14 + 64, 720 + 14 + 64 + 60, 0,

- 480, 480 + 3, 480 + 3 + 3, 480 + 3 + 3 + 16, 0,

+ 480, 480 + 7, 480 + 7 + 6, 525, 0,

DRM_MODE_FLAG_INTERLACE)

};



static const struct drm_display_mode pal_mode = {

DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 13500,

720, 720 + 20, 720 + 20 + 64, 720 + 20 + 64 + 60, 0,

- 576, 576 + 2, 576 + 2 + 3, 576 + 2 + 3 + 20, 0,

+ 576, 576 + 4, 576 + 4 + 6, 625, 0,

DRM_MODE_FLAG_INTERLACE)

};





--

b4 0.10.0-dev-65ba7

2022-08-29 13:48:29

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH v2 07/41] drm/connector: Only register TV mode property if present

The drm_create_tv_properties() will create the TV mode property

unconditionally.



However, since we'll gradually phase it out, let's register it only if we

have a list passed as an argument. This will make the transition easier.



Signed-off-by: Maxime Ripard <[email protected]>

Acked-by: Noralf Trønnes <[email protected]>



diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c

index ede6025638d7..17a5913cefe3 100644

--- a/drivers/gpu/drm/drm_connector.c

+++ b/drivers/gpu/drm/drm_connector.c

@@ -1686,15 +1686,18 @@ int drm_mode_create_tv_properties(struct drm_device *dev,

if (drm_mode_create_tv_margin_properties(dev))

goto nomem;



- dev->mode_config.legacy_tv_mode_property =

- drm_property_create(dev, DRM_MODE_PROP_ENUM,

- "mode", num_modes);

- if (!dev->mode_config.legacy_tv_mode_property)

- goto nomem;



- for (i = 0; i < num_modes; i++)

- drm_property_add_enum(dev->mode_config.legacy_tv_mode_property,

- i, modes[i]);

+ if (num_modes) {

+ dev->mode_config.legacy_tv_mode_property =

+ drm_property_create(dev, DRM_MODE_PROP_ENUM,

+ "mode", num_modes);

+ if (!dev->mode_config.legacy_tv_mode_property)

+ goto nomem;

+

+ for (i = 0; i < num_modes; i++)

+ drm_property_add_enum(dev->mode_config.legacy_tv_mode_property,

+ i, modes[i]);

+ }



dev->mode_config.tv_brightness_property =

drm_property_create_range(dev, 0, "brightness", 0, 100);



--

b4 0.10.0-dev-65ba7

2022-08-29 13:49:47

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH v2 19/41] drm/modes: Introduce the tv_mode property as a command-line option

Our new tv mode option allows to specify the TV mode from a property.

However, it can still be useful, for example to avoid any boot time

artifact, to set that property directly from the kernel command line.



Let's add some code to allow it, and some unit tests to exercise that code.



Signed-off-by: Maxime Ripard <[email protected]>



diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c

index 73d01e755496..a759a4ba0036 100644

--- a/drivers/gpu/drm/drm_modes.c

+++ b/drivers/gpu/drm/drm_modes.c

@@ -2115,6 +2115,30 @@ static int drm_mode_parse_panel_orientation(const char *delim,

return 0;

}



+static int drm_mode_parse_tv_mode(const char *delim,

+ struct drm_cmdline_mode *mode)

+{

+ const char *value;

+ unsigned int len;

+ int ret;

+

+ if (*delim != '=')

+ return -EINVAL;

+

+ value = delim + 1;

+ delim = strchr(value, ',');

+ if (!delim)

+ delim = value + strlen(value);

+

+ ret = drm_get_tv_mode_from_name(value, delim - value);

+ if (ret < 0)

+ return ret;

+

+ mode->tv_mode = ret;

+

+ return 0;

+}

+

static int drm_mode_parse_cmdline_options(const char *str,

bool freestanding,

const struct drm_connector *connector,

@@ -2184,6 +2208,9 @@ static int drm_mode_parse_cmdline_options(const char *str,

} else if (!strncmp(option, "panel_orientation", delim - option)) {

if (drm_mode_parse_panel_orientation(delim, mode))

return -EINVAL;

+ } else if (!strncmp(option, "tv_mode", delim - option)) {

+ if (drm_mode_parse_tv_mode(delim, mode))

+ return -EINVAL;

} else {

return -EINVAL;

}

@@ -2212,20 +2239,22 @@ struct drm_named_mode {

unsigned int xres;

unsigned int yres;

unsigned int flags;

+ unsigned int tv_mode;

};



-#define NAMED_MODE(_name, _pclk, _x, _y, _flags) \

+#define NAMED_MODE(_name, _pclk, _x, _y, _flags, _mode) \

{ \

.name = _name, \

.pixel_clock_khz = _pclk, \

.xres = _x, \

.yres = _y, \

.flags = _flags, \

+ .tv_mode = _mode, \

}



static const struct drm_named_mode drm_named_modes[] = {

- NAMED_MODE("NTSC", 13500, 720, 480, DRM_MODE_FLAG_INTERLACE),

- NAMED_MODE("PAL", 13500, 720, 576, DRM_MODE_FLAG_INTERLACE),

+ NAMED_MODE("NTSC", 13500, 720, 480, DRM_MODE_FLAG_INTERLACE, DRM_MODE_TV_MODE_NTSC_M),

+ NAMED_MODE("PAL", 13500, 720, 576, DRM_MODE_FLAG_INTERLACE, DRM_MODE_TV_MODE_PAL_B),

};



static int drm_mode_parse_cmdline_named_mode(const char *name,

@@ -2271,6 +2300,7 @@ static int drm_mode_parse_cmdline_named_mode(const char *name,

cmdline_mode->xres = mode->xres;

cmdline_mode->yres = mode->yres;

cmdline_mode->interlace = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);

+ cmdline_mode->tv_mode = mode->tv_mode;

cmdline_mode->specified = true;



return 1;

diff --git a/drivers/gpu/drm/tests/drm_cmdline_parser_test.c b/drivers/gpu/drm/tests/drm_cmdline_parser_test.c

index 59b29cdfdd35..f1e73ed65be0 100644

--- a/drivers/gpu/drm/tests/drm_cmdline_parser_test.c

+++ b/drivers/gpu/drm/tests/drm_cmdline_parser_test.c

@@ -885,6 +885,201 @@ static void drm_cmdline_test_multiple_options(struct kunit *test)

KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_UNSPECIFIED);

}



+static void drm_cmdline_test_tv_options(struct kunit *test,

+ const char *cmdline,

+ const struct drm_display_mode *expected_mode,

+ unsigned int expected_tv_mode)

+{

+ struct drm_cmdline_mode mode = { };

+

+ KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,

+ &no_connector, &mode));

+ KUNIT_EXPECT_TRUE(test, mode.specified);

+ KUNIT_EXPECT_EQ(test, mode.xres, expected_mode->hdisplay);

+ KUNIT_EXPECT_EQ(test, mode.yres, expected_mode->vdisplay);

+ KUNIT_EXPECT_EQ(test, mode.tv_mode, expected_tv_mode);

+

+ KUNIT_EXPECT_FALSE(test, mode.refresh_specified);

+

+ KUNIT_EXPECT_FALSE(test, mode.bpp_specified);

+

+ KUNIT_EXPECT_FALSE(test, mode.rb);

+ KUNIT_EXPECT_FALSE(test, mode.cvt);

+ KUNIT_EXPECT_EQ(test, mode.interlace, !!(expected_mode->flags & DRM_MODE_FLAG_INTERLACE));

+ KUNIT_EXPECT_FALSE(test, mode.margins);

+ KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_UNSPECIFIED);

+}

+

+static void drm_cmdline_test_tv_option_ntsc_443(struct kunit *test)

+{

+ drm_cmdline_test_tv_options(test,

+ "720x480i,tv_mode=NTSC-443",

+ drm_mode_analog_ntsc_480i(NULL),

+ DRM_MODE_TV_MODE_NTSC_443);

+}

+

+static void drm_cmdline_test_tv_option_ntsc_j(struct kunit *test)

+{

+ drm_cmdline_test_tv_options(test,

+ "720x480i,tv_mode=NTSC-J",

+ drm_mode_analog_ntsc_480i(NULL),

+ DRM_MODE_TV_MODE_NTSC_J);

+}

+

+static void drm_cmdline_test_tv_option_ntsc_m(struct kunit *test)

+{

+ drm_cmdline_test_tv_options(test,

+ "720x480i,tv_mode=NTSC-M",

+ drm_mode_analog_ntsc_480i(NULL),

+ DRM_MODE_TV_MODE_NTSC_M);

+}

+

+static void drm_cmdline_test_tv_option_pal_60(struct kunit *test)

+{

+ drm_cmdline_test_tv_options(test,

+ "720x576i,tv_mode=PAL-60",

+ drm_mode_analog_pal_576i(NULL),

+ DRM_MODE_TV_MODE_PAL_60);

+}

+

+static void drm_cmdline_test_tv_option_pal_b(struct kunit *test)

+{

+ drm_cmdline_test_tv_options(test,

+ "720x576i,tv_mode=PAL-B",

+ drm_mode_analog_pal_576i(NULL),

+ DRM_MODE_TV_MODE_PAL_B);

+}

+

+static void drm_cmdline_test_tv_option_pal_d(struct kunit *test)

+{

+ drm_cmdline_test_tv_options(test,

+ "720x576i,tv_mode=PAL-D",

+ drm_mode_analog_pal_576i(NULL),

+ DRM_MODE_TV_MODE_PAL_D);

+}

+

+static void drm_cmdline_test_tv_option_pal_g(struct kunit *test)

+{

+ drm_cmdline_test_tv_options(test,

+ "720x576i,tv_mode=PAL-G",

+ drm_mode_analog_pal_576i(NULL),

+ DRM_MODE_TV_MODE_PAL_G);

+}

+

+static void drm_cmdline_test_tv_option_pal_h(struct kunit *test)

+{

+ drm_cmdline_test_tv_options(test,

+ "720x576i,tv_mode=PAL-H",

+ drm_mode_analog_pal_576i(NULL),

+ DRM_MODE_TV_MODE_PAL_H);

+}

+

+static void drm_cmdline_test_tv_option_pal_i(struct kunit *test)

+{

+ drm_cmdline_test_tv_options(test,

+ "720x576i,tv_mode=PAL-I",

+ drm_mode_analog_pal_576i(NULL),

+ DRM_MODE_TV_MODE_PAL_I);

+}

+

+static void drm_cmdline_test_tv_option_pal_m(struct kunit *test)

+{

+ drm_cmdline_test_tv_options(test,

+ "720x480i,tv_mode=PAL-M",

+ drm_mode_analog_ntsc_480i(NULL),

+ DRM_MODE_TV_MODE_PAL_M);

+}

+

+static void drm_cmdline_test_tv_option_pal_n(struct kunit *test)

+{

+ drm_cmdline_test_tv_options(test,

+ "720x576i,tv_mode=PAL-N",

+ drm_mode_analog_pal_576i(NULL),

+ DRM_MODE_TV_MODE_PAL_N);

+}

+

+static void drm_cmdline_test_tv_option_pal_nc(struct kunit *test)

+{

+ drm_cmdline_test_tv_options(test,

+ "720x576i,tv_mode=PAL-Nc",

+ drm_mode_analog_pal_576i(NULL),

+ DRM_MODE_TV_MODE_PAL_NC);

+}

+

+static void drm_cmdline_test_tv_option_secam_60(struct kunit *test)

+{

+ drm_cmdline_test_tv_options(test,

+ "720x576i,tv_mode=SECAM-60",

+ drm_mode_analog_pal_576i(NULL),

+ DRM_MODE_TV_MODE_SECAM_60);

+}

+

+static void drm_cmdline_test_tv_option_secam_b(struct kunit *test)

+{

+ drm_cmdline_test_tv_options(test,

+ "720x576i,tv_mode=SECAM-B",

+ drm_mode_analog_pal_576i(NULL),

+ DRM_MODE_TV_MODE_SECAM_B);

+}

+

+static void drm_cmdline_test_tv_option_secam_d(struct kunit *test)

+{

+ drm_cmdline_test_tv_options(test,

+ "720x576i,tv_mode=SECAM-D",

+ drm_mode_analog_pal_576i(NULL),

+ DRM_MODE_TV_MODE_SECAM_D);

+}

+

+static void drm_cmdline_test_tv_option_secam_g(struct kunit *test)

+{

+ drm_cmdline_test_tv_options(test,

+ "720x576i,tv_mode=SECAM-G",

+ drm_mode_analog_pal_576i(NULL),

+ DRM_MODE_TV_MODE_SECAM_G);

+}

+

+static void drm_cmdline_test_tv_option_secam_k(struct kunit *test)

+{

+ drm_cmdline_test_tv_options(test,

+ "720x576i,tv_mode=SECAM-K",

+ drm_mode_analog_pal_576i(NULL),

+ DRM_MODE_TV_MODE_SECAM_K);

+}

+

+static void drm_cmdline_test_tv_option_secam_k1(struct kunit *test)

+{

+ drm_cmdline_test_tv_options(test,

+ "720x576i,tv_mode=SECAM-K1",

+ drm_mode_analog_pal_576i(NULL),

+ DRM_MODE_TV_MODE_SECAM_K1);

+}

+

+static void drm_cmdline_test_tv_option_secam_l(struct kunit *test)

+{

+ drm_cmdline_test_tv_options(test,

+ "720x576i,tv_mode=SECAM-L",

+ drm_mode_analog_pal_576i(NULL),

+ DRM_MODE_TV_MODE_SECAM_L);

+}

+

+static void drm_cmdline_test_tv_option_invalid(struct kunit *test)

+{

+ struct drm_cmdline_mode mode = { };

+ const char *cmdline = "720x480i,tv_mode=invalid";

+

+ KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline,

+ &no_connector, &mode));

+}

+

+static void drm_cmdline_test_tv_option_truncated(struct kunit *test)

+{

+ struct drm_cmdline_mode mode = { };

+ const char *cmdline = "720x480i,tv_mode=NTSC";

+

+ KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline,

+ &no_connector, &mode));

+}

+

static void drm_cmdline_test_invalid_option(struct kunit *test)

{

struct drm_cmdline_mode mode = { };

@@ -1047,6 +1242,27 @@ static struct kunit_case drm_cmdline_parser_tests[] = {

KUNIT_CASE(drm_cmdline_test_name_refresh_invalid_mode),

KUNIT_CASE(drm_cmdline_test_name_option),

KUNIT_CASE(drm_cmdline_test_name_bpp_option),

+ KUNIT_CASE(drm_cmdline_test_tv_option_ntsc_443),

+ KUNIT_CASE(drm_cmdline_test_tv_option_ntsc_j),

+ KUNIT_CASE(drm_cmdline_test_tv_option_ntsc_m),

+ KUNIT_CASE(drm_cmdline_test_tv_option_pal_60),

+ KUNIT_CASE(drm_cmdline_test_tv_option_pal_b),

+ KUNIT_CASE(drm_cmdline_test_tv_option_pal_d),

+ KUNIT_CASE(drm_cmdline_test_tv_option_pal_g),

+ KUNIT_CASE(drm_cmdline_test_tv_option_pal_h),

+ KUNIT_CASE(drm_cmdline_test_tv_option_pal_i),

+ KUNIT_CASE(drm_cmdline_test_tv_option_pal_m),

+ KUNIT_CASE(drm_cmdline_test_tv_option_pal_n),

+ KUNIT_CASE(drm_cmdline_test_tv_option_pal_nc),

+ KUNIT_CASE(drm_cmdline_test_tv_option_secam_60),

+ KUNIT_CASE(drm_cmdline_test_tv_option_secam_b),

+ KUNIT_CASE(drm_cmdline_test_tv_option_secam_d),

+ KUNIT_CASE(drm_cmdline_test_tv_option_secam_g),

+ KUNIT_CASE(drm_cmdline_test_tv_option_secam_k),

+ KUNIT_CASE(drm_cmdline_test_tv_option_secam_k1),

+ KUNIT_CASE(drm_cmdline_test_tv_option_secam_l),

+ KUNIT_CASE(drm_cmdline_test_tv_option_invalid),

+ KUNIT_CASE(drm_cmdline_test_tv_option_truncated),

KUNIT_CASE(drm_cmdline_test_rotate_0),

KUNIT_CASE(drm_cmdline_test_rotate_90),

KUNIT_CASE(drm_cmdline_test_rotate_180),

diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h

index 49d261977d4e..9589108ba202 100644

--- a/include/drm/drm_connector.h

+++ b/include/drm/drm_connector.h

@@ -1447,6 +1447,11 @@ struct drm_cmdline_mode {

* @tv_margins: TV margins to apply to the mode.

*/

struct drm_connector_tv_margins tv_margins;

+

+ /**

+ * @tv_mode: TV mode standard. See DRM_MODE_TV_MODE_*.

+ */

+ enum drm_connector_tv_mode tv_mode;

};



/**



--

b4 0.10.0-dev-65ba7

2022-08-29 13:50:35

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH v2 29/41] drm/vc4: vec: Switch for common modes

Now that the core has a definition for the 525 and 625 lines analog TV

modes, let's switch to it for vc4.



Signed-off-by: Maxime Ripard <[email protected]>



diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c

index d1d40b69279e..63e4e617e321 100644

--- a/drivers/gpu/drm/vc4/vc4_vec.c

+++ b/drivers/gpu/drm/vc4/vc4_vec.c

@@ -224,38 +224,24 @@ static const struct debugfs_reg32 vec_regs[] = {

VC4_REG32(VEC_DAC_MISC),

};



-static const struct drm_display_mode ntsc_mode = {

- DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 13500,

- 720, 720 + 14, 720 + 14 + 64, 720 + 14 + 64 + 60, 0,

- 480, 480 + 7, 480 + 7 + 6, 525, 0,

- DRM_MODE_FLAG_INTERLACE)

-};

-

-static const struct drm_display_mode pal_mode = {

- DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 13500,

- 720, 720 + 20, 720 + 20 + 64, 720 + 20 + 64 + 60, 0,

- 576, 576 + 4, 576 + 4 + 6, 625, 0,

- DRM_MODE_FLAG_INTERLACE)

-};

-

static const struct vc4_vec_tv_mode vc4_vec_tv_modes[] = {

[VC4_VEC_TV_MODE_NTSC] = {

- .mode = &ntsc_mode,

+ .mode = &drm_mode_480i,

.config0 = VEC_CONFIG0_NTSC_STD | VEC_CONFIG0_PDEN,

.config1 = VEC_CONFIG1_C_CVBS_CVBS,

},

[VC4_VEC_TV_MODE_NTSC_J] = {

- .mode = &ntsc_mode,

+ .mode = &drm_mode_480i,

.config0 = VEC_CONFIG0_NTSC_STD,

.config1 = VEC_CONFIG1_C_CVBS_CVBS,

},

[VC4_VEC_TV_MODE_PAL] = {

- .mode = &pal_mode,

+ .mode = &drm_mode_576i,

.config0 = VEC_CONFIG0_PAL_BDGHI_STD,

.config1 = VEC_CONFIG1_C_CVBS_CVBS,

},

[VC4_VEC_TV_MODE_PAL_M] = {

- .mode = &pal_mode,

+ .mode = &drm_mode_576i,

.config0 = VEC_CONFIG0_PAL_BDGHI_STD,

.config1 = VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ,

.custom_freq = 0x223b61d1,



--

b4 0.10.0-dev-65ba7

2022-08-29 13:50:38

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH v2 41/41] drm/sun4i: tv: Convert to the new TV mode property

Now that the core can deal fine with analog TV modes, let's convert the

sun4i TV driver to leverage those new features.



Signed-off-by: Maxime Ripard <[email protected]>



diff --git a/drivers/gpu/drm/sun4i/sun4i_tv.c b/drivers/gpu/drm/sun4i/sun4i_tv.c

index 74ff5ad6a8b9..10c0d727d700 100644

--- a/drivers/gpu/drm/sun4i/sun4i_tv.c

+++ b/drivers/gpu/drm/sun4i/sun4i_tv.c

@@ -140,23 +140,14 @@ struct resync_parameters {

struct tv_mode {

char *name;



+ unsigned int tv_mode;

+

u32 mode;

u32 chroma_freq;

u16 back_porch;

u16 front_porch;

- u16 line_number;

u16 vblank_level;



- u32 hdisplay;

- u16 hfront_porch;

- u16 hsync_len;

- u16 hback_porch;

-

- u32 vdisplay;

- u16 vfront_porch;

- u16 vsync_len;

- u16 vback_porch;

-

bool yc_en;

bool dac3_en;

bool dac_bit25_en;

@@ -212,7 +203,7 @@ static const struct resync_parameters pal_resync_parameters = {



static const struct tv_mode tv_modes[] = {

{

- .name = "NTSC",

+ .tv_mode = DRM_MODE_TV_MODE_NTSC_M,

.mode = SUN4I_TVE_CFG0_RES_480i,

.chroma_freq = 0x21f07c1f,

.yc_en = true,

@@ -221,17 +212,6 @@ static const struct tv_mode tv_modes[] = {



.back_porch = 118,

.front_porch = 32,

- .line_number = 525,

-

- .hdisplay = 720,

- .hfront_porch = 18,

- .hsync_len = 2,

- .hback_porch = 118,

-

- .vdisplay = 480,

- .vfront_porch = 26,

- .vsync_len = 2,

- .vback_porch = 17,



.vblank_level = 240,



@@ -241,23 +221,12 @@ static const struct tv_mode tv_modes[] = {

.resync_params = &ntsc_resync_parameters,

},

{

- .name = "PAL",

+ .tv_mode = DRM_MODE_TV_MODE_PAL_B,

.mode = SUN4I_TVE_CFG0_RES_576i,

.chroma_freq = 0x2a098acb,



.back_porch = 138,

.front_porch = 24,

- .line_number = 625,

-

- .hdisplay = 720,

- .hfront_porch = 3,

- .hsync_len = 2,

- .hback_porch = 139,

-

- .vdisplay = 576,

- .vfront_porch = 28,

- .vsync_len = 2,

- .vback_porch = 19,



.vblank_level = 252,



@@ -275,63 +244,21 @@ drm_encoder_to_sun4i_tv(struct drm_encoder *encoder)

encoder);

}



-/*

- * FIXME: If only the drm_display_mode private field was usable, this

- * could go away...

- *

- * So far, it doesn't seem to be preserved when the mode is passed by

- * to mode_set for some reason.

- */

-static const struct tv_mode *sun4i_tv_find_tv_by_mode(const struct drm_display_mode *mode)

+static const struct tv_mode *

+sun4i_tv_find_tv_by_mode(unsigned int mode)

{

int i;



- /* First try to identify the mode by name */

for (i = 0; i < ARRAY_SIZE(tv_modes); i++) {

const struct tv_mode *tv_mode = &tv_modes[i];



- DRM_DEBUG_DRIVER("Comparing mode %s vs %s",

- mode->name, tv_mode->name);

-

- if (!strcmp(mode->name, tv_mode->name))

- return tv_mode;

- }

-

- /* Then by number of lines */

- for (i = 0; i < ARRAY_SIZE(tv_modes); i++) {

- const struct tv_mode *tv_mode = &tv_modes[i];

-

- DRM_DEBUG_DRIVER("Comparing mode %s vs %s (X: %d vs %d)",

- mode->name, tv_mode->name,

- mode->vdisplay, tv_mode->vdisplay);

-

- if (mode->vdisplay == tv_mode->vdisplay)

+ if (tv_mode->tv_mode == mode)

return tv_mode;

}



return NULL;

}



-static void sun4i_tv_mode_to_drm_mode(const struct tv_mode *tv_mode,

- struct drm_display_mode *mode)

-{

- DRM_DEBUG_DRIVER("Creating mode %s\n", mode->name);

-

- mode->type = DRM_MODE_TYPE_DRIVER;

- mode->clock = 13500;

- mode->flags = DRM_MODE_FLAG_INTERLACE;

-

- mode->hdisplay = tv_mode->hdisplay;

- mode->hsync_start = mode->hdisplay + tv_mode->hfront_porch;

- mode->hsync_end = mode->hsync_start + tv_mode->hsync_len;

- mode->htotal = mode->hsync_end + tv_mode->hback_porch;

-

- mode->vdisplay = tv_mode->vdisplay;

- mode->vsync_start = mode->vdisplay + tv_mode->vfront_porch;

- mode->vsync_end = mode->vsync_start + tv_mode->vsync_len;

- mode->vtotal = mode->vsync_end + tv_mode->vback_porch;

-}

-

static void sun4i_tv_disable(struct drm_encoder *encoder,

struct drm_atomic_state *state)

{

@@ -355,7 +282,11 @@ static void sun4i_tv_enable(struct drm_encoder *encoder,

struct drm_crtc_state *crtc_state =

drm_atomic_get_new_crtc_state(state, encoder->crtc);

struct drm_display_mode *mode = &crtc_state->mode;

- const struct tv_mode *tv_mode = sun4i_tv_find_tv_by_mode(mode);

+ struct drm_connector *connector = &tv->connector;

+ struct drm_connector_state *conn_state =

+ drm_atomic_get_new_connector_state(state, connector);

+ const struct tv_mode *tv_mode =

+ sun4i_tv_find_tv_by_mode(conn_state->tv.mode);



DRM_DEBUG_DRIVER("Enabling the TV Output\n");



@@ -403,7 +334,7 @@ static void sun4i_tv_enable(struct drm_encoder *encoder,

/* Set the lines setup */

regmap_write(tv->regs, SUN4I_TVE_LINE_REG,

SUN4I_TVE_LINE_FIRST(22) |

- SUN4I_TVE_LINE_NUMBER(tv_mode->line_number));

+ SUN4I_TVE_LINE_NUMBER(mode->vtotal));



regmap_write(tv->regs, SUN4I_TVE_LEVEL_REG,

SUN4I_TVE_LEVEL_BLANK(tv_mode->video_levels->blank) |

@@ -466,35 +397,45 @@ static const struct drm_encoder_helper_funcs sun4i_tv_helper_funcs = {



static int sun4i_tv_comp_get_modes(struct drm_connector *connector)

{

- int i;

+ struct drm_display_mode *mode;

+ int count = 0;



- for (i = 0; i < ARRAY_SIZE(tv_modes); i++) {

- struct drm_display_mode *mode;

- const struct tv_mode *tv_mode = &tv_modes[i];

-

- mode = drm_mode_create(connector->dev);

- if (!mode) {

- DRM_ERROR("Failed to create a new display mode\n");

- return 0;

- }

+ mode = drm_mode_analog_ntsc_480i(connector->dev);

+ if (!mode) {

+ DRM_ERROR("Failed to create a new display mode\n");

+ return -ENOMEM;

+ }



- strcpy(mode->name, tv_mode->name);

+ drm_mode_probed_add(connector, mode);

+ count += 1;



- sun4i_tv_mode_to_drm_mode(tv_mode, mode);

- drm_mode_probed_add(connector, mode);

+ mode = drm_mode_analog_pal_576i(connector->dev);

+ if (!mode) {

+ DRM_ERROR("Failed to create a new display mode\n");

+ return -ENOMEM;

}



- return i;

+ drm_mode_probed_add(connector, mode);

+ count += 1;

+

+ return count;

}



static const struct drm_connector_helper_funcs sun4i_tv_comp_connector_helper_funcs = {

+ .atomic_check = drm_atomic_helper_connector_tv_check,

.get_modes = sun4i_tv_comp_get_modes,

};



+static void sun4i_tv_connector_reset(struct drm_connector *connector)

+{

+ drm_atomic_helper_connector_reset(connector);

+ drm_atomic_helper_connector_tv_reset(connector);

+}

+

static const struct drm_connector_funcs sun4i_tv_comp_connector_funcs = {

.fill_modes = drm_helper_probe_single_connector_modes,

.destroy = drm_connector_cleanup,

- .reset = drm_atomic_helper_connector_reset,

+ .reset = sun4i_tv_connector_reset,

.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,

.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,

};

@@ -586,8 +527,20 @@ static int sun4i_tv_bind(struct device *dev, struct device *master,



drm_connector_attach_encoder(&tv->connector, &tv->encoder);



+ ret = drm_mode_create_tv_properties(drm,

+ BIT(DRM_MODE_TV_MODE_NTSC_M) |

+ BIT(DRM_MODE_TV_MODE_PAL_B));

+ if (ret)

+ goto err_cleanup_connector;

+

+ drm_object_attach_property(&connector->base,

+ dev->mode_config.tv_mode_property,

+ DRM_MODE_TV_MODE_NTSC_M);

+

return 0;



+err_cleanup_connector:

+ drm_connector_cleanup(&tv->connector);

err_cleanup_encoder:

drm_encoder_cleanup(&tv->encoder);

err_disable_clk:



--

b4 0.10.0-dev-65ba7

2022-08-29 13:53:06

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH v2 26/41] drm/vc4: vec: Refactor VEC TV mode setting

From: Mateusz Kwiatkowski <[email protected]>



Change the mode_set function pointer logic to declarative config0,

config1 and custom_freq fields, to make TV mode setting logic more

concise and uniform.



Signed-off-by: Mateusz Kwiatkowski <[email protected]>

Signed-off-by: Maxime Ripard <[email protected]>



diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c

index 72eee0cbb615..9a37c3fcc295 100644

--- a/drivers/gpu/drm/vc4/vc4_vec.c

+++ b/drivers/gpu/drm/vc4/vc4_vec.c

@@ -194,7 +194,9 @@ enum vc4_vec_tv_mode_id {



struct vc4_vec_tv_mode {

const struct drm_display_mode *mode;

- void (*mode_set)(struct vc4_vec *vec);

+ u32 config0;

+ u32 config1;

+ u32 custom_freq;

};



static const struct debugfs_reg32 vec_regs[] = {

@@ -224,34 +226,6 @@ static const struct debugfs_reg32 vec_regs[] = {

VC4_REG32(VEC_DAC_MISC),

};



-static void vc4_vec_ntsc_mode_set(struct vc4_vec *vec)

-{

- struct drm_device *drm = vec->connector.dev;

- int idx;

-

- if (!drm_dev_enter(drm, &idx))

- return;

-

- VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_NTSC_STD | VEC_CONFIG0_PDEN);

- VEC_WRITE(VEC_CONFIG1, VEC_CONFIG1_C_CVBS_CVBS);

-

- drm_dev_exit(idx);

-}

-

-static void vc4_vec_ntsc_j_mode_set(struct vc4_vec *vec)

-{

- struct drm_device *drm = vec->connector.dev;

- int idx;

-

- if (!drm_dev_enter(drm, &idx))

- return;

-

- VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_NTSC_STD);

- VEC_WRITE(VEC_CONFIG1, VEC_CONFIG1_C_CVBS_CVBS);

-

- drm_dev_exit(idx);

-}

-

static const struct drm_display_mode ntsc_mode = {

DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 13500,

720, 720 + 14, 720 + 14 + 64, 720 + 14 + 64 + 60, 0,

@@ -259,37 +233,6 @@ static const struct drm_display_mode ntsc_mode = {

DRM_MODE_FLAG_INTERLACE)

};



-static void vc4_vec_pal_mode_set(struct vc4_vec *vec)

-{

- struct drm_device *drm = vec->connector.dev;

- int idx;

-

- if (!drm_dev_enter(drm, &idx))

- return;

-

- VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_PAL_BDGHI_STD);

- VEC_WRITE(VEC_CONFIG1, VEC_CONFIG1_C_CVBS_CVBS);

-

- drm_dev_exit(idx);

-}

-

-static void vc4_vec_pal_m_mode_set(struct vc4_vec *vec)

-{

- struct drm_device *drm = vec->connector.dev;

- int idx;

-

- if (!drm_dev_enter(drm, &idx))

- return;

-

- VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_PAL_BDGHI_STD);

- VEC_WRITE(VEC_CONFIG1,

- VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ);

- VEC_WRITE(VEC_FREQ3_2, 0x223b);

- VEC_WRITE(VEC_FREQ1_0, 0x61d1);

-

- drm_dev_exit(idx);

-}

-

static const struct drm_display_mode pal_mode = {

DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 13500,

720, 720 + 20, 720 + 20 + 64, 720 + 20 + 64 + 60, 0,

@@ -300,19 +243,24 @@ static const struct drm_display_mode pal_mode = {

static const struct vc4_vec_tv_mode vc4_vec_tv_modes[] = {

[VC4_VEC_TV_MODE_NTSC] = {

.mode = &ntsc_mode,

- .mode_set = vc4_vec_ntsc_mode_set,

+ .config0 = VEC_CONFIG0_NTSC_STD | VEC_CONFIG0_PDEN,

+ .config1 = VEC_CONFIG1_C_CVBS_CVBS,

},

[VC4_VEC_TV_MODE_NTSC_J] = {

.mode = &ntsc_mode,

- .mode_set = vc4_vec_ntsc_j_mode_set,

+ .config0 = VEC_CONFIG0_NTSC_STD,

+ .config1 = VEC_CONFIG1_C_CVBS_CVBS,

},

[VC4_VEC_TV_MODE_PAL] = {

.mode = &pal_mode,

- .mode_set = vc4_vec_pal_mode_set,

+ .config0 = VEC_CONFIG0_PAL_BDGHI_STD,

+ .config1 = VEC_CONFIG1_C_CVBS_CVBS,

},

[VC4_VEC_TV_MODE_PAL_M] = {

.mode = &pal_mode,

- .mode_set = vc4_vec_pal_m_mode_set,

+ .config0 = VEC_CONFIG0_PAL_BDGHI_STD,

+ .config1 = VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ,

+ .custom_freq = 0x223b61d1,

},

};



@@ -470,7 +418,16 @@ static void vc4_vec_encoder_enable(struct drm_encoder *encoder,

/* Mask all interrupts. */

VEC_WRITE(VEC_MASK0, 0);



- vec->tv_mode->mode_set(vec);

+ VEC_WRITE(VEC_CONFIG0, vec->tv_mode->config0);

+ VEC_WRITE(VEC_CONFIG1, vec->tv_mode->config1);

+

+ if (vec->tv_mode->custom_freq != 0) {

+ VEC_WRITE(VEC_FREQ3_2,

+ (vec->tv_mode->custom_freq >> 16) &

+ 0xffff);

+ VEC_WRITE(VEC_FREQ1_0,

+ vec->tv_mode->custom_freq & 0xffff);

+ }



VEC_WRITE(VEC_DAC_MISC,

VEC_DAC_MISC_VID_ACT | VEC_DAC_MISC_DAC_RST_N);



--

b4 0.10.0-dev-65ba7

2022-08-29 13:53:15

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH v2 15/41] drm/modes: Switch to named mode descriptors

The current named mode parsing relies only the mode name, and doesn't allow

to specify any other parameter.



Let's convert that string list to an array of a custom structure that will

hold the name and some additional parameters in the future.



Signed-off-by: Maxime Ripard <[email protected]>



diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c

index 0636cb707544..1fdfa004b139 100644

--- a/drivers/gpu/drm/drm_modes.c

+++ b/drivers/gpu/drm/drm_modes.c

@@ -2206,9 +2206,13 @@ static int drm_mode_parse_cmdline_options(const char *str,

return 0;

}



-static const char * const drm_named_modes_whitelist[] = {

- "NTSC",

- "PAL",

+struct drm_named_mode {

+ const char *name;

+};

+

+static const struct drm_named_mode drm_named_modes[] = {

+ { "NTSC", },

+ { "PAL", },

};



static int drm_mode_parse_cmdline_named_mode(const char *name,

@@ -2241,14 +2245,15 @@ static int drm_mode_parse_cmdline_named_mode(const char *name,

* We're sure we're a named mode at that point, iterate over the

* list of modes we're aware of.

*/

- for (i = 0; i < ARRAY_SIZE(drm_named_modes_whitelist); i++) {

+ for (i = 0; i < ARRAY_SIZE(drm_named_modes); i++) {

+ const struct drm_named_mode *mode = &drm_named_modes[i];

int ret;



- ret = str_has_prefix(name, drm_named_modes_whitelist[i]);

+ ret = str_has_prefix(name, mode->name);

if (ret != name_end)

continue;



- strcpy(cmdline_mode->name, drm_named_modes_whitelist[i]);

+ strcpy(cmdline_mode->name, mode->name);

cmdline_mode->specified = true;



return 1;



--

b4 0.10.0-dev-65ba7

2022-08-29 13:53:24

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH v2 30/41] drm/vc4: vec: Fix definition of PAL-M mode

From: Mateusz Kwiatkowski <[email protected]>



PAL-M is a Brazilian analog TV standard that uses a PAL-style chroma

subcarrier at 3.575611[888111] MHz on top of 525-line (480i60) timings.

This commit makes the driver actually use the proper VEC preset for this

mode instead of just changing PAL subcarrier frequency.



Signed-off-by: Mateusz Kwiatkowski <[email protected]>

Signed-off-by: Maxime Ripard <[email protected]>



diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c

index 63e4e617e321..fa85dd260742 100644

--- a/drivers/gpu/drm/vc4/vc4_vec.c

+++ b/drivers/gpu/drm/vc4/vc4_vec.c

@@ -69,6 +69,7 @@

#define VEC_CONFIG0_STD_MASK GENMASK(1, 0)

#define VEC_CONFIG0_NTSC_STD 0

#define VEC_CONFIG0_PAL_BDGHI_STD 1

+#define VEC_CONFIG0_PAL_M_STD 2

#define VEC_CONFIG0_PAL_N_STD 3



#define VEC_SCHPH 0x108

@@ -241,10 +242,9 @@ static const struct vc4_vec_tv_mode vc4_vec_tv_modes[] = {

.config1 = VEC_CONFIG1_C_CVBS_CVBS,

},

[VC4_VEC_TV_MODE_PAL_M] = {

- .mode = &drm_mode_576i,

- .config0 = VEC_CONFIG0_PAL_BDGHI_STD,

- .config1 = VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ,

- .custom_freq = 0x223b61d1,

+ .mode = &drm_mode_480i,

+ .config0 = VEC_CONFIG0_PAL_M_STD,

+ .config1 = VEC_CONFIG1_C_CVBS_CVBS,

},

};





--

b4 0.10.0-dev-65ba7

2022-08-29 13:55:45

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH v2 18/41] drm/connector: Add a function to lookup a TV mode by its name

As part of the command line parsing rework coming in the next patches,

we'll need to lookup drm_connector_tv_mode values by their name, already

defined in drm_tv_mode_enum_list.



In order to avoid any code duplication, let's do a function that will

perform a lookup of a TV mode name and return its value.



Signed-off-by: Maxime Ripard <[email protected]>



diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c

index b1fcacd150e8..0fe01a1c20ad 100644

--- a/drivers/gpu/drm/drm_connector.c

+++ b/drivers/gpu/drm/drm_connector.c

@@ -1003,6 +1003,30 @@ static const struct drm_prop_enum_list drm_tv_mode_enum_list[] = {

};

DRM_ENUM_NAME_FN(drm_get_tv_mode_name, drm_tv_mode_enum_list)



+/**

+ * drm_get_tv_mode_from_name - Translates a TV mode name into its enum value

+ * @name: TV Mode name we want to convert

+ * @len: Length of @name

+ *

+ * Translates @name into an enum drm_connector_tv_mode.

+ *

+ * Returns: the enum value on success, a negative errno otherwise.

+ */

+int drm_get_tv_mode_from_name(const char *name, size_t len)

+{

+ unsigned int i;

+

+ for (i = 0; i < ARRAY_SIZE(drm_tv_mode_enum_list); i++) {

+ const struct drm_prop_enum_list *item = &drm_tv_mode_enum_list[i];

+

+ if (strlen(item->name) == len && !strncmp(item->name, name, len))

+ return item->type;

+ }

+

+ return -EINVAL;

+}

+EXPORT_SYMBOL(drm_get_tv_mode_from_name)

+

static const struct drm_prop_enum_list drm_tv_select_enum_list[] = {

{ DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */

{ DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */

diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h

index bb39d2bb806e..49d261977d4e 100644

--- a/include/drm/drm_connector.h

+++ b/include/drm/drm_connector.h

@@ -1943,6 +1943,8 @@ const char *drm_get_dp_subconnector_name(int val);

const char *drm_get_content_protection_name(int val);

const char *drm_get_hdcp_content_type_name(int val);



+int drm_get_tv_mode_from_name(const char *name, size_t len);

+

int drm_mode_create_dvi_i_properties(struct drm_device *dev);

void drm_connector_attach_dp_subconnector_property(struct drm_connector *connector);





--

b4 0.10.0-dev-65ba7

2022-08-29 13:56:44

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH v2 31/41] drm/vc4: vec: Use TV Reset implementation

The analog TV properties created by the drm_mode_create_tv_properties() are

not properly initialised at reset. Let's switch our implementation to call

drm_atomic_helper_connector_tv_reset().



Signed-off-by: Maxime Ripard <[email protected]>



diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c

index fa85dd260742..ba6f81908923 100644

--- a/drivers/gpu/drm/vc4/vc4_vec.c

+++ b/drivers/gpu/drm/vc4/vc4_vec.c

@@ -254,6 +254,12 @@ vc4_vec_connector_detect(struct drm_connector *connector, bool force)

return connector_status_unknown;

}



+static void vc4_vec_connector_reset(struct drm_connector *connector)

+{

+ drm_atomic_helper_connector_reset(connector);

+ drm_atomic_helper_connector_tv_reset(connector);

+}

+

static int vc4_vec_connector_get_modes(struct drm_connector *connector)

{

struct drm_connector_state *state = connector->state;

@@ -274,7 +280,7 @@ static int vc4_vec_connector_get_modes(struct drm_connector *connector)

static const struct drm_connector_funcs vc4_vec_connector_funcs = {

.detect = vc4_vec_connector_detect,

.fill_modes = drm_helper_probe_single_connector_modes,

- .reset = drm_atomic_helper_connector_reset,

+ .reset = vc4_vec_connector_reset,

.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,

.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,

};



--

b4 0.10.0-dev-65ba7

2022-08-29 13:57:24

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH v2 27/41] drm/vc4: vec: Remove redundant atomic_mode_set

From: Mateusz Kwiatkowski <[email protected]>



Let's remove the superfluous tv_mode field, which was redundant with the

mode field in struct drm_tv_connector_state.



Signed-off-by: Mateusz Kwiatkowski <[email protected]>

Signed-off-by: Maxime Ripard <[email protected]>



diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c

index 9a37c3fcc295..4d7bc7c20704 100644

--- a/drivers/gpu/drm/vc4/vc4_vec.c

+++ b/drivers/gpu/drm/vc4/vc4_vec.c

@@ -171,8 +171,6 @@ struct vc4_vec {



struct clk *clock;



- const struct vc4_vec_tv_mode *tv_mode;

-

struct debugfs_regset32 regset;

};



@@ -316,7 +314,6 @@ static int vc4_vec_connector_init(struct drm_device *dev, struct vc4_vec *vec)

drm_object_attach_property(&connector->base,

dev->mode_config.legacy_tv_mode_property,

VC4_VEC_TV_MODE_NTSC);

- vec->tv_mode = &vc4_vec_tv_modes[VC4_VEC_TV_MODE_NTSC];



drm_connector_attach_encoder(connector, &vec->encoder.base);



@@ -360,6 +357,11 @@ static void vc4_vec_encoder_enable(struct drm_encoder *encoder,

{

struct drm_device *drm = encoder->dev;

struct vc4_vec *vec = encoder_to_vc4_vec(encoder);

+ struct drm_connector *connector = &vec->connector;

+ struct drm_connector_state *conn_state =

+ drm_atomic_get_new_connector_state(state, connector);

+ const struct vc4_vec_tv_mode *tv_mode =

+ &vc4_vec_tv_modes[conn_state->tv.mode];

int idx, ret;



if (!drm_dev_enter(drm, &idx))

@@ -418,15 +420,14 @@ static void vc4_vec_encoder_enable(struct drm_encoder *encoder,

/* Mask all interrupts. */

VEC_WRITE(VEC_MASK0, 0);



- VEC_WRITE(VEC_CONFIG0, vec->tv_mode->config0);

- VEC_WRITE(VEC_CONFIG1, vec->tv_mode->config1);

+ VEC_WRITE(VEC_CONFIG0, tv_mode->config0);

+ VEC_WRITE(VEC_CONFIG1, tv_mode->config1);



- if (vec->tv_mode->custom_freq != 0) {

+ if (tv_mode->custom_freq != 0) {

VEC_WRITE(VEC_FREQ3_2,

- (vec->tv_mode->custom_freq >> 16) &

- 0xffff);

+ (tv_mode->custom_freq >> 16) & 0xffff);

VEC_WRITE(VEC_FREQ1_0,

- vec->tv_mode->custom_freq & 0xffff);

+ tv_mode->custom_freq & 0xffff);

}



VEC_WRITE(VEC_DAC_MISC,

@@ -442,15 +443,6 @@ static void vc4_vec_encoder_enable(struct drm_encoder *encoder,

drm_dev_exit(idx);

}



-static void vc4_vec_encoder_atomic_mode_set(struct drm_encoder *encoder,

- struct drm_crtc_state *crtc_state,

- struct drm_connector_state *conn_state)

-{

- struct vc4_vec *vec = encoder_to_vc4_vec(encoder);

-

- vec->tv_mode = &vc4_vec_tv_modes[conn_state->tv.mode];

-}

-

static int vc4_vec_encoder_atomic_check(struct drm_encoder *encoder,

struct drm_crtc_state *crtc_state,

struct drm_connector_state *conn_state)

@@ -470,7 +462,6 @@ static const struct drm_encoder_helper_funcs vc4_vec_encoder_helper_funcs = {

.atomic_check = vc4_vec_encoder_atomic_check,

.atomic_disable = vc4_vec_encoder_disable,

.atomic_enable = vc4_vec_encoder_enable,

- .atomic_mode_set = vc4_vec_encoder_atomic_mode_set,

};



static int vc4_vec_late_register(struct drm_encoder *encoder)



--

b4 0.10.0-dev-65ba7

2022-08-30 12:45:58

by Maira Canal

[permalink] [raw]
Subject: Re: [PATCH v2 19/41] drm/modes: Introduce the tv_mode property as a command-line option

On 8/29/22 10:11, Maxime Ripard wrote:
> Our new tv mode option allows to specify the TV mode from a property.
> However, it can still be useful, for example to avoid any boot time
> artifact, to set that property directly from the kernel command line.
>
> Let's add some code to allow it, and some unit tests to exercise that code.
>
> Signed-off-by: Maxime Ripard <[email protected]>
>
> diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
> index 73d01e755496..a759a4ba0036 100644
> --- a/drivers/gpu/drm/drm_modes.c
> +++ b/drivers/gpu/drm/drm_modes.c
> @@ -2115,6 +2115,30 @@ static int drm_mode_parse_panel_orientation(const char *delim,
> return 0;
> }
>
> +static int drm_mode_parse_tv_mode(const char *delim,
> + struct drm_cmdline_mode *mode)
> +{
> + const char *value;
> + unsigned int len;
> + int ret;
> +
> + if (*delim != '=')
> + return -EINVAL;
> +
> + value = delim + 1;
> + delim = strchr(value, ',');
> + if (!delim)
> + delim = value + strlen(value);
> +
> + ret = drm_get_tv_mode_from_name(value, delim - value);
> + if (ret < 0)
> + return ret;
> +
> + mode->tv_mode = ret;
> +
> + return 0;
> +}
> +
> static int drm_mode_parse_cmdline_options(const char *str,
> bool freestanding,
> const struct drm_connector *connector,
> @@ -2184,6 +2208,9 @@ static int drm_mode_parse_cmdline_options(const char *str,
> } else if (!strncmp(option, "panel_orientation", delim - option)) {
> if (drm_mode_parse_panel_orientation(delim, mode))
> return -EINVAL;
> + } else if (!strncmp(option, "tv_mode", delim - option)) {
> + if (drm_mode_parse_tv_mode(delim, mode))
> + return -EINVAL;
> } else {
> return -EINVAL;
> }
> @@ -2212,20 +2239,22 @@ struct drm_named_mode {
> unsigned int xres;
> unsigned int yres;
> unsigned int flags;
> + unsigned int tv_mode;
> };
>
> -#define NAMED_MODE(_name, _pclk, _x, _y, _flags) \
> +#define NAMED_MODE(_name, _pclk, _x, _y, _flags, _mode) \
> { \
> .name = _name, \
> .pixel_clock_khz = _pclk, \
> .xres = _x, \
> .yres = _y, \
> .flags = _flags, \
> + .tv_mode = _mode, \
> }
>
> static const struct drm_named_mode drm_named_modes[] = {
> - NAMED_MODE("NTSC", 13500, 720, 480, DRM_MODE_FLAG_INTERLACE),
> - NAMED_MODE("PAL", 13500, 720, 576, DRM_MODE_FLAG_INTERLACE),
> + NAMED_MODE("NTSC", 13500, 720, 480, DRM_MODE_FLAG_INTERLACE, DRM_MODE_TV_MODE_NTSC_M),
> + NAMED_MODE("PAL", 13500, 720, 576, DRM_MODE_FLAG_INTERLACE, DRM_MODE_TV_MODE_PAL_B),
> };
>
> static int drm_mode_parse_cmdline_named_mode(const char *name,
> @@ -2271,6 +2300,7 @@ static int drm_mode_parse_cmdline_named_mode(const char *name,
> cmdline_mode->xres = mode->xres;
> cmdline_mode->yres = mode->yres;
> cmdline_mode->interlace = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
> + cmdline_mode->tv_mode = mode->tv_mode;
> cmdline_mode->specified = true;
>
> return 1;
> diff --git a/drivers/gpu/drm/tests/drm_cmdline_parser_test.c b/drivers/gpu/drm/tests/drm_cmdline_parser_test.c
> index 59b29cdfdd35..f1e73ed65be0 100644
> --- a/drivers/gpu/drm/tests/drm_cmdline_parser_test.c
> +++ b/drivers/gpu/drm/tests/drm_cmdline_parser_test.c
> @@ -885,6 +885,201 @@ static void drm_cmdline_test_multiple_options(struct kunit *test)
> KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_UNSPECIFIED);
> }
>
> +static void drm_cmdline_test_tv_options(struct kunit *test,
> + const char *cmdline,
> + const struct drm_display_mode *expected_mode,
> + unsigned int expected_tv_mode)
> +{
> + struct drm_cmdline_mode mode = { };
> +
> + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
> + &no_connector, &mode));
> + KUNIT_EXPECT_TRUE(test, mode.specified);
> + KUNIT_EXPECT_EQ(test, mode.xres, expected_mode->hdisplay);
> + KUNIT_EXPECT_EQ(test, mode.yres, expected_mode->vdisplay);
> + KUNIT_EXPECT_EQ(test, mode.tv_mode, expected_tv_mode);
> +
> + KUNIT_EXPECT_FALSE(test, mode.refresh_specified);
> +
> + KUNIT_EXPECT_FALSE(test, mode.bpp_specified);
> +
> + KUNIT_EXPECT_FALSE(test, mode.rb);
> + KUNIT_EXPECT_FALSE(test, mode.cvt);
> + KUNIT_EXPECT_EQ(test, mode.interlace, !!(expected_mode->flags & DRM_MODE_FLAG_INTERLACE));
> + KUNIT_EXPECT_FALSE(test, mode.margins);
> + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_UNSPECIFIED);
> +}
> +
> +static void drm_cmdline_test_tv_option_ntsc_443(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x480i,tv_mode=NTSC-443",
> + drm_mode_analog_ntsc_480i(NULL),
> + DRM_MODE_TV_MODE_NTSC_443);
> +}
> +
> +static void drm_cmdline_test_tv_option_ntsc_j(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x480i,tv_mode=NTSC-J",
> + drm_mode_analog_ntsc_480i(NULL),
> + DRM_MODE_TV_MODE_NTSC_J);
> +}
> +
> +static void drm_cmdline_test_tv_option_ntsc_m(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x480i,tv_mode=NTSC-M",
> + drm_mode_analog_ntsc_480i(NULL),
> + DRM_MODE_TV_MODE_NTSC_M);
> +}
> +
> +static void drm_cmdline_test_tv_option_pal_60(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x576i,tv_mode=PAL-60",
> + drm_mode_analog_pal_576i(NULL),
> + DRM_MODE_TV_MODE_PAL_60);
> +}
> +
> +static void drm_cmdline_test_tv_option_pal_b(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x576i,tv_mode=PAL-B",
> + drm_mode_analog_pal_576i(NULL),
> + DRM_MODE_TV_MODE_PAL_B);
> +}
> +
> +static void drm_cmdline_test_tv_option_pal_d(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x576i,tv_mode=PAL-D",
> + drm_mode_analog_pal_576i(NULL),
> + DRM_MODE_TV_MODE_PAL_D);
> +}
> +
> +static void drm_cmdline_test_tv_option_pal_g(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x576i,tv_mode=PAL-G",
> + drm_mode_analog_pal_576i(NULL),
> + DRM_MODE_TV_MODE_PAL_G);
> +}
> +
> +static void drm_cmdline_test_tv_option_pal_h(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x576i,tv_mode=PAL-H",
> + drm_mode_analog_pal_576i(NULL),
> + DRM_MODE_TV_MODE_PAL_H);
> +}
> +
> +static void drm_cmdline_test_tv_option_pal_i(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x576i,tv_mode=PAL-I",
> + drm_mode_analog_pal_576i(NULL),
> + DRM_MODE_TV_MODE_PAL_I);
> +}
> +
> +static void drm_cmdline_test_tv_option_pal_m(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x480i,tv_mode=PAL-M",
> + drm_mode_analog_ntsc_480i(NULL),
> + DRM_MODE_TV_MODE_PAL_M);
> +}
> +
> +static void drm_cmdline_test_tv_option_pal_n(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x576i,tv_mode=PAL-N",
> + drm_mode_analog_pal_576i(NULL),
> + DRM_MODE_TV_MODE_PAL_N);
> +}
> +
> +static void drm_cmdline_test_tv_option_pal_nc(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x576i,tv_mode=PAL-Nc",
> + drm_mode_analog_pal_576i(NULL),
> + DRM_MODE_TV_MODE_PAL_NC);
> +}
> +
> +static void drm_cmdline_test_tv_option_secam_60(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x576i,tv_mode=SECAM-60",
> + drm_mode_analog_pal_576i(NULL),
> + DRM_MODE_TV_MODE_SECAM_60);
> +}
> +
> +static void drm_cmdline_test_tv_option_secam_b(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x576i,tv_mode=SECAM-B",
> + drm_mode_analog_pal_576i(NULL),
> + DRM_MODE_TV_MODE_SECAM_B);
> +}
> +
> +static void drm_cmdline_test_tv_option_secam_d(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x576i,tv_mode=SECAM-D",
> + drm_mode_analog_pal_576i(NULL),
> + DRM_MODE_TV_MODE_SECAM_D);
> +}
> +
> +static void drm_cmdline_test_tv_option_secam_g(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x576i,tv_mode=SECAM-G",
> + drm_mode_analog_pal_576i(NULL),
> + DRM_MODE_TV_MODE_SECAM_G);
> +}
> +
> +static void drm_cmdline_test_tv_option_secam_k(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x576i,tv_mode=SECAM-K",
> + drm_mode_analog_pal_576i(NULL),
> + DRM_MODE_TV_MODE_SECAM_K);
> +}
> +
> +static void drm_cmdline_test_tv_option_secam_k1(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x576i,tv_mode=SECAM-K1",
> + drm_mode_analog_pal_576i(NULL),
> + DRM_MODE_TV_MODE_SECAM_K1);
> +}
> +
> +static void drm_cmdline_test_tv_option_secam_l(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x576i,tv_mode=SECAM-L",
> + drm_mode_analog_pal_576i(NULL),
> + DRM_MODE_TV_MODE_SECAM_L);
> +}

Instead of creating a function to each drm_cmdline_test_tv_options test,
you can create a parameterized test [1] for this function. This will
help the readability of the tests.

[1] https://docs.kernel.org/dev-tools/kunit/usage.html#parameterized-testing

> +
> +static void drm_cmdline_test_tv_option_invalid(struct kunit *test)
> +{
> + struct drm_cmdline_mode mode = { };
> + const char *cmdline = "720x480i,tv_mode=invalid";
> +
> + KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline,
> + &no_connector, &mode));
> +}
> +
> +static void drm_cmdline_test_tv_option_truncated(struct kunit *test)
> +{
> + struct drm_cmdline_mode mode = { };
> + const char *cmdline = "720x480i,tv_mode=NTSC";
> +
> + KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline,
> + &no_connector, &mode));
> +}
> +

I guess this tests can be incorporated in the future to the negative
tests from Michał Winiarski [2]. I don't know which one will be merged
first.

[2]
https://lore.kernel.org/dri-devel/[email protected]/

Best Regards,
- Maíra Canal

> static void drm_cmdline_test_invalid_option(struct kunit *test)
> {
> struct drm_cmdline_mode mode = { };
> @@ -1047,6 +1242,27 @@ static struct kunit_case drm_cmdline_parser_tests[] = {
> KUNIT_CASE(drm_cmdline_test_name_refresh_invalid_mode),
> KUNIT_CASE(drm_cmdline_test_name_option),
> KUNIT_CASE(drm_cmdline_test_name_bpp_option),
> + KUNIT_CASE(drm_cmdline_test_tv_option_ntsc_443),
> + KUNIT_CASE(drm_cmdline_test_tv_option_ntsc_j),
> + KUNIT_CASE(drm_cmdline_test_tv_option_ntsc_m),
> + KUNIT_CASE(drm_cmdline_test_tv_option_pal_60),
> + KUNIT_CASE(drm_cmdline_test_tv_option_pal_b),
> + KUNIT_CASE(drm_cmdline_test_tv_option_pal_d),
> + KUNIT_CASE(drm_cmdline_test_tv_option_pal_g),
> + KUNIT_CASE(drm_cmdline_test_tv_option_pal_h),
> + KUNIT_CASE(drm_cmdline_test_tv_option_pal_i),
> + KUNIT_CASE(drm_cmdline_test_tv_option_pal_m),
> + KUNIT_CASE(drm_cmdline_test_tv_option_pal_n),
> + KUNIT_CASE(drm_cmdline_test_tv_option_pal_nc),
> + KUNIT_CASE(drm_cmdline_test_tv_option_secam_60),
> + KUNIT_CASE(drm_cmdline_test_tv_option_secam_b),
> + KUNIT_CASE(drm_cmdline_test_tv_option_secam_d),
> + KUNIT_CASE(drm_cmdline_test_tv_option_secam_g),
> + KUNIT_CASE(drm_cmdline_test_tv_option_secam_k),
> + KUNIT_CASE(drm_cmdline_test_tv_option_secam_k1),
> + KUNIT_CASE(drm_cmdline_test_tv_option_secam_l),
> + KUNIT_CASE(drm_cmdline_test_tv_option_invalid),
> + KUNIT_CASE(drm_cmdline_test_tv_option_truncated),
> KUNIT_CASE(drm_cmdline_test_rotate_0),
> KUNIT_CASE(drm_cmdline_test_rotate_90),
> KUNIT_CASE(drm_cmdline_test_rotate_180),
> diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
> index 49d261977d4e..9589108ba202 100644
> --- a/include/drm/drm_connector.h
> +++ b/include/drm/drm_connector.h
> @@ -1447,6 +1447,11 @@ struct drm_cmdline_mode {
> * @tv_margins: TV margins to apply to the mode.
> */
> struct drm_connector_tv_margins tv_margins;
> +
> + /**
> + * @tv_mode: TV mode standard. See DRM_MODE_TV_MODE_*.
> + */
> + enum drm_connector_tv_mode tv_mode;
> };
>
> /**
>

2022-08-30 12:54:42

by Maira Canal

[permalink] [raw]
Subject: Re: [PATCH v2 19/41] drm/modes: Introduce the tv_mode property as a command-line option

Hi Maxime,

On 8/29/22 10:11, Maxime Ripard wrote:
> Our new tv mode option allows to specify the TV mode from a property.
> However, it can still be useful, for example to avoid any boot time
> artifact, to set that property directly from the kernel command line.
>
> Let's add some code to allow it, and some unit tests to exercise that code.
>
> Signed-off-by: Maxime Ripard <[email protected]>
>
> diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
> index 73d01e755496..a759a4ba0036 100644
> --- a/drivers/gpu/drm/drm_modes.c
> +++ b/drivers/gpu/drm/drm_modes.c
> @@ -2115,6 +2115,30 @@ static int drm_mode_parse_panel_orientation(const char *delim,
> return 0;
> }
>
> +static int drm_mode_parse_tv_mode(const char *delim,
> + struct drm_cmdline_mode *mode)
> +{
> + const char *value;
> + unsigned int len;

Looks like this variable len is not being used and is producing the
following warning:

../drivers/gpu/drm/drm_modes.c:2122:15: warning: unused variable 'len'
[-Wunused-variable]
unsigned int len;
^

Best Regards,
- Maíra Canal

> + int ret;
> +
> + if (*delim != '=')
> + return -EINVAL;
> +
> + value = delim + 1;
> + delim = strchr(value, ',');
> + if (!delim)
> + delim = value + strlen(value);
> +
> + ret = drm_get_tv_mode_from_name(value, delim - value);
> + if (ret < 0)
> + return ret;
> +
> + mode->tv_mode = ret;
> +
> + return 0;
> +}
> +
> static int drm_mode_parse_cmdline_options(const char *str,
> bool freestanding,
> const struct drm_connector *connector,
> @@ -2184,6 +2208,9 @@ static int drm_mode_parse_cmdline_options(const char *str,
> } else if (!strncmp(option, "panel_orientation", delim - option)) {
> if (drm_mode_parse_panel_orientation(delim, mode))
> return -EINVAL;
> + } else if (!strncmp(option, "tv_mode", delim - option)) {
> + if (drm_mode_parse_tv_mode(delim, mode))
> + return -EINVAL;
> } else {
> return -EINVAL;
> }
> @@ -2212,20 +2239,22 @@ struct drm_named_mode {
> unsigned int xres;
> unsigned int yres;
> unsigned int flags;
> + unsigned int tv_mode;
> };
>
> -#define NAMED_MODE(_name, _pclk, _x, _y, _flags) \
> +#define NAMED_MODE(_name, _pclk, _x, _y, _flags, _mode) \
> { \
> .name = _name, \
> .pixel_clock_khz = _pclk, \
> .xres = _x, \
> .yres = _y, \
> .flags = _flags, \
> + .tv_mode = _mode, \
> }
>
> static const struct drm_named_mode drm_named_modes[] = {
> - NAMED_MODE("NTSC", 13500, 720, 480, DRM_MODE_FLAG_INTERLACE),
> - NAMED_MODE("PAL", 13500, 720, 576, DRM_MODE_FLAG_INTERLACE),
> + NAMED_MODE("NTSC", 13500, 720, 480, DRM_MODE_FLAG_INTERLACE, DRM_MODE_TV_MODE_NTSC_M),
> + NAMED_MODE("PAL", 13500, 720, 576, DRM_MODE_FLAG_INTERLACE, DRM_MODE_TV_MODE_PAL_B),
> };
>
> static int drm_mode_parse_cmdline_named_mode(const char *name,
> @@ -2271,6 +2300,7 @@ static int drm_mode_parse_cmdline_named_mode(const char *name,
> cmdline_mode->xres = mode->xres;
> cmdline_mode->yres = mode->yres;
> cmdline_mode->interlace = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
> + cmdline_mode->tv_mode = mode->tv_mode;
> cmdline_mode->specified = true;
>
> return 1;
> diff --git a/drivers/gpu/drm/tests/drm_cmdline_parser_test.c b/drivers/gpu/drm/tests/drm_cmdline_parser_test.c
> index 59b29cdfdd35..f1e73ed65be0 100644
> --- a/drivers/gpu/drm/tests/drm_cmdline_parser_test.c
> +++ b/drivers/gpu/drm/tests/drm_cmdline_parser_test.c
> @@ -885,6 +885,201 @@ static void drm_cmdline_test_multiple_options(struct kunit *test)
> KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_UNSPECIFIED);
> }
>
> +static void drm_cmdline_test_tv_options(struct kunit *test,
> + const char *cmdline,
> + const struct drm_display_mode *expected_mode,
> + unsigned int expected_tv_mode)
> +{
> + struct drm_cmdline_mode mode = { };
> +
> + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
> + &no_connector, &mode));
> + KUNIT_EXPECT_TRUE(test, mode.specified);
> + KUNIT_EXPECT_EQ(test, mode.xres, expected_mode->hdisplay);
> + KUNIT_EXPECT_EQ(test, mode.yres, expected_mode->vdisplay);
> + KUNIT_EXPECT_EQ(test, mode.tv_mode, expected_tv_mode);
> +
> + KUNIT_EXPECT_FALSE(test, mode.refresh_specified);
> +
> + KUNIT_EXPECT_FALSE(test, mode.bpp_specified);
> +
> + KUNIT_EXPECT_FALSE(test, mode.rb);
> + KUNIT_EXPECT_FALSE(test, mode.cvt);
> + KUNIT_EXPECT_EQ(test, mode.interlace, !!(expected_mode->flags & DRM_MODE_FLAG_INTERLACE));
> + KUNIT_EXPECT_FALSE(test, mode.margins);
> + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_UNSPECIFIED);
> +}
> +
> +static void drm_cmdline_test_tv_option_ntsc_443(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x480i,tv_mode=NTSC-443",
> + drm_mode_analog_ntsc_480i(NULL),
> + DRM_MODE_TV_MODE_NTSC_443);
> +}
> +
> +static void drm_cmdline_test_tv_option_ntsc_j(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x480i,tv_mode=NTSC-J",
> + drm_mode_analog_ntsc_480i(NULL),
> + DRM_MODE_TV_MODE_NTSC_J);
> +}
> +
> +static void drm_cmdline_test_tv_option_ntsc_m(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x480i,tv_mode=NTSC-M",
> + drm_mode_analog_ntsc_480i(NULL),
> + DRM_MODE_TV_MODE_NTSC_M);
> +}
> +
> +static void drm_cmdline_test_tv_option_pal_60(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x576i,tv_mode=PAL-60",
> + drm_mode_analog_pal_576i(NULL),
> + DRM_MODE_TV_MODE_PAL_60);
> +}
> +
> +static void drm_cmdline_test_tv_option_pal_b(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x576i,tv_mode=PAL-B",
> + drm_mode_analog_pal_576i(NULL),
> + DRM_MODE_TV_MODE_PAL_B);
> +}
> +
> +static void drm_cmdline_test_tv_option_pal_d(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x576i,tv_mode=PAL-D",
> + drm_mode_analog_pal_576i(NULL),
> + DRM_MODE_TV_MODE_PAL_D);
> +}
> +
> +static void drm_cmdline_test_tv_option_pal_g(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x576i,tv_mode=PAL-G",
> + drm_mode_analog_pal_576i(NULL),
> + DRM_MODE_TV_MODE_PAL_G);
> +}
> +
> +static void drm_cmdline_test_tv_option_pal_h(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x576i,tv_mode=PAL-H",
> + drm_mode_analog_pal_576i(NULL),
> + DRM_MODE_TV_MODE_PAL_H);
> +}
> +
> +static void drm_cmdline_test_tv_option_pal_i(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x576i,tv_mode=PAL-I",
> + drm_mode_analog_pal_576i(NULL),
> + DRM_MODE_TV_MODE_PAL_I);
> +}
> +
> +static void drm_cmdline_test_tv_option_pal_m(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x480i,tv_mode=PAL-M",
> + drm_mode_analog_ntsc_480i(NULL),
> + DRM_MODE_TV_MODE_PAL_M);
> +}
> +
> +static void drm_cmdline_test_tv_option_pal_n(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x576i,tv_mode=PAL-N",
> + drm_mode_analog_pal_576i(NULL),
> + DRM_MODE_TV_MODE_PAL_N);
> +}
> +
> +static void drm_cmdline_test_tv_option_pal_nc(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x576i,tv_mode=PAL-Nc",
> + drm_mode_analog_pal_576i(NULL),
> + DRM_MODE_TV_MODE_PAL_NC);
> +}
> +
> +static void drm_cmdline_test_tv_option_secam_60(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x576i,tv_mode=SECAM-60",
> + drm_mode_analog_pal_576i(NULL),
> + DRM_MODE_TV_MODE_SECAM_60);
> +}
> +
> +static void drm_cmdline_test_tv_option_secam_b(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x576i,tv_mode=SECAM-B",
> + drm_mode_analog_pal_576i(NULL),
> + DRM_MODE_TV_MODE_SECAM_B);
> +}
> +
> +static void drm_cmdline_test_tv_option_secam_d(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x576i,tv_mode=SECAM-D",
> + drm_mode_analog_pal_576i(NULL),
> + DRM_MODE_TV_MODE_SECAM_D);
> +}
> +
> +static void drm_cmdline_test_tv_option_secam_g(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x576i,tv_mode=SECAM-G",
> + drm_mode_analog_pal_576i(NULL),
> + DRM_MODE_TV_MODE_SECAM_G);
> +}
> +
> +static void drm_cmdline_test_tv_option_secam_k(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x576i,tv_mode=SECAM-K",
> + drm_mode_analog_pal_576i(NULL),
> + DRM_MODE_TV_MODE_SECAM_K);
> +}
> +
> +static void drm_cmdline_test_tv_option_secam_k1(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x576i,tv_mode=SECAM-K1",
> + drm_mode_analog_pal_576i(NULL),
> + DRM_MODE_TV_MODE_SECAM_K1);
> +}
> +
> +static void drm_cmdline_test_tv_option_secam_l(struct kunit *test)
> +{
> + drm_cmdline_test_tv_options(test,
> + "720x576i,tv_mode=SECAM-L",
> + drm_mode_analog_pal_576i(NULL),
> + DRM_MODE_TV_MODE_SECAM_L);
> +}
> +
> +static void drm_cmdline_test_tv_option_invalid(struct kunit *test)
> +{
> + struct drm_cmdline_mode mode = { };
> + const char *cmdline = "720x480i,tv_mode=invalid";
> +
> + KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline,
> + &no_connector, &mode));
> +}
> +
> +static void drm_cmdline_test_tv_option_truncated(struct kunit *test)
> +{
> + struct drm_cmdline_mode mode = { };
> + const char *cmdline = "720x480i,tv_mode=NTSC";
> +
> + KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline,
> + &no_connector, &mode));
> +}
> +
> static void drm_cmdline_test_invalid_option(struct kunit *test)
> {
> struct drm_cmdline_mode mode = { };
> @@ -1047,6 +1242,27 @@ static struct kunit_case drm_cmdline_parser_tests[] = {
> KUNIT_CASE(drm_cmdline_test_name_refresh_invalid_mode),
> KUNIT_CASE(drm_cmdline_test_name_option),
> KUNIT_CASE(drm_cmdline_test_name_bpp_option),
> + KUNIT_CASE(drm_cmdline_test_tv_option_ntsc_443),
> + KUNIT_CASE(drm_cmdline_test_tv_option_ntsc_j),
> + KUNIT_CASE(drm_cmdline_test_tv_option_ntsc_m),
> + KUNIT_CASE(drm_cmdline_test_tv_option_pal_60),
> + KUNIT_CASE(drm_cmdline_test_tv_option_pal_b),
> + KUNIT_CASE(drm_cmdline_test_tv_option_pal_d),
> + KUNIT_CASE(drm_cmdline_test_tv_option_pal_g),
> + KUNIT_CASE(drm_cmdline_test_tv_option_pal_h),
> + KUNIT_CASE(drm_cmdline_test_tv_option_pal_i),
> + KUNIT_CASE(drm_cmdline_test_tv_option_pal_m),
> + KUNIT_CASE(drm_cmdline_test_tv_option_pal_n),
> + KUNIT_CASE(drm_cmdline_test_tv_option_pal_nc),
> + KUNIT_CASE(drm_cmdline_test_tv_option_secam_60),
> + KUNIT_CASE(drm_cmdline_test_tv_option_secam_b),
> + KUNIT_CASE(drm_cmdline_test_tv_option_secam_d),
> + KUNIT_CASE(drm_cmdline_test_tv_option_secam_g),
> + KUNIT_CASE(drm_cmdline_test_tv_option_secam_k),
> + KUNIT_CASE(drm_cmdline_test_tv_option_secam_k1),
> + KUNIT_CASE(drm_cmdline_test_tv_option_secam_l),
> + KUNIT_CASE(drm_cmdline_test_tv_option_invalid),
> + KUNIT_CASE(drm_cmdline_test_tv_option_truncated),
> KUNIT_CASE(drm_cmdline_test_rotate_0),
> KUNIT_CASE(drm_cmdline_test_rotate_90),
> KUNIT_CASE(drm_cmdline_test_rotate_180),
> diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
> index 49d261977d4e..9589108ba202 100644
> --- a/include/drm/drm_connector.h
> +++ b/include/drm/drm_connector.h
> @@ -1447,6 +1447,11 @@ struct drm_cmdline_mode {
> * @tv_margins: TV margins to apply to the mode.
> */
> struct drm_connector_tv_margins tv_margins;
> +
> + /**
> + * @tv_mode: TV mode standard. See DRM_MODE_TV_MODE_*.
> + */
> + enum drm_connector_tv_mode tv_mode;
> };
>
> /**
>

2022-08-30 15:49:28

by Noralf Trønnes

[permalink] [raw]
Subject: Re: [PATCH v2 26/41] drm/vc4: vec: Refactor VEC TV mode setting



Den 29.08.2022 15.11, skrev Maxime Ripard:
> From: Mateusz Kwiatkowski <[email protected]>
>
>
>
> Change the mode_set function pointer logic to declarative config0,
>
> config1 and custom_freq fields, to make TV mode setting logic more
>
> concise and uniform.
>
>
>
> Signed-off-by: Mateusz Kwiatkowski <[email protected]>
>
> Signed-off-by: Maxime Ripard <[email protected]>
>
>
>
> diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c
>
> index 72eee0cbb615..9a37c3fcc295 100644
>
> --- a/drivers/gpu/drm/vc4/vc4_vec.c
>
> +++ b/drivers/gpu/drm/vc4/vc4_vec.c
>
> @@ -194,7 +194,9 @@ enum vc4_vec_tv_mode_id {
>
>
>
> struct vc4_vec_tv_mode {
>
> const struct drm_display_mode *mode;
>
> - void (*mode_set)(struct vc4_vec *vec);
>
> + u32 config0;
>
> + u32 config1;
>
> + u32 custom_freq;
>
> };
>
>
>
> static const struct debugfs_reg32 vec_regs[] = {
>
> @@ -224,34 +226,6 @@ static const struct debugfs_reg32 vec_regs[] = {
>
> VC4_REG32(VEC_DAC_MISC),
>
> };
>
>
>
> -static void vc4_vec_ntsc_mode_set(struct vc4_vec *vec)
>
> -{
>
> - struct drm_device *drm = vec->connector.dev;
>
> - int idx;
>
> -
>
> - if (!drm_dev_enter(drm, &idx))
>
> - return;
>
> -
>
> - VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_NTSC_STD | VEC_CONFIG0_PDEN);
>
> - VEC_WRITE(VEC_CONFIG1, VEC_CONFIG1_C_CVBS_CVBS);
>
> -
>
> - drm_dev_exit(idx);
>
> -}
>
> -
>
> -static void vc4_vec_ntsc_j_mode_set(struct vc4_vec *vec)
>
> -{
>
> - struct drm_device *drm = vec->connector.dev;
>
> - int idx;
>
> -
>
> - if (!drm_dev_enter(drm, &idx))
>
> - return;
>
> -
>
> - VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_NTSC_STD);
>
> - VEC_WRITE(VEC_CONFIG1, VEC_CONFIG1_C_CVBS_CVBS);
>
> -
>
> - drm_dev_exit(idx);
>
> -}
>
> -
>
> static const struct drm_display_mode ntsc_mode = {
>
> DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 13500,
>
> 720, 720 + 14, 720 + 14 + 64, 720 + 14 + 64 + 60, 0,
>
> @@ -259,37 +233,6 @@ static const struct drm_display_mode ntsc_mode = {
>
> DRM_MODE_FLAG_INTERLACE)
>
> };
>
>
>
> -static void vc4_vec_pal_mode_set(struct vc4_vec *vec)
>
> -{
>
> - struct drm_device *drm = vec->connector.dev;
>
> - int idx;
>
> -
>
> - if (!drm_dev_enter(drm, &idx))
>
> - return;
>
> -
>
> - VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_PAL_BDGHI_STD);
>
> - VEC_WRITE(VEC_CONFIG1, VEC_CONFIG1_C_CVBS_CVBS);
>
> -
>
> - drm_dev_exit(idx);
>
> -}
>
> -
>
> -static void vc4_vec_pal_m_mode_set(struct vc4_vec *vec)
>
> -{
>
> - struct drm_device *drm = vec->connector.dev;
>
> - int idx;
>
> -
>
> - if (!drm_dev_enter(drm, &idx))
>
> - return;
>
> -
>
> - VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_PAL_BDGHI_STD);
>
> - VEC_WRITE(VEC_CONFIG1,
>
> - VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ);
>
> - VEC_WRITE(VEC_FREQ3_2, 0x223b);
>
> - VEC_WRITE(VEC_FREQ1_0, 0x61d1);
>
> -
>
> - drm_dev_exit(idx);
>
> -}
>
> -
>
> static const struct drm_display_mode pal_mode = {
>
> DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 13500,
>
> 720, 720 + 20, 720 + 20 + 64, 720 + 20 + 64 + 60, 0,
>
> @@ -300,19 +243,24 @@ static const struct drm_display_mode pal_mode = {
>
> static const struct vc4_vec_tv_mode vc4_vec_tv_modes[] = {
>
> [VC4_VEC_TV_MODE_NTSC] = {
>
> .mode = &ntsc_mode,
>
> - .mode_set = vc4_vec_ntsc_mode_set,
>
> + .config0 = VEC_CONFIG0_NTSC_STD | VEC_CONFIG0_PDEN,
>
> + .config1 = VEC_CONFIG1_C_CVBS_CVBS,
>
> },
>
> [VC4_VEC_TV_MODE_NTSC_J] = {
>
> .mode = &ntsc_mode,
>
> - .mode_set = vc4_vec_ntsc_j_mode_set,
>
> + .config0 = VEC_CONFIG0_NTSC_STD,
>
> + .config1 = VEC_CONFIG1_C_CVBS_CVBS,
>
> },
>
> [VC4_VEC_TV_MODE_PAL] = {
>
> .mode = &pal_mode,
>
> - .mode_set = vc4_vec_pal_mode_set,
>
> + .config0 = VEC_CONFIG0_PAL_BDGHI_STD,
>
> + .config1 = VEC_CONFIG1_C_CVBS_CVBS,
>
> },
>
> [VC4_VEC_TV_MODE_PAL_M] = {
>
> .mode = &pal_mode,
>
> - .mode_set = vc4_vec_pal_m_mode_set,
>
> + .config0 = VEC_CONFIG0_PAL_BDGHI_STD,
>
> + .config1 = VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ,
>
> + .custom_freq = 0x223b61d1,
>
> },
>
> };
>
>
>
> @@ -470,7 +418,16 @@ static void vc4_vec_encoder_enable(struct drm_encoder *encoder,
>
> /* Mask all interrupts. */
>
> VEC_WRITE(VEC_MASK0, 0);
>
>
>
> - vec->tv_mode->mode_set(vec);
>
> + VEC_WRITE(VEC_CONFIG0, vec->tv_mode->config0);
>
> + VEC_WRITE(VEC_CONFIG1, vec->tv_mode->config1);
>
> +
>
> + if (vec->tv_mode->custom_freq != 0) {

Nit: '!= 0' is not necessary and not common either in kernel code.

Reviewed-by: Noralf Trønnes <[email protected]>

>
> + VEC_WRITE(VEC_FREQ3_2,
>
> + (vec->tv_mode->custom_freq >> 16) &
>
> + 0xffff);
>
> + VEC_WRITE(VEC_FREQ1_0,
>
> + vec->tv_mode->custom_freq & 0xffff);
>
> + }
>
>
>
> VEC_WRITE(VEC_DAC_MISC,
>
> VEC_DAC_MISC_VID_ACT | VEC_DAC_MISC_DAC_RST_N);
>
>
>

2022-08-30 16:19:07

by Noralf Trønnes

[permalink] [raw]
Subject: Re: [PATCH v2 27/41] drm/vc4: vec: Remove redundant atomic_mode_set



Den 29.08.2022 15.11, skrev Maxime Ripard:
> From: Mateusz Kwiatkowski <[email protected]>
>
>
>
> Let's remove the superfluous tv_mode field, which was redundant with the
>
> mode field in struct drm_tv_connector_state.
>
>
>
> Signed-off-by: Mateusz Kwiatkowski <[email protected]>
>
> Signed-off-by: Maxime Ripard <[email protected]>
>
>
>
> diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c
>
> index 9a37c3fcc295..4d7bc7c20704 100644
>
> --- a/drivers/gpu/drm/vc4/vc4_vec.c
>
> +++ b/drivers/gpu/drm/vc4/vc4_vec.c
>
> @@ -171,8 +171,6 @@ struct vc4_vec {
>
>
>
> struct clk *clock;
>
>
>
> - const struct vc4_vec_tv_mode *tv_mode;
>
> -
>
> struct debugfs_regset32 regset;
>
> };
>
>
>
> @@ -316,7 +314,6 @@ static int vc4_vec_connector_init(struct drm_device *dev, struct vc4_vec *vec)
>
> drm_object_attach_property(&connector->base,
>
> dev->mode_config.legacy_tv_mode_property,
>
> VC4_VEC_TV_MODE_NTSC);
>
> - vec->tv_mode = &vc4_vec_tv_modes[VC4_VEC_TV_MODE_NTSC];
>
>
>
> drm_connector_attach_encoder(connector, &vec->encoder.base);
>
>
>
> @@ -360,6 +357,11 @@ static void vc4_vec_encoder_enable(struct drm_encoder *encoder,
>
> {
>
> struct drm_device *drm = encoder->dev;
>
> struct vc4_vec *vec = encoder_to_vc4_vec(encoder);
>
> + struct drm_connector *connector = &vec->connector;
>
> + struct drm_connector_state *conn_state =
>
> + drm_atomic_get_new_connector_state(state, connector);
>
> + const struct vc4_vec_tv_mode *tv_mode =
>
> + &vc4_vec_tv_modes[conn_state->tv.mode];
>
> int idx, ret;
>
>
>
> if (!drm_dev_enter(drm, &idx))
>
> @@ -418,15 +420,14 @@ static void vc4_vec_encoder_enable(struct drm_encoder *encoder,
>
> /* Mask all interrupts. */
>
> VEC_WRITE(VEC_MASK0, 0);
>
>
>
> - VEC_WRITE(VEC_CONFIG0, vec->tv_mode->config0);
>
> - VEC_WRITE(VEC_CONFIG1, vec->tv_mode->config1);
>
> + VEC_WRITE(VEC_CONFIG0, tv_mode->config0);
>
> + VEC_WRITE(VEC_CONFIG1, tv_mode->config1);
>
>
>
> - if (vec->tv_mode->custom_freq != 0) {
>
> + if (tv_mode->custom_freq != 0) {
>
> VEC_WRITE(VEC_FREQ3_2,
>
> - (vec->tv_mode->custom_freq >> 16) &
>
> - 0xffff);
>
> + (tv_mode->custom_freq >> 16) & 0xffff);
>
> VEC_WRITE(VEC_FREQ1_0,
>
> - vec->tv_mode->custom_freq & 0xffff);
>
> + tv_mode->custom_freq & 0xffff);
>

Nit: This patch will be smaller if you add the tv_mode variable to the
previous patch.

Reviewed-by: Noralf Trønnes <[email protected]>

> }
>
>
>
> VEC_WRITE(VEC_DAC_MISC,
>
> @@ -442,15 +443,6 @@ static void vc4_vec_encoder_enable(struct drm_encoder *encoder,
>
> drm_dev_exit(idx);
>
> }
>
>
>
> -static void vc4_vec_encoder_atomic_mode_set(struct drm_encoder *encoder,
>
> - struct drm_crtc_state *crtc_state,
>
> - struct drm_connector_state *conn_state)
>
> -{
>
> - struct vc4_vec *vec = encoder_to_vc4_vec(encoder);
>
> -
>
> - vec->tv_mode = &vc4_vec_tv_modes[conn_state->tv.mode];
>
> -}
>
> -
>
> static int vc4_vec_encoder_atomic_check(struct drm_encoder *encoder,
>
> struct drm_crtc_state *crtc_state,
>
> struct drm_connector_state *conn_state)
>
> @@ -470,7 +462,6 @@ static const struct drm_encoder_helper_funcs vc4_vec_encoder_helper_funcs = {
>
> .atomic_check = vc4_vec_encoder_atomic_check,
>
> .atomic_disable = vc4_vec_encoder_disable,
>
> .atomic_enable = vc4_vec_encoder_enable,
>
> - .atomic_mode_set = vc4_vec_encoder_atomic_mode_set,
>
> };
>
>
>
> static int vc4_vec_late_register(struct drm_encoder *encoder)
>
>
>

2022-08-30 18:37:57

by Noralf Trønnes

[permalink] [raw]
Subject: Re: [PATCH v2 28/41] drm/vc4: vec: Fix timings for VEC modes



Den 29.08.2022 15.11, skrev Maxime Ripard:
> From: Mateusz Kwiatkowski <[email protected]>
>
>
>
> This commit fixes vertical timings of the VEC (composite output) modes
>
> to accurately represent the 525-line ("NTSC") and 625-line ("PAL") ITU-R
>
> standards.
>
>
>
> Previous timings were actually defined as 502 and 601 lines, resulting
>
> in non-standard 62.69 Hz and 52 Hz signals being generated,
>
> respectively.
>
>
>
> Signed-off-by: Mateusz Kwiatkowski <[email protected]>
>
> Signed-off-by: Maxime Ripard <[email protected]>
>
>

Acked-by: Noralf Trønnes <[email protected]>

2022-08-30 18:40:49

by Noralf Trønnes

[permalink] [raw]
Subject: Re: [PATCH v2 29/41] drm/vc4: vec: Switch for common modes



Den 29.08.2022 15.11, skrev Maxime Ripard:
> Now that the core has a definition for the 525 and 625 lines analog TV
>
> modes, let's switch to it for vc4.
>
>
>
> Signed-off-by: Maxime Ripard <[email protected]>
>
>
>
> diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c
>
> index d1d40b69279e..63e4e617e321 100644
>
> --- a/drivers/gpu/drm/vc4/vc4_vec.c
>
> +++ b/drivers/gpu/drm/vc4/vc4_vec.c
>
> @@ -224,38 +224,24 @@ static const struct debugfs_reg32 vec_regs[] = {
>
> VC4_REG32(VEC_DAC_MISC),
>
> };
>
>
>
> -static const struct drm_display_mode ntsc_mode = {
>
> - DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 13500,
>
> - 720, 720 + 14, 720 + 14 + 64, 720 + 14 + 64 + 60, 0,
>
> - 480, 480 + 7, 480 + 7 + 6, 525, 0,
>
> - DRM_MODE_FLAG_INTERLACE)
>
> -};
>
> -
>
> -static const struct drm_display_mode pal_mode = {
>
> - DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 13500,
>
> - 720, 720 + 20, 720 + 20 + 64, 720 + 20 + 64 + 60, 0,
>
> - 576, 576 + 4, 576 + 4 + 6, 625, 0,
>
> - DRM_MODE_FLAG_INTERLACE)
>
> -};
>
> -
>
> static const struct vc4_vec_tv_mode vc4_vec_tv_modes[] = {
>
> [VC4_VEC_TV_MODE_NTSC] = {
>
> - .mode = &ntsc_mode,
>
> + .mode = &drm_mode_480i,
>

I can't find drm_mode_480i anywhere, maybe the compiler doesn't complain
since you remove the reference in a later patch?

Noralf.

> .config0 = VEC_CONFIG0_NTSC_STD | VEC_CONFIG0_PDEN,
>
> .config1 = VEC_CONFIG1_C_CVBS_CVBS,
>
> },
>
> [VC4_VEC_TV_MODE_NTSC_J] = {
>
> - .mode = &ntsc_mode,
>
> + .mode = &drm_mode_480i,
>
> .config0 = VEC_CONFIG0_NTSC_STD,
>
> .config1 = VEC_CONFIG1_C_CVBS_CVBS,
>
> },
>
> [VC4_VEC_TV_MODE_PAL] = {
>
> - .mode = &pal_mode,
>
> + .mode = &drm_mode_576i,
>
> .config0 = VEC_CONFIG0_PAL_BDGHI_STD,
>
> .config1 = VEC_CONFIG1_C_CVBS_CVBS,
>
> },
>
> [VC4_VEC_TV_MODE_PAL_M] = {
>
> - .mode = &pal_mode,
>
> + .mode = &drm_mode_576i,
>
> .config0 = VEC_CONFIG0_PAL_BDGHI_STD,
>
> .config1 = VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ,
>
> .custom_freq = 0x223b61d1,
>
>
>

2022-08-30 19:39:56

by Noralf Trønnes

[permalink] [raw]
Subject: Re: [PATCH v2 31/41] drm/vc4: vec: Use TV Reset implementation



Den 29.08.2022 15.11, skrev Maxime Ripard:
> The analog TV properties created by the drm_mode_create_tv_properties() are
>
> not properly initialised at reset. Let's switch our implementation to call
>
> drm_atomic_helper_connector_tv_reset().
>
>
>
> Signed-off-by: Maxime Ripard <[email protected]>
>

Reviewed-by: Noralf Trønnes <[email protected]>

2022-08-31 19:19:31

by Noralf Trønnes

[permalink] [raw]
Subject: Re: [PATCH v2 18/41] drm/connector: Add a function to lookup a TV mode by its name



Den 29.08.2022 15.11, skrev Maxime Ripard:
> As part of the command line parsing rework coming in the next patches,
>
> we'll need to lookup drm_connector_tv_mode values by their name, already
>
> defined in drm_tv_mode_enum_list.
>
>
>
> In order to avoid any code duplication, let's do a function that will
>
> perform a lookup of a TV mode name and return its value.
>
>
>
> Signed-off-by: Maxime Ripard <[email protected]>
>
>
>
> diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
>
> index b1fcacd150e8..0fe01a1c20ad 100644
>
> --- a/drivers/gpu/drm/drm_connector.c
>
> +++ b/drivers/gpu/drm/drm_connector.c
>
> @@ -1003,6 +1003,30 @@ static const struct drm_prop_enum_list drm_tv_mode_enum_list[] = {
>
> };
>
> DRM_ENUM_NAME_FN(drm_get_tv_mode_name, drm_tv_mode_enum_list)
>
>
>
> +/**
>
> + * drm_get_tv_mode_from_name - Translates a TV mode name into its enum value
>
> + * @name: TV Mode name we want to convert
>
> + * @len: Length of @name
>
> + *
>
> + * Translates @name into an enum drm_connector_tv_mode.
>
> + *
>
> + * Returns: the enum value on success, a negative errno otherwise.
>
> + */
>
> +int drm_get_tv_mode_from_name(const char *name, size_t len)
>
> +{
>
> + unsigned int i;
>
> +
>
> + for (i = 0; i < ARRAY_SIZE(drm_tv_mode_enum_list); i++) {
>
> + const struct drm_prop_enum_list *item = &drm_tv_mode_enum_list[i];
>
> +
>
> + if (strlen(item->name) == len && !strncmp(item->name, name, len))
>
> + return item->type;
>
> + }
>
> +
>
> + return -EINVAL;
>
> +}
>
> +EXPORT_SYMBOL(drm_get_tv_mode_from_name)

Missing semicolon.

Noralf.

>
> +
>
> static const struct drm_prop_enum_list drm_tv_select_enum_list[] = {
>
> { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */
>
> { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */
>
> diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
>
> index bb39d2bb806e..49d261977d4e 100644
>
> --- a/include/drm/drm_connector.h
>
> +++ b/include/drm/drm_connector.h
>
> @@ -1943,6 +1943,8 @@ const char *drm_get_dp_subconnector_name(int val);
>
> const char *drm_get_content_protection_name(int val);
>
> const char *drm_get_hdcp_content_type_name(int val);
>
>
>
> +int drm_get_tv_mode_from_name(const char *name, size_t len);
>
> +
>
> int drm_mode_create_dvi_i_properties(struct drm_device *dev);
>
> void drm_connector_attach_dp_subconnector_property(struct drm_connector *connector);
>
>
>
>
>

2022-09-01 20:25:55

by Noralf Trønnes

[permalink] [raw]
Subject: Re: [PATCH v2 00/41] drm: Analog TV Improvements



Den 29.08.2022 15.11, skrev Maxime Ripard:
> Hi,
>
>
>
> Here's a series aiming at improving the command line named modes support,
>
> and more importantly how we deal with all the analog TV variants.
>
>
>
> The named modes support were initially introduced to allow to specify the
>
> analog TV mode to be used.
>
>
>
> However, this was causing multiple issues:
>
>
>
> * The mode name parsed on the command line was passed directly to the
>
> driver, which had to figure out which mode it was suppose to match;
>
>
>
> * Figuring that out wasn't really easy, since the video= argument or what
>
> the userspace might not even have a name in the first place, but
>
> instead could have passed a mode with the same timings;
>
>
>
> * The fallback to matching on the timings was mostly working as long as
>
> we were supporting one 525 lines (most likely NSTC) and one 625 lines
>
> (PAL), but couldn't differentiate between two modes with the same
>
> timings (NTSC vs PAL-M vs NSTC-J for example);
>
>
>
> * There was also some overlap with the tv mode property registered by
>
> drm_mode_create_tv_properties(), but named modes weren't interacting
>
> with that property at all.
>
>
>
> * Even though that property was generic, its possible values were
>
> specific to each drivers, which made some generic support difficult.
>
>
>
> Thus, I chose to tackle in multiple steps:
>
>
>
> * A new TV norm property was introduced, with generic values, each driver
>
> reporting through a bitmask what standard it supports to the userspace;
>
>
>
> * This option was added to the command line parsing code to be able to
>
> specify it on the kernel command line, and new atomic_check and reset
>
> helpers were created to integrate properly into atomic KMS;
>
>
>
> * The named mode parsing code is now creating a proper display mode for
>
> the given named mode, and the TV standard will thus be part of the
>
> connector state;
>
>
>
> * Two drivers were converted and tested for now (vc4 and sun4i), with
>
> some backward compatibility code to translate the old TV mode to the
>
> new TV mode;
>
>
>
> Unit tests were created along the way.
>
>
>
> One can switch from NTSC to PAL now using (on vc4)
>
>
>
> modetest -M vc4 -s 53:720x480i -w 53:'tv norm':0
>
>
>
> modetest -M vc4 -s 53:720x480i -w 53:'tv norm':4
>

The property name has changed, this gives me PAL:

$ modetest -M vc4 -s 45:720x576i -w 45:'TV mode':4


I have finally found a workaround for my kernel hangs.

Dom had a look at my kernel and found that the VideoCore was fine, and
he said this:

> That suggests cause of lockup was on arm side rather than VC side.
>
> But it's hard to diagnose further. Once you've had a peripheral not
> respond, the AXI bus locks up and no further operations are possible.
> Usual causes of this are required clocks being stopped or domains
> disabled and then trying to access the hardware.
>

So when I got this on my 64-bit build:

[ 166.702171] SError Interrupt on CPU1, code 0x00000000bf000002 -- SError
[ 166.702187] CPU: 1 PID: 8 Comm: kworker/u8:0 Tainted: G W
5.19.0-rc6-00096-gba7973977976-dirty #1
[ 166.702200] Hardware name: Raspberry Pi 4 Model B Rev 1.1 (DT)
[ 166.702206] Workqueue: events_freezable_power_ thermal_zone_device_check
[ 166.702231] pstate: 200000c5 (nzCv daIF -PAN -UAO -TCO -DIT -SSBS
BTYPE=--)
[ 166.702242] pc : regmap_mmio_read32le+0x10/0x28
[ 166.702261] lr : regmap_mmio_read+0x44/0x70
...
[ 166.702606] bcm2711_get_temp+0x58/0xb0 [bcm2711_thermal]

I wondered if that reg read was stalled due to a clock being stopped.

Lo and behold, disabling runtime pm and keeping the vec clock running
all the time fixed it[1].

I don't know what the problem is, but at least I can now test this patchset.

[1] https://gist.github.com/notro/23b984e7fa05cfbda2db50a421cac065

Noralf.

2022-09-01 23:37:44

by Mateusz Kwiatkowski

[permalink] [raw]
Subject: Re: [PATCH v2 19/41] drm/modes: Introduce the tv_mode property as a command-line option

Hi Maxime,

> @@ -2212,20 +2239,22 @@ struct drm_named_mode {
>      unsigned int xres;
>      unsigned int yres;
>      unsigned int flags;
> +    unsigned int tv_mode;
>  };

Are _all_ named modes supposed to be about analog TV?

If so, then probably this structure should be renamed drm_named_analog_tv_mode
or something.

If not, then including tv_mode in all of them sounds almost dangrous. 0 is a
valid value for enum drm_connector_tv_mode, corresponding to
DRM_MODE_TV_MODE_NTSC_443. This is a very weird default (maybe it shouldn't be
the one that has a numeric value of 0?) and if there ever is a named mode that
is not related to analog TV, it looks that it will refer to NTSC-443.

Not sure where could that actually propagate, and maybe what I'm saying can't
happen, but I'm imagining weird scenarios where a GPU that has both a
VGA/HDMI/whatever output, and a composite output, switches to NTSC-443 on the
composite output by default because a named mode for the modern output is
selected.

Maybe something like DRM_MODE_TV_MODE_NONE = 0 would make sense?

Maybe not. This is not an actual suggestion, just "thinking out loud".

Best regards,
Mateusz Kwiatkowski

2022-09-02 11:35:15

by Noralf Trønnes

[permalink] [raw]
Subject: Re: [PATCH v2 00/41] drm: Analog TV Improvements



Den 01.09.2022 21.35, skrev Noralf Trønnes:
>
>
> I have finally found a workaround for my kernel hangs.
>
> Dom had a look at my kernel and found that the VideoCore was fine, and
> he said this:
>
>> That suggests cause of lockup was on arm side rather than VC side.
>>
>> But it's hard to diagnose further. Once you've had a peripheral not
>> respond, the AXI bus locks up and no further operations are possible.
>> Usual causes of this are required clocks being stopped or domains
>> disabled and then trying to access the hardware.
>>
>
> So when I got this on my 64-bit build:
>
> [ 166.702171] SError Interrupt on CPU1, code 0x00000000bf000002 -- SError
> [ 166.702187] CPU: 1 PID: 8 Comm: kworker/u8:0 Tainted: G W
> 5.19.0-rc6-00096-gba7973977976-dirty #1
> [ 166.702200] Hardware name: Raspberry Pi 4 Model B Rev 1.1 (DT)
> [ 166.702206] Workqueue: events_freezable_power_ thermal_zone_device_check
> [ 166.702231] pstate: 200000c5 (nzCv daIF -PAN -UAO -TCO -DIT -SSBS
> BTYPE=--)
> [ 166.702242] pc : regmap_mmio_read32le+0x10/0x28
> [ 166.702261] lr : regmap_mmio_read+0x44/0x70
> ...
> [ 166.702606] bcm2711_get_temp+0x58/0xb0 [bcm2711_thermal]
>
> I wondered if that reg read was stalled due to a clock being stopped.
>
> Lo and behold, disabling runtime pm and keeping the vec clock running
> all the time fixed it[1].
>
> I don't know what the problem is, but at least I can now test this patchset.
>
> [1] https://gist.github.com/notro/23b984e7fa05cfbda2db50a421cac065
>

It turns out I didn't have to disable runtime pm:
https://gist.github.com/notro/0adcfcb12460b54e54458afe11dc8ea2

Noralf.

2022-09-05 14:36:07

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v2 19/41] drm/modes: Introduce the tv_mode property as a command-line option

On Fri, Sep 02, 2022 at 12:46:29AM +0200, Mateusz Kwiatkowski wrote:
> > @@ -2212,20 +2239,22 @@ struct drm_named_mode {
> > ??? ?unsigned int xres;
> > ??? ?unsigned int yres;
> > ??? ?unsigned int flags;
> > +?? ?unsigned int tv_mode;
> >? };
>
> Are _all_ named modes supposed to be about analog TV?
>
> If so, then probably this structure should be renamed drm_named_analog_tv_mode
> or something.

I don't think they need to, but it's the only use case we've had so far.
We could also imagine using UHD for 3840x2160 for example, so I wouldn't
say it's limited to analog tv.

> If not, then including tv_mode in all of them sounds almost dangrous. 0 is a
> valid value for enum drm_connector_tv_mode, corresponding to
> DRM_MODE_TV_MODE_NTSC_443. This is a very weird default (maybe it shouldn't be
> the one that has a numeric value of 0?) and if there ever is a named mode that
> is not related to analog TV, it looks that it will refer to NTSC-443.
>
> Not sure where could that actually propagate, and maybe what I'm saying can't
> happen, but I'm imagining weird scenarios where a GPU that has both a
> VGA/HDMI/whatever output, and a composite output, switches to NTSC-443 on the
> composite output by default because a named mode for the modern output is
> selected.

So, named modes are per-connector so the fact that there's another
output doesn't really matter. Then, the answer is quite simple actually,
the HDMI driver wouldn't register and use the TV mode property at all,
so it would completely ignore it, no matter what value it has.

So it's not really a concern.

> Maybe something like DRM_MODE_TV_MODE_NONE = 0 would make sense?

But I guess we can add it still.

Maxime


Attachments:
(No filename) (1.70 kB)
signature.asc (235.00 B)
Download all attachments

2022-09-05 15:16:19

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v2 00/41] drm: Analog TV Improvements

On Fri, Sep 02, 2022 at 01:28:16PM +0200, Noralf Tr?nnes wrote:
>
>
> Den 01.09.2022 21.35, skrev Noralf Tr?nnes:
> >
> >
> > I have finally found a workaround for my kernel hangs.
> >
> > Dom had a look at my kernel and found that the VideoCore was fine, and
> > he said this:
> >
> >> That suggests cause of lockup was on arm side rather than VC side.
> >>
> >> But it's hard to diagnose further. Once you've had a peripheral not
> >> respond, the AXI bus locks up and no further operations are possible.
> >> Usual causes of this are required clocks being stopped or domains
> >> disabled and then trying to access the hardware.
> >>
> >
> > So when I got this on my 64-bit build:
> >
> > [ 166.702171] SError Interrupt on CPU1, code 0x00000000bf000002 -- SError
> > [ 166.702187] CPU: 1 PID: 8 Comm: kworker/u8:0 Tainted: G W
> > 5.19.0-rc6-00096-gba7973977976-dirty #1
> > [ 166.702200] Hardware name: Raspberry Pi 4 Model B Rev 1.1 (DT)
> > [ 166.702206] Workqueue: events_freezable_power_ thermal_zone_device_check
> > [ 166.702231] pstate: 200000c5 (nzCv daIF -PAN -UAO -TCO -DIT -SSBS
> > BTYPE=--)
> > [ 166.702242] pc : regmap_mmio_read32le+0x10/0x28
> > [ 166.702261] lr : regmap_mmio_read+0x44/0x70
> > ...
> > [ 166.702606] bcm2711_get_temp+0x58/0xb0 [bcm2711_thermal]
> >
> > I wondered if that reg read was stalled due to a clock being stopped.
> >
> > Lo and behold, disabling runtime pm and keeping the vec clock running
> > all the time fixed it[1].
> >
> > I don't know what the problem is, but at least I can now test this patchset.
> >
> > [1] https://gist.github.com/notro/23b984e7fa05cfbda2db50a421cac065
> >
>
> It turns out I didn't have to disable runtime pm:
> https://gist.github.com/notro/0adcfcb12460b54e54458afe11dc8ea2

If the bcm2711_thermal IP needs that clock to be enabled, it should grab
a reference itself, but it looks like even the device tree binding
doesn't ask for one.

Maxime


Attachments:
(No filename) (1.96 kB)
signature.asc (235.00 B)
Download all attachments

2022-09-05 16:21:45

by Noralf Trønnes

[permalink] [raw]
Subject: Re: [PATCH v2 00/41] drm: Analog TV Improvements



Den 05.09.2022 16.57, skrev Maxime Ripard:
> On Fri, Sep 02, 2022 at 01:28:16PM +0200, Noralf Trønnes wrote:
>>
>>
>> Den 01.09.2022 21.35, skrev Noralf Trønnes:
>>>
>>>
>>> I have finally found a workaround for my kernel hangs.
>>>
>>> Dom had a look at my kernel and found that the VideoCore was fine, and
>>> he said this:
>>>
>>>> That suggests cause of lockup was on arm side rather than VC side.
>>>>
>>>> But it's hard to diagnose further. Once you've had a peripheral not
>>>> respond, the AXI bus locks up and no further operations are possible.
>>>> Usual causes of this are required clocks being stopped or domains
>>>> disabled and then trying to access the hardware.
>>>>
>>>
>>> So when I got this on my 64-bit build:
>>>
>>> [ 166.702171] SError Interrupt on CPU1, code 0x00000000bf000002 -- SError
>>> [ 166.702187] CPU: 1 PID: 8 Comm: kworker/u8:0 Tainted: G W
>>> 5.19.0-rc6-00096-gba7973977976-dirty #1
>>> [ 166.702200] Hardware name: Raspberry Pi 4 Model B Rev 1.1 (DT)
>>> [ 166.702206] Workqueue: events_freezable_power_ thermal_zone_device_check
>>> [ 166.702231] pstate: 200000c5 (nzCv daIF -PAN -UAO -TCO -DIT -SSBS
>>> BTYPE=--)
>>> [ 166.702242] pc : regmap_mmio_read32le+0x10/0x28
>>> [ 166.702261] lr : regmap_mmio_read+0x44/0x70
>>> ...
>>> [ 166.702606] bcm2711_get_temp+0x58/0xb0 [bcm2711_thermal]
>>>
>>> I wondered if that reg read was stalled due to a clock being stopped.
>>>
>>> Lo and behold, disabling runtime pm and keeping the vec clock running
>>> all the time fixed it[1].
>>>
>>> I don't know what the problem is, but at least I can now test this patchset.
>>>
>>> [1] https://gist.github.com/notro/23b984e7fa05cfbda2db50a421cac065
>>>
>>
>> It turns out I didn't have to disable runtime pm:
>> https://gist.github.com/notro/0adcfcb12460b54e54458afe11dc8ea2
>
> If the bcm2711_thermal IP needs that clock to be enabled, it should grab
> a reference itself, but it looks like even the device tree binding
> doesn't ask for one.
>

The first thing I tried was to unload the bcm2711_thermal module before
running modeset and it still hung, so I don't think that's the problem.

Noralf.

2022-09-07 08:49:37

by Maxime Ripard

[permalink] [raw]
Subject: Re: (subset) [PATCH v2 27/41] drm/vc4: vec: Remove redundant atomic_mode_set

On Mon, 29 Aug 2022 15:11:41 +0200, Maxime Ripard wrote:
> From: Mateusz Kwiatkowski <[email protected]>
>
> Let's remove the superfluous tv_mode field, which was redundant with the
> mode field in struct drm_tv_connector_state.
>
>

Applied to drm/drm-misc (drm-misc-next).

Thanks!
Maxime

2022-09-07 08:49:53

by Maxime Ripard

[permalink] [raw]
Subject: Re: (subset) [PATCH v2 28/41] drm/vc4: vec: Fix timings for VEC modes

On Mon, 29 Aug 2022 15:11:42 +0200, Maxime Ripard wrote:
> From: Mateusz Kwiatkowski <[email protected]>
>
> This commit fixes vertical timings of the VEC (composite output) modes
> to accurately represent the 525-line ("NTSC") and 625-line ("PAL") ITU-R
> standards.
>
> Previous timings were actually defined as 502 and 601 lines, resulting
> in non-standard 62.69 Hz and 52 Hz signals being generated,
> respectively.
>
> [...]

Applied to drm/drm-misc (drm-misc-next).

Thanks!
Maxime

2022-09-07 08:50:12

by Maxime Ripard

[permalink] [raw]
Subject: Re: (subset) [PATCH v2 34/41] drm/sun4i: tv: Remove unused mode_valid

On Mon, 29 Aug 2022 15:11:48 +0200, Maxime Ripard wrote:
> The mode_valid implementation is pretty much a nop, let's remove it.
>
>

Applied to drm/drm-misc (drm-misc-next).

Thanks!
Maxime

2022-09-07 09:43:19

by Maxime Ripard

[permalink] [raw]
Subject: Re: (subset) [PATCH v2 26/41] drm/vc4: vec: Refactor VEC TV mode setting

On Mon, 29 Aug 2022 15:11:40 +0200, Maxime Ripard wrote:
> From: Mateusz Kwiatkowski <[email protected]>
>
> Change the mode_set function pointer logic to declarative config0,
> config1 and custom_freq fields, to make TV mode setting logic more
> concise and uniform.
>
>
> [...]

Applied to drm/drm-misc (drm-misc-next).

Thanks!
Maxime

2022-09-07 10:12:33

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v2 00/41] drm: Analog TV Improvements

On Mon, Sep 05, 2022 at 05:17:18PM +0200, Noralf Tr?nnes wrote:
> Den 05.09.2022 16.57, skrev Maxime Ripard:
> > On Fri, Sep 02, 2022 at 01:28:16PM +0200, Noralf Tr?nnes wrote:
> >>
> >>
> >> Den 01.09.2022 21.35, skrev Noralf Tr?nnes:
> >>>
> >>>
> >>> I have finally found a workaround for my kernel hangs.
> >>>
> >>> Dom had a look at my kernel and found that the VideoCore was fine, and
> >>> he said this:
> >>>
> >>>> That suggests cause of lockup was on arm side rather than VC side.
> >>>>
> >>>> But it's hard to diagnose further. Once you've had a peripheral not
> >>>> respond, the AXI bus locks up and no further operations are possible.
> >>>> Usual causes of this are required clocks being stopped or domains
> >>>> disabled and then trying to access the hardware.
> >>>>
> >>>
> >>> So when I got this on my 64-bit build:
> >>>
> >>> [ 166.702171] SError Interrupt on CPU1, code 0x00000000bf000002 -- SError
> >>> [ 166.702187] CPU: 1 PID: 8 Comm: kworker/u8:0 Tainted: G W
> >>> 5.19.0-rc6-00096-gba7973977976-dirty #1
> >>> [ 166.702200] Hardware name: Raspberry Pi 4 Model B Rev 1.1 (DT)
> >>> [ 166.702206] Workqueue: events_freezable_power_ thermal_zone_device_check
> >>> [ 166.702231] pstate: 200000c5 (nzCv daIF -PAN -UAO -TCO -DIT -SSBS
> >>> BTYPE=--)
> >>> [ 166.702242] pc : regmap_mmio_read32le+0x10/0x28
> >>> [ 166.702261] lr : regmap_mmio_read+0x44/0x70
> >>> ...
> >>> [ 166.702606] bcm2711_get_temp+0x58/0xb0 [bcm2711_thermal]
> >>>
> >>> I wondered if that reg read was stalled due to a clock being stopped.
> >>>
> >>> Lo and behold, disabling runtime pm and keeping the vec clock running
> >>> all the time fixed it[1].
> >>>
> >>> I don't know what the problem is, but at least I can now test this patchset.
> >>>
> >>> [1] https://gist.github.com/notro/23b984e7fa05cfbda2db50a421cac065
> >>>
> >>
> >> It turns out I didn't have to disable runtime pm:
> >> https://gist.github.com/notro/0adcfcb12460b54e54458afe11dc8ea2
> >
> > If the bcm2711_thermal IP needs that clock to be enabled, it should grab
> > a reference itself, but it looks like even the device tree binding
> > doesn't ask for one.
> >
>
> The first thing I tried was to unload the bcm2711_thermal module before
> running modeset and it still hung, so I don't think that's the problem.

Ack. Just to confirm, is this happening on mainline or on the downstream tree?

Maxime


Attachments:
(No filename) (2.40 kB)
signature.asc (235.00 B)
Download all attachments

2022-09-07 11:02:20

by Noralf Trønnes

[permalink] [raw]
Subject: Re: [PATCH v2 00/41] drm: Analog TV Improvements



Den 07.09.2022 11.58, skrev Maxime Ripard:
> On Mon, Sep 05, 2022 at 05:17:18PM +0200, Noralf Trønnes wrote:
>> Den 05.09.2022 16.57, skrev Maxime Ripard:
>>> On Fri, Sep 02, 2022 at 01:28:16PM +0200, Noralf Trønnes wrote:
>>>>
>>>>
>>>> Den 01.09.2022 21.35, skrev Noralf Trønnes:
>>>>>
>>>>>
>>>>> I have finally found a workaround for my kernel hangs.
>>>>>
>>>>> Dom had a look at my kernel and found that the VideoCore was fine, and
>>>>> he said this:
>>>>>
>>>>>> That suggests cause of lockup was on arm side rather than VC side.
>>>>>>
>>>>>> But it's hard to diagnose further. Once you've had a peripheral not
>>>>>> respond, the AXI bus locks up and no further operations are possible.
>>>>>> Usual causes of this are required clocks being stopped or domains
>>>>>> disabled and then trying to access the hardware.
>>>>>>
>>>>>
>>>>> So when I got this on my 64-bit build:
>>>>>
>>>>> [ 166.702171] SError Interrupt on CPU1, code 0x00000000bf000002 -- SError
>>>>> [ 166.702187] CPU: 1 PID: 8 Comm: kworker/u8:0 Tainted: G W
>>>>> 5.19.0-rc6-00096-gba7973977976-dirty #1
>>>>> [ 166.702200] Hardware name: Raspberry Pi 4 Model B Rev 1.1 (DT)
>>>>> [ 166.702206] Workqueue: events_freezable_power_ thermal_zone_device_check
>>>>> [ 166.702231] pstate: 200000c5 (nzCv daIF -PAN -UAO -TCO -DIT -SSBS
>>>>> BTYPE=--)
>>>>> [ 166.702242] pc : regmap_mmio_read32le+0x10/0x28
>>>>> [ 166.702261] lr : regmap_mmio_read+0x44/0x70
>>>>> ...
>>>>> [ 166.702606] bcm2711_get_temp+0x58/0xb0 [bcm2711_thermal]
>>>>>
>>>>> I wondered if that reg read was stalled due to a clock being stopped.
>>>>>
>>>>> Lo and behold, disabling runtime pm and keeping the vec clock running
>>>>> all the time fixed it[1].
>>>>>
>>>>> I don't know what the problem is, but at least I can now test this patchset.
>>>>>
>>>>> [1] https://gist.github.com/notro/23b984e7fa05cfbda2db50a421cac065
>>>>>
>>>>
>>>> It turns out I didn't have to disable runtime pm:
>>>> https://gist.github.com/notro/0adcfcb12460b54e54458afe11dc8ea2
>>>
>>> If the bcm2711_thermal IP needs that clock to be enabled, it should grab
>>> a reference itself, but it looks like even the device tree binding
>>> doesn't ask for one.
>>>
>>
>> The first thing I tried was to unload the bcm2711_thermal module before
>> running modeset and it still hung, so I don't think that's the problem.
>
> Ack. Just to confirm, is this happening on mainline or on the downstream tree?
>

It's mainline.

Noralf.

2022-09-07 11:53:35

by Stefan Wahren

[permalink] [raw]
Subject: Re: [PATCH v2 00/41] drm: Analog TV Improvements

Hi Maxime,

Am 05.09.22 um 16:57 schrieb Maxime Ripard:
> On Fri, Sep 02, 2022 at 01:28:16PM +0200, Noralf Trønnes wrote:
>>
>> Den 01.09.2022 21.35, skrev Noralf Trønnes:
>>>
>>> I have finally found a workaround for my kernel hangs.
>>>
>>> Dom had a look at my kernel and found that the VideoCore was fine, and
>>> he said this:
>>>
>>>> That suggests cause of lockup was on arm side rather than VC side.
>>>>
>>>> But it's hard to diagnose further. Once you've had a peripheral not
>>>> respond, the AXI bus locks up and no further operations are possible.
>>>> Usual causes of this are required clocks being stopped or domains
>>>> disabled and then trying to access the hardware.
>>>>
>>> So when I got this on my 64-bit build:
>>>
>>> [ 166.702171] SError Interrupt on CPU1, code 0x00000000bf000002 -- SError
>>> [ 166.702187] CPU: 1 PID: 8 Comm: kworker/u8:0 Tainted: G W
>>> 5.19.0-rc6-00096-gba7973977976-dirty #1
>>> [ 166.702200] Hardware name: Raspberry Pi 4 Model B Rev 1.1 (DT)
>>> [ 166.702206] Workqueue: events_freezable_power_ thermal_zone_device_check
>>> [ 166.702231] pstate: 200000c5 (nzCv daIF -PAN -UAO -TCO -DIT -SSBS
>>> BTYPE=--)
>>> [ 166.702242] pc : regmap_mmio_read32le+0x10/0x28
>>> [ 166.702261] lr : regmap_mmio_read+0x44/0x70
>>> ...
>>> [ 166.702606] bcm2711_get_temp+0x58/0xb0 [bcm2711_thermal]
>>>
>>> I wondered if that reg read was stalled due to a clock being stopped.
>>>
>>> Lo and behold, disabling runtime pm and keeping the vec clock running
>>> all the time fixed it[1].
>>>
>>> I don't know what the problem is, but at least I can now test this patchset.
>>>
>>> [1] https://gist.github.com/notro/23b984e7fa05cfbda2db50a421cac065
>>>
>> It turns out I didn't have to disable runtime pm:
>> https://gist.github.com/notro/0adcfcb12460b54e54458afe11dc8ea2
> If the bcm2711_thermal IP needs that clock to be enabled, it should grab
> a reference itself, but it looks like even the device tree binding
> doesn't ask for one.
The missing clock in the device tree binding is expected, because
despite of the code there is not much information about the BCM2711
clock tree. But i'm skeptical that the AVS IP actually needs the VEC
clock, i think it's more likely that the VEC clock parent is changed and
that cause this issue. I could take care of the bcm2711 binding & driver
if i know which clock is really necessary.
>
> Maxime
>
> _______________________________________________
> linux-arm-kernel mailing list
> [email protected]
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

2022-09-07 16:48:15

by Noralf Trønnes

[permalink] [raw]
Subject: Re: [PATCH v2 00/41] drm: Analog TV Improvements



Den 07.09.2022 12.36, skrev Stefan Wahren:
> Hi Maxime,
>
> Am 05.09.22 um 16:57 schrieb Maxime Ripard:
>> On Fri, Sep 02, 2022 at 01:28:16PM +0200, Noralf Trønnes wrote:
>>>
>>> Den 01.09.2022 21.35, skrev Noralf Trønnes:
>>>>
>>>> I have finally found a workaround for my kernel hangs.
>>>>
>>>> Dom had a look at my kernel and found that the VideoCore was fine, and
>>>> he said this:
>>>>
>>>>> That suggests cause of lockup was on arm side rather than VC side.
>>>>>
>>>>> But it's hard to diagnose further. Once you've had a peripheral not
>>>>> respond, the AXI bus locks up and no further operations are possible.
>>>>> Usual causes of this are required clocks being stopped or domains
>>>>> disabled and then trying to access the hardware.
>>>>>
>>>> So when I got this on my 64-bit build:
>>>>
>>>> [  166.702171] SError Interrupt on CPU1, code 0x00000000bf000002 --
>>>> SError
>>>> [  166.702187] CPU: 1 PID: 8 Comm: kworker/u8:0 Tainted: G        W
>>>>      5.19.0-rc6-00096-gba7973977976-dirty #1
>>>> [  166.702200] Hardware name: Raspberry Pi 4 Model B Rev 1.1 (DT)
>>>> [  166.702206] Workqueue: events_freezable_power_
>>>> thermal_zone_device_check
>>>> [  166.702231] pstate: 200000c5 (nzCv daIF -PAN -UAO -TCO -DIT -SSBS
>>>> BTYPE=--)
>>>> [  166.702242] pc : regmap_mmio_read32le+0x10/0x28
>>>> [  166.702261] lr : regmap_mmio_read+0x44/0x70
>>>> ...
>>>> [  166.702606]  bcm2711_get_temp+0x58/0xb0 [bcm2711_thermal]
>>>>
>>>> I wondered if that reg read was stalled due to a clock being stopped.
>>>>
>>>> Lo and behold, disabling runtime pm and keeping the vec clock running
>>>> all the time fixed it[1].
>>>>
>>>> I don't know what the problem is, but at least I can now test this
>>>> patchset.
>>>>
>>>> [1] https://gist.github.com/notro/23b984e7fa05cfbda2db50a421cac065
>>>>
>>> It turns out I didn't have to disable runtime pm:
>>> https://gist.github.com/notro/0adcfcb12460b54e54458afe11dc8ea2
>> If the bcm2711_thermal IP needs that clock to be enabled, it should grab
>> a reference itself, but it looks like even the device tree binding
>> doesn't ask for one.
> The missing clock in the device tree binding is expected, because
> despite of the code there is not much information about the BCM2711
> clock tree. But i'm skeptical that the AVS IP actually needs the VEC
> clock, i think it's more likely that the VEC clock parent is changed and
> that cause this issue. I could take care of the bcm2711 binding & driver
> if i know which clock is really necessary.

Seems you're right, keeping the parent always enabled is enough:

clk_prepare_enable(clk_get_parent(vec->clock)); // pllc_per

I tried enabling just the grandparent clock as well, but that didn't help.

Without the clock hack it seems the hang occurs when switching between
NTSC and PAL, at most I've been able to do that 4-5 times before it hangs.

For a while it looked like fbdev/fbcon had a play in this, but then I
realised that it just gave me a NTSC mode to start from and to go back
to when qutting modetest.

Noralf.

2022-09-10 15:46:31

by Noralf Trønnes

[permalink] [raw]
Subject: Re: [PATCH v2 00/41] drm: Analog TV Improvements



Den 07.09.2022 18.44, skrev Noralf Trønnes:
>
>
> Den 07.09.2022 12.36, skrev Stefan Wahren:
>> Hi Maxime,
>>
>> Am 05.09.22 um 16:57 schrieb Maxime Ripard:
>>> On Fri, Sep 02, 2022 at 01:28:16PM +0200, Noralf Trønnes wrote:
>>>>
>>>> Den 01.09.2022 21.35, skrev Noralf Trønnes:
>>>>>
>>>>> I have finally found a workaround for my kernel hangs.
>>>>>
>>>>> Dom had a look at my kernel and found that the VideoCore was fine, and
>>>>> he said this:
>>>>>
>>>>>> That suggests cause of lockup was on arm side rather than VC side.
>>>>>>
>>>>>> But it's hard to diagnose further. Once you've had a peripheral not
>>>>>> respond, the AXI bus locks up and no further operations are possible.
>>>>>> Usual causes of this are required clocks being stopped or domains
>>>>>> disabled and then trying to access the hardware.
>>>>>>
>>>>> So when I got this on my 64-bit build:
>>>>>
>>>>> [  166.702171] SError Interrupt on CPU1, code 0x00000000bf000002 --
>>>>> SError
>>>>> [  166.702187] CPU: 1 PID: 8 Comm: kworker/u8:0 Tainted: G        W
>>>>>      5.19.0-rc6-00096-gba7973977976-dirty #1
>>>>> [  166.702200] Hardware name: Raspberry Pi 4 Model B Rev 1.1 (DT)
>>>>> [  166.702206] Workqueue: events_freezable_power_
>>>>> thermal_zone_device_check
>>>>> [  166.702231] pstate: 200000c5 (nzCv daIF -PAN -UAO -TCO -DIT -SSBS
>>>>> BTYPE=--)
>>>>> [  166.702242] pc : regmap_mmio_read32le+0x10/0x28
>>>>> [  166.702261] lr : regmap_mmio_read+0x44/0x70
>>>>> ...
>>>>> [  166.702606]  bcm2711_get_temp+0x58/0xb0 [bcm2711_thermal]
>>>>>
>>>>> I wondered if that reg read was stalled due to a clock being stopped.
>>>>>
>>>>> Lo and behold, disabling runtime pm and keeping the vec clock running
>>>>> all the time fixed it[1].
>>>>>
>>>>> I don't know what the problem is, but at least I can now test this
>>>>> patchset.
>>>>>
>>>>> [1] https://gist.github.com/notro/23b984e7fa05cfbda2db50a421cac065
>>>>>
>>>> It turns out I didn't have to disable runtime pm:
>>>> https://gist.github.com/notro/0adcfcb12460b54e54458afe11dc8ea2
>>> If the bcm2711_thermal IP needs that clock to be enabled, it should grab
>>> a reference itself, but it looks like even the device tree binding
>>> doesn't ask for one.
>> The missing clock in the device tree binding is expected, because
>> despite of the code there is not much information about the BCM2711
>> clock tree. But i'm skeptical that the AVS IP actually needs the VEC
>> clock, i think it's more likely that the VEC clock parent is changed and
>> that cause this issue. I could take care of the bcm2711 binding & driver
>> if i know which clock is really necessary.
>
> Seems you're right, keeping the parent always enabled is enough:
>
> clk_prepare_enable(clk_get_parent(vec->clock)); // pllc_per
>
> I tried enabling just the grandparent clock as well, but that didn't help.
>
> Without the clock hack it seems the hang occurs when switching between
> NTSC and PAL, at most I've been able to do that 4-5 times before it hangs.
>
> For a while it looked like fbdev/fbcon had a play in this, but then I
> realised that it just gave me a NTSC mode to start from and to go back
> to when qutting modetest.
>

I've looked some more into this problem and I see that downstream is
using a firmware clock for vec:

clk: Move vec clock to clk-raspberrypi
https://github.com/raspberrypi/linux/pull/4639

If I do the same my problem goes away.

It's interesting to note that on downstream 5.10.103-v7l+ #1530,
pllc_per is enabled even if tvout is not enabled:

$ sudo cat /sys/kernel/debug/clk/pllc_per/regdump
cm = 0x00000000
a2w = 0x00000004 (disable bit(8) is not set)

It's when mainline vc4_vec disables this vec parent clock that the crash
occurs.

Sidenote: Another downstream fw clock change with a vec reference[1]:


Another issue not related to the clock crash problem:

I assumed that unloading the vc4 module would release the clocks, but
this didn't happen.

When I looked at it I remembered that there's a catch in the DRM unplug
machinery when it comes to unloading a driver and the DRM disable hooks.

static void vc4_drm_unbind(struct device *dev)
{
struct drm_device *drm = dev_get_drvdata(dev);

drm_dev_unplug(drm);
drm_atomic_helper_shutdown(drm);
}

Here the drm_device is first marked as unplugged and then the pipeline
is disabled. Since vc4_vec_encoder_disable() is protected by
drm_dev_enter() the VEC is not disabled, clocks are not released and PM
is left on.

In the drivers that I have written where the hardware is not expected to
have gone away on device unbind (SPI), I've just left out the
drm_dev_enter() check in the disable hook.

Noralf.

[1] https://github.com/raspberrypi/linux/pull/4706

2022-09-21 14:26:56

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v2 00/41] drm: Analog TV Improvements

On Wed, Sep 07, 2022 at 06:44:53PM +0200, Noralf Tr?nnes wrote:
>
>
> Den 07.09.2022 12.36, skrev Stefan Wahren:
> > Hi Maxime,
> >
> > Am 05.09.22 um 16:57 schrieb Maxime Ripard:
> >> On Fri, Sep 02, 2022 at 01:28:16PM +0200, Noralf Tr?nnes wrote:
> >>>
> >>> Den 01.09.2022 21.35, skrev Noralf Tr?nnes:
> >>>>
> >>>> I have finally found a workaround for my kernel hangs.
> >>>>
> >>>> Dom had a look at my kernel and found that the VideoCore was fine, and
> >>>> he said this:
> >>>>
> >>>>> That suggests cause of lockup was on arm side rather than VC side.
> >>>>>
> >>>>> But it's hard to diagnose further. Once you've had a peripheral not
> >>>>> respond, the AXI bus locks up and no further operations are possible.
> >>>>> Usual causes of this are required clocks being stopped or domains
> >>>>> disabled and then trying to access the hardware.
> >>>>>
> >>>> So when I got this on my 64-bit build:
> >>>>
> >>>> [? 166.702171] SError Interrupt on CPU1, code 0x00000000bf000002 --
> >>>> SError
> >>>> [? 166.702187] CPU: 1 PID: 8 Comm: kworker/u8:0 Tainted: G??????? W
> >>>> ???? 5.19.0-rc6-00096-gba7973977976-dirty #1
> >>>> [? 166.702200] Hardware name: Raspberry Pi 4 Model B Rev 1.1 (DT)
> >>>> [? 166.702206] Workqueue: events_freezable_power_
> >>>> thermal_zone_device_check
> >>>> [? 166.702231] pstate: 200000c5 (nzCv daIF -PAN -UAO -TCO -DIT -SSBS
> >>>> BTYPE=--)
> >>>> [? 166.702242] pc : regmap_mmio_read32le+0x10/0x28
> >>>> [? 166.702261] lr : regmap_mmio_read+0x44/0x70
> >>>> ...
> >>>> [? 166.702606]? bcm2711_get_temp+0x58/0xb0 [bcm2711_thermal]
> >>>>
> >>>> I wondered if that reg read was stalled due to a clock being stopped.
> >>>>
> >>>> Lo and behold, disabling runtime pm and keeping the vec clock running
> >>>> all the time fixed it[1].
> >>>>
> >>>> I don't know what the problem is, but at least I can now test this
> >>>> patchset.
> >>>>
> >>>> [1] https://gist.github.com/notro/23b984e7fa05cfbda2db50a421cac065
> >>>>
> >>> It turns out I didn't have to disable runtime pm:
> >>> https://gist.github.com/notro/0adcfcb12460b54e54458afe11dc8ea2
> >> If the bcm2711_thermal IP needs that clock to be enabled, it should grab
> >> a reference itself, but it looks like even the device tree binding
> >> doesn't ask for one.
> > The missing clock in the device tree binding is expected, because
> > despite of the code there is not much information about the BCM2711
> > clock tree. But i'm skeptical that the AVS IP actually needs the VEC
> > clock, i think it's more likely that the VEC clock parent is changed and
> > that cause this issue. I could take care of the bcm2711 binding & driver
> > if i know which clock is really necessary.
>
> Seems you're right, keeping the parent always enabled is enough:
>
> clk_prepare_enable(clk_get_parent(vec->clock)); // pllc_per
>
> I tried enabling just the grandparent clock as well, but that didn't help.

Yeah, adding tracing to the clock framework shows that it indeed
disables PLLC_PER. So there's probably some other device that depends on
it but doesn't take a reference to it.

I had a look, but it's not really obvious what that might be.

This patch makes sure that the PLL*_PER are never disabled, could you
test it? It seems to work for me.


diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c
index 48a1eb9f2d55..3839261ee419 100644
--- a/drivers/clk/bcm/clk-bcm2835.c
+++ b/drivers/clk/bcm/clk-bcm2835.c
@@ -1675,7 +1675,7 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.load_mask = CM_PLLA_LOADPER,
.hold_mask = CM_PLLA_HOLDPER,
.fixed_divider = 1,
- .flags = CLK_SET_RATE_PARENT),
+ .flags = CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
[BCM2835_PLLA_DSI0] = REGISTER_PLL_DIV(
SOC_ALL,
.name = "plla_dsi0",
@@ -1784,7 +1784,7 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.load_mask = CM_PLLC_LOADPER,
.hold_mask = CM_PLLC_HOLDPER,
.fixed_divider = 1,
- .flags = CLK_SET_RATE_PARENT),
+ .flags = CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),

/*
* PLLD is the display PLL, used to drive DSI display panels.
@@ -1891,7 +1891,7 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.load_mask = CM_PLLH_LOADAUX,
.hold_mask = 0,
.fixed_divider = 1,
- .flags = CLK_SET_RATE_PARENT),
+ .flags = CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
[BCM2835_PLLH_PIX] = REGISTER_PLL_DIV(
SOC_BCM2835,
.name = "pllh_pix",


Maxime


Attachments:
(No filename) (4.43 kB)
signature.asc (235.00 B)
Download all attachments

2022-09-24 16:07:03

by Noralf Trønnes

[permalink] [raw]
Subject: Re: [PATCH v2 00/41] drm: Analog TV Improvements



Den 21.09.2022 16.03, skrev Maxime Ripard:
> On Wed, Sep 07, 2022 at 06:44:53PM +0200, Noralf Trønnes wrote:
>>
>>
>> Den 07.09.2022 12.36, skrev Stefan Wahren:
>>> Hi Maxime,
>>>
>>> Am 05.09.22 um 16:57 schrieb Maxime Ripard:
>>>> On Fri, Sep 02, 2022 at 01:28:16PM +0200, Noralf Trønnes wrote:
>>>>>
>>>>> Den 01.09.2022 21.35, skrev Noralf Trønnes:
>>>>>>
>>>>>> I have finally found a workaround for my kernel hangs.
>>>>>>
>>>>>> Dom had a look at my kernel and found that the VideoCore was fine, and
>>>>>> he said this:
>>>>>>
>>>>>>> That suggests cause of lockup was on arm side rather than VC side.
>>>>>>>
>>>>>>> But it's hard to diagnose further. Once you've had a peripheral not
>>>>>>> respond, the AXI bus locks up and no further operations are possible.
>>>>>>> Usual causes of this are required clocks being stopped or domains
>>>>>>> disabled and then trying to access the hardware.
>>>>>>>
>>>>>> So when I got this on my 64-bit build:
>>>>>>
>>>>>> [  166.702171] SError Interrupt on CPU1, code 0x00000000bf000002 --
>>>>>> SError
>>>>>> [  166.702187] CPU: 1 PID: 8 Comm: kworker/u8:0 Tainted: G        W
>>>>>>      5.19.0-rc6-00096-gba7973977976-dirty #1
>>>>>> [  166.702200] Hardware name: Raspberry Pi 4 Model B Rev 1.1 (DT)
>>>>>> [  166.702206] Workqueue: events_freezable_power_
>>>>>> thermal_zone_device_check
>>>>>> [  166.702231] pstate: 200000c5 (nzCv daIF -PAN -UAO -TCO -DIT -SSBS
>>>>>> BTYPE=--)
>>>>>> [  166.702242] pc : regmap_mmio_read32le+0x10/0x28
>>>>>> [  166.702261] lr : regmap_mmio_read+0x44/0x70
>>>>>> ...
>>>>>> [  166.702606]  bcm2711_get_temp+0x58/0xb0 [bcm2711_thermal]
>>>>>>
>>>>>> I wondered if that reg read was stalled due to a clock being stopped.
>>>>>>
>>>>>> Lo and behold, disabling runtime pm and keeping the vec clock running
>>>>>> all the time fixed it[1].
>>>>>>
>>>>>> I don't know what the problem is, but at least I can now test this
>>>>>> patchset.
>>>>>>
>>>>>> [1] https://gist.github.com/notro/23b984e7fa05cfbda2db50a421cac065
>>>>>>
>>>>> It turns out I didn't have to disable runtime pm:
>>>>> https://gist.github.com/notro/0adcfcb12460b54e54458afe11dc8ea2
>>>> If the bcm2711_thermal IP needs that clock to be enabled, it should grab
>>>> a reference itself, but it looks like even the device tree binding
>>>> doesn't ask for one.
>>> The missing clock in the device tree binding is expected, because
>>> despite of the code there is not much information about the BCM2711
>>> clock tree. But i'm skeptical that the AVS IP actually needs the VEC
>>> clock, i think it's more likely that the VEC clock parent is changed and
>>> that cause this issue. I could take care of the bcm2711 binding & driver
>>> if i know which clock is really necessary.
>>
>> Seems you're right, keeping the parent always enabled is enough:
>>
>> clk_prepare_enable(clk_get_parent(vec->clock)); // pllc_per
>>
>> I tried enabling just the grandparent clock as well, but that didn't help.
>
> Yeah, adding tracing to the clock framework shows that it indeed
> disables PLLC_PER. So there's probably some other device that depends on
> it but doesn't take a reference to it.
>
> I had a look, but it's not really obvious what that might be.
>
> This patch makes sure that the PLL*_PER are never disabled, could you
> test it? It seems to work for me.
>
>
> diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c
> index 48a1eb9f2d55..3839261ee419 100644
> --- a/drivers/clk/bcm/clk-bcm2835.c
> +++ b/drivers/clk/bcm/clk-bcm2835.c
> @@ -1675,7 +1675,7 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
> .load_mask = CM_PLLA_LOADPER,
> .hold_mask = CM_PLLA_HOLDPER,
> .fixed_divider = 1,
> - .flags = CLK_SET_RATE_PARENT),
> + .flags = CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
> [BCM2835_PLLA_DSI0] = REGISTER_PLL_DIV(
> SOC_ALL,
> .name = "plla_dsi0",
> @@ -1784,7 +1784,7 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
> .load_mask = CM_PLLC_LOADPER,
> .hold_mask = CM_PLLC_HOLDPER,
> .fixed_divider = 1,
> - .flags = CLK_SET_RATE_PARENT),
> + .flags = CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
>

Yes, this worked, but it's enough to mark pllc_per as critical.

Noralf.

> /*
> * PLLD is the display PLL, used to drive DSI display panels.
> @@ -1891,7 +1891,7 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
> .load_mask = CM_PLLH_LOADAUX,
> .hold_mask = 0,
> .fixed_divider = 1,
> - .flags = CLK_SET_RATE_PARENT),
> + .flags = CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
> [BCM2835_PLLH_PIX] = REGISTER_PLL_DIV(
> SOC_BCM2835,
> .name = "pllh_pix",
>
>
> Maxime