<div dir="ltr">They are important for retrogaming and connecting TV out to CRT TV or using emulator.<div><br></div><div>I have PS1 that is using PAL-60 for example.</div><div><br></div><div>Can you add 240p and 288p non-interlaced modes for NTSC and PAL, please?</div><div><br></div><div>Lukas</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Mon, Nov 7, 2022 at 3:19 PM Maxime Ripard <maxime@cerno.tech> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">From: Mateusz Kwiatkowski <<a href="mailto:kfyatek%2Bpublicgit@gmail.com" target="_blank">kfyatek+publicgit@gmail.com</a>><br>
<br>
Add support for the following composite output modes (all of them are<br>
somewhat more obscure than the previously defined ones):<br>
<br>
- NTSC_443 - NTSC-style signal with the chroma subcarrier shifted to<br>
4.43361875 MHz (the PAL subcarrier frequency). Never used for<br>
broadcasting, but sometimes used as a hack to play NTSC content in PAL<br>
regions (e.g. on VCRs).<br>
- PAL_N - PAL with alternative chroma subcarrier frequency,<br>
3.58205625 MHz. Used as a broadcast standard in Argentina, Paraguay<br>
and Uruguay to fit 576i50 with colour in 6 MHz channel raster.<br>
- PAL60 - 480i60 signal with PAL-style color at normal European PAL<br>
frequency. Another non-standard, non-broadcast mode, used in similar<br>
contexts as NTSC_443. Some displays support one but not the other.<br>
- SECAM - French frequency-modulated analog color standard; also have<br>
been broadcast in Eastern Europe and various parts of Africa and Asia.<br>
Uses the same 576i50 timings as PAL.<br>
<br>
Also added some comments explaining color subcarrier frequency<br>
registers.<br>
<br>
Acked-by: Noralf Trønnes <<a href="mailto:noralf@tronnes.org" target="_blank">noralf@tronnes.org</a>><br>
Signed-off-by: Mateusz Kwiatkowski <<a href="mailto:kfyatek%2Bpublicgit@gmail.com" target="_blank">kfyatek+publicgit@gmail.com</a>><br>
Signed-off-by: Maxime Ripard <maxime@cerno.tech><br>
<br>
---<br>
Changes in v6:<br>
- Support PAL60 again<br>
---<br>
drivers/gpu/drm/vc4/vc4_vec.c | 111 ++++++++++++++++++++++++++++++++++++++++--<br>
1 file changed, 107 insertions(+), 4 deletions(-)<br>
<br>
diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c<br>
index a828fc6fb776..d23dbad3cbf6 100644<br>
--- a/drivers/gpu/drm/vc4/vc4_vec.c<br>
+++ b/drivers/gpu/drm/vc4/vc4_vec.c<br>
@@ -46,6 +46,7 @@<br>
#define VEC_CONFIG0_YDEL(x) ((x) << 26)<br>
#define VEC_CONFIG0_CDEL_MASK GENMASK(25, 24)<br>
#define VEC_CONFIG0_CDEL(x) ((x) << 24)<br>
+#define VEC_CONFIG0_SECAM_STD BIT(21)<br>
#define VEC_CONFIG0_PBPR_FIL BIT(18)<br>
#define VEC_CONFIG0_CHROMA_GAIN_MASK GENMASK(17, 16)<br>
#define VEC_CONFIG0_CHROMA_GAIN_UNITY (0 << 16)<br>
@@ -76,6 +77,27 @@<br>
#define VEC_SOFT_RESET 0x10c<br>
#define VEC_CLMP0_START 0x144<br>
#define VEC_CLMP0_END 0x148<br>
+<br>
+/*<br>
+ * These set the color subcarrier frequency<br>
+ * if VEC_CONFIG1_CUSTOM_FREQ is enabled.<br>
+ *<br>
+ * VEC_FREQ1_0 contains the most significant 16-bit half-word,<br>
+ * VEC_FREQ3_2 contains the least significant 16-bit half-word.<br>
+ * 0x80000000 seems to be equivalent to the pixel clock<br>
+ * (which itself is the VEC clock divided by 8).<br>
+ *<br>
+ * Reference values (with the default pixel clock of 13.5 MHz):<br>
+ *<br>
+ * NTSC (3579545.[45] Hz) - 0x21F07C1F<br>
+ * PAL (4433618.75 Hz) - 0x2A098ACB<br>
+ * PAL-M (3575611.[888111] Hz) - 0x21E6EFE3<br>
+ * PAL-N (3582056.25 Hz) - 0x21F69446<br>
+ *<br>
+ * NOTE: For SECAM, it is used as the Dr center frequency,<br>
+ * regardless of whether VEC_CONFIG1_CUSTOM_FREQ is enabled or not;<br>
+ * that is specified as 4406250 Hz, which corresponds to 0x29C71C72.<br>
+ */<br>
#define VEC_FREQ3_2 0x180<br>
#define VEC_FREQ1_0 0x184<br>
<br>
@@ -118,6 +140,14 @@<br>
<br>
#define VEC_INTERRUPT_CONTROL 0x190<br>
#define VEC_INTERRUPT_STATUS 0x194<br>
+<br>
+/*<br>
+ * Db center frequency for SECAM; the clock for this is the same as for<br>
+ * VEC_FREQ3_2/VEC_FREQ1_0, which is used for Dr center frequency.<br>
+ *<br>
+ * This is specified as 4250000 Hz, which corresponds to 0x284BDA13.<br>
+ * That is also the default value, so no need to set it explicitly.<br>
+ */<br>
#define VEC_FCW_SECAM_B 0x198<br>
#define VEC_SECAM_GAIN_VAL 0x19c<br>
<br>
@@ -197,10 +227,15 @@ enum vc4_vec_tv_mode_id {<br>
VC4_VEC_TV_MODE_NTSC_J,<br>
VC4_VEC_TV_MODE_PAL,<br>
VC4_VEC_TV_MODE_PAL_M,<br>
+ VC4_VEC_TV_MODE_NTSC_443,<br>
+ VC4_VEC_TV_MODE_PAL_60,<br>
+ VC4_VEC_TV_MODE_PAL_N,<br>
+ VC4_VEC_TV_MODE_SECAM,<br>
};<br>
<br>
struct vc4_vec_tv_mode {<br>
unsigned int mode;<br>
+ u16 expected_htotal;<br>
u32 config0;<br>
u32 config1;<br>
u32 custom_freq;<br>
@@ -236,35 +271,68 @@ static const struct debugfs_reg32 vec_regs[] = {<br>
static const struct vc4_vec_tv_mode vc4_vec_tv_modes[] = {<br>
{<br>
.mode = DRM_MODE_TV_MODE_NTSC,<br>
+ .expected_htotal = 858,<br>
.config0 = VEC_CONFIG0_NTSC_STD | VEC_CONFIG0_PDEN,<br>
.config1 = VEC_CONFIG1_C_CVBS_CVBS,<br>
},<br>
+ {<br>
+ .mode = DRM_MODE_TV_MODE_NTSC_443,<br>
+ .expected_htotal = 858,<br>
+ .config0 = VEC_CONFIG0_NTSC_STD,<br>
+ .config1 = VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ,<br>
+ .custom_freq = 0x2a098acb,<br>
+ },<br>
{<br>
.mode = DRM_MODE_TV_MODE_NTSC_J,<br>
+ .expected_htotal = 858,<br>
.config0 = VEC_CONFIG0_NTSC_STD,<br>
.config1 = VEC_CONFIG1_C_CVBS_CVBS,<br>
},<br>
{<br>
.mode = DRM_MODE_TV_MODE_PAL,<br>
+ .expected_htotal = 864,<br>
.config0 = VEC_CONFIG0_PAL_BDGHI_STD,<br>
.config1 = VEC_CONFIG1_C_CVBS_CVBS,<br>
},<br>
+ {<br>
+ /* PAL-60 */<br>
+ .mode = DRM_MODE_TV_MODE_PAL,<br>
+ .expected_htotal = 858,<br>
+ .config0 = VEC_CONFIG0_PAL_M_STD,<br>
+ .config1 = VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ,<br>
+ .custom_freq = 0x2a098acb,<br>
+ },<br>
{<br>
.mode = DRM_MODE_TV_MODE_PAL_M,<br>
+ .expected_htotal = 858,<br>
.config0 = VEC_CONFIG0_PAL_M_STD,<br>
.config1 = VEC_CONFIG1_C_CVBS_CVBS,<br>
},<br>
+ {<br>
+ .mode = DRM_MODE_TV_MODE_PAL_N,<br>
+ .expected_htotal = 864,<br>
+ .config0 = VEC_CONFIG0_PAL_N_STD,<br>
+ .config1 = VEC_CONFIG1_C_CVBS_CVBS,<br>
+ },<br>
+ {<br>
+ .mode = DRM_MODE_TV_MODE_SECAM,<br>
+ .expected_htotal = 864,<br>
+ .config0 = VEC_CONFIG0_SECAM_STD,<br>
+ .config1 = VEC_CONFIG1_C_CVBS_CVBS,<br>
+ .custom_freq = 0x29c71c72,<br>
+ },<br>
};<br>
<br>
static inline const struct vc4_vec_tv_mode *<br>
-vc4_vec_tv_mode_lookup(unsigned int mode)<br>
+vc4_vec_tv_mode_lookup(unsigned int mode, u16 htotal)<br>
{<br>
unsigned int i;<br>
<br>
for (i = 0; i < ARRAY_SIZE(vc4_vec_tv_modes); i++) {<br>
const struct vc4_vec_tv_mode *tv_mode = &vc4_vec_tv_modes[i];<br>
<br>
- if (tv_mode->mode == mode)<br>
+ if (tv_mode->mode == mode &&<br>
+ tv_mode->expected_htotal == htotal)<br>
return tv_mode;<br>
}<br>
<br>
@@ -273,9 +341,13 @@ vc4_vec_tv_mode_lookup(unsigned int mode)<br>
<br>
static const struct drm_prop_enum_list legacy_tv_mode_names[] = {<br>
{ VC4_VEC_TV_MODE_NTSC, "NTSC", },<br>
+ { VC4_VEC_TV_MODE_NTSC_443, "NTSC-443", },<br>
{ VC4_VEC_TV_MODE_NTSC_J, "NTSC-J", },<br>
{ VC4_VEC_TV_MODE_PAL, "PAL", },<br>
+ { VC4_VEC_TV_MODE_PAL_60, "PAL-60", },<br>
{ VC4_VEC_TV_MODE_PAL_M, "PAL-M", },<br>
+ { VC4_VEC_TV_MODE_PAL_N, "PAL-N", },<br>
+ { VC4_VEC_TV_MODE_SECAM, "SECAM", },<br>
};<br>
<br>
static enum drm_connector_status<br>
@@ -306,11 +378,16 @@ vc4_vec_connector_set_property(struct drm_connector *connector,<br>
state->tv.mode = DRM_MODE_TV_MODE_NTSC;<br>
break;<br>
<br>
+ case VC4_VEC_TV_MODE_NTSC_443:<br>
+ state->tv.mode = DRM_MODE_TV_MODE_NTSC_443;<br>
+ break;<br>
+<br>
case VC4_VEC_TV_MODE_NTSC_J:<br>
state->tv.mode = DRM_MODE_TV_MODE_NTSC_J;<br>
break;<br>
<br>
case VC4_VEC_TV_MODE_PAL:<br>
+ case VC4_VEC_TV_MODE_PAL_60:<br>
state->tv.mode = DRM_MODE_TV_MODE_PAL;<br>
break;<br>
<br>
@@ -318,6 +395,14 @@ vc4_vec_connector_set_property(struct drm_connector *connector,<br>
state->tv.mode = DRM_MODE_TV_MODE_PAL_M;<br>
break;<br>
<br>
+ case VC4_VEC_TV_MODE_PAL_N:<br>
+ state->tv.mode = DRM_MODE_TV_MODE_PAL_N;<br>
+ break;<br>
+<br>
+ case VC4_VEC_TV_MODE_SECAM:<br>
+ state->tv.mode = DRM_MODE_TV_MODE_SECAM;<br>
+ break;<br>
+<br>
default:<br>
return -EINVAL;<br>
}<br>
@@ -341,6 +426,10 @@ vc4_vec_connector_get_property(struct drm_connector *connector,<br>
*val = VC4_VEC_TV_MODE_NTSC;<br>
break;<br>
<br>
+ case DRM_MODE_TV_MODE_NTSC_443:<br>
+ *val = VC4_VEC_TV_MODE_NTSC_443;<br>
+ break;<br>
+<br>
case DRM_MODE_TV_MODE_NTSC_J:<br>
*val = VC4_VEC_TV_MODE_NTSC_J;<br>
break;<br>
@@ -353,6 +442,14 @@ vc4_vec_connector_get_property(struct drm_connector *connector,<br>
*val = VC4_VEC_TV_MODE_PAL_M;<br>
break;<br>
<br>
+ case DRM_MODE_TV_MODE_PAL_N:<br>
+ *val = VC4_VEC_TV_MODE_PAL_N;<br>
+ break;<br>
+<br>
+ case DRM_MODE_TV_MODE_SECAM:<br>
+ *val = VC4_VEC_TV_MODE_SECAM;<br>
+ break;<br>
+<br>
default:<br>
return -EINVAL;<br>
}<br>
@@ -448,13 +545,16 @@ static void vc4_vec_encoder_enable(struct drm_encoder *encoder,<br>
struct drm_connector *connector = &vec->connector;<br>
struct drm_connector_state *conn_state =<br>
drm_atomic_get_new_connector_state(state, connector);<br>
+ struct drm_display_mode *adjusted_mode =<br>
+ &encoder->crtc->state->adjusted_mode;<br>
const struct vc4_vec_tv_mode *tv_mode;<br>
int idx, ret;<br>
<br>
if (!drm_dev_enter(drm, &idx))<br>
return;<br>
<br>
- tv_mode = vc4_vec_tv_mode_lookup(conn_state->tv.mode);<br>
+ tv_mode = vc4_vec_tv_mode_lookup(conn_state->tv.mode,<br>
+ adjusted_mode->htotal);<br>
if (!tv_mode)<br>
goto err_dev_exit;<br>
<br>
@@ -648,9 +748,12 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data)<br>
<br>
ret = drm_mode_create_tv_properties(drm,<br>
BIT(DRM_MODE_TV_MODE_NTSC) |<br>
+ BIT(DRM_MODE_TV_MODE_NTSC_443) |<br>
BIT(DRM_MODE_TV_MODE_NTSC_J) |<br>
BIT(DRM_MODE_TV_MODE_PAL) |<br>
- BIT(DRM_MODE_TV_MODE_PAL_M));<br>
+ BIT(DRM_MODE_TV_MODE_PAL_M) |<br>
+ BIT(DRM_MODE_TV_MODE_PAL_N) |<br>
+ BIT(DRM_MODE_TV_MODE_SECAM));<br>
if (ret)<br>
return ret;<br>
<br>
<br>
-- <br>
b4 0.11.0-dev-99e3a<br>
</blockquote></div>