summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/vc4/vc4_vec.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/vc4/vc4_vec.c')
-rw-r--r--drivers/gpu/drm/vc4/vc4_vec.c216
1 files changed, 113 insertions, 103 deletions
diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c
index 11fc3d6f66b1..4a788c1c9058 100644
--- a/drivers/gpu/drm/vc4/vc4_vec.c
+++ b/drivers/gpu/drm/vc4/vc4_vec.c
@@ -14,6 +14,7 @@
*/
#include <drm/drm_atomic_helper.h>
+#include <drm/drm_drv.h>
#include <drm/drm_edid.h>
#include <drm/drm_panel.h>
#include <drm/drm_probe_helper.h>
@@ -160,12 +161,12 @@ struct vc4_vec_variant {
/* General VEC hardware state. */
struct vc4_vec {
+ struct vc4_encoder encoder;
+ struct drm_connector connector;
+
struct platform_device *pdev;
const struct vc4_vec_variant *variant;
- struct drm_encoder *encoder;
- struct drm_connector *connector;
-
void __iomem *regs;
struct clk *clock;
@@ -178,30 +179,12 @@ struct vc4_vec {
#define VEC_READ(offset) readl(vec->regs + (offset))
#define VEC_WRITE(offset, val) writel(val, vec->regs + (offset))
-/* VC4 VEC encoder KMS struct */
-struct vc4_vec_encoder {
- struct vc4_encoder base;
- struct vc4_vec *vec;
-};
-
-static inline struct vc4_vec_encoder *
-to_vc4_vec_encoder(struct drm_encoder *encoder)
+static inline struct vc4_vec *
+encoder_to_vc4_vec(struct drm_encoder *encoder)
{
- return container_of(encoder, struct vc4_vec_encoder, base.base);
+ return container_of(encoder, struct vc4_vec, encoder.base);
}
-/* VC4 VEC connector KMS struct */
-struct vc4_vec_connector {
- struct drm_connector base;
- struct vc4_vec *vec;
-
- /* Since the connector is attached to just the one encoder,
- * this is the reference to it so we can do the best_encoder()
- * hook.
- */
- struct drm_encoder *encoder;
-};
-
enum vc4_vec_tv_mode_id {
VC4_VEC_TV_MODE_NTSC,
VC4_VEC_TV_MODE_NTSC_J,
@@ -243,14 +226,30 @@ static const struct debugfs_reg32 vec_regs[] = {
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 = {
@@ -262,17 +261,33 @@ static const struct drm_display_mode ntsc_mode = {
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 = {
@@ -307,12 +322,6 @@ vc4_vec_connector_detect(struct drm_connector *connector, bool force)
return connector_status_unknown;
}
-static void vc4_vec_connector_destroy(struct drm_connector *connector)
-{
- drm_connector_unregister(connector);
- drm_connector_cleanup(connector);
-}
-
static int vc4_vec_connector_get_modes(struct drm_connector *connector)
{
struct drm_connector_state *state = connector->state;
@@ -333,7 +342,6 @@ 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,
- .destroy = vc4_vec_connector_destroy,
.reset = drm_atomic_helper_connector_reset,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
@@ -343,25 +351,18 @@ static const struct drm_connector_helper_funcs vc4_vec_connector_helper_funcs =
.get_modes = vc4_vec_connector_get_modes,
};
-static struct drm_connector *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 = NULL;
- struct vc4_vec_connector *vec_connector;
-
- vec_connector = devm_kzalloc(dev->dev, sizeof(*vec_connector),
- GFP_KERNEL);
- if (!vec_connector)
- return ERR_PTR(-ENOMEM);
+ struct drm_connector *connector = &vec->connector;
+ int ret;
- connector = &vec_connector->base;
connector->interlace_allowed = true;
- vec_connector->encoder = vec->encoder;
- vec_connector->vec = vec;
+ ret = drmm_connector_init(dev, connector, &vc4_vec_connector_funcs,
+ DRM_MODE_CONNECTOR_Composite, NULL);
+ if (ret)
+ return ret;
- drm_connector_init(dev, connector, &vc4_vec_connector_funcs,
- DRM_MODE_CONNECTOR_Composite);
drm_connector_helper_add(connector, &vc4_vec_connector_helper_funcs);
drm_object_attach_property(&connector->base,
@@ -369,16 +370,19 @@ static struct drm_connector *vc4_vec_connector_init(struct drm_device *dev,
VC4_VEC_TV_MODE_NTSC);
vec->tv_mode = &vc4_vec_tv_modes[VC4_VEC_TV_MODE_NTSC];
- drm_connector_attach_encoder(connector, vec->encoder);
+ drm_connector_attach_encoder(connector, &vec->encoder.base);
- return connector;
+ return 0;
}
static void vc4_vec_encoder_disable(struct drm_encoder *encoder)
{
- struct vc4_vec_encoder *vc4_vec_encoder = to_vc4_vec_encoder(encoder);
- struct vc4_vec *vec = vc4_vec_encoder->vec;
- int ret;
+ struct drm_device *drm = encoder->dev;
+ struct vc4_vec *vec = encoder_to_vc4_vec(encoder);
+ int idx, ret;
+
+ if (!drm_dev_enter(drm, &idx))
+ return;
VEC_WRITE(VEC_CFG, 0);
VEC_WRITE(VEC_DAC_MISC,
@@ -392,20 +396,29 @@ static void vc4_vec_encoder_disable(struct drm_encoder *encoder)
ret = pm_runtime_put(&vec->pdev->dev);
if (ret < 0) {
DRM_ERROR("Failed to release power domain: %d\n", ret);
- return;
+ goto err_dev_exit;
}
+
+ drm_dev_exit(idx);
+ return;
+
+err_dev_exit:
+ drm_dev_exit(idx);
}
static void vc4_vec_encoder_enable(struct drm_encoder *encoder)
{
- struct vc4_vec_encoder *vc4_vec_encoder = to_vc4_vec_encoder(encoder);
- struct vc4_vec *vec = vc4_vec_encoder->vec;
- int ret;
+ struct drm_device *drm = encoder->dev;
+ struct vc4_vec *vec = encoder_to_vc4_vec(encoder);
+ int idx, ret;
+
+ if (!drm_dev_enter(drm, &idx))
+ return;
ret = pm_runtime_get_sync(&vec->pdev->dev);
if (ret < 0) {
DRM_ERROR("Failed to retain power domain: %d\n", ret);
- return;
+ goto err_dev_exit;
}
/*
@@ -418,13 +431,13 @@ static void vc4_vec_encoder_enable(struct drm_encoder *encoder)
ret = clk_set_rate(vec->clock, 108000000);
if (ret) {
DRM_ERROR("Failed to set clock rate: %d\n", ret);
- return;
+ goto err_put_runtime_pm;
}
ret = clk_prepare_enable(vec->clock);
if (ret) {
DRM_ERROR("Failed to turn on core clock: %d\n", ret);
- return;
+ goto err_put_runtime_pm;
}
/* Reset the different blocks */
@@ -460,6 +473,14 @@ static void vc4_vec_encoder_enable(struct drm_encoder *encoder)
VEC_WRITE(VEC_DAC_MISC,
VEC_DAC_MISC_VID_ACT | VEC_DAC_MISC_DAC_RST_N);
VEC_WRITE(VEC_CFG, VEC_CFG_VEC_EN);
+
+ drm_dev_exit(idx);
+ return;
+
+err_put_runtime_pm:
+ pm_runtime_put(&vec->pdev->dev);
+err_dev_exit:
+ drm_dev_exit(idx);
}
@@ -474,8 +495,7 @@ 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_encoder *vc4_vec_encoder = to_vc4_vec_encoder(encoder);
- struct vc4_vec *vec = vc4_vec_encoder->vec;
+ struct vc4_vec *vec = encoder_to_vc4_vec(encoder);
vec->tv_mode = &vc4_vec_tv_modes[conn_state->tv.mode];
}
@@ -503,6 +523,24 @@ static const struct drm_encoder_helper_funcs vc4_vec_encoder_helper_funcs = {
.atomic_mode_set = vc4_vec_encoder_atomic_mode_set,
};
+static int vc4_vec_late_register(struct drm_encoder *encoder)
+{
+ struct drm_device *drm = encoder->dev;
+ struct vc4_vec *vec = encoder_to_vc4_vec(encoder);
+ int ret;
+
+ ret = vc4_debugfs_add_regset32(drm->primary, "vec_regs",
+ &vec->regset);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const struct drm_encoder_funcs vc4_vec_encoder_funcs = {
+ .late_register = vc4_vec_late_register,
+};
+
static const struct vc4_vec_variant bcm2835_vec_variant = {
.dac_config = VEC_DAC_CONFIG_DAC_CTRL(0xc) |
VEC_DAC_CONFIG_DRIVER_CTRL(0xc) |
@@ -532,9 +570,7 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data)
{
struct platform_device *pdev = to_platform_device(dev);
struct drm_device *drm = dev_get_drvdata(master);
- struct vc4_dev *vc4 = to_vc4_dev(drm);
struct vc4_vec *vec;
- struct vc4_vec_encoder *vc4_vec_encoder;
int ret;
ret = drm_mode_create_tv_properties(drm, ARRAY_SIZE(tv_mode_names),
@@ -542,18 +578,11 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data)
if (ret)
return ret;
- vec = devm_kzalloc(dev, sizeof(*vec), GFP_KERNEL);
+ vec = drmm_kzalloc(drm, sizeof(*vec), GFP_KERNEL);
if (!vec)
return -ENOMEM;
- vc4_vec_encoder = devm_kzalloc(dev, sizeof(*vc4_vec_encoder),
- GFP_KERNEL);
- if (!vc4_vec_encoder)
- return -ENOMEM;
- vc4_vec_encoder->base.type = VC4_ENCODER_TYPE_VEC;
- vc4_vec_encoder->vec = vec;
- vec->encoder = &vc4_vec_encoder->base.base;
-
+ vec->encoder.type = VC4_ENCODER_TYPE_VEC;
vec->pdev = pdev;
vec->variant = (const struct vc4_vec_variant *)
of_device_get_match_data(dev);
@@ -572,49 +601,30 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data)
return ret;
}
- pm_runtime_enable(dev);
-
- drm_simple_encoder_init(drm, vec->encoder, DRM_MODE_ENCODER_TVDAC);
- drm_encoder_helper_add(vec->encoder, &vc4_vec_encoder_helper_funcs);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
- vec->connector = vc4_vec_connector_init(drm, vec);
- if (IS_ERR(vec->connector)) {
- ret = PTR_ERR(vec->connector);
- goto err_destroy_encoder;
- }
+ ret = drmm_encoder_init(drm, &vec->encoder.base,
+ &vc4_vec_encoder_funcs,
+ DRM_MODE_ENCODER_TVDAC,
+ NULL);
+ if (ret)
+ return ret;
- dev_set_drvdata(dev, vec);
+ drm_encoder_helper_add(&vec->encoder.base, &vc4_vec_encoder_helper_funcs);
- vc4->vec = vec;
+ ret = vc4_vec_connector_init(drm, vec);
+ if (ret)
+ return ret;
- vc4_debugfs_add_regset32(drm, "vec_regs", &vec->regset);
+ dev_set_drvdata(dev, vec);
return 0;
-
-err_destroy_encoder:
- drm_encoder_cleanup(vec->encoder);
- pm_runtime_disable(dev);
-
- return ret;
-}
-
-static void vc4_vec_unbind(struct device *dev, struct device *master,
- void *data)
-{
- struct drm_device *drm = dev_get_drvdata(master);
- struct vc4_dev *vc4 = to_vc4_dev(drm);
- struct vc4_vec *vec = dev_get_drvdata(dev);
-
- vc4_vec_connector_destroy(vec->connector);
- drm_encoder_cleanup(vec->encoder);
- pm_runtime_disable(dev);
-
- vc4->vec = NULL;
}
static const struct component_ops vc4_vec_ops = {
.bind = vc4_vec_bind,
- .unbind = vc4_vec_unbind,
};
static int vc4_vec_dev_probe(struct platform_device *pdev)