Commit 91112a6f authored by Maxime Ripard's avatar Maxime Ripard

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

Now that the core can deal fine with analog TV modes, let's convert the vc4
VEC driver to leverage those new features.

We've added some backward compatibility to support the old TV mode property
and translate it into the new TV norm property. We're also making use of
the new analog TV atomic_check helper to make sure we trigger a modeset
whenever the TV mode is updated.
Acked-by: default avatarNoralf Trønnes <noralf@tronnes.org>
Tested-by: default avatarMateusz Kwiatkowski <kfyatek+publicgit@gmail.com>
Acked-in-principle-or-something-like-that-by: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
Link: https://lore.kernel.org/r/20220728-rpi-analog-tv-properties-v10-17-256dad125326@cerno.techSigned-off-by: default avatarMaxime Ripard <maxime@cerno.tech>
parent b5da40af
...@@ -172,6 +172,8 @@ struct vc4_vec { ...@@ -172,6 +172,8 @@ struct vc4_vec {
struct clk *clock; struct clk *clock;
struct drm_property *legacy_tv_mode_property;
struct debugfs_regset32 regset; struct debugfs_regset32 regset;
}; };
...@@ -184,6 +186,12 @@ encoder_to_vc4_vec(struct drm_encoder *encoder) ...@@ -184,6 +186,12 @@ encoder_to_vc4_vec(struct drm_encoder *encoder)
return container_of(encoder, struct vc4_vec, encoder.base); return container_of(encoder, struct vc4_vec, encoder.base);
} }
static inline struct vc4_vec *
connector_to_vc4_vec(struct drm_connector *connector)
{
return container_of(connector, struct vc4_vec, connector);
}
enum vc4_vec_tv_mode_id { enum vc4_vec_tv_mode_id {
VC4_VEC_TV_MODE_NTSC, VC4_VEC_TV_MODE_NTSC,
VC4_VEC_TV_MODE_NTSC_J, VC4_VEC_TV_MODE_NTSC_J,
...@@ -192,7 +200,7 @@ enum vc4_vec_tv_mode_id { ...@@ -192,7 +200,7 @@ enum vc4_vec_tv_mode_id {
}; };
struct vc4_vec_tv_mode { struct vc4_vec_tv_mode {
const struct drm_display_mode *mode; unsigned int mode;
u32 config0; u32 config0;
u32 config1; u32 config1;
u32 custom_freq; u32 custom_freq;
...@@ -225,43 +233,51 @@ static const struct debugfs_reg32 vec_regs[] = { ...@@ -225,43 +233,51 @@ static const struct debugfs_reg32 vec_regs[] = {
VC4_REG32(VEC_DAC_MISC), 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[] = { static const struct vc4_vec_tv_mode vc4_vec_tv_modes[] = {
[VC4_VEC_TV_MODE_NTSC] = { {
.mode = &ntsc_mode, .mode = DRM_MODE_TV_MODE_NTSC,
.config0 = VEC_CONFIG0_NTSC_STD | VEC_CONFIG0_PDEN, .config0 = VEC_CONFIG0_NTSC_STD | VEC_CONFIG0_PDEN,
.config1 = VEC_CONFIG1_C_CVBS_CVBS, .config1 = VEC_CONFIG1_C_CVBS_CVBS,
}, },
[VC4_VEC_TV_MODE_NTSC_J] = { {
.mode = &ntsc_mode, .mode = DRM_MODE_TV_MODE_NTSC_J,
.config0 = VEC_CONFIG0_NTSC_STD, .config0 = VEC_CONFIG0_NTSC_STD,
.config1 = VEC_CONFIG1_C_CVBS_CVBS, .config1 = VEC_CONFIG1_C_CVBS_CVBS,
}, },
[VC4_VEC_TV_MODE_PAL] = { {
.mode = &pal_mode, .mode = DRM_MODE_TV_MODE_PAL,
.config0 = VEC_CONFIG0_PAL_BDGHI_STD, .config0 = VEC_CONFIG0_PAL_BDGHI_STD,
.config1 = VEC_CONFIG1_C_CVBS_CVBS, .config1 = VEC_CONFIG1_C_CVBS_CVBS,
}, },
[VC4_VEC_TV_MODE_PAL_M] = { {
.mode = &ntsc_mode, .mode = DRM_MODE_TV_MODE_PAL_M,
.config0 = VEC_CONFIG0_PAL_M_STD, .config0 = VEC_CONFIG0_PAL_M_STD,
.config1 = VEC_CONFIG1_C_CVBS_CVBS, .config1 = VEC_CONFIG1_C_CVBS_CVBS,
}, },
}; };
static inline const struct vc4_vec_tv_mode *
vc4_vec_tv_mode_lookup(unsigned int mode)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(vc4_vec_tv_modes); i++) {
const struct vc4_vec_tv_mode *tv_mode = &vc4_vec_tv_modes[i];
if (tv_mode->mode == mode)
return tv_mode;
}
return NULL;
}
static const struct drm_prop_enum_list legacy_tv_mode_names[] = {
{ VC4_VEC_TV_MODE_NTSC, "NTSC", },
{ VC4_VEC_TV_MODE_NTSC_J, "NTSC-J", },
{ VC4_VEC_TV_MODE_PAL, "PAL", },
{ VC4_VEC_TV_MODE_PAL_M, "PAL-M", },
};
static enum drm_connector_status static enum drm_connector_status
vc4_vec_connector_detect(struct drm_connector *connector, bool force) vc4_vec_connector_detect(struct drm_connector *connector, bool force)
{ {
...@@ -274,21 +290,74 @@ static void vc4_vec_connector_reset(struct drm_connector *connector) ...@@ -274,21 +290,74 @@ static void vc4_vec_connector_reset(struct drm_connector *connector)
drm_atomic_helper_connector_tv_reset(connector); drm_atomic_helper_connector_tv_reset(connector);
} }
static int vc4_vec_connector_get_modes(struct drm_connector *connector) static int
vc4_vec_connector_set_property(struct drm_connector *connector,
struct drm_connector_state *state,
struct drm_property *property,
uint64_t val)
{ {
struct drm_connector_state *state = connector->state; struct vc4_vec *vec = connector_to_vc4_vec(connector);
struct drm_display_mode *mode;
mode = drm_mode_duplicate(connector->dev, if (property != vec->legacy_tv_mode_property)
vc4_vec_tv_modes[state->tv.legacy_mode].mode); return -EINVAL;
if (!mode) {
DRM_ERROR("Failed to create a new display mode\n"); switch (val) {
return -ENOMEM; case VC4_VEC_TV_MODE_NTSC:
state->tv.mode = DRM_MODE_TV_MODE_NTSC;
break;
case VC4_VEC_TV_MODE_NTSC_J:
state->tv.mode = DRM_MODE_TV_MODE_NTSC_J;
break;
case VC4_VEC_TV_MODE_PAL:
state->tv.mode = DRM_MODE_TV_MODE_PAL;
break;
case VC4_VEC_TV_MODE_PAL_M:
state->tv.mode = DRM_MODE_TV_MODE_PAL_M;
break;
default:
return -EINVAL;
} }
drm_mode_probed_add(connector, mode); return 0;
}
static int
vc4_vec_connector_get_property(struct drm_connector *connector,
const struct drm_connector_state *state,
struct drm_property *property,
uint64_t *val)
{
struct vc4_vec *vec = connector_to_vc4_vec(connector);
if (property != vec->legacy_tv_mode_property)
return -EINVAL;
return 1; switch (state->tv.mode) {
case DRM_MODE_TV_MODE_NTSC:
*val = VC4_VEC_TV_MODE_NTSC;
break;
case DRM_MODE_TV_MODE_NTSC_J:
*val = VC4_VEC_TV_MODE_NTSC_J;
break;
case DRM_MODE_TV_MODE_PAL:
*val = VC4_VEC_TV_MODE_PAL;
break;
case DRM_MODE_TV_MODE_PAL_M:
*val = VC4_VEC_TV_MODE_PAL_M;
break;
default:
return -EINVAL;
}
return 0;
} }
static const struct drm_connector_funcs vc4_vec_connector_funcs = { static const struct drm_connector_funcs vc4_vec_connector_funcs = {
...@@ -297,15 +366,19 @@ static const struct drm_connector_funcs vc4_vec_connector_funcs = { ...@@ -297,15 +366,19 @@ static const struct drm_connector_funcs vc4_vec_connector_funcs = {
.reset = vc4_vec_connector_reset, .reset = vc4_vec_connector_reset,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
.atomic_get_property = vc4_vec_connector_get_property,
.atomic_set_property = vc4_vec_connector_set_property,
}; };
static const struct drm_connector_helper_funcs vc4_vec_connector_helper_funcs = { static const struct drm_connector_helper_funcs vc4_vec_connector_helper_funcs = {
.get_modes = vc4_vec_connector_get_modes, .atomic_check = drm_atomic_helper_connector_tv_check,
.get_modes = drm_connector_helper_tv_get_modes,
}; };
static int vc4_vec_connector_init(struct drm_device *dev, struct vc4_vec *vec) static int vc4_vec_connector_init(struct drm_device *dev, struct vc4_vec *vec)
{ {
struct drm_connector *connector = &vec->connector; struct drm_connector *connector = &vec->connector;
struct drm_property *prop;
int ret; int ret;
connector->interlace_allowed = true; connector->interlace_allowed = true;
...@@ -318,8 +391,17 @@ static int vc4_vec_connector_init(struct drm_device *dev, struct vc4_vec *vec) ...@@ -318,8 +391,17 @@ static int vc4_vec_connector_init(struct drm_device *dev, struct vc4_vec *vec)
drm_connector_helper_add(connector, &vc4_vec_connector_helper_funcs); drm_connector_helper_add(connector, &vc4_vec_connector_helper_funcs);
drm_object_attach_property(&connector->base, drm_object_attach_property(&connector->base,
dev->mode_config.legacy_tv_mode_property, dev->mode_config.tv_mode_property,
VC4_VEC_TV_MODE_NTSC); DRM_MODE_TV_MODE_NTSC);
prop = drm_property_create_enum(dev, 0, "mode",
legacy_tv_mode_names,
ARRAY_SIZE(legacy_tv_mode_names));
if (!prop)
return -ENOMEM;
vec->legacy_tv_mode_property = prop;
drm_object_attach_property(&connector->base, prop, VC4_VEC_TV_MODE_NTSC);
drm_connector_attach_encoder(connector, &vec->encoder.base); drm_connector_attach_encoder(connector, &vec->encoder.base);
...@@ -366,13 +448,16 @@ static void vc4_vec_encoder_enable(struct drm_encoder *encoder, ...@@ -366,13 +448,16 @@ static void vc4_vec_encoder_enable(struct drm_encoder *encoder,
struct drm_connector *connector = &vec->connector; struct drm_connector *connector = &vec->connector;
struct drm_connector_state *conn_state = struct drm_connector_state *conn_state =
drm_atomic_get_new_connector_state(state, connector); drm_atomic_get_new_connector_state(state, connector);
const struct vc4_vec_tv_mode *tv_mode = const struct vc4_vec_tv_mode *tv_mode;
&vc4_vec_tv_modes[conn_state->tv.legacy_mode];
int idx, ret; int idx, ret;
if (!drm_dev_enter(drm, &idx)) if (!drm_dev_enter(drm, &idx))
return; return;
tv_mode = vc4_vec_tv_mode_lookup(conn_state->tv.mode);
if (!tv_mode)
goto err_dev_exit;
ret = pm_runtime_get_sync(&vec->pdev->dev); ret = pm_runtime_get_sync(&vec->pdev->dev);
if (ret < 0) { if (ret < 0) {
DRM_ERROR("Failed to retain power domain: %d\n", ret); DRM_ERROR("Failed to retain power domain: %d\n", ret);
...@@ -454,12 +539,10 @@ static int vc4_vec_encoder_atomic_check(struct drm_encoder *encoder, ...@@ -454,12 +539,10 @@ static int vc4_vec_encoder_atomic_check(struct drm_encoder *encoder,
struct drm_connector_state *conn_state) struct drm_connector_state *conn_state)
{ {
const struct drm_display_mode *mode = &crtc_state->adjusted_mode; const struct drm_display_mode *mode = &crtc_state->adjusted_mode;
const struct vc4_vec_tv_mode *vec_mode; const struct vc4_vec_tv_mode *tv_mode;
vec_mode = &vc4_vec_tv_modes[conn_state->tv.legacy_mode]; tv_mode = vc4_vec_tv_mode_lookup(conn_state->tv.mode, mode->htotal);
if (!tv_mode)
if (conn_state->crtc &&
!drm_mode_equal(vec_mode->mode, &crtc_state->adjusted_mode))
return -EINVAL; return -EINVAL;
if (mode->crtc_hdisplay % 4) if (mode->crtc_hdisplay % 4)
...@@ -556,13 +639,6 @@ static const struct of_device_id vc4_vec_dt_match[] = { ...@@ -556,13 +639,6 @@ static const struct of_device_id vc4_vec_dt_match[] = {
{ /* sentinel */ }, { /* sentinel */ },
}; };
static const char * const tv_mode_names[] = {
[VC4_VEC_TV_MODE_NTSC] = "NTSC",
[VC4_VEC_TV_MODE_NTSC_J] = "NTSC-J",
[VC4_VEC_TV_MODE_PAL] = "PAL",
[VC4_VEC_TV_MODE_PAL_M] = "PAL-M",
};
static int vc4_vec_bind(struct device *dev, struct device *master, void *data) static int vc4_vec_bind(struct device *dev, struct device *master, void *data)
{ {
struct platform_device *pdev = to_platform_device(dev); struct platform_device *pdev = to_platform_device(dev);
...@@ -570,9 +646,11 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data) ...@@ -570,9 +646,11 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data)
struct vc4_vec *vec; struct vc4_vec *vec;
int ret; int ret;
ret = drm_mode_create_tv_properties_legacy(drm, ret = drm_mode_create_tv_properties(drm,
ARRAY_SIZE(tv_mode_names), BIT(DRM_MODE_TV_MODE_NTSC) |
tv_mode_names); BIT(DRM_MODE_TV_MODE_NTSC_J) |
BIT(DRM_MODE_TV_MODE_PAL) |
BIT(DRM_MODE_TV_MODE_PAL_M));
if (ret) if (ret)
return ret; return ret;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment