* [PATCH RFC v2 2/6] drm/i2c: tda998x: Move tda998x to a couple encoder/connector
2014-03-21 10:27 ` Jean-Francois Moine
@ 2014-03-21 8:17 ` Jean-Francois Moine
-1 siblings, 0 replies; 32+ messages in thread
From: Jean-Francois Moine @ 2014-03-21 8:17 UTC (permalink / raw)
To: Russell King, Rob Clark, dri-devel; +Cc: linux-arm-kernel, linux-media
The 'slave encoder' structure of the tda998x driver asks for glue
between the DRM driver and the encoder/connector structures.
This patch changes the driver to a normal DRM encoder/connector
thanks to the infrastructure for componentised subsystems.
Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
drivers/gpu/drm/i2c/tda998x_drv.c | 323 ++++++++++++++++++++++----------------
1 file changed, 188 insertions(+), 135 deletions(-)
diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index fd6751c..1c25e40 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -20,11 +20,12 @@
#include <linux/hdmi.h>
#include <linux/module.h>
#include <linux/irq.h>
+#include <linux/of_platform.h>
+#include <linux/component.h>
#include <sound/asoundef.h>
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
-#include <drm/drm_encoder_slave.h>
#include <drm/drm_edid.h>
#include <drm/i2c/tda998x.h>
@@ -44,10 +45,14 @@ struct tda998x_priv {
wait_queue_head_t wq_edid;
volatile int wq_edid_wait;
- struct drm_encoder *encoder;
+ struct drm_encoder encoder;
+ struct drm_connector connector;
};
-#define to_tda998x_priv(x) ((struct tda998x_priv *)to_encoder_slave(x)->slave_priv)
+#define connector_priv(e) \
+ container_of(connector, struct tda998x_priv, connector)
+#define encoder_priv(e) \
+ container_of(encoder, struct tda998x_priv, encoder)
/* The TDA9988 series of devices use a paged register scheme.. to simplify
* things we encode the page # in upper bits of the register #. To read/
@@ -559,9 +564,8 @@ static irqreturn_t tda998x_irq_thread(int irq, void *data)
if ((flag2 & INT_FLAGS_2_EDID_BLK_RD) && priv->wq_edid_wait) {
priv->wq_edid_wait = 0;
wake_up(&priv->wq_edid);
- } else if (cec != 0) { /* HPD change */
- if (priv->encoder && priv->encoder->dev)
- drm_helper_hpd_irq_event(priv->encoder->dev);
+ } else if (cec != 0 && priv->encoder.dev) { /* HPD change */
+ drm_helper_hpd_irq_event(priv->encoder.dev);
}
return IRQ_HANDLED;
}
@@ -731,9 +735,8 @@ tda998x_configure_audio(struct tda998x_priv *priv,
/* DRM encoder functions */
static void
-tda998x_encoder_set_config(struct drm_encoder *encoder, void *params)
+tda998x_encoder_set_config(struct tda998x_priv *priv, void *params)
{
- struct tda998x_priv *priv = to_tda998x_priv(encoder);
struct tda998x_encoder_params *p = params;
priv->vip_cntrl_0 = VIP_CNTRL_0_SWAP_A(p->swap_a) |
@@ -755,7 +758,7 @@ tda998x_encoder_set_config(struct drm_encoder *encoder, void *params)
static void
tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
{
- struct tda998x_priv *priv = to_tda998x_priv(encoder);
+ struct tda998x_priv *priv = encoder_priv(encoder);
/* we only care about on or off: */
if (mode != DRM_MODE_DPMS_ON)
@@ -786,18 +789,6 @@ tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
priv->dpms = mode;
}
-static void
-tda998x_encoder_save(struct drm_encoder *encoder)
-{
- DBG("");
-}
-
-static void
-tda998x_encoder_restore(struct drm_encoder *encoder)
-{
- DBG("");
-}
-
static bool
tda998x_encoder_mode_fixup(struct drm_encoder *encoder,
const struct drm_display_mode *mode,
@@ -806,11 +797,14 @@ tda998x_encoder_mode_fixup(struct drm_encoder *encoder,
return true;
}
-static int
-tda998x_encoder_mode_valid(struct drm_encoder *encoder,
- struct drm_display_mode *mode)
+static void tda998x_encoder_prepare(struct drm_encoder *encoder)
{
- return MODE_OK;
+ tda998x_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+}
+
+static void tda998x_encoder_commit(struct drm_encoder *encoder)
+{
+ tda998x_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
}
static void
@@ -818,7 +812,7 @@ tda998x_encoder_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
- struct tda998x_priv *priv = to_tda998x_priv(encoder);
+ struct tda998x_priv *priv = encoder_priv(encoder);
uint16_t ref_pix, ref_line, n_pix, n_line;
uint16_t hs_pix_s, hs_pix_e;
uint16_t vs1_pix_s, vs1_pix_e, vs1_line_s, vs1_line_e;
@@ -1006,10 +1000,9 @@ tda998x_encoder_mode_set(struct drm_encoder *encoder,
}
static enum drm_connector_status
-tda998x_encoder_detect(struct drm_encoder *encoder,
- struct drm_connector *connector)
+tda998x_connector_detect(struct drm_connector *connector, bool force)
{
- struct tda998x_priv *priv = to_tda998x_priv(encoder);
+ struct tda998x_priv *priv = connector_priv(connector);
uint8_t val = cec_read(priv, REG_CEC_RXSHPDLEV);
return (val & CEC_RXSHPDLEV_HPD) ? connector_status_connected :
@@ -1017,9 +1010,8 @@ tda998x_encoder_detect(struct drm_encoder *encoder,
}
static int
-read_edid_block(struct drm_encoder *encoder, uint8_t *buf, int blk)
+read_edid_block(struct tda998x_priv *priv, uint8_t *buf, int blk)
{
- struct tda998x_priv *priv = to_tda998x_priv(encoder);
uint8_t offset, segptr;
int ret, i;
@@ -1073,10 +1065,8 @@ read_edid_block(struct drm_encoder *encoder, uint8_t *buf, int blk)
return 0;
}
-static uint8_t *
-do_get_edid(struct drm_encoder *encoder)
+static uint8_t *do_get_edid(struct tda998x_priv *priv)
{
- struct tda998x_priv *priv = to_tda998x_priv(encoder);
int j, valid_extensions = 0;
uint8_t *block, *new;
bool print_bad_edid = drm_debug & DRM_UT_KMS;
@@ -1088,7 +1078,7 @@ do_get_edid(struct drm_encoder *encoder)
reg_clear(priv, REG_TX4, TX4_PD_RAM);
/* base block fetch */
- if (read_edid_block(encoder, block, 0))
+ if (read_edid_block(priv, block, 0))
goto fail;
if (!drm_edid_block_valid(block, 0, print_bad_edid))
@@ -1105,7 +1095,7 @@ do_get_edid(struct drm_encoder *encoder)
for (j = 1; j <= block[0x7e]; j++) {
uint8_t *ext_block = block + (valid_extensions + 1) * EDID_LENGTH;
- if (read_edid_block(encoder, ext_block, j))
+ if (read_edid_block(priv, ext_block, j))
goto fail;
if (!drm_edid_block_valid(ext_block, j, print_bad_edid))
@@ -1137,12 +1127,28 @@ fail:
return NULL;
}
+/* DRM connector functions */
+
+static struct drm_encoder *
+tda998x_connector_best_encoder(struct drm_connector *connector)
+{
+ struct tda998x_priv *priv = connector_priv(connector);
+
+ return &priv->encoder;
+}
+
+static int
+tda998x_connector_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ return MODE_OK;
+}
+
static int
-tda998x_encoder_get_modes(struct drm_encoder *encoder,
- struct drm_connector *connector)
+tda998x_connector_get_modes(struct drm_connector *connector)
{
- struct tda998x_priv *priv = to_tda998x_priv(encoder);
- struct edid *edid = (struct edid *)do_get_edid(encoder);
+ struct tda998x_priv *priv = connector_priv(connector);
+ struct edid *edid = (struct edid *) do_get_edid(priv);
int n = 0;
if (edid) {
@@ -1156,22 +1162,7 @@ tda998x_encoder_get_modes(struct drm_encoder *encoder,
}
static int
-tda998x_encoder_create_resources(struct drm_encoder *encoder,
- struct drm_connector *connector)
-{
- struct tda998x_priv *priv = to_tda998x_priv(encoder);
-
- if (priv->hdmi->irq)
- connector->polled = DRM_CONNECTOR_POLL_HPD;
- else
- connector->polled = DRM_CONNECTOR_POLL_CONNECT |
- DRM_CONNECTOR_POLL_DISCONNECT;
- return 0;
-}
-
-static int
-tda998x_encoder_set_property(struct drm_encoder *encoder,
- struct drm_connector *connector,
+tda998x_connector_set_property(struct drm_connector *connector,
struct drm_property *property,
uint64_t val)
{
@@ -1179,56 +1170,117 @@ tda998x_encoder_set_property(struct drm_encoder *encoder,
return 0;
}
-static void
-tda998x_encoder_destroy(struct drm_encoder *encoder)
+static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
+ .dpms = tda998x_encoder_dpms,
+ .mode_fixup = tda998x_encoder_mode_fixup,
+ .prepare = tda998x_encoder_prepare,
+ .commit = tda998x_encoder_commit,
+ .mode_set = tda998x_encoder_mode_set,
+};
+
+static void tda998x_encoder_destroy(struct drm_encoder *encoder)
{
- struct tda998x_priv *priv = to_tda998x_priv(encoder);
- drm_i2c_encoder_destroy(encoder);
+ drm_encoder_cleanup(encoder);
+}
- /* disable all IRQs and free the IRQ handler */
- cec_write(priv, REG_CEC_RXSHPDINTENA, 0);
- reg_clear(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);
- if (priv->hdmi->irq)
- free_irq(priv->hdmi->irq, priv);
+static const struct drm_encoder_funcs encoder_funcs = {
+ .destroy = tda998x_encoder_destroy,
+};
- if (priv->cec)
- i2c_unregister_device(priv->cec);
- kfree(priv);
+static const struct drm_connector_helper_funcs connector_helper_funcs = {
+ .get_modes = tda998x_connector_get_modes,
+ .mode_valid = tda998x_connector_mode_valid,
+ .best_encoder = tda998x_connector_best_encoder,
+};
+
+static void tda998x_connector_destroy(struct drm_connector *connector)
+{
+ if (!connector->dev)
+ return;
+ drm_sysfs_connector_remove(connector);
+ drm_connector_cleanup(connector);
}
-static struct drm_encoder_slave_funcs tda998x_encoder_funcs = {
- .set_config = tda998x_encoder_set_config,
- .destroy = tda998x_encoder_destroy,
- .dpms = tda998x_encoder_dpms,
- .save = tda998x_encoder_save,
- .restore = tda998x_encoder_restore,
- .mode_fixup = tda998x_encoder_mode_fixup,
- .mode_valid = tda998x_encoder_mode_valid,
- .mode_set = tda998x_encoder_mode_set,
- .detect = tda998x_encoder_detect,
- .get_modes = tda998x_encoder_get_modes,
- .create_resources = tda998x_encoder_create_resources,
- .set_property = tda998x_encoder_set_property,
+static const struct drm_connector_funcs connector_funcs = {
+ .detect = tda998x_connector_detect,
+ .set_property = tda998x_connector_set_property,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .dpms = drm_helper_connector_dpms,
+ .destroy = tda998x_connector_destroy,
};
/* I2C driver functions */
-static int
-tda998x_probe(struct i2c_client *client, const struct i2c_device_id *id)
+static int tda_bind(struct device *dev, struct device *master, void *data)
{
+ struct drm_device *drm = data;
+ struct i2c_client *i2c_client = to_i2c_client(dev);
+ struct tda998x_priv *priv = i2c_get_clientdata(i2c_client);
+ struct drm_connector *connector = &priv->connector;
+ struct drm_encoder *encoder = &priv->encoder;
+ int ret;
+
+ if (!try_module_get(THIS_MODULE)) {
+ dev_err(dev, "cannot get module %s\n", THIS_MODULE->name);
+ return -EINVAL;
+ }
+
+ ret = drm_connector_init(drm, connector,
+ &connector_funcs,
+ DRM_MODE_CONNECTOR_HDMIA);
+ if (ret < 0)
+ return ret;
+ drm_connector_helper_add(connector, &connector_helper_funcs);
+
+ ret = drm_encoder_init(drm, encoder,
+ &encoder_funcs,
+ DRM_MODE_ENCODER_TMDS);
+
+ encoder->possible_crtcs = 1; // 1 << lcd_num
+
+ if (ret < 0)
+ goto err;
+ drm_encoder_helper_add(encoder, &encoder_helper_funcs);
+
+ ret = drm_mode_connector_attach_encoder(connector, encoder);
+ if (ret < 0)
+ goto err;
+ connector->encoder = encoder;
+
+ drm_sysfs_connector_add(connector);
+
+ drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
+ ret = drm_object_property_set_value(&connector->base,
+ drm->mode_config.dpms_property,
+ DRM_MODE_DPMS_OFF);
+
+ if (priv->hdmi->irq)
+ connector->polled = DRM_CONNECTOR_POLL_HPD;
+ else
+ connector->polled = DRM_CONNECTOR_POLL_CONNECT |
+ DRM_CONNECTOR_POLL_DISCONNECT;
return 0;
+
+err:
+ if (encoder->dev)
+ drm_encoder_cleanup(encoder);
+ if (connector->dev)
+ drm_connector_cleanup(connector);
+ return ret;
}
-static int
-tda998x_remove(struct i2c_client *client)
+static void tda_unbind(struct device *dev, struct device *master, void *data)
{
- return 0;
+ module_put(THIS_MODULE);
}
+static const struct component_ops comp_ops = {
+ .bind = tda_bind,
+ .unbind = tda_unbind,
+};
+
static int
-tda998x_encoder_init(struct i2c_client *client,
- struct drm_device *dev,
- struct drm_encoder_slave *encoder_slave)
+tda998x_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct tda998x_priv *priv;
struct device_node *np = client->dev.of_node;
@@ -1239,6 +1291,8 @@ tda998x_encoder_init(struct i2c_client *client,
if (!priv)
return -ENOMEM;
+ i2c_set_clientdata(client, priv);
+
priv->vip_cntrl_0 = VIP_CNTRL_0_SWAP_A(2) | VIP_CNTRL_0_SWAP_B(3);
priv->vip_cntrl_1 = VIP_CNTRL_1_SWAP_C(0) | VIP_CNTRL_1_SWAP_D(1);
priv->vip_cntrl_2 = VIP_CNTRL_2_SWAP_E(4) | VIP_CNTRL_2_SWAP_F(5);
@@ -1250,13 +1304,8 @@ tda998x_encoder_init(struct i2c_client *client,
kfree(priv);
return -ENODEV;
}
-
- priv->encoder = &encoder_slave->base;
priv->dpms = DRM_MODE_DPMS_OFF;
- encoder_slave->slave_priv = priv;
- encoder_slave->slave_funcs = &tda998x_encoder_funcs;
-
/* wake up the device: */
cec_write(priv, REG_CEC_ENAMODS,
CEC_ENAMODS_EN_RXSENS | CEC_ENAMODS_EN_HDMI);
@@ -1340,31 +1389,55 @@ tda998x_encoder_init(struct i2c_client *client,
/* enable EDID read irq: */
reg_set(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);
- if (!np)
- return 0; /* non-DT */
+ if (np) { /* if DT */
- /* get the optional video properties */
- ret = of_property_read_u32(np, "video-ports", &video);
- if (ret == 0) {
- priv->vip_cntrl_0 = video >> 16;
- priv->vip_cntrl_1 = video >> 8;
- priv->vip_cntrl_2 = video;
+ /* get the optional video properties */
+ ret = of_property_read_u32(np, "video-ports", &video);
+ if (ret == 0) {
+ priv->vip_cntrl_0 = video >> 16;
+ priv->vip_cntrl_1 = video >> 8;
+ priv->vip_cntrl_2 = video;
+ }
+ } else {
+ struct tda998x_encoder_params *params =
+ (struct tda998x_encoder_params)
+ client->dev.platform_data;
+
+ if (params)
+ tda998x_encoder_set_config(priv, params);
}
+ /* tda998x video component ready */
+ component_add(&client->dev, &comp_ops);
+
return 0;
fail:
- /* if encoder_init fails, the encoder slave is never registered,
- * so cleanup here:
- */
if (priv->cec)
i2c_unregister_device(priv->cec);
kfree(priv);
- encoder_slave->slave_priv = NULL;
- encoder_slave->slave_funcs = NULL;
return -ENXIO;
}
+static int
+tda998x_remove(struct i2c_client *client)
+{
+ struct tda998x_priv *priv = i2c_get_clientdata(client);
+
+ /* disable all IRQs and free the IRQ handler */
+ cec_write(priv, REG_CEC_RXSHPDINTENA, 0);
+ reg_clear(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);
+ if (priv->hdmi->irq)
+ free_irq(priv->hdmi->irq, priv);
+
+ component_del(&client->dev, &comp_ops);
+
+ if (priv->cec)
+ i2c_unregister_device(priv->cec);
+ kfree(priv);
+ return 0;
+}
+
#ifdef CONFIG_OF
static const struct of_device_id tda998x_dt_ids[] = {
{ .compatible = "nxp,tda9989", },
@@ -1381,38 +1454,18 @@ static struct i2c_device_id tda998x_ids[] = {
};
MODULE_DEVICE_TABLE(i2c, tda998x_ids);
-static struct drm_i2c_encoder_driver tda998x_driver = {
- .i2c_driver = {
- .probe = tda998x_probe,
- .remove = tda998x_remove,
- .driver = {
- .name = "tda998x",
- .of_match_table = of_match_ptr(tda998x_dt_ids),
- },
- .id_table = tda998x_ids,
+static struct i2c_driver tda998x_driver = {
+ .probe = tda998x_probe,
+ .remove = tda998x_remove,
+ .driver = {
+ .name = "tda998x",
+ .of_match_table = of_match_ptr(tda998x_dt_ids),
},
- .encoder_init = tda998x_encoder_init,
+ .id_table = tda998x_ids,
};
-/* Module initialization */
-
-static int __init
-tda998x_init(void)
-{
- DBG("");
- return drm_i2c_encoder_register(THIS_MODULE, &tda998x_driver);
-}
-
-static void __exit
-tda998x_exit(void)
-{
- DBG("");
- drm_i2c_encoder_unregister(&tda998x_driver);
-}
+module_i2c_driver(tda998x_driver);
MODULE_AUTHOR("Rob Clark <robdclark@gmail.com");
MODULE_DESCRIPTION("NXP Semiconductors TDA998X HDMI Encoder");
MODULE_LICENSE("GPL");
-
-module_init(tda998x_init);
-module_exit(tda998x_exit);
--
1.9.1
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH RFC v2 2/6] drm/i2c: tda998x: Move tda998x to a couple encoder/connector
@ 2014-03-21 8:17 ` Jean-Francois Moine
0 siblings, 0 replies; 32+ messages in thread
From: Jean-Francois Moine @ 2014-03-21 8:17 UTC (permalink / raw)
To: linux-arm-kernel
The 'slave encoder' structure of the tda998x driver asks for glue
between the DRM driver and the encoder/connector structures.
This patch changes the driver to a normal DRM encoder/connector
thanks to the infrastructure for componentised subsystems.
Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
drivers/gpu/drm/i2c/tda998x_drv.c | 323 ++++++++++++++++++++++----------------
1 file changed, 188 insertions(+), 135 deletions(-)
diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index fd6751c..1c25e40 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -20,11 +20,12 @@
#include <linux/hdmi.h>
#include <linux/module.h>
#include <linux/irq.h>
+#include <linux/of_platform.h>
+#include <linux/component.h>
#include <sound/asoundef.h>
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
-#include <drm/drm_encoder_slave.h>
#include <drm/drm_edid.h>
#include <drm/i2c/tda998x.h>
@@ -44,10 +45,14 @@ struct tda998x_priv {
wait_queue_head_t wq_edid;
volatile int wq_edid_wait;
- struct drm_encoder *encoder;
+ struct drm_encoder encoder;
+ struct drm_connector connector;
};
-#define to_tda998x_priv(x) ((struct tda998x_priv *)to_encoder_slave(x)->slave_priv)
+#define connector_priv(e) \
+ container_of(connector, struct tda998x_priv, connector)
+#define encoder_priv(e) \
+ container_of(encoder, struct tda998x_priv, encoder)
/* The TDA9988 series of devices use a paged register scheme.. to simplify
* things we encode the page # in upper bits of the register #. To read/
@@ -559,9 +564,8 @@ static irqreturn_t tda998x_irq_thread(int irq, void *data)
if ((flag2 & INT_FLAGS_2_EDID_BLK_RD) && priv->wq_edid_wait) {
priv->wq_edid_wait = 0;
wake_up(&priv->wq_edid);
- } else if (cec != 0) { /* HPD change */
- if (priv->encoder && priv->encoder->dev)
- drm_helper_hpd_irq_event(priv->encoder->dev);
+ } else if (cec != 0 && priv->encoder.dev) { /* HPD change */
+ drm_helper_hpd_irq_event(priv->encoder.dev);
}
return IRQ_HANDLED;
}
@@ -731,9 +735,8 @@ tda998x_configure_audio(struct tda998x_priv *priv,
/* DRM encoder functions */
static void
-tda998x_encoder_set_config(struct drm_encoder *encoder, void *params)
+tda998x_encoder_set_config(struct tda998x_priv *priv, void *params)
{
- struct tda998x_priv *priv = to_tda998x_priv(encoder);
struct tda998x_encoder_params *p = params;
priv->vip_cntrl_0 = VIP_CNTRL_0_SWAP_A(p->swap_a) |
@@ -755,7 +758,7 @@ tda998x_encoder_set_config(struct drm_encoder *encoder, void *params)
static void
tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
{
- struct tda998x_priv *priv = to_tda998x_priv(encoder);
+ struct tda998x_priv *priv = encoder_priv(encoder);
/* we only care about on or off: */
if (mode != DRM_MODE_DPMS_ON)
@@ -786,18 +789,6 @@ tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
priv->dpms = mode;
}
-static void
-tda998x_encoder_save(struct drm_encoder *encoder)
-{
- DBG("");
-}
-
-static void
-tda998x_encoder_restore(struct drm_encoder *encoder)
-{
- DBG("");
-}
-
static bool
tda998x_encoder_mode_fixup(struct drm_encoder *encoder,
const struct drm_display_mode *mode,
@@ -806,11 +797,14 @@ tda998x_encoder_mode_fixup(struct drm_encoder *encoder,
return true;
}
-static int
-tda998x_encoder_mode_valid(struct drm_encoder *encoder,
- struct drm_display_mode *mode)
+static void tda998x_encoder_prepare(struct drm_encoder *encoder)
{
- return MODE_OK;
+ tda998x_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+}
+
+static void tda998x_encoder_commit(struct drm_encoder *encoder)
+{
+ tda998x_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
}
static void
@@ -818,7 +812,7 @@ tda998x_encoder_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
- struct tda998x_priv *priv = to_tda998x_priv(encoder);
+ struct tda998x_priv *priv = encoder_priv(encoder);
uint16_t ref_pix, ref_line, n_pix, n_line;
uint16_t hs_pix_s, hs_pix_e;
uint16_t vs1_pix_s, vs1_pix_e, vs1_line_s, vs1_line_e;
@@ -1006,10 +1000,9 @@ tda998x_encoder_mode_set(struct drm_encoder *encoder,
}
static enum drm_connector_status
-tda998x_encoder_detect(struct drm_encoder *encoder,
- struct drm_connector *connector)
+tda998x_connector_detect(struct drm_connector *connector, bool force)
{
- struct tda998x_priv *priv = to_tda998x_priv(encoder);
+ struct tda998x_priv *priv = connector_priv(connector);
uint8_t val = cec_read(priv, REG_CEC_RXSHPDLEV);
return (val & CEC_RXSHPDLEV_HPD) ? connector_status_connected :
@@ -1017,9 +1010,8 @@ tda998x_encoder_detect(struct drm_encoder *encoder,
}
static int
-read_edid_block(struct drm_encoder *encoder, uint8_t *buf, int blk)
+read_edid_block(struct tda998x_priv *priv, uint8_t *buf, int blk)
{
- struct tda998x_priv *priv = to_tda998x_priv(encoder);
uint8_t offset, segptr;
int ret, i;
@@ -1073,10 +1065,8 @@ read_edid_block(struct drm_encoder *encoder, uint8_t *buf, int blk)
return 0;
}
-static uint8_t *
-do_get_edid(struct drm_encoder *encoder)
+static uint8_t *do_get_edid(struct tda998x_priv *priv)
{
- struct tda998x_priv *priv = to_tda998x_priv(encoder);
int j, valid_extensions = 0;
uint8_t *block, *new;
bool print_bad_edid = drm_debug & DRM_UT_KMS;
@@ -1088,7 +1078,7 @@ do_get_edid(struct drm_encoder *encoder)
reg_clear(priv, REG_TX4, TX4_PD_RAM);
/* base block fetch */
- if (read_edid_block(encoder, block, 0))
+ if (read_edid_block(priv, block, 0))
goto fail;
if (!drm_edid_block_valid(block, 0, print_bad_edid))
@@ -1105,7 +1095,7 @@ do_get_edid(struct drm_encoder *encoder)
for (j = 1; j <= block[0x7e]; j++) {
uint8_t *ext_block = block + (valid_extensions + 1) * EDID_LENGTH;
- if (read_edid_block(encoder, ext_block, j))
+ if (read_edid_block(priv, ext_block, j))
goto fail;
if (!drm_edid_block_valid(ext_block, j, print_bad_edid))
@@ -1137,12 +1127,28 @@ fail:
return NULL;
}
+/* DRM connector functions */
+
+static struct drm_encoder *
+tda998x_connector_best_encoder(struct drm_connector *connector)
+{
+ struct tda998x_priv *priv = connector_priv(connector);
+
+ return &priv->encoder;
+}
+
+static int
+tda998x_connector_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ return MODE_OK;
+}
+
static int
-tda998x_encoder_get_modes(struct drm_encoder *encoder,
- struct drm_connector *connector)
+tda998x_connector_get_modes(struct drm_connector *connector)
{
- struct tda998x_priv *priv = to_tda998x_priv(encoder);
- struct edid *edid = (struct edid *)do_get_edid(encoder);
+ struct tda998x_priv *priv = connector_priv(connector);
+ struct edid *edid = (struct edid *) do_get_edid(priv);
int n = 0;
if (edid) {
@@ -1156,22 +1162,7 @@ tda998x_encoder_get_modes(struct drm_encoder *encoder,
}
static int
-tda998x_encoder_create_resources(struct drm_encoder *encoder,
- struct drm_connector *connector)
-{
- struct tda998x_priv *priv = to_tda998x_priv(encoder);
-
- if (priv->hdmi->irq)
- connector->polled = DRM_CONNECTOR_POLL_HPD;
- else
- connector->polled = DRM_CONNECTOR_POLL_CONNECT |
- DRM_CONNECTOR_POLL_DISCONNECT;
- return 0;
-}
-
-static int
-tda998x_encoder_set_property(struct drm_encoder *encoder,
- struct drm_connector *connector,
+tda998x_connector_set_property(struct drm_connector *connector,
struct drm_property *property,
uint64_t val)
{
@@ -1179,56 +1170,117 @@ tda998x_encoder_set_property(struct drm_encoder *encoder,
return 0;
}
-static void
-tda998x_encoder_destroy(struct drm_encoder *encoder)
+static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
+ .dpms = tda998x_encoder_dpms,
+ .mode_fixup = tda998x_encoder_mode_fixup,
+ .prepare = tda998x_encoder_prepare,
+ .commit = tda998x_encoder_commit,
+ .mode_set = tda998x_encoder_mode_set,
+};
+
+static void tda998x_encoder_destroy(struct drm_encoder *encoder)
{
- struct tda998x_priv *priv = to_tda998x_priv(encoder);
- drm_i2c_encoder_destroy(encoder);
+ drm_encoder_cleanup(encoder);
+}
- /* disable all IRQs and free the IRQ handler */
- cec_write(priv, REG_CEC_RXSHPDINTENA, 0);
- reg_clear(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);
- if (priv->hdmi->irq)
- free_irq(priv->hdmi->irq, priv);
+static const struct drm_encoder_funcs encoder_funcs = {
+ .destroy = tda998x_encoder_destroy,
+};
- if (priv->cec)
- i2c_unregister_device(priv->cec);
- kfree(priv);
+static const struct drm_connector_helper_funcs connector_helper_funcs = {
+ .get_modes = tda998x_connector_get_modes,
+ .mode_valid = tda998x_connector_mode_valid,
+ .best_encoder = tda998x_connector_best_encoder,
+};
+
+static void tda998x_connector_destroy(struct drm_connector *connector)
+{
+ if (!connector->dev)
+ return;
+ drm_sysfs_connector_remove(connector);
+ drm_connector_cleanup(connector);
}
-static struct drm_encoder_slave_funcs tda998x_encoder_funcs = {
- .set_config = tda998x_encoder_set_config,
- .destroy = tda998x_encoder_destroy,
- .dpms = tda998x_encoder_dpms,
- .save = tda998x_encoder_save,
- .restore = tda998x_encoder_restore,
- .mode_fixup = tda998x_encoder_mode_fixup,
- .mode_valid = tda998x_encoder_mode_valid,
- .mode_set = tda998x_encoder_mode_set,
- .detect = tda998x_encoder_detect,
- .get_modes = tda998x_encoder_get_modes,
- .create_resources = tda998x_encoder_create_resources,
- .set_property = tda998x_encoder_set_property,
+static const struct drm_connector_funcs connector_funcs = {
+ .detect = tda998x_connector_detect,
+ .set_property = tda998x_connector_set_property,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .dpms = drm_helper_connector_dpms,
+ .destroy = tda998x_connector_destroy,
};
/* I2C driver functions */
-static int
-tda998x_probe(struct i2c_client *client, const struct i2c_device_id *id)
+static int tda_bind(struct device *dev, struct device *master, void *data)
{
+ struct drm_device *drm = data;
+ struct i2c_client *i2c_client = to_i2c_client(dev);
+ struct tda998x_priv *priv = i2c_get_clientdata(i2c_client);
+ struct drm_connector *connector = &priv->connector;
+ struct drm_encoder *encoder = &priv->encoder;
+ int ret;
+
+ if (!try_module_get(THIS_MODULE)) {
+ dev_err(dev, "cannot get module %s\n", THIS_MODULE->name);
+ return -EINVAL;
+ }
+
+ ret = drm_connector_init(drm, connector,
+ &connector_funcs,
+ DRM_MODE_CONNECTOR_HDMIA);
+ if (ret < 0)
+ return ret;
+ drm_connector_helper_add(connector, &connector_helper_funcs);
+
+ ret = drm_encoder_init(drm, encoder,
+ &encoder_funcs,
+ DRM_MODE_ENCODER_TMDS);
+
+ encoder->possible_crtcs = 1; // 1 << lcd_num
+
+ if (ret < 0)
+ goto err;
+ drm_encoder_helper_add(encoder, &encoder_helper_funcs);
+
+ ret = drm_mode_connector_attach_encoder(connector, encoder);
+ if (ret < 0)
+ goto err;
+ connector->encoder = encoder;
+
+ drm_sysfs_connector_add(connector);
+
+ drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
+ ret = drm_object_property_set_value(&connector->base,
+ drm->mode_config.dpms_property,
+ DRM_MODE_DPMS_OFF);
+
+ if (priv->hdmi->irq)
+ connector->polled = DRM_CONNECTOR_POLL_HPD;
+ else
+ connector->polled = DRM_CONNECTOR_POLL_CONNECT |
+ DRM_CONNECTOR_POLL_DISCONNECT;
return 0;
+
+err:
+ if (encoder->dev)
+ drm_encoder_cleanup(encoder);
+ if (connector->dev)
+ drm_connector_cleanup(connector);
+ return ret;
}
-static int
-tda998x_remove(struct i2c_client *client)
+static void tda_unbind(struct device *dev, struct device *master, void *data)
{
- return 0;
+ module_put(THIS_MODULE);
}
+static const struct component_ops comp_ops = {
+ .bind = tda_bind,
+ .unbind = tda_unbind,
+};
+
static int
-tda998x_encoder_init(struct i2c_client *client,
- struct drm_device *dev,
- struct drm_encoder_slave *encoder_slave)
+tda998x_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct tda998x_priv *priv;
struct device_node *np = client->dev.of_node;
@@ -1239,6 +1291,8 @@ tda998x_encoder_init(struct i2c_client *client,
if (!priv)
return -ENOMEM;
+ i2c_set_clientdata(client, priv);
+
priv->vip_cntrl_0 = VIP_CNTRL_0_SWAP_A(2) | VIP_CNTRL_0_SWAP_B(3);
priv->vip_cntrl_1 = VIP_CNTRL_1_SWAP_C(0) | VIP_CNTRL_1_SWAP_D(1);
priv->vip_cntrl_2 = VIP_CNTRL_2_SWAP_E(4) | VIP_CNTRL_2_SWAP_F(5);
@@ -1250,13 +1304,8 @@ tda998x_encoder_init(struct i2c_client *client,
kfree(priv);
return -ENODEV;
}
-
- priv->encoder = &encoder_slave->base;
priv->dpms = DRM_MODE_DPMS_OFF;
- encoder_slave->slave_priv = priv;
- encoder_slave->slave_funcs = &tda998x_encoder_funcs;
-
/* wake up the device: */
cec_write(priv, REG_CEC_ENAMODS,
CEC_ENAMODS_EN_RXSENS | CEC_ENAMODS_EN_HDMI);
@@ -1340,31 +1389,55 @@ tda998x_encoder_init(struct i2c_client *client,
/* enable EDID read irq: */
reg_set(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);
- if (!np)
- return 0; /* non-DT */
+ if (np) { /* if DT */
- /* get the optional video properties */
- ret = of_property_read_u32(np, "video-ports", &video);
- if (ret == 0) {
- priv->vip_cntrl_0 = video >> 16;
- priv->vip_cntrl_1 = video >> 8;
- priv->vip_cntrl_2 = video;
+ /* get the optional video properties */
+ ret = of_property_read_u32(np, "video-ports", &video);
+ if (ret == 0) {
+ priv->vip_cntrl_0 = video >> 16;
+ priv->vip_cntrl_1 = video >> 8;
+ priv->vip_cntrl_2 = video;
+ }
+ } else {
+ struct tda998x_encoder_params *params =
+ (struct tda998x_encoder_params)
+ client->dev.platform_data;
+
+ if (params)
+ tda998x_encoder_set_config(priv, params);
}
+ /* tda998x video component ready */
+ component_add(&client->dev, &comp_ops);
+
return 0;
fail:
- /* if encoder_init fails, the encoder slave is never registered,
- * so cleanup here:
- */
if (priv->cec)
i2c_unregister_device(priv->cec);
kfree(priv);
- encoder_slave->slave_priv = NULL;
- encoder_slave->slave_funcs = NULL;
return -ENXIO;
}
+static int
+tda998x_remove(struct i2c_client *client)
+{
+ struct tda998x_priv *priv = i2c_get_clientdata(client);
+
+ /* disable all IRQs and free the IRQ handler */
+ cec_write(priv, REG_CEC_RXSHPDINTENA, 0);
+ reg_clear(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);
+ if (priv->hdmi->irq)
+ free_irq(priv->hdmi->irq, priv);
+
+ component_del(&client->dev, &comp_ops);
+
+ if (priv->cec)
+ i2c_unregister_device(priv->cec);
+ kfree(priv);
+ return 0;
+}
+
#ifdef CONFIG_OF
static const struct of_device_id tda998x_dt_ids[] = {
{ .compatible = "nxp,tda9989", },
@@ -1381,38 +1454,18 @@ static struct i2c_device_id tda998x_ids[] = {
};
MODULE_DEVICE_TABLE(i2c, tda998x_ids);
-static struct drm_i2c_encoder_driver tda998x_driver = {
- .i2c_driver = {
- .probe = tda998x_probe,
- .remove = tda998x_remove,
- .driver = {
- .name = "tda998x",
- .of_match_table = of_match_ptr(tda998x_dt_ids),
- },
- .id_table = tda998x_ids,
+static struct i2c_driver tda998x_driver = {
+ .probe = tda998x_probe,
+ .remove = tda998x_remove,
+ .driver = {
+ .name = "tda998x",
+ .of_match_table = of_match_ptr(tda998x_dt_ids),
},
- .encoder_init = tda998x_encoder_init,
+ .id_table = tda998x_ids,
};
-/* Module initialization */
-
-static int __init
-tda998x_init(void)
-{
- DBG("");
- return drm_i2c_encoder_register(THIS_MODULE, &tda998x_driver);
-}
-
-static void __exit
-tda998x_exit(void)
-{
- DBG("");
- drm_i2c_encoder_unregister(&tda998x_driver);
-}
+module_i2c_driver(tda998x_driver);
MODULE_AUTHOR("Rob Clark <robdclark@gmail.com");
MODULE_DESCRIPTION("NXP Semiconductors TDA998X HDMI Encoder");
MODULE_LICENSE("GPL");
-
-module_init(tda998x_init);
-module_exit(tda998x_exit);
--
1.9.1
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH RFC v2 4/6] drm/tilcdc: Change the interface with the tda998x driver
2014-03-21 10:27 ` Jean-Francois Moine
@ 2014-03-21 8:36 ` Jean-Francois Moine
-1 siblings, 0 replies; 32+ messages in thread
From: Jean-Francois Moine @ 2014-03-21 8:36 UTC (permalink / raw)
To: Russell King, Rob Clark, dri-devel; +Cc: linux-arm-kernel, linux-media
The tda998x being moved from a 'slave encoder' to a normal DRM
encoder/connector, the tilcdc_slave glue is not needed anymore.
This patch uses the infrastructure for componentised subsystems
for waiting to the tda998x full start and to give it the pointer
to the DRM device.
Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
drivers/gpu/drm/tilcdc/Makefile | 1 -
drivers/gpu/drm/tilcdc/tilcdc_drv.c | 85 +++++--
drivers/gpu/drm/tilcdc/tilcdc_slave.c | 406 ----------------------------------
drivers/gpu/drm/tilcdc/tilcdc_slave.h | 26 ---
4 files changed, 68 insertions(+), 450 deletions(-)
delete mode 100644 drivers/gpu/drm/tilcdc/tilcdc_slave.c
delete mode 100644 drivers/gpu/drm/tilcdc/tilcdc_slave.h
diff --git a/drivers/gpu/drm/tilcdc/Makefile b/drivers/gpu/drm/tilcdc/Makefile
index 7d2eefe..44485f9 100644
--- a/drivers/gpu/drm/tilcdc/Makefile
+++ b/drivers/gpu/drm/tilcdc/Makefile
@@ -6,7 +6,6 @@ endif
tilcdc-y := \
tilcdc_crtc.o \
tilcdc_tfp410.o \
- tilcdc_slave.o \
tilcdc_panel.o \
tilcdc_drv.o
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c
index 171a820..dd6ebd3 100644
--- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c
+++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c
@@ -17,16 +17,16 @@
/* LCDC DRM driver, based on da8xx-fb */
+#include <linux/component.h>
+
#include "tilcdc_drv.h"
#include "tilcdc_regs.h"
#include "tilcdc_tfp410.h"
-#include "tilcdc_slave.h"
#include "tilcdc_panel.h"
#include "drm_fb_helper.h"
static LIST_HEAD(module_list);
-static bool slave_probing;
void tilcdc_module_init(struct tilcdc_module *mod, const char *name,
const struct tilcdc_module_ops *funcs)
@@ -42,11 +42,6 @@ void tilcdc_module_cleanup(struct tilcdc_module *mod)
list_del(&mod->list);
}
-void tilcdc_slave_probedefer(bool defered)
-{
- slave_probing = defered;
-}
-
static struct of_device_id tilcdc_of_match[];
static struct drm_framebuffer *tilcdc_fb_create(struct drm_device *dev,
@@ -277,6 +272,10 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)
platform_set_drvdata(pdev, dev);
+ /* initialize the subdevices */
+ ret = component_bind_all(&pdev->dev, dev);
+ if (ret < 0)
+ goto fail;
list_for_each_entry(mod, &module_list, list) {
DBG("%s: preferred_bpp: %d", mod->name, mod->preferred_bpp);
@@ -577,6 +576,66 @@ static const struct dev_pm_ops tilcdc_pm_ops = {
* Platform driver:
*/
+/* component stuff */
+static int of_dev_node_match(struct device *dev, void *data)
+{
+ return dev->of_node == data;
+}
+
+static int tilcdc_add_components(struct device *master, struct master *m)
+{
+ struct device_node *np = master->of_node, *child;
+ int ret;
+
+ /* scan the video ports */
+ child = NULL;
+ for (;;) {
+ struct device_node *endpoint, *port, *i2c_node;
+
+ child = of_get_next_child(np, child);
+ if (!child)
+ break;
+ if (strcmp(child->name, "port") != 0)
+ continue;
+
+ endpoint = of_get_next_child(child, NULL);
+ if (!endpoint) {
+ dev_err(master, "tilcdc: no port endpoint\n");
+ return -EINVAL;
+ }
+ port = of_parse_phandle(endpoint, "remote-endpoint", 0);
+ of_node_put(endpoint);
+ if (!port) {
+ dev_err(master, "ticldc: no remote-endpoint\n");
+ return -EINVAL;
+ }
+ i2c_node = of_get_parent(of_get_parent(port));
+ of_node_put(port);
+ ret = component_master_add_child(m, of_dev_node_match,
+ i2c_node);
+ of_node_put(i2c_node);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+static int tilcdc_bind(struct device *dev)
+{
+ return drm_platform_init(&tilcdc_driver, to_platform_device(dev));
+}
+
+static void tilcdc_unbind(struct device *dev)
+{
+ drm_put_dev(dev_get_drvdata(dev));
+}
+
+static const struct component_master_ops tilcdc_comp_ops = {
+ .add_components = tilcdc_add_components,
+ .bind = tilcdc_bind,
+ .unbind = tilcdc_unbind,
+};
+
static int tilcdc_pdev_probe(struct platform_device *pdev)
{
/* bail out early if no DT data: */
@@ -584,18 +643,12 @@ static int tilcdc_pdev_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "device-tree data is missing\n");
return -ENXIO;
}
-
- /* defer probing if slave is in deferred probing */
- if (slave_probing == true)
- return -EPROBE_DEFER;
-
- return drm_platform_init(&tilcdc_driver, pdev);
+ return component_master_add(&pdev->dev, &tilcdc_comp_ops);
}
static int tilcdc_pdev_remove(struct platform_device *pdev)
{
- drm_put_dev(platform_get_drvdata(pdev));
-
+ component_master_del(&pdev->dev, &tilcdc_comp_ops);
return 0;
}
@@ -620,7 +673,6 @@ static int __init tilcdc_drm_init(void)
{
DBG("init");
tilcdc_tfp410_init();
- tilcdc_slave_init();
tilcdc_panel_init();
return platform_driver_register(&tilcdc_platform_driver);
}
@@ -629,7 +681,6 @@ static void __exit tilcdc_drm_fini(void)
{
DBG("fini");
tilcdc_tfp410_fini();
- tilcdc_slave_fini();
tilcdc_panel_fini();
platform_driver_unregister(&tilcdc_platform_driver);
}
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_slave.c b/drivers/gpu/drm/tilcdc/tilcdc_slave.c
deleted file mode 100644
index 595068b..0000000
--- a/drivers/gpu/drm/tilcdc/tilcdc_slave.c
+++ /dev/null
@@ -1,406 +0,0 @@
-/*
- * Copyright (C) 2012 Texas Instruments
- * Author: Rob Clark <robdclark@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <linux/i2c.h>
-#include <linux/pinctrl/pinmux.h>
-#include <linux/pinctrl/consumer.h>
-#include <drm/drm_encoder_slave.h>
-
-#include "tilcdc_drv.h"
-
-struct slave_module {
- struct tilcdc_module base;
- struct i2c_adapter *i2c;
-};
-#define to_slave_module(x) container_of(x, struct slave_module, base)
-
-static const struct tilcdc_panel_info slave_info = {
- .bpp = 16,
- .ac_bias = 255,
- .ac_bias_intrpt = 0,
- .dma_burst_sz = 16,
- .fdd = 0x80,
- .tft_alt_mode = 0,
- .sync_edge = 0,
- .sync_ctrl = 1,
- .raster_order = 0,
-};
-
-
-/*
- * Encoder:
- */
-
-struct slave_encoder {
- struct drm_encoder_slave base;
- struct slave_module *mod;
-};
-#define to_slave_encoder(x) container_of(to_encoder_slave(x), struct slave_encoder, base)
-
-static inline struct drm_encoder_slave_funcs *
-get_slave_funcs(struct drm_encoder *enc)
-{
- return to_encoder_slave(enc)->slave_funcs;
-}
-
-static void slave_encoder_destroy(struct drm_encoder *encoder)
-{
- struct slave_encoder *slave_encoder = to_slave_encoder(encoder);
- if (get_slave_funcs(encoder))
- get_slave_funcs(encoder)->destroy(encoder);
- drm_encoder_cleanup(encoder);
- kfree(slave_encoder);
-}
-
-static void slave_encoder_prepare(struct drm_encoder *encoder)
-{
- drm_i2c_encoder_prepare(encoder);
- tilcdc_crtc_set_panel_info(encoder->crtc, &slave_info);
-}
-
-static bool slave_encoder_fixup(struct drm_encoder *encoder,
- const struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
-{
- /*
- * tilcdc does not generate VESA-complient sync but aligns
- * VS on the second edge of HS instead of first edge.
- * We use adjusted_mode, to fixup sync by aligning both rising
- * edges and add HSKEW offset to let the slave encoder fix it up.
- */
- adjusted_mode->hskew = mode->hsync_end - mode->hsync_start;
- adjusted_mode->flags |= DRM_MODE_FLAG_HSKEW;
-
- if (mode->flags & DRM_MODE_FLAG_NHSYNC) {
- adjusted_mode->flags |= DRM_MODE_FLAG_PHSYNC;
- adjusted_mode->flags &= ~DRM_MODE_FLAG_NHSYNC;
- } else {
- adjusted_mode->flags |= DRM_MODE_FLAG_NHSYNC;
- adjusted_mode->flags &= ~DRM_MODE_FLAG_PHSYNC;
- }
-
- return drm_i2c_encoder_mode_fixup(encoder, mode, adjusted_mode);
-}
-
-
-static const struct drm_encoder_funcs slave_encoder_funcs = {
- .destroy = slave_encoder_destroy,
-};
-
-static const struct drm_encoder_helper_funcs slave_encoder_helper_funcs = {
- .dpms = drm_i2c_encoder_dpms,
- .mode_fixup = slave_encoder_fixup,
- .prepare = slave_encoder_prepare,
- .commit = drm_i2c_encoder_commit,
- .mode_set = drm_i2c_encoder_mode_set,
- .save = drm_i2c_encoder_save,
- .restore = drm_i2c_encoder_restore,
-};
-
-static const struct i2c_board_info info = {
- I2C_BOARD_INFO("tda998x", 0x70)
-};
-
-static struct drm_encoder *slave_encoder_create(struct drm_device *dev,
- struct slave_module *mod)
-{
- struct slave_encoder *slave_encoder;
- struct drm_encoder *encoder;
- int ret;
-
- slave_encoder = kzalloc(sizeof(*slave_encoder), GFP_KERNEL);
- if (!slave_encoder) {
- dev_err(dev->dev, "allocation failed\n");
- return NULL;
- }
-
- slave_encoder->mod = mod;
-
- encoder = &slave_encoder->base.base;
- encoder->possible_crtcs = 1;
-
- ret = drm_encoder_init(dev, encoder, &slave_encoder_funcs,
- DRM_MODE_ENCODER_TMDS);
- if (ret)
- goto fail;
-
- drm_encoder_helper_add(encoder, &slave_encoder_helper_funcs);
-
- ret = drm_i2c_encoder_init(dev, to_encoder_slave(encoder), mod->i2c, &info);
- if (ret)
- goto fail;
-
- return encoder;
-
-fail:
- slave_encoder_destroy(encoder);
- return NULL;
-}
-
-/*
- * Connector:
- */
-
-struct slave_connector {
- struct drm_connector base;
-
- struct drm_encoder *encoder; /* our connected encoder */
- struct slave_module *mod;
-};
-#define to_slave_connector(x) container_of(x, struct slave_connector, base)
-
-static void slave_connector_destroy(struct drm_connector *connector)
-{
- struct slave_connector *slave_connector = to_slave_connector(connector);
- drm_connector_cleanup(connector);
- kfree(slave_connector);
-}
-
-static enum drm_connector_status slave_connector_detect(
- struct drm_connector *connector,
- bool force)
-{
- struct drm_encoder *encoder = to_slave_connector(connector)->encoder;
- return get_slave_funcs(encoder)->detect(encoder, connector);
-}
-
-static int slave_connector_get_modes(struct drm_connector *connector)
-{
- struct drm_encoder *encoder = to_slave_connector(connector)->encoder;
- return get_slave_funcs(encoder)->get_modes(encoder, connector);
-}
-
-static int slave_connector_mode_valid(struct drm_connector *connector,
- struct drm_display_mode *mode)
-{
- struct drm_encoder *encoder = to_slave_connector(connector)->encoder;
- struct tilcdc_drm_private *priv = connector->dev->dev_private;
- int ret;
-
- ret = tilcdc_crtc_mode_valid(priv->crtc, mode);
- if (ret != MODE_OK)
- return ret;
-
- return get_slave_funcs(encoder)->mode_valid(encoder, mode);
-}
-
-static struct drm_encoder *slave_connector_best_encoder(
- struct drm_connector *connector)
-{
- struct slave_connector *slave_connector = to_slave_connector(connector);
- return slave_connector->encoder;
-}
-
-static int slave_connector_set_property(struct drm_connector *connector,
- struct drm_property *property, uint64_t value)
-{
- struct drm_encoder *encoder = to_slave_connector(connector)->encoder;
- return get_slave_funcs(encoder)->set_property(encoder,
- connector, property, value);
-}
-
-static const struct drm_connector_funcs slave_connector_funcs = {
- .destroy = slave_connector_destroy,
- .dpms = drm_helper_connector_dpms,
- .detect = slave_connector_detect,
- .fill_modes = drm_helper_probe_single_connector_modes,
- .set_property = slave_connector_set_property,
-};
-
-static const struct drm_connector_helper_funcs slave_connector_helper_funcs = {
- .get_modes = slave_connector_get_modes,
- .mode_valid = slave_connector_mode_valid,
- .best_encoder = slave_connector_best_encoder,
-};
-
-static struct drm_connector *slave_connector_create(struct drm_device *dev,
- struct slave_module *mod, struct drm_encoder *encoder)
-{
- struct slave_connector *slave_connector;
- struct drm_connector *connector;
- int ret;
-
- slave_connector = kzalloc(sizeof(*slave_connector), GFP_KERNEL);
- if (!slave_connector) {
- dev_err(dev->dev, "allocation failed\n");
- return NULL;
- }
-
- slave_connector->encoder = encoder;
- slave_connector->mod = mod;
-
- connector = &slave_connector->base;
-
- drm_connector_init(dev, connector, &slave_connector_funcs,
- DRM_MODE_CONNECTOR_HDMIA);
- drm_connector_helper_add(connector, &slave_connector_helper_funcs);
-
- connector->polled = DRM_CONNECTOR_POLL_CONNECT |
- DRM_CONNECTOR_POLL_DISCONNECT;
-
- connector->interlace_allowed = 0;
- connector->doublescan_allowed = 0;
-
- get_slave_funcs(encoder)->create_resources(encoder, connector);
-
- ret = drm_mode_connector_attach_encoder(connector, encoder);
- if (ret)
- goto fail;
-
- drm_sysfs_connector_add(connector);
-
- return connector;
-
-fail:
- slave_connector_destroy(connector);
- return NULL;
-}
-
-/*
- * Module:
- */
-
-static int slave_modeset_init(struct tilcdc_module *mod, struct drm_device *dev)
-{
- struct slave_module *slave_mod = to_slave_module(mod);
- struct tilcdc_drm_private *priv = dev->dev_private;
- struct drm_encoder *encoder;
- struct drm_connector *connector;
-
- encoder = slave_encoder_create(dev, slave_mod);
- if (!encoder)
- return -ENOMEM;
-
- connector = slave_connector_create(dev, slave_mod, encoder);
- if (!connector)
- return -ENOMEM;
-
- priv->encoders[priv->num_encoders++] = encoder;
- priv->connectors[priv->num_connectors++] = connector;
-
- return 0;
-}
-
-static void slave_destroy(struct tilcdc_module *mod)
-{
- struct slave_module *slave_mod = to_slave_module(mod);
-
- tilcdc_module_cleanup(mod);
- kfree(slave_mod);
-}
-
-static const struct tilcdc_module_ops slave_module_ops = {
- .modeset_init = slave_modeset_init,
- .destroy = slave_destroy,
-};
-
-/*
- * Device:
- */
-
-static struct of_device_id slave_of_match[];
-
-static int slave_probe(struct platform_device *pdev)
-{
- struct device_node *node = pdev->dev.of_node;
- struct device_node *i2c_node;
- struct slave_module *slave_mod;
- struct tilcdc_module *mod;
- struct pinctrl *pinctrl;
- uint32_t i2c_phandle;
- struct i2c_adapter *slavei2c;
- int ret = -EINVAL;
-
- /* bail out early if no DT data: */
- if (!node) {
- dev_err(&pdev->dev, "device-tree data is missing\n");
- return -ENXIO;
- }
-
- /* Bail out early if i2c not specified */
- if (of_property_read_u32(node, "i2c", &i2c_phandle)) {
- dev_err(&pdev->dev, "could not get i2c bus phandle\n");
- return ret;
- }
-
- i2c_node = of_find_node_by_phandle(i2c_phandle);
- if (!i2c_node) {
- dev_err(&pdev->dev, "could not get i2c bus node\n");
- return ret;
- }
-
- /* but defer the probe if it can't be initialized it might come later */
- slavei2c = of_find_i2c_adapter_by_node(i2c_node);
- of_node_put(i2c_node);
-
- if (!slavei2c) {
- ret = -EPROBE_DEFER;
- tilcdc_slave_probedefer(true);
- dev_err(&pdev->dev, "could not get i2c\n");
- return ret;
- }
-
- slave_mod = kzalloc(sizeof(*slave_mod), GFP_KERNEL);
- if (!slave_mod)
- return -ENOMEM;
-
- mod = &slave_mod->base;
-
- mod->preferred_bpp = slave_info.bpp;
-
- slave_mod->i2c = slavei2c;
-
- tilcdc_module_init(mod, "slave", &slave_module_ops);
-
- pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
- if (IS_ERR(pinctrl))
- dev_warn(&pdev->dev, "pins are not configured\n");
-
- tilcdc_slave_probedefer(false);
-
- return 0;
-}
-
-static int slave_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
-static struct of_device_id slave_of_match[] = {
- { .compatible = "ti,tilcdc,slave", },
- { },
-};
-
-struct platform_driver slave_driver = {
- .probe = slave_probe,
- .remove = slave_remove,
- .driver = {
- .owner = THIS_MODULE,
- .name = "slave",
- .of_match_table = slave_of_match,
- },
-};
-
-int __init tilcdc_slave_init(void)
-{
- return platform_driver_register(&slave_driver);
-}
-
-void __exit tilcdc_slave_fini(void)
-{
- platform_driver_unregister(&slave_driver);
-}
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_slave.h b/drivers/gpu/drm/tilcdc/tilcdc_slave.h
deleted file mode 100644
index 2f85048..0000000
--- a/drivers/gpu/drm/tilcdc/tilcdc_slave.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2012 Texas Instruments
- * Author: Rob Clark <robdclark@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef __TILCDC_SLAVE_H__
-#define __TILCDC_SLAVE_H__
-
-/* sub-module for i2c slave encoder output */
-
-int tilcdc_slave_init(void);
-void tilcdc_slave_fini(void);
-
-#endif /* __TILCDC_SLAVE_H__ */
--
1.9.1
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH RFC v2 4/6] drm/tilcdc: Change the interface with the tda998x driver
@ 2014-03-21 8:36 ` Jean-Francois Moine
0 siblings, 0 replies; 32+ messages in thread
From: Jean-Francois Moine @ 2014-03-21 8:36 UTC (permalink / raw)
To: linux-arm-kernel
The tda998x being moved from a 'slave encoder' to a normal DRM
encoder/connector, the tilcdc_slave glue is not needed anymore.
This patch uses the infrastructure for componentised subsystems
for waiting to the tda998x full start and to give it the pointer
to the DRM device.
Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
drivers/gpu/drm/tilcdc/Makefile | 1 -
drivers/gpu/drm/tilcdc/tilcdc_drv.c | 85 +++++--
drivers/gpu/drm/tilcdc/tilcdc_slave.c | 406 ----------------------------------
drivers/gpu/drm/tilcdc/tilcdc_slave.h | 26 ---
4 files changed, 68 insertions(+), 450 deletions(-)
delete mode 100644 drivers/gpu/drm/tilcdc/tilcdc_slave.c
delete mode 100644 drivers/gpu/drm/tilcdc/tilcdc_slave.h
diff --git a/drivers/gpu/drm/tilcdc/Makefile b/drivers/gpu/drm/tilcdc/Makefile
index 7d2eefe..44485f9 100644
--- a/drivers/gpu/drm/tilcdc/Makefile
+++ b/drivers/gpu/drm/tilcdc/Makefile
@@ -6,7 +6,6 @@ endif
tilcdc-y := \
tilcdc_crtc.o \
tilcdc_tfp410.o \
- tilcdc_slave.o \
tilcdc_panel.o \
tilcdc_drv.o
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c
index 171a820..dd6ebd3 100644
--- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c
+++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c
@@ -17,16 +17,16 @@
/* LCDC DRM driver, based on da8xx-fb */
+#include <linux/component.h>
+
#include "tilcdc_drv.h"
#include "tilcdc_regs.h"
#include "tilcdc_tfp410.h"
-#include "tilcdc_slave.h"
#include "tilcdc_panel.h"
#include "drm_fb_helper.h"
static LIST_HEAD(module_list);
-static bool slave_probing;
void tilcdc_module_init(struct tilcdc_module *mod, const char *name,
const struct tilcdc_module_ops *funcs)
@@ -42,11 +42,6 @@ void tilcdc_module_cleanup(struct tilcdc_module *mod)
list_del(&mod->list);
}
-void tilcdc_slave_probedefer(bool defered)
-{
- slave_probing = defered;
-}
-
static struct of_device_id tilcdc_of_match[];
static struct drm_framebuffer *tilcdc_fb_create(struct drm_device *dev,
@@ -277,6 +272,10 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)
platform_set_drvdata(pdev, dev);
+ /* initialize the subdevices */
+ ret = component_bind_all(&pdev->dev, dev);
+ if (ret < 0)
+ goto fail;
list_for_each_entry(mod, &module_list, list) {
DBG("%s: preferred_bpp: %d", mod->name, mod->preferred_bpp);
@@ -577,6 +576,66 @@ static const struct dev_pm_ops tilcdc_pm_ops = {
* Platform driver:
*/
+/* component stuff */
+static int of_dev_node_match(struct device *dev, void *data)
+{
+ return dev->of_node == data;
+}
+
+static int tilcdc_add_components(struct device *master, struct master *m)
+{
+ struct device_node *np = master->of_node, *child;
+ int ret;
+
+ /* scan the video ports */
+ child = NULL;
+ for (;;) {
+ struct device_node *endpoint, *port, *i2c_node;
+
+ child = of_get_next_child(np, child);
+ if (!child)
+ break;
+ if (strcmp(child->name, "port") != 0)
+ continue;
+
+ endpoint = of_get_next_child(child, NULL);
+ if (!endpoint) {
+ dev_err(master, "tilcdc: no port endpoint\n");
+ return -EINVAL;
+ }
+ port = of_parse_phandle(endpoint, "remote-endpoint", 0);
+ of_node_put(endpoint);
+ if (!port) {
+ dev_err(master, "ticldc: no remote-endpoint\n");
+ return -EINVAL;
+ }
+ i2c_node = of_get_parent(of_get_parent(port));
+ of_node_put(port);
+ ret = component_master_add_child(m, of_dev_node_match,
+ i2c_node);
+ of_node_put(i2c_node);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+static int tilcdc_bind(struct device *dev)
+{
+ return drm_platform_init(&tilcdc_driver, to_platform_device(dev));
+}
+
+static void tilcdc_unbind(struct device *dev)
+{
+ drm_put_dev(dev_get_drvdata(dev));
+}
+
+static const struct component_master_ops tilcdc_comp_ops = {
+ .add_components = tilcdc_add_components,
+ .bind = tilcdc_bind,
+ .unbind = tilcdc_unbind,
+};
+
static int tilcdc_pdev_probe(struct platform_device *pdev)
{
/* bail out early if no DT data: */
@@ -584,18 +643,12 @@ static int tilcdc_pdev_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "device-tree data is missing\n");
return -ENXIO;
}
-
- /* defer probing if slave is in deferred probing */
- if (slave_probing == true)
- return -EPROBE_DEFER;
-
- return drm_platform_init(&tilcdc_driver, pdev);
+ return component_master_add(&pdev->dev, &tilcdc_comp_ops);
}
static int tilcdc_pdev_remove(struct platform_device *pdev)
{
- drm_put_dev(platform_get_drvdata(pdev));
-
+ component_master_del(&pdev->dev, &tilcdc_comp_ops);
return 0;
}
@@ -620,7 +673,6 @@ static int __init tilcdc_drm_init(void)
{
DBG("init");
tilcdc_tfp410_init();
- tilcdc_slave_init();
tilcdc_panel_init();
return platform_driver_register(&tilcdc_platform_driver);
}
@@ -629,7 +681,6 @@ static void __exit tilcdc_drm_fini(void)
{
DBG("fini");
tilcdc_tfp410_fini();
- tilcdc_slave_fini();
tilcdc_panel_fini();
platform_driver_unregister(&tilcdc_platform_driver);
}
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_slave.c b/drivers/gpu/drm/tilcdc/tilcdc_slave.c
deleted file mode 100644
index 595068b..0000000
--- a/drivers/gpu/drm/tilcdc/tilcdc_slave.c
+++ /dev/null
@@ -1,406 +0,0 @@
-/*
- * Copyright (C) 2012 Texas Instruments
- * Author: Rob Clark <robdclark@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <linux/i2c.h>
-#include <linux/pinctrl/pinmux.h>
-#include <linux/pinctrl/consumer.h>
-#include <drm/drm_encoder_slave.h>
-
-#include "tilcdc_drv.h"
-
-struct slave_module {
- struct tilcdc_module base;
- struct i2c_adapter *i2c;
-};
-#define to_slave_module(x) container_of(x, struct slave_module, base)
-
-static const struct tilcdc_panel_info slave_info = {
- .bpp = 16,
- .ac_bias = 255,
- .ac_bias_intrpt = 0,
- .dma_burst_sz = 16,
- .fdd = 0x80,
- .tft_alt_mode = 0,
- .sync_edge = 0,
- .sync_ctrl = 1,
- .raster_order = 0,
-};
-
-
-/*
- * Encoder:
- */
-
-struct slave_encoder {
- struct drm_encoder_slave base;
- struct slave_module *mod;
-};
-#define to_slave_encoder(x) container_of(to_encoder_slave(x), struct slave_encoder, base)
-
-static inline struct drm_encoder_slave_funcs *
-get_slave_funcs(struct drm_encoder *enc)
-{
- return to_encoder_slave(enc)->slave_funcs;
-}
-
-static void slave_encoder_destroy(struct drm_encoder *encoder)
-{
- struct slave_encoder *slave_encoder = to_slave_encoder(encoder);
- if (get_slave_funcs(encoder))
- get_slave_funcs(encoder)->destroy(encoder);
- drm_encoder_cleanup(encoder);
- kfree(slave_encoder);
-}
-
-static void slave_encoder_prepare(struct drm_encoder *encoder)
-{
- drm_i2c_encoder_prepare(encoder);
- tilcdc_crtc_set_panel_info(encoder->crtc, &slave_info);
-}
-
-static bool slave_encoder_fixup(struct drm_encoder *encoder,
- const struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
-{
- /*
- * tilcdc does not generate VESA-complient sync but aligns
- * VS on the second edge of HS instead of first edge.
- * We use adjusted_mode, to fixup sync by aligning both rising
- * edges and add HSKEW offset to let the slave encoder fix it up.
- */
- adjusted_mode->hskew = mode->hsync_end - mode->hsync_start;
- adjusted_mode->flags |= DRM_MODE_FLAG_HSKEW;
-
- if (mode->flags & DRM_MODE_FLAG_NHSYNC) {
- adjusted_mode->flags |= DRM_MODE_FLAG_PHSYNC;
- adjusted_mode->flags &= ~DRM_MODE_FLAG_NHSYNC;
- } else {
- adjusted_mode->flags |= DRM_MODE_FLAG_NHSYNC;
- adjusted_mode->flags &= ~DRM_MODE_FLAG_PHSYNC;
- }
-
- return drm_i2c_encoder_mode_fixup(encoder, mode, adjusted_mode);
-}
-
-
-static const struct drm_encoder_funcs slave_encoder_funcs = {
- .destroy = slave_encoder_destroy,
-};
-
-static const struct drm_encoder_helper_funcs slave_encoder_helper_funcs = {
- .dpms = drm_i2c_encoder_dpms,
- .mode_fixup = slave_encoder_fixup,
- .prepare = slave_encoder_prepare,
- .commit = drm_i2c_encoder_commit,
- .mode_set = drm_i2c_encoder_mode_set,
- .save = drm_i2c_encoder_save,
- .restore = drm_i2c_encoder_restore,
-};
-
-static const struct i2c_board_info info = {
- I2C_BOARD_INFO("tda998x", 0x70)
-};
-
-static struct drm_encoder *slave_encoder_create(struct drm_device *dev,
- struct slave_module *mod)
-{
- struct slave_encoder *slave_encoder;
- struct drm_encoder *encoder;
- int ret;
-
- slave_encoder = kzalloc(sizeof(*slave_encoder), GFP_KERNEL);
- if (!slave_encoder) {
- dev_err(dev->dev, "allocation failed\n");
- return NULL;
- }
-
- slave_encoder->mod = mod;
-
- encoder = &slave_encoder->base.base;
- encoder->possible_crtcs = 1;
-
- ret = drm_encoder_init(dev, encoder, &slave_encoder_funcs,
- DRM_MODE_ENCODER_TMDS);
- if (ret)
- goto fail;
-
- drm_encoder_helper_add(encoder, &slave_encoder_helper_funcs);
-
- ret = drm_i2c_encoder_init(dev, to_encoder_slave(encoder), mod->i2c, &info);
- if (ret)
- goto fail;
-
- return encoder;
-
-fail:
- slave_encoder_destroy(encoder);
- return NULL;
-}
-
-/*
- * Connector:
- */
-
-struct slave_connector {
- struct drm_connector base;
-
- struct drm_encoder *encoder; /* our connected encoder */
- struct slave_module *mod;
-};
-#define to_slave_connector(x) container_of(x, struct slave_connector, base)
-
-static void slave_connector_destroy(struct drm_connector *connector)
-{
- struct slave_connector *slave_connector = to_slave_connector(connector);
- drm_connector_cleanup(connector);
- kfree(slave_connector);
-}
-
-static enum drm_connector_status slave_connector_detect(
- struct drm_connector *connector,
- bool force)
-{
- struct drm_encoder *encoder = to_slave_connector(connector)->encoder;
- return get_slave_funcs(encoder)->detect(encoder, connector);
-}
-
-static int slave_connector_get_modes(struct drm_connector *connector)
-{
- struct drm_encoder *encoder = to_slave_connector(connector)->encoder;
- return get_slave_funcs(encoder)->get_modes(encoder, connector);
-}
-
-static int slave_connector_mode_valid(struct drm_connector *connector,
- struct drm_display_mode *mode)
-{
- struct drm_encoder *encoder = to_slave_connector(connector)->encoder;
- struct tilcdc_drm_private *priv = connector->dev->dev_private;
- int ret;
-
- ret = tilcdc_crtc_mode_valid(priv->crtc, mode);
- if (ret != MODE_OK)
- return ret;
-
- return get_slave_funcs(encoder)->mode_valid(encoder, mode);
-}
-
-static struct drm_encoder *slave_connector_best_encoder(
- struct drm_connector *connector)
-{
- struct slave_connector *slave_connector = to_slave_connector(connector);
- return slave_connector->encoder;
-}
-
-static int slave_connector_set_property(struct drm_connector *connector,
- struct drm_property *property, uint64_t value)
-{
- struct drm_encoder *encoder = to_slave_connector(connector)->encoder;
- return get_slave_funcs(encoder)->set_property(encoder,
- connector, property, value);
-}
-
-static const struct drm_connector_funcs slave_connector_funcs = {
- .destroy = slave_connector_destroy,
- .dpms = drm_helper_connector_dpms,
- .detect = slave_connector_detect,
- .fill_modes = drm_helper_probe_single_connector_modes,
- .set_property = slave_connector_set_property,
-};
-
-static const struct drm_connector_helper_funcs slave_connector_helper_funcs = {
- .get_modes = slave_connector_get_modes,
- .mode_valid = slave_connector_mode_valid,
- .best_encoder = slave_connector_best_encoder,
-};
-
-static struct drm_connector *slave_connector_create(struct drm_device *dev,
- struct slave_module *mod, struct drm_encoder *encoder)
-{
- struct slave_connector *slave_connector;
- struct drm_connector *connector;
- int ret;
-
- slave_connector = kzalloc(sizeof(*slave_connector), GFP_KERNEL);
- if (!slave_connector) {
- dev_err(dev->dev, "allocation failed\n");
- return NULL;
- }
-
- slave_connector->encoder = encoder;
- slave_connector->mod = mod;
-
- connector = &slave_connector->base;
-
- drm_connector_init(dev, connector, &slave_connector_funcs,
- DRM_MODE_CONNECTOR_HDMIA);
- drm_connector_helper_add(connector, &slave_connector_helper_funcs);
-
- connector->polled = DRM_CONNECTOR_POLL_CONNECT |
- DRM_CONNECTOR_POLL_DISCONNECT;
-
- connector->interlace_allowed = 0;
- connector->doublescan_allowed = 0;
-
- get_slave_funcs(encoder)->create_resources(encoder, connector);
-
- ret = drm_mode_connector_attach_encoder(connector, encoder);
- if (ret)
- goto fail;
-
- drm_sysfs_connector_add(connector);
-
- return connector;
-
-fail:
- slave_connector_destroy(connector);
- return NULL;
-}
-
-/*
- * Module:
- */
-
-static int slave_modeset_init(struct tilcdc_module *mod, struct drm_device *dev)
-{
- struct slave_module *slave_mod = to_slave_module(mod);
- struct tilcdc_drm_private *priv = dev->dev_private;
- struct drm_encoder *encoder;
- struct drm_connector *connector;
-
- encoder = slave_encoder_create(dev, slave_mod);
- if (!encoder)
- return -ENOMEM;
-
- connector = slave_connector_create(dev, slave_mod, encoder);
- if (!connector)
- return -ENOMEM;
-
- priv->encoders[priv->num_encoders++] = encoder;
- priv->connectors[priv->num_connectors++] = connector;
-
- return 0;
-}
-
-static void slave_destroy(struct tilcdc_module *mod)
-{
- struct slave_module *slave_mod = to_slave_module(mod);
-
- tilcdc_module_cleanup(mod);
- kfree(slave_mod);
-}
-
-static const struct tilcdc_module_ops slave_module_ops = {
- .modeset_init = slave_modeset_init,
- .destroy = slave_destroy,
-};
-
-/*
- * Device:
- */
-
-static struct of_device_id slave_of_match[];
-
-static int slave_probe(struct platform_device *pdev)
-{
- struct device_node *node = pdev->dev.of_node;
- struct device_node *i2c_node;
- struct slave_module *slave_mod;
- struct tilcdc_module *mod;
- struct pinctrl *pinctrl;
- uint32_t i2c_phandle;
- struct i2c_adapter *slavei2c;
- int ret = -EINVAL;
-
- /* bail out early if no DT data: */
- if (!node) {
- dev_err(&pdev->dev, "device-tree data is missing\n");
- return -ENXIO;
- }
-
- /* Bail out early if i2c not specified */
- if (of_property_read_u32(node, "i2c", &i2c_phandle)) {
- dev_err(&pdev->dev, "could not get i2c bus phandle\n");
- return ret;
- }
-
- i2c_node = of_find_node_by_phandle(i2c_phandle);
- if (!i2c_node) {
- dev_err(&pdev->dev, "could not get i2c bus node\n");
- return ret;
- }
-
- /* but defer the probe if it can't be initialized it might come later */
- slavei2c = of_find_i2c_adapter_by_node(i2c_node);
- of_node_put(i2c_node);
-
- if (!slavei2c) {
- ret = -EPROBE_DEFER;
- tilcdc_slave_probedefer(true);
- dev_err(&pdev->dev, "could not get i2c\n");
- return ret;
- }
-
- slave_mod = kzalloc(sizeof(*slave_mod), GFP_KERNEL);
- if (!slave_mod)
- return -ENOMEM;
-
- mod = &slave_mod->base;
-
- mod->preferred_bpp = slave_info.bpp;
-
- slave_mod->i2c = slavei2c;
-
- tilcdc_module_init(mod, "slave", &slave_module_ops);
-
- pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
- if (IS_ERR(pinctrl))
- dev_warn(&pdev->dev, "pins are not configured\n");
-
- tilcdc_slave_probedefer(false);
-
- return 0;
-}
-
-static int slave_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
-static struct of_device_id slave_of_match[] = {
- { .compatible = "ti,tilcdc,slave", },
- { },
-};
-
-struct platform_driver slave_driver = {
- .probe = slave_probe,
- .remove = slave_remove,
- .driver = {
- .owner = THIS_MODULE,
- .name = "slave",
- .of_match_table = slave_of_match,
- },
-};
-
-int __init tilcdc_slave_init(void)
-{
- return platform_driver_register(&slave_driver);
-}
-
-void __exit tilcdc_slave_fini(void)
-{
- platform_driver_unregister(&slave_driver);
-}
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_slave.h b/drivers/gpu/drm/tilcdc/tilcdc_slave.h
deleted file mode 100644
index 2f85048..0000000
--- a/drivers/gpu/drm/tilcdc/tilcdc_slave.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2012 Texas Instruments
- * Author: Rob Clark <robdclark@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef __TILCDC_SLAVE_H__
-#define __TILCDC_SLAVE_H__
-
-/* sub-module for i2c slave encoder output */
-
-int tilcdc_slave_init(void);
-void tilcdc_slave_fini(void);
-
-#endif /* __TILCDC_SLAVE_H__ */
--
1.9.1
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH RFC v2 6/6] ARM: AM33XX: dts: Change the tda998x description
2014-03-21 10:27 ` Jean-Francois Moine
@ 2014-03-21 8:52 ` Jean-Francois Moine
-1 siblings, 0 replies; 32+ messages in thread
From: Jean-Francois Moine @ 2014-03-21 8:52 UTC (permalink / raw)
To: Russell King, Rob Clark, dri-devel
Cc: devicetree, linux-arm-kernel, linux-media
The tda998x being moved from a 'slave encoder' to a normal DRM
encoder/connector and the tilcdc_slave glue being removed, the
declaration of the HDMI transmitter description must be changed in
the associated DTs.
Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
arch/arm/boot/dts/am335x-base0033.dts | 28 +++++++++++++++++++---------
arch/arm/boot/dts/am335x-boneblack.dts | 21 ++++++++++++++++-----
2 files changed, 35 insertions(+), 14 deletions(-)
diff --git a/arch/arm/boot/dts/am335x-base0033.dts b/arch/arm/boot/dts/am335x-base0033.dts
index 72a9b3f..05f2b8f 100644
--- a/arch/arm/boot/dts/am335x-base0033.dts
+++ b/arch/arm/boot/dts/am335x-base0033.dts
@@ -14,15 +14,6 @@
model = "IGEP COM AM335x on AQUILA Expansion";
compatible = "isee,am335x-base0033", "isee,am335x-igep0033", "ti,am33xx";
- hdmi {
- compatible = "ti,tilcdc,slave";
- i2c = <&i2c0>;
- pinctrl-names = "default", "off";
- pinctrl-0 = <&nxp_hdmi_pins>;
- pinctrl-1 = <&nxp_hdmi_off_pins>;
- status = "okay";
- };
-
leds_base {
pinctrl-names = "default";
pinctrl-0 = <&leds_base_pins>;
@@ -85,6 +76,11 @@
&lcdc {
status = "okay";
+ port {
+ lcd_0: endpoint@0 {
+ remote-endpoint = <&hdmi_0>;
+ };
+ };
};
&i2c0 {
@@ -92,4 +88,18 @@
compatible = "at,24c256";
reg = <0x50>;
};
+ hdmi: hdmi-encoder {
+ compatible = "nxp,tda19988";
+ reg = <0x70>;
+
+ pinctrl-names = "default", "off";
+ pinctrl-0 = <&nxp_hdmi_pins>;
+ pinctrl-1 = <&nxp_hdmi_off_pins>;
+
+ port {
+ hdmi_0: endpoint@0 {
+ remote-endpoint = <&lcd_0>;
+ };
+ };
+ };
};
diff --git a/arch/arm/boot/dts/am335x-boneblack.dts b/arch/arm/boot/dts/am335x-boneblack.dts
index 6b71ad9..b94d8bd 100644
--- a/arch/arm/boot/dts/am335x-boneblack.dts
+++ b/arch/arm/boot/dts/am335x-boneblack.dts
@@ -64,15 +64,26 @@
&lcdc {
status = "okay";
+ port {
+ lcd_0: endpoint@0 {
+ remote-endpoint = <&hdmi_0>;
+ };
+ };
};
-/ {
- hdmi {
- compatible = "ti,tilcdc,slave";
- i2c = <&i2c0>;
+&i2c0 {
+ hdmi: hdmi-encoder {
+ compatible = "nxp,tda19988";
+ reg = <0x70>;
+
pinctrl-names = "default", "off";
pinctrl-0 = <&nxp_hdmi_bonelt_pins>;
pinctrl-1 = <&nxp_hdmi_bonelt_off_pins>;
- status = "okay";
+
+ port {
+ hdmi_0: endpoint@0 {
+ remote-endpoint = <&lcd_0>;
+ };
+ };
};
};
--
1.9.1
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH RFC v2 6/6] ARM: AM33XX: dts: Change the tda998x description
@ 2014-03-21 8:52 ` Jean-Francois Moine
0 siblings, 0 replies; 32+ messages in thread
From: Jean-Francois Moine @ 2014-03-21 8:52 UTC (permalink / raw)
To: linux-arm-kernel
The tda998x being moved from a 'slave encoder' to a normal DRM
encoder/connector and the tilcdc_slave glue being removed, the
declaration of the HDMI transmitter description must be changed in
the associated DTs.
Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
arch/arm/boot/dts/am335x-base0033.dts | 28 +++++++++++++++++++---------
arch/arm/boot/dts/am335x-boneblack.dts | 21 ++++++++++++++++-----
2 files changed, 35 insertions(+), 14 deletions(-)
diff --git a/arch/arm/boot/dts/am335x-base0033.dts b/arch/arm/boot/dts/am335x-base0033.dts
index 72a9b3f..05f2b8f 100644
--- a/arch/arm/boot/dts/am335x-base0033.dts
+++ b/arch/arm/boot/dts/am335x-base0033.dts
@@ -14,15 +14,6 @@
model = "IGEP COM AM335x on AQUILA Expansion";
compatible = "isee,am335x-base0033", "isee,am335x-igep0033", "ti,am33xx";
- hdmi {
- compatible = "ti,tilcdc,slave";
- i2c = <&i2c0>;
- pinctrl-names = "default", "off";
- pinctrl-0 = <&nxp_hdmi_pins>;
- pinctrl-1 = <&nxp_hdmi_off_pins>;
- status = "okay";
- };
-
leds_base {
pinctrl-names = "default";
pinctrl-0 = <&leds_base_pins>;
@@ -85,6 +76,11 @@
&lcdc {
status = "okay";
+ port {
+ lcd_0: endpoint at 0 {
+ remote-endpoint = <&hdmi_0>;
+ };
+ };
};
&i2c0 {
@@ -92,4 +88,18 @@
compatible = "at,24c256";
reg = <0x50>;
};
+ hdmi: hdmi-encoder {
+ compatible = "nxp,tda19988";
+ reg = <0x70>;
+
+ pinctrl-names = "default", "off";
+ pinctrl-0 = <&nxp_hdmi_pins>;
+ pinctrl-1 = <&nxp_hdmi_off_pins>;
+
+ port {
+ hdmi_0: endpoint at 0 {
+ remote-endpoint = <&lcd_0>;
+ };
+ };
+ };
};
diff --git a/arch/arm/boot/dts/am335x-boneblack.dts b/arch/arm/boot/dts/am335x-boneblack.dts
index 6b71ad9..b94d8bd 100644
--- a/arch/arm/boot/dts/am335x-boneblack.dts
+++ b/arch/arm/boot/dts/am335x-boneblack.dts
@@ -64,15 +64,26 @@
&lcdc {
status = "okay";
+ port {
+ lcd_0: endpoint at 0 {
+ remote-endpoint = <&hdmi_0>;
+ };
+ };
};
-/ {
- hdmi {
- compatible = "ti,tilcdc,slave";
- i2c = <&i2c0>;
+&i2c0 {
+ hdmi: hdmi-encoder {
+ compatible = "nxp,tda19988";
+ reg = <0x70>;
+
pinctrl-names = "default", "off";
pinctrl-0 = <&nxp_hdmi_bonelt_pins>;
pinctrl-1 = <&nxp_hdmi_bonelt_off_pins>;
- status = "okay";
+
+ port {
+ hdmi_0: endpoint at 0 {
+ remote-endpoint = <&lcd_0>;
+ };
+ };
};
};
--
1.9.1
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH RFC v2 3/6] drm/tilcd: dts: Add the video output port
2014-03-21 10:27 ` Jean-Francois Moine
@ 2014-03-21 9:31 ` Jean-Francois Moine
-1 siblings, 0 replies; 32+ messages in thread
From: Jean-Francois Moine @ 2014-03-21 9:31 UTC (permalink / raw)
To: Russell King, Rob Clark, dri-devel
Cc: devicetree, linux-arm-kernel, linux-media
The connection between the video source and sink must follow
the media video interface.
Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
Documentation/devicetree/bindings/drm/tilcdc/tilcdc.txt | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/Documentation/devicetree/bindings/drm/tilcdc/tilcdc.txt b/Documentation/devicetree/bindings/drm/tilcdc/tilcdc.txt
index fff10da..d0de848 100644
--- a/Documentation/devicetree/bindings/drm/tilcdc/tilcdc.txt
+++ b/Documentation/devicetree/bindings/drm/tilcdc/tilcdc.txt
@@ -18,6 +18,12 @@ Optional properties:
- max-pixelclock: The maximum pixel clock that can be supported
by the lcd controller in KHz.
+Optional nodes:
+
+ - port: reference of the video sink as described in media/video-interfaces.
+ This reference is required when the video sink is the TDA19988 HDMI
+ transmitter.
+
Example:
fb: fb@4830e000 {
@@ -27,3 +33,11 @@ Example:
interrupts = <36>;
ti,hwmods = "lcdc";
};
+
+ &fb {
+ port {
+ lcd_0: endpoint@0 {
+ remote-endpoint = <&hdmi_0>;
+ };
+ };
+ };
--
1.9.1
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH RFC v2 5/6] drm/tilcd: dts: Remove unused slave description
2014-03-21 10:27 ` Jean-Francois Moine
@ 2014-03-21 9:31 ` Jean-Francois Moine
-1 siblings, 0 replies; 32+ messages in thread
From: Jean-Francois Moine @ 2014-03-21 9:31 UTC (permalink / raw)
To: Russell King, Rob Clark, dri-devel
Cc: devicetree, linux-arm-kernel, linux-media
The tda998x being converted to a normal DRM encoder/connector,
there is no slave notion anymore.
Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
Documentation/devicetree/bindings/drm/tilcdc/slave.txt | 18 ------------------
1 file changed, 18 deletions(-)
delete mode 100644 Documentation/devicetree/bindings/drm/tilcdc/slave.txt
diff --git a/Documentation/devicetree/bindings/drm/tilcdc/slave.txt b/Documentation/devicetree/bindings/drm/tilcdc/slave.txt
deleted file mode 100644
index 3d2c524..0000000
--- a/Documentation/devicetree/bindings/drm/tilcdc/slave.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-Device-Tree bindings for tilcdc DRM encoder slave output driver
-
-Required properties:
- - compatible: value should be "ti,tilcdc,slave".
- - i2c: the phandle for the i2c device the encoder slave is connected to
-
-Recommended properties:
- - pinctrl-names, pinctrl-0: the pincontrol settings to configure
- muxing properly for pins that connect to TFP410 device
-
-Example:
-
- hdmi {
- compatible = "ti,tilcdc,slave";
- i2c = <&i2c0>;
- pinctrl-names = "default";
- pinctrl-0 = <&nxp_hdmi_bonelt_pins>;
- };
--
1.9.1
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH RFC v2 3/6] drm/tilcd: dts: Add the video output port
@ 2014-03-21 9:31 ` Jean-Francois Moine
0 siblings, 0 replies; 32+ messages in thread
From: Jean-Francois Moine @ 2014-03-21 9:31 UTC (permalink / raw)
To: linux-arm-kernel
The connection between the video source and sink must follow
the media video interface.
Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
Documentation/devicetree/bindings/drm/tilcdc/tilcdc.txt | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/Documentation/devicetree/bindings/drm/tilcdc/tilcdc.txt b/Documentation/devicetree/bindings/drm/tilcdc/tilcdc.txt
index fff10da..d0de848 100644
--- a/Documentation/devicetree/bindings/drm/tilcdc/tilcdc.txt
+++ b/Documentation/devicetree/bindings/drm/tilcdc/tilcdc.txt
@@ -18,6 +18,12 @@ Optional properties:
- max-pixelclock: The maximum pixel clock that can be supported
by the lcd controller in KHz.
+Optional nodes:
+
+ - port: reference of the video sink as described in media/video-interfaces.
+ This reference is required when the video sink is the TDA19988 HDMI
+ transmitter.
+
Example:
fb: fb at 4830e000 {
@@ -27,3 +33,11 @@ Example:
interrupts = <36>;
ti,hwmods = "lcdc";
};
+
+ &fb {
+ port {
+ lcd_0: endpoint at 0 {
+ remote-endpoint = <&hdmi_0>;
+ };
+ };
+ };
--
1.9.1
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH RFC v2 5/6] drm/tilcd: dts: Remove unused slave description
@ 2014-03-21 9:31 ` Jean-Francois Moine
0 siblings, 0 replies; 32+ messages in thread
From: Jean-Francois Moine @ 2014-03-21 9:31 UTC (permalink / raw)
To: linux-arm-kernel
The tda998x being converted to a normal DRM encoder/connector,
there is no slave notion anymore.
Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
Documentation/devicetree/bindings/drm/tilcdc/slave.txt | 18 ------------------
1 file changed, 18 deletions(-)
delete mode 100644 Documentation/devicetree/bindings/drm/tilcdc/slave.txt
diff --git a/Documentation/devicetree/bindings/drm/tilcdc/slave.txt b/Documentation/devicetree/bindings/drm/tilcdc/slave.txt
deleted file mode 100644
index 3d2c524..0000000
--- a/Documentation/devicetree/bindings/drm/tilcdc/slave.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-Device-Tree bindings for tilcdc DRM encoder slave output driver
-
-Required properties:
- - compatible: value should be "ti,tilcdc,slave".
- - i2c: the phandle for the i2c device the encoder slave is connected to
-
-Recommended properties:
- - pinctrl-names, pinctrl-0: the pincontrol settings to configure
- muxing properly for pins that connect to TFP410 device
-
-Example:
-
- hdmi {
- compatible = "ti,tilcdc,slave";
- i2c = <&i2c0>;
- pinctrl-names = "default";
- pinctrl-0 = <&nxp_hdmi_bonelt_pins>;
- };
--
1.9.1
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH RFC v2 1/6] drm/i2c: tda998x: Add the required port property
2014-03-21 10:27 ` Jean-Francois Moine
@ 2014-03-21 9:48 ` Jean-Francois Moine
-1 siblings, 0 replies; 32+ messages in thread
From: Jean-Francois Moine @ 2014-03-21 9:48 UTC (permalink / raw)
To: Russell King, Rob Clark, dri-devel
Cc: devicetree, linux-arm-kernel, linux-media
According to the media video interface, the video source and sink
ports must be identified by mutual phandles.
This patch adds the 'port' property of the tda998x (sink side).
Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
Documentation/devicetree/bindings/drm/i2c/tda998x.txt | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/drm/i2c/tda998x.txt b/Documentation/devicetree/bindings/drm/i2c/tda998x.txt
index e3f3d65..10c5b5e 100644
--- a/Documentation/devicetree/bindings/drm/i2c/tda998x.txt
+++ b/Documentation/devicetree/bindings/drm/i2c/tda998x.txt
@@ -17,13 +17,22 @@ Optional properties:
- video-ports: 24 bits value which defines how the video controller
output is wired to the TDA998x input - default: <0x230145>
+Required nodes:
+ - port: reference of the video source as described in media/video-interfaces
+
Example:
- tda998x: hdmi-encoder {
+ hdmi: hdmi-encoder {
compatible = "nxp,tda19988";
reg = <0x70>;
interrupt-parent = <&gpio0>;
interrupts = <27 2>; /* falling edge */
pinctrl-0 = <&pmx_camera>;
pinctrl-names = "default";
+
+ port {
+ hdmi_0: endpoint@0 {
+ remote-endpoint = <&lcd0_0>;
+ };
+ };
};
--
1.9.1
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH RFC v2 1/6] drm/i2c: tda998x: Add the required port property
@ 2014-03-21 9:48 ` Jean-Francois Moine
0 siblings, 0 replies; 32+ messages in thread
From: Jean-Francois Moine @ 2014-03-21 9:48 UTC (permalink / raw)
To: linux-arm-kernel
According to the media video interface, the video source and sink
ports must be identified by mutual phandles.
This patch adds the 'port' property of the tda998x (sink side).
Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
Documentation/devicetree/bindings/drm/i2c/tda998x.txt | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/drm/i2c/tda998x.txt b/Documentation/devicetree/bindings/drm/i2c/tda998x.txt
index e3f3d65..10c5b5e 100644
--- a/Documentation/devicetree/bindings/drm/i2c/tda998x.txt
+++ b/Documentation/devicetree/bindings/drm/i2c/tda998x.txt
@@ -17,13 +17,22 @@ Optional properties:
- video-ports: 24 bits value which defines how the video controller
output is wired to the TDA998x input - default: <0x230145>
+Required nodes:
+ - port: reference of the video source as described in media/video-interfaces
+
Example:
- tda998x: hdmi-encoder {
+ hdmi: hdmi-encoder {
compatible = "nxp,tda19988";
reg = <0x70>;
interrupt-parent = <&gpio0>;
interrupts = <27 2>; /* falling edge */
pinctrl-0 = <&pmx_camera>;
pinctrl-names = "default";
+
+ port {
+ hdmi_0: endpoint at 0 {
+ remote-endpoint = <&lcd0_0>;
+ };
+ };
};
--
1.9.1
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH RFC v2 0/6] drm/i2c: Move tda998x to a couple encoder/connector
@ 2014-03-21 10:27 ` Jean-Francois Moine
0 siblings, 0 replies; 32+ messages in thread
From: Jean-Francois Moine @ 2014-03-21 10:27 UTC (permalink / raw)
To: Russell King, Rob Clark, dri-devel
Cc: devicetree, linux-arm-kernel, linux-media
The 'slave encoder' structure of the tda998x driver asks for glue
between the DRM driver and the encoder/connector structures.
Changing the tda998x driver to a simple encoder/connector simplifies
the code of the tilcdc driver. This change is permitted by
Russell's infrastructure for componentised subsystems.
The proposed patch set does not include changes to the Armada DRM driver.
These changes should already have been prepared by Russell King, as
told in the message
https://www.mail-archive.com/linux-media@vger.kernel.org/msg71202.html
The tilcdc part of this patch set has not been tested.
This patch set applies after the patchs:
drm/i2c: tda998x: Fix lack of required reg in DT documentation
drm/i2c: tda998x: Change the compatible strings
- v2
- fix lack of call to component_bind_all() in tilcdc_drv.c
- add tda998x configuration for non-DT systems
Jean-Francois Moine (6):
drm/i2c: tda998x: Add the required port property
drm/i2c: tda998x: Move tda998x to a couple encoder/connector
drm/tilcd: dts: Add the video output port
drm/tilcdc: Change the interface with the tda998x driver
drm/tilcd: dts: Remove unused slave description
ARM: AM33XX: dts: Change the tda998x description
.../devicetree/bindings/drm/i2c/tda998x.txt | 11 +-
.../devicetree/bindings/drm/tilcdc/slave.txt | 18 -
.../devicetree/bindings/drm/tilcdc/tilcdc.txt | 14 +
arch/arm/boot/dts/am335x-base0033.dts | 28 +-
arch/arm/boot/dts/am335x-boneblack.dts | 21 +-
drivers/gpu/drm/i2c/tda998x_drv.c | 323 +++++++++-------
drivers/gpu/drm/tilcdc/Makefile | 1 -
drivers/gpu/drm/tilcdc/tilcdc_drv.c | 85 ++++-
drivers/gpu/drm/tilcdc/tilcdc_slave.c | 406 ---------------------
drivers/gpu/drm/tilcdc/tilcdc_slave.h | 26 --
10 files changed, 315 insertions(+), 618 deletions(-)
delete mode 100644 Documentation/devicetree/bindings/drm/tilcdc/slave.txt
delete mode 100644 drivers/gpu/drm/tilcdc/tilcdc_slave.c
delete mode 100644 drivers/gpu/drm/tilcdc/tilcdc_slave.h
--
1.9.1
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH RFC v2 0/6] drm/i2c: Move tda998x to a couple encoder/connector
@ 2014-03-21 10:27 ` Jean-Francois Moine
0 siblings, 0 replies; 32+ messages in thread
From: Jean-Francois Moine @ 2014-03-21 10:27 UTC (permalink / raw)
To: linux-arm-kernel
The 'slave encoder' structure of the tda998x driver asks for glue
between the DRM driver and the encoder/connector structures.
Changing the tda998x driver to a simple encoder/connector simplifies
the code of the tilcdc driver. This change is permitted by
Russell's infrastructure for componentised subsystems.
The proposed patch set does not include changes to the Armada DRM driver.
These changes should already have been prepared by Russell King, as
told in the message
https://www.mail-archive.com/linux-media at vger.kernel.org/msg71202.html
The tilcdc part of this patch set has not been tested.
This patch set applies after the patchs:
drm/i2c: tda998x: Fix lack of required reg in DT documentation
drm/i2c: tda998x: Change the compatible strings
- v2
- fix lack of call to component_bind_all() in tilcdc_drv.c
- add tda998x configuration for non-DT systems
Jean-Francois Moine (6):
drm/i2c: tda998x: Add the required port property
drm/i2c: tda998x: Move tda998x to a couple encoder/connector
drm/tilcd: dts: Add the video output port
drm/tilcdc: Change the interface with the tda998x driver
drm/tilcd: dts: Remove unused slave description
ARM: AM33XX: dts: Change the tda998x description
.../devicetree/bindings/drm/i2c/tda998x.txt | 11 +-
.../devicetree/bindings/drm/tilcdc/slave.txt | 18 -
.../devicetree/bindings/drm/tilcdc/tilcdc.txt | 14 +
arch/arm/boot/dts/am335x-base0033.dts | 28 +-
arch/arm/boot/dts/am335x-boneblack.dts | 21 +-
drivers/gpu/drm/i2c/tda998x_drv.c | 323 +++++++++-------
drivers/gpu/drm/tilcdc/Makefile | 1 -
drivers/gpu/drm/tilcdc/tilcdc_drv.c | 85 ++++-
drivers/gpu/drm/tilcdc/tilcdc_slave.c | 406 ---------------------
drivers/gpu/drm/tilcdc/tilcdc_slave.h | 26 --
10 files changed, 315 insertions(+), 618 deletions(-)
delete mode 100644 Documentation/devicetree/bindings/drm/tilcdc/slave.txt
delete mode 100644 drivers/gpu/drm/tilcdc/tilcdc_slave.c
delete mode 100644 drivers/gpu/drm/tilcdc/tilcdc_slave.h
--
1.9.1
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH RFC v2 0/6] drm/i2c: Move tda998x to a couple encoder/connector
2014-03-21 10:27 ` Jean-Francois Moine
(?)
@ 2014-03-21 11:21 ` Russell King - ARM Linux
-1 siblings, 0 replies; 32+ messages in thread
From: Russell King - ARM Linux @ 2014-03-21 11:21 UTC (permalink / raw)
To: Jean-Francois Moine; +Cc: devicetree, linux-arm-kernel, dri-devel, linux-media
On Fri, Mar 21, 2014 at 11:27:45AM +0100, Jean-Francois Moine wrote:
> The 'slave encoder' structure of the tda998x driver asks for glue
> between the DRM driver and the encoder/connector structures.
Given how close we are to the coming merge window, that the discussion
about the of-graph bindings is still going on without concensus being
reached, and that this driver is not in staging, this needs to be
delayed until the following merge window when hopefully we have a
clearer picture about the DT side of this.
--
FTTC broadband for 0.8mile line: now at 9.7Mbps down 460kbps up... slowly
improving, and getting towards what was expected from it.
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH RFC v2 0/6] drm/i2c: Move tda998x to a couple encoder/connector
@ 2014-03-21 11:21 ` Russell King - ARM Linux
0 siblings, 0 replies; 32+ messages in thread
From: Russell King - ARM Linux @ 2014-03-21 11:21 UTC (permalink / raw)
To: Jean-Francois Moine
Cc: Rob Clark, dri-devel, devicetree, linux-arm-kernel, linux-media
On Fri, Mar 21, 2014 at 11:27:45AM +0100, Jean-Francois Moine wrote:
> The 'slave encoder' structure of the tda998x driver asks for glue
> between the DRM driver and the encoder/connector structures.
Given how close we are to the coming merge window, that the discussion
about the of-graph bindings is still going on without concensus being
reached, and that this driver is not in staging, this needs to be
delayed until the following merge window when hopefully we have a
clearer picture about the DT side of this.
--
FTTC broadband for 0.8mile line: now at 9.7Mbps down 460kbps up... slowly
improving, and getting towards what was expected from it.
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH RFC v2 0/6] drm/i2c: Move tda998x to a couple encoder/connector
@ 2014-03-21 11:21 ` Russell King - ARM Linux
0 siblings, 0 replies; 32+ messages in thread
From: Russell King - ARM Linux @ 2014-03-21 11:21 UTC (permalink / raw)
To: linux-arm-kernel
On Fri, Mar 21, 2014 at 11:27:45AM +0100, Jean-Francois Moine wrote:
> The 'slave encoder' structure of the tda998x driver asks for glue
> between the DRM driver and the encoder/connector structures.
Given how close we are to the coming merge window, that the discussion
about the of-graph bindings is still going on without concensus being
reached, and that this driver is not in staging, this needs to be
delayed until the following merge window when hopefully we have a
clearer picture about the DT side of this.
--
FTTC broadband for 0.8mile line: now at 9.7Mbps down 460kbps up... slowly
improving, and getting towards what was expected from it.
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH RFC v2 2/6] drm/i2c: tda998x: Move tda998x to a couple encoder/connector
2014-03-21 8:17 ` Jean-Francois Moine
(?)
@ 2014-03-24 22:39 ` Laurent Pinchart
-1 siblings, 0 replies; 32+ messages in thread
From: Laurent Pinchart @ 2014-03-24 22:39 UTC (permalink / raw)
To: dri-devel
Cc: Jean-Francois Moine, Russell King, Rob Clark, linux-arm-kernel,
linux-media
Hi Jean-François,
Thank you for the patch.
On Friday 21 March 2014 09:17:32 Jean-Francois Moine wrote:
> The 'slave encoder' structure of the tda998x driver asks for glue
> between the DRM driver and the encoder/connector structures.
>
> This patch changes the driver to a normal DRM encoder/connector
> thanks to the infrastructure for componentised subsystems.
I like the idea, but I'm not really happy with the implementation. Let me try
to explain why below.
> Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
> ---
> drivers/gpu/drm/i2c/tda998x_drv.c | 323 +++++++++++++++++++----------------
> 1 file changed, 188 insertions(+), 135 deletions(-)
>
> diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c
> b/drivers/gpu/drm/i2c/tda998x_drv.c index fd6751c..1c25e40 100644
> --- a/drivers/gpu/drm/i2c/tda998x_drv.c
> +++ b/drivers/gpu/drm/i2c/tda998x_drv.c
[snip]
> @@ -44,10 +45,14 @@ struct tda998x_priv {
>
> wait_queue_head_t wq_edid;
> volatile int wq_edid_wait;
> - struct drm_encoder *encoder;
> + struct drm_encoder encoder;
> + struct drm_connector connector;
> };
[snip]
> -static int
> -tda998x_probe(struct i2c_client *client, const struct i2c_device_id *id)
> +static int tda_bind(struct device *dev, struct device *master, void *data)
> {
> + struct drm_device *drm = data;
This is the part that bothers me. You're making two assumptions here, that the
DRM driver will pass a struct drm_device pointer to the bind operation, and
that the I2C encoder driver can take control of DRM encoder and connector
creation. Although it could become problematic later, the first assumption
isn't too much of an issue for now. I'll thus focus on the second one.
The component framework isolate the encoder and DRM master drivers as far as
component creation and binding is concerned, but doesn't provide a way for the
two drivers to communicate together (nor should it). You're solving this by
passing a pointer to the DRM device to the encoder bind operation, making the
encoder driver create a DRM encoder and connector, and relying on the DRM core
to orchestrate CRTCs, encoders and connectors. You thus assume that the
encoder hardware should be represented by a DRM encoder object, and that its
output is connected to a connector that should be represented by a DRM
connector object. While this can work in your use case, that won't always hold
true. Hardware encoders can be chained together, while DRM encoders can't. The
DRM core has recently received support for bridge objects to overcome that
limitation. Depending on the hardware topology, a given hardware encoder
should be modeled as a DRM encoder or as a DRM bridge. That decision shouldn't
be taken by the encoder driver but by the DRM master driver. The I2C encoder
driver thus shouldn't create the DRM encoder and DRM connector itself.
I believe the encoder/master communication problem should be solved
differently. Instead of passing a pointer to the DRM device to the encoder
driver and making the encoder driver control DRM encoder and connector
creation, the encoder driver should instead create an object not visible to
userspace that can be retrieved by the DRM master driver (possibly through
registration with the DRM core, or by going through drvdata in the encoder's
struct device). The DRM master could use that object to communicate with the
encoder, and would register the DRM encoder and DRM connector itself based on
hardware topology.
> + struct i2c_client *i2c_client = to_i2c_client(dev);
> + struct tda998x_priv *priv = i2c_get_clientdata(i2c_client);
> + struct drm_connector *connector = &priv->connector;
> + struct drm_encoder *encoder = &priv->encoder;
> + int ret;
> +
> + if (!try_module_get(THIS_MODULE)) {
> + dev_err(dev, "cannot get module %s\n", THIS_MODULE->name);
> + return -EINVAL;
> + }
> +
> + ret = drm_connector_init(drm, connector,
> + &connector_funcs,
> + DRM_MODE_CONNECTOR_HDMIA);
This is one example of the shortcomings I've explained above. An encoder
driver can't always know what connector type it is connected to. If I'm not
mistaken possible options here are DVII, DVID, HDMIA and HDMIB. It should be
up to the master driver to select the connector type based on its overall view
of the hardware, or even to a connector driver that would be bound to a
connector DT node (as proposed in https://www.mail-archive.com/devicetree@vger.kernel.org/msg16585.html).
> + if (ret < 0)
> + return ret;
> + drm_connector_helper_add(connector, &connector_helper_funcs);
> +
> + ret = drm_encoder_init(drm, encoder,
> + &encoder_funcs,
> + DRM_MODE_ENCODER_TMDS);
> +
> + encoder->possible_crtcs = 1; // 1 << lcd_num
> +
> + if (ret < 0)
> + goto err;
> + drm_encoder_helper_add(encoder, &encoder_helper_funcs);
> +
> + ret = drm_mode_connector_attach_encoder(connector, encoder);
> + if (ret < 0)
> + goto err;
> + connector->encoder = encoder;
> +
> + drm_sysfs_connector_add(connector);
> +
> + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
> + ret = drm_object_property_set_value(&connector->base,
> + drm->mode_config.dpms_property,
> + DRM_MODE_DPMS_OFF);
> +
> + if (priv->hdmi->irq)
> + connector->polled = DRM_CONNECTOR_POLL_HPD;
> + else
> + connector->polled = DRM_CONNECTOR_POLL_CONNECT |
> + DRM_CONNECTOR_POLL_DISCONNECT;
> return 0;
> +
> +err:
> + if (encoder->dev)
> + drm_encoder_cleanup(encoder);
> + if (connector->dev)
> + drm_connector_cleanup(connector);
> + return ret;
> }
>
> -static int
> -tda998x_remove(struct i2c_client *client)
> +static void tda_unbind(struct device *dev, struct device *master, void
> *data) {
> - return 0;
> + module_put(THIS_MODULE);
> }
>
> +static const struct component_ops comp_ops = {
> + .bind = tda_bind,
> + .unbind = tda_unbind,
> +};
> +
> static int
> -tda998x_encoder_init(struct i2c_client *client,
> - struct drm_device *dev,
> - struct drm_encoder_slave *encoder_slave)
> +tda998x_probe(struct i2c_client *client, const struct i2c_device_id *id)
> {
> struct tda998x_priv *priv;
> struct device_node *np = client->dev.of_node;
> @@ -1239,6 +1291,8 @@ tda998x_encoder_init(struct i2c_client *client,
> if (!priv)
> return -ENOMEM;
>
> + i2c_set_clientdata(client, priv);
> +
> priv->vip_cntrl_0 = VIP_CNTRL_0_SWAP_A(2) | VIP_CNTRL_0_SWAP_B(3);
> priv->vip_cntrl_1 = VIP_CNTRL_1_SWAP_C(0) | VIP_CNTRL_1_SWAP_D(1);
> priv->vip_cntrl_2 = VIP_CNTRL_2_SWAP_E(4) | VIP_CNTRL_2_SWAP_F(5);
> @@ -1250,13 +1304,8 @@ tda998x_encoder_init(struct i2c_client *client,
> kfree(priv);
> return -ENODEV;
> }
> -
> - priv->encoder = &encoder_slave->base;
> priv->dpms = DRM_MODE_DPMS_OFF;
>
> - encoder_slave->slave_priv = priv;
> - encoder_slave->slave_funcs = &tda998x_encoder_funcs;
> -
> /* wake up the device: */
> cec_write(priv, REG_CEC_ENAMODS,
> CEC_ENAMODS_EN_RXSENS | CEC_ENAMODS_EN_HDMI);
> @@ -1340,31 +1389,55 @@ tda998x_encoder_init(struct i2c_client *client,
> /* enable EDID read irq: */
> reg_set(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);
>
> - if (!np)
> - return 0; /* non-DT */
> + if (np) { /* if DT */
>
> - /* get the optional video properties */
> - ret = of_property_read_u32(np, "video-ports", &video);
> - if (ret == 0) {
> - priv->vip_cntrl_0 = video >> 16;
> - priv->vip_cntrl_1 = video >> 8;
> - priv->vip_cntrl_2 = video;
> + /* get the optional video properties */
> + ret = of_property_read_u32(np, "video-ports", &video);
> + if (ret == 0) {
> + priv->vip_cntrl_0 = video >> 16;
> + priv->vip_cntrl_1 = video >> 8;
> + priv->vip_cntrl_2 = video;
> + }
> + } else {
> + struct tda998x_encoder_params *params =
> + (struct tda998x_encoder_params)
> + client->dev.platform_data;
> +
> + if (params)
> + tda998x_encoder_set_config(priv, params);
> }
>
> + /* tda998x video component ready */
> + component_add(&client->dev, &comp_ops);
> +
> return 0;
>
> fail:
> - /* if encoder_init fails, the encoder slave is never registered,
> - * so cleanup here:
> - */
> if (priv->cec)
> i2c_unregister_device(priv->cec);
> kfree(priv);
> - encoder_slave->slave_priv = NULL;
> - encoder_slave->slave_funcs = NULL;
> return -ENXIO;
> }
>
> +static int
> +tda998x_remove(struct i2c_client *client)
> +{
> + struct tda998x_priv *priv = i2c_get_clientdata(client);
> +
> + /* disable all IRQs and free the IRQ handler */
> + cec_write(priv, REG_CEC_RXSHPDINTENA, 0);
> + reg_clear(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);
> + if (priv->hdmi->irq)
> + free_irq(priv->hdmi->irq, priv);
> +
> + component_del(&client->dev, &comp_ops);
> +
> + if (priv->cec)
> + i2c_unregister_device(priv->cec);
> + kfree(priv);
> + return 0;
> +}
> +
> #ifdef CONFIG_OF
> static const struct of_device_id tda998x_dt_ids[] = {
> { .compatible = "nxp,tda9989", },
> @@ -1381,38 +1454,18 @@ static struct i2c_device_id tda998x_ids[] = {
> };
> MODULE_DEVICE_TABLE(i2c, tda998x_ids);
>
> -static struct drm_i2c_encoder_driver tda998x_driver = {
> - .i2c_driver = {
> - .probe = tda998x_probe,
> - .remove = tda998x_remove,
> - .driver = {
> - .name = "tda998x",
> - .of_match_table = of_match_ptr(tda998x_dt_ids),
> - },
> - .id_table = tda998x_ids,
> +static struct i2c_driver tda998x_driver = {
> + .probe = tda998x_probe,
> + .remove = tda998x_remove,
> + .driver = {
> + .name = "tda998x",
> + .of_match_table = of_match_ptr(tda998x_dt_ids),
> },
> - .encoder_init = tda998x_encoder_init,
> + .id_table = tda998x_ids,
> };
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH RFC v2 2/6] drm/i2c: tda998x: Move tda998x to a couple encoder/connector
@ 2014-03-24 22:39 ` Laurent Pinchart
0 siblings, 0 replies; 32+ messages in thread
From: Laurent Pinchart @ 2014-03-24 22:39 UTC (permalink / raw)
To: linux-arm-kernel
Hi Jean-Fran?ois,
Thank you for the patch.
On Friday 21 March 2014 09:17:32 Jean-Francois Moine wrote:
> The 'slave encoder' structure of the tda998x driver asks for glue
> between the DRM driver and the encoder/connector structures.
>
> This patch changes the driver to a normal DRM encoder/connector
> thanks to the infrastructure for componentised subsystems.
I like the idea, but I'm not really happy with the implementation. Let me try
to explain why below.
> Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
> ---
> drivers/gpu/drm/i2c/tda998x_drv.c | 323 +++++++++++++++++++----------------
> 1 file changed, 188 insertions(+), 135 deletions(-)
>
> diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c
> b/drivers/gpu/drm/i2c/tda998x_drv.c index fd6751c..1c25e40 100644
> --- a/drivers/gpu/drm/i2c/tda998x_drv.c
> +++ b/drivers/gpu/drm/i2c/tda998x_drv.c
[snip]
> @@ -44,10 +45,14 @@ struct tda998x_priv {
>
> wait_queue_head_t wq_edid;
> volatile int wq_edid_wait;
> - struct drm_encoder *encoder;
> + struct drm_encoder encoder;
> + struct drm_connector connector;
> };
[snip]
> -static int
> -tda998x_probe(struct i2c_client *client, const struct i2c_device_id *id)
> +static int tda_bind(struct device *dev, struct device *master, void *data)
> {
> + struct drm_device *drm = data;
This is the part that bothers me. You're making two assumptions here, that the
DRM driver will pass a struct drm_device pointer to the bind operation, and
that the I2C encoder driver can take control of DRM encoder and connector
creation. Although it could become problematic later, the first assumption
isn't too much of an issue for now. I'll thus focus on the second one.
The component framework isolate the encoder and DRM master drivers as far as
component creation and binding is concerned, but doesn't provide a way for the
two drivers to communicate together (nor should it). You're solving this by
passing a pointer to the DRM device to the encoder bind operation, making the
encoder driver create a DRM encoder and connector, and relying on the DRM core
to orchestrate CRTCs, encoders and connectors. You thus assume that the
encoder hardware should be represented by a DRM encoder object, and that its
output is connected to a connector that should be represented by a DRM
connector object. While this can work in your use case, that won't always hold
true. Hardware encoders can be chained together, while DRM encoders can't. The
DRM core has recently received support for bridge objects to overcome that
limitation. Depending on the hardware topology, a given hardware encoder
should be modeled as a DRM encoder or as a DRM bridge. That decision shouldn't
be taken by the encoder driver but by the DRM master driver. The I2C encoder
driver thus shouldn't create the DRM encoder and DRM connector itself.
I believe the encoder/master communication problem should be solved
differently. Instead of passing a pointer to the DRM device to the encoder
driver and making the encoder driver control DRM encoder and connector
creation, the encoder driver should instead create an object not visible to
userspace that can be retrieved by the DRM master driver (possibly through
registration with the DRM core, or by going through drvdata in the encoder's
struct device). The DRM master could use that object to communicate with the
encoder, and would register the DRM encoder and DRM connector itself based on
hardware topology.
> + struct i2c_client *i2c_client = to_i2c_client(dev);
> + struct tda998x_priv *priv = i2c_get_clientdata(i2c_client);
> + struct drm_connector *connector = &priv->connector;
> + struct drm_encoder *encoder = &priv->encoder;
> + int ret;
> +
> + if (!try_module_get(THIS_MODULE)) {
> + dev_err(dev, "cannot get module %s\n", THIS_MODULE->name);
> + return -EINVAL;
> + }
> +
> + ret = drm_connector_init(drm, connector,
> + &connector_funcs,
> + DRM_MODE_CONNECTOR_HDMIA);
This is one example of the shortcomings I've explained above. An encoder
driver can't always know what connector type it is connected to. If I'm not
mistaken possible options here are DVII, DVID, HDMIA and HDMIB. It should be
up to the master driver to select the connector type based on its overall view
of the hardware, or even to a connector driver that would be bound to a
connector DT node (as proposed in https://www.mail-archive.com/devicetree at vger.kernel.org/msg16585.html).
> + if (ret < 0)
> + return ret;
> + drm_connector_helper_add(connector, &connector_helper_funcs);
> +
> + ret = drm_encoder_init(drm, encoder,
> + &encoder_funcs,
> + DRM_MODE_ENCODER_TMDS);
> +
> + encoder->possible_crtcs = 1; // 1 << lcd_num
> +
> + if (ret < 0)
> + goto err;
> + drm_encoder_helper_add(encoder, &encoder_helper_funcs);
> +
> + ret = drm_mode_connector_attach_encoder(connector, encoder);
> + if (ret < 0)
> + goto err;
> + connector->encoder = encoder;
> +
> + drm_sysfs_connector_add(connector);
> +
> + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
> + ret = drm_object_property_set_value(&connector->base,
> + drm->mode_config.dpms_property,
> + DRM_MODE_DPMS_OFF);
> +
> + if (priv->hdmi->irq)
> + connector->polled = DRM_CONNECTOR_POLL_HPD;
> + else
> + connector->polled = DRM_CONNECTOR_POLL_CONNECT |
> + DRM_CONNECTOR_POLL_DISCONNECT;
> return 0;
> +
> +err:
> + if (encoder->dev)
> + drm_encoder_cleanup(encoder);
> + if (connector->dev)
> + drm_connector_cleanup(connector);
> + return ret;
> }
>
> -static int
> -tda998x_remove(struct i2c_client *client)
> +static void tda_unbind(struct device *dev, struct device *master, void
> *data) {
> - return 0;
> + module_put(THIS_MODULE);
> }
>
> +static const struct component_ops comp_ops = {
> + .bind = tda_bind,
> + .unbind = tda_unbind,
> +};
> +
> static int
> -tda998x_encoder_init(struct i2c_client *client,
> - struct drm_device *dev,
> - struct drm_encoder_slave *encoder_slave)
> +tda998x_probe(struct i2c_client *client, const struct i2c_device_id *id)
> {
> struct tda998x_priv *priv;
> struct device_node *np = client->dev.of_node;
> @@ -1239,6 +1291,8 @@ tda998x_encoder_init(struct i2c_client *client,
> if (!priv)
> return -ENOMEM;
>
> + i2c_set_clientdata(client, priv);
> +
> priv->vip_cntrl_0 = VIP_CNTRL_0_SWAP_A(2) | VIP_CNTRL_0_SWAP_B(3);
> priv->vip_cntrl_1 = VIP_CNTRL_1_SWAP_C(0) | VIP_CNTRL_1_SWAP_D(1);
> priv->vip_cntrl_2 = VIP_CNTRL_2_SWAP_E(4) | VIP_CNTRL_2_SWAP_F(5);
> @@ -1250,13 +1304,8 @@ tda998x_encoder_init(struct i2c_client *client,
> kfree(priv);
> return -ENODEV;
> }
> -
> - priv->encoder = &encoder_slave->base;
> priv->dpms = DRM_MODE_DPMS_OFF;
>
> - encoder_slave->slave_priv = priv;
> - encoder_slave->slave_funcs = &tda998x_encoder_funcs;
> -
> /* wake up the device: */
> cec_write(priv, REG_CEC_ENAMODS,
> CEC_ENAMODS_EN_RXSENS | CEC_ENAMODS_EN_HDMI);
> @@ -1340,31 +1389,55 @@ tda998x_encoder_init(struct i2c_client *client,
> /* enable EDID read irq: */
> reg_set(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);
>
> - if (!np)
> - return 0; /* non-DT */
> + if (np) { /* if DT */
>
> - /* get the optional video properties */
> - ret = of_property_read_u32(np, "video-ports", &video);
> - if (ret == 0) {
> - priv->vip_cntrl_0 = video >> 16;
> - priv->vip_cntrl_1 = video >> 8;
> - priv->vip_cntrl_2 = video;
> + /* get the optional video properties */
> + ret = of_property_read_u32(np, "video-ports", &video);
> + if (ret == 0) {
> + priv->vip_cntrl_0 = video >> 16;
> + priv->vip_cntrl_1 = video >> 8;
> + priv->vip_cntrl_2 = video;
> + }
> + } else {
> + struct tda998x_encoder_params *params =
> + (struct tda998x_encoder_params)
> + client->dev.platform_data;
> +
> + if (params)
> + tda998x_encoder_set_config(priv, params);
> }
>
> + /* tda998x video component ready */
> + component_add(&client->dev, &comp_ops);
> +
> return 0;
>
> fail:
> - /* if encoder_init fails, the encoder slave is never registered,
> - * so cleanup here:
> - */
> if (priv->cec)
> i2c_unregister_device(priv->cec);
> kfree(priv);
> - encoder_slave->slave_priv = NULL;
> - encoder_slave->slave_funcs = NULL;
> return -ENXIO;
> }
>
> +static int
> +tda998x_remove(struct i2c_client *client)
> +{
> + struct tda998x_priv *priv = i2c_get_clientdata(client);
> +
> + /* disable all IRQs and free the IRQ handler */
> + cec_write(priv, REG_CEC_RXSHPDINTENA, 0);
> + reg_clear(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);
> + if (priv->hdmi->irq)
> + free_irq(priv->hdmi->irq, priv);
> +
> + component_del(&client->dev, &comp_ops);
> +
> + if (priv->cec)
> + i2c_unregister_device(priv->cec);
> + kfree(priv);
> + return 0;
> +}
> +
> #ifdef CONFIG_OF
> static const struct of_device_id tda998x_dt_ids[] = {
> { .compatible = "nxp,tda9989", },
> @@ -1381,38 +1454,18 @@ static struct i2c_device_id tda998x_ids[] = {
> };
> MODULE_DEVICE_TABLE(i2c, tda998x_ids);
>
> -static struct drm_i2c_encoder_driver tda998x_driver = {
> - .i2c_driver = {
> - .probe = tda998x_probe,
> - .remove = tda998x_remove,
> - .driver = {
> - .name = "tda998x",
> - .of_match_table = of_match_ptr(tda998x_dt_ids),
> - },
> - .id_table = tda998x_ids,
> +static struct i2c_driver tda998x_driver = {
> + .probe = tda998x_probe,
> + .remove = tda998x_remove,
> + .driver = {
> + .name = "tda998x",
> + .of_match_table = of_match_ptr(tda998x_dt_ids),
> },
> - .encoder_init = tda998x_encoder_init,
> + .id_table = tda998x_ids,
> };
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH RFC v2 2/6] drm/i2c: tda998x: Move tda998x to a couple encoder/connector
@ 2014-03-24 22:39 ` Laurent Pinchart
0 siblings, 0 replies; 32+ messages in thread
From: Laurent Pinchart @ 2014-03-24 22:39 UTC (permalink / raw)
To: dri-devel; +Cc: linux-media, linux-arm-kernel, Russell King
Hi Jean-François,
Thank you for the patch.
On Friday 21 March 2014 09:17:32 Jean-Francois Moine wrote:
> The 'slave encoder' structure of the tda998x driver asks for glue
> between the DRM driver and the encoder/connector structures.
>
> This patch changes the driver to a normal DRM encoder/connector
> thanks to the infrastructure for componentised subsystems.
I like the idea, but I'm not really happy with the implementation. Let me try
to explain why below.
> Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
> ---
> drivers/gpu/drm/i2c/tda998x_drv.c | 323 +++++++++++++++++++----------------
> 1 file changed, 188 insertions(+), 135 deletions(-)
>
> diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c
> b/drivers/gpu/drm/i2c/tda998x_drv.c index fd6751c..1c25e40 100644
> --- a/drivers/gpu/drm/i2c/tda998x_drv.c
> +++ b/drivers/gpu/drm/i2c/tda998x_drv.c
[snip]
> @@ -44,10 +45,14 @@ struct tda998x_priv {
>
> wait_queue_head_t wq_edid;
> volatile int wq_edid_wait;
> - struct drm_encoder *encoder;
> + struct drm_encoder encoder;
> + struct drm_connector connector;
> };
[snip]
> -static int
> -tda998x_probe(struct i2c_client *client, const struct i2c_device_id *id)
> +static int tda_bind(struct device *dev, struct device *master, void *data)
> {
> + struct drm_device *drm = data;
This is the part that bothers me. You're making two assumptions here, that the
DRM driver will pass a struct drm_device pointer to the bind operation, and
that the I2C encoder driver can take control of DRM encoder and connector
creation. Although it could become problematic later, the first assumption
isn't too much of an issue for now. I'll thus focus on the second one.
The component framework isolate the encoder and DRM master drivers as far as
component creation and binding is concerned, but doesn't provide a way for the
two drivers to communicate together (nor should it). You're solving this by
passing a pointer to the DRM device to the encoder bind operation, making the
encoder driver create a DRM encoder and connector, and relying on the DRM core
to orchestrate CRTCs, encoders and connectors. You thus assume that the
encoder hardware should be represented by a DRM encoder object, and that its
output is connected to a connector that should be represented by a DRM
connector object. While this can work in your use case, that won't always hold
true. Hardware encoders can be chained together, while DRM encoders can't. The
DRM core has recently received support for bridge objects to overcome that
limitation. Depending on the hardware topology, a given hardware encoder
should be modeled as a DRM encoder or as a DRM bridge. That decision shouldn't
be taken by the encoder driver but by the DRM master driver. The I2C encoder
driver thus shouldn't create the DRM encoder and DRM connector itself.
I believe the encoder/master communication problem should be solved
differently. Instead of passing a pointer to the DRM device to the encoder
driver and making the encoder driver control DRM encoder and connector
creation, the encoder driver should instead create an object not visible to
userspace that can be retrieved by the DRM master driver (possibly through
registration with the DRM core, or by going through drvdata in the encoder's
struct device). The DRM master could use that object to communicate with the
encoder, and would register the DRM encoder and DRM connector itself based on
hardware topology.
> + struct i2c_client *i2c_client = to_i2c_client(dev);
> + struct tda998x_priv *priv = i2c_get_clientdata(i2c_client);
> + struct drm_connector *connector = &priv->connector;
> + struct drm_encoder *encoder = &priv->encoder;
> + int ret;
> +
> + if (!try_module_get(THIS_MODULE)) {
> + dev_err(dev, "cannot get module %s\n", THIS_MODULE->name);
> + return -EINVAL;
> + }
> +
> + ret = drm_connector_init(drm, connector,
> + &connector_funcs,
> + DRM_MODE_CONNECTOR_HDMIA);
This is one example of the shortcomings I've explained above. An encoder
driver can't always know what connector type it is connected to. If I'm not
mistaken possible options here are DVII, DVID, HDMIA and HDMIB. It should be
up to the master driver to select the connector type based on its overall view
of the hardware, or even to a connector driver that would be bound to a
connector DT node (as proposed in https://www.mail-archive.com/devicetree@vger.kernel.org/msg16585.html).
> + if (ret < 0)
> + return ret;
> + drm_connector_helper_add(connector, &connector_helper_funcs);
> +
> + ret = drm_encoder_init(drm, encoder,
> + &encoder_funcs,
> + DRM_MODE_ENCODER_TMDS);
> +
> + encoder->possible_crtcs = 1; // 1 << lcd_num
> +
> + if (ret < 0)
> + goto err;
> + drm_encoder_helper_add(encoder, &encoder_helper_funcs);
> +
> + ret = drm_mode_connector_attach_encoder(connector, encoder);
> + if (ret < 0)
> + goto err;
> + connector->encoder = encoder;
> +
> + drm_sysfs_connector_add(connector);
> +
> + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
> + ret = drm_object_property_set_value(&connector->base,
> + drm->mode_config.dpms_property,
> + DRM_MODE_DPMS_OFF);
> +
> + if (priv->hdmi->irq)
> + connector->polled = DRM_CONNECTOR_POLL_HPD;
> + else
> + connector->polled = DRM_CONNECTOR_POLL_CONNECT |
> + DRM_CONNECTOR_POLL_DISCONNECT;
> return 0;
> +
> +err:
> + if (encoder->dev)
> + drm_encoder_cleanup(encoder);
> + if (connector->dev)
> + drm_connector_cleanup(connector);
> + return ret;
> }
>
> -static int
> -tda998x_remove(struct i2c_client *client)
> +static void tda_unbind(struct device *dev, struct device *master, void
> *data) {
> - return 0;
> + module_put(THIS_MODULE);
> }
>
> +static const struct component_ops comp_ops = {
> + .bind = tda_bind,
> + .unbind = tda_unbind,
> +};
> +
> static int
> -tda998x_encoder_init(struct i2c_client *client,
> - struct drm_device *dev,
> - struct drm_encoder_slave *encoder_slave)
> +tda998x_probe(struct i2c_client *client, const struct i2c_device_id *id)
> {
> struct tda998x_priv *priv;
> struct device_node *np = client->dev.of_node;
> @@ -1239,6 +1291,8 @@ tda998x_encoder_init(struct i2c_client *client,
> if (!priv)
> return -ENOMEM;
>
> + i2c_set_clientdata(client, priv);
> +
> priv->vip_cntrl_0 = VIP_CNTRL_0_SWAP_A(2) | VIP_CNTRL_0_SWAP_B(3);
> priv->vip_cntrl_1 = VIP_CNTRL_1_SWAP_C(0) | VIP_CNTRL_1_SWAP_D(1);
> priv->vip_cntrl_2 = VIP_CNTRL_2_SWAP_E(4) | VIP_CNTRL_2_SWAP_F(5);
> @@ -1250,13 +1304,8 @@ tda998x_encoder_init(struct i2c_client *client,
> kfree(priv);
> return -ENODEV;
> }
> -
> - priv->encoder = &encoder_slave->base;
> priv->dpms = DRM_MODE_DPMS_OFF;
>
> - encoder_slave->slave_priv = priv;
> - encoder_slave->slave_funcs = &tda998x_encoder_funcs;
> -
> /* wake up the device: */
> cec_write(priv, REG_CEC_ENAMODS,
> CEC_ENAMODS_EN_RXSENS | CEC_ENAMODS_EN_HDMI);
> @@ -1340,31 +1389,55 @@ tda998x_encoder_init(struct i2c_client *client,
> /* enable EDID read irq: */
> reg_set(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);
>
> - if (!np)
> - return 0; /* non-DT */
> + if (np) { /* if DT */
>
> - /* get the optional video properties */
> - ret = of_property_read_u32(np, "video-ports", &video);
> - if (ret == 0) {
> - priv->vip_cntrl_0 = video >> 16;
> - priv->vip_cntrl_1 = video >> 8;
> - priv->vip_cntrl_2 = video;
> + /* get the optional video properties */
> + ret = of_property_read_u32(np, "video-ports", &video);
> + if (ret == 0) {
> + priv->vip_cntrl_0 = video >> 16;
> + priv->vip_cntrl_1 = video >> 8;
> + priv->vip_cntrl_2 = video;
> + }
> + } else {
> + struct tda998x_encoder_params *params =
> + (struct tda998x_encoder_params)
> + client->dev.platform_data;
> +
> + if (params)
> + tda998x_encoder_set_config(priv, params);
> }
>
> + /* tda998x video component ready */
> + component_add(&client->dev, &comp_ops);
> +
> return 0;
>
> fail:
> - /* if encoder_init fails, the encoder slave is never registered,
> - * so cleanup here:
> - */
> if (priv->cec)
> i2c_unregister_device(priv->cec);
> kfree(priv);
> - encoder_slave->slave_priv = NULL;
> - encoder_slave->slave_funcs = NULL;
> return -ENXIO;
> }
>
> +static int
> +tda998x_remove(struct i2c_client *client)
> +{
> + struct tda998x_priv *priv = i2c_get_clientdata(client);
> +
> + /* disable all IRQs and free the IRQ handler */
> + cec_write(priv, REG_CEC_RXSHPDINTENA, 0);
> + reg_clear(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);
> + if (priv->hdmi->irq)
> + free_irq(priv->hdmi->irq, priv);
> +
> + component_del(&client->dev, &comp_ops);
> +
> + if (priv->cec)
> + i2c_unregister_device(priv->cec);
> + kfree(priv);
> + return 0;
> +}
> +
> #ifdef CONFIG_OF
> static const struct of_device_id tda998x_dt_ids[] = {
> { .compatible = "nxp,tda9989", },
> @@ -1381,38 +1454,18 @@ static struct i2c_device_id tda998x_ids[] = {
> };
> MODULE_DEVICE_TABLE(i2c, tda998x_ids);
>
> -static struct drm_i2c_encoder_driver tda998x_driver = {
> - .i2c_driver = {
> - .probe = tda998x_probe,
> - .remove = tda998x_remove,
> - .driver = {
> - .name = "tda998x",
> - .of_match_table = of_match_ptr(tda998x_dt_ids),
> - },
> - .id_table = tda998x_ids,
> +static struct i2c_driver tda998x_driver = {
> + .probe = tda998x_probe,
> + .remove = tda998x_remove,
> + .driver = {
> + .name = "tda998x",
> + .of_match_table = of_match_ptr(tda998x_dt_ids),
> },
> - .encoder_init = tda998x_encoder_init,
> + .id_table = tda998x_ids,
> };
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH RFC v2 2/6] drm/i2c: tda998x: Move tda998x to a couple encoder/connector
2014-03-24 22:39 ` Laurent Pinchart
(?)
@ 2014-03-25 15:55 ` Jean-Francois Moine
-1 siblings, 0 replies; 32+ messages in thread
From: Jean-Francois Moine @ 2014-03-25 15:55 UTC (permalink / raw)
To: Laurent Pinchart
Cc: dri-devel, Russell King, Rob Clark, linux-arm-kernel, linux-media
On Mon, 24 Mar 2014 23:39:01 +0100
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:
> Hi Jean-François,
Hi Laurent,
> Thank you for the patch.
>
> On Friday 21 March 2014 09:17:32 Jean-Francois Moine wrote:
> > The 'slave encoder' structure of the tda998x driver asks for glue
> > between the DRM driver and the encoder/connector structures.
> >
> > This patch changes the driver to a normal DRM encoder/connector
> > thanks to the infrastructure for componentised subsystems.
>
> I like the idea, but I'm not really happy with the implementation. Let me try
> to explain why below.
>
> > Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
> > ---
> > drivers/gpu/drm/i2c/tda998x_drv.c | 323 +++++++++++++++++++----------------
> > 1 file changed, 188 insertions(+), 135 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c
> > b/drivers/gpu/drm/i2c/tda998x_drv.c index fd6751c..1c25e40 100644
> > --- a/drivers/gpu/drm/i2c/tda998x_drv.c
> > +++ b/drivers/gpu/drm/i2c/tda998x_drv.c
>
> [snip]
>
> > @@ -44,10 +45,14 @@ struct tda998x_priv {
> >
> > wait_queue_head_t wq_edid;
> > volatile int wq_edid_wait;
> > - struct drm_encoder *encoder;
> > + struct drm_encoder encoder;
> > + struct drm_connector connector;
> > };
>
> [snip]
>
> > -static int
> > -tda998x_probe(struct i2c_client *client, const struct i2c_device_id *id)
> > +static int tda_bind(struct device *dev, struct device *master, void *data)
> > {
> > + struct drm_device *drm = data;
>
> This is the part that bothers me. You're making two assumptions here, that the
> DRM driver will pass a struct drm_device pointer to the bind operation, and
> that the I2C encoder driver can take control of DRM encoder and connector
> creation. Although it could become problematic later, the first assumption
> isn't too much of an issue for now. I'll thus focus on the second one.
>
> The component framework isolate the encoder and DRM master drivers as far as
> component creation and binding is concerned, but doesn't provide a way for the
> two drivers to communicate together (nor should it). You're solving this by
> passing a pointer to the DRM device to the encoder bind operation, making the
> encoder driver create a DRM encoder and connector, and relying on the DRM core
> to orchestrate CRTCs, encoders and connectors. You thus assume that the
> encoder hardware should be represented by a DRM encoder object, and that its
> output is connected to a connector that should be represented by a DRM
> connector object. While this can work in your use case, that won't always hold
> true. Hardware encoders can be chained together, while DRM encoders can't. The
> DRM core has recently received support for bridge objects to overcome that
> limitation. Depending on the hardware topology, a given hardware encoder
> should be modeled as a DRM encoder or as a DRM bridge. That decision shouldn't
> be taken by the encoder driver but by the DRM master driver. The I2C encoder
> driver thus shouldn't create the DRM encoder and DRM connector itself.
>
> I believe the encoder/master communication problem should be solved
> differently. Instead of passing a pointer to the DRM device to the encoder
> driver and making the encoder driver control DRM encoder and connector
> creation, the encoder driver should instead create an object not visible to
> userspace that can be retrieved by the DRM master driver (possibly through
> registration with the DRM core, or by going through drvdata in the encoder's
> struct device). The DRM master could use that object to communicate with the
> encoder, and would register the DRM encoder and DRM connector itself based on
> hardware topology.
>
> > + struct i2c_client *i2c_client = to_i2c_client(dev);
> > + struct tda998x_priv *priv = i2c_get_clientdata(i2c_client);
> > + struct drm_connector *connector = &priv->connector;
> > + struct drm_encoder *encoder = &priv->encoder;
> > + int ret;
> > +
> > + if (!try_module_get(THIS_MODULE)) {
> > + dev_err(dev, "cannot get module %s\n", THIS_MODULE->name);
> > + return -EINVAL;
> > + }
> > +
> > + ret = drm_connector_init(drm, connector,
> > + &connector_funcs,
> > + DRM_MODE_CONNECTOR_HDMIA);
>
> This is one example of the shortcomings I've explained above. An encoder
> driver can't always know what connector type it is connected to. If I'm not
> mistaken possible options here are DVII, DVID, HDMIA and HDMIB. It should be
> up to the master driver to select the connector type based on its overall view
> of the hardware, or even to a connector driver that would be bound to a
> connector DT node (as proposed in https://www.mail-archive.com/devicetree@vger.kernel.org/msg16585.html).
[snip]
The tda998x, as a HDMI transmitter, has to deal with both video and
audio.
Whereas the hardware connection schemes are the same in both worlds,
the way they are translated to computer objects are very different:
- video
DRM card -> CRTCs -> encoders -> (bridges) -> connectors
- audio
ALSA card -> CPUs -> (CODECs) -> CODECs
and it would be nice to have a common layout.
Actually, the tda998x is a slave encoder, that is, it plays the roles
of both encoder and connector. In the 2 DRM drivers (armada and tilcdc)
which use it, yes, the encoders and connectors are created by the main
DRM drivers, but, there is no notion of bridge, and, also, the encoder
is DRM_MODE_ENCODER_TMDS and the connector is DRM_MODE_CONNECTOR_HDMIA.
Then, nothing is changed in the global system working.
About the connector, yes, I let its type as hard-coded, but this could
be changed by configuration in the platform data or in the DT. Anyway,
there is nothing as such in the proposed patch
'Add DT binding documentation for HDMI Connector'
I also dislike this patch because it adds a device which is of no use.
I had a same remark from Mark Brown about a tda998x CODEC proposal of
mine:
hdmi_codec: hdmi-codec {
compatible = "nxp,tda998x-codec";
audio-ports = <0x03>, <0x04>;
};
So, the next tda998x CODEC will be directly included in the tda998x
driver, the audio output being the HDMI connector. Here is the DT
definition I have for the Cubox:
&i2c0 {
hdmi: hdmi-encoder {
compatible = "nxp,tda9989";
reg = <0x70>;
interrupt-parent = <&gpio0>;
interrupts = <27 IRQ_TYPE_EDGE_FALLING>;
pinctrl-0 = <&pmx_camera>;
pinctrl-names = "default";
audio-ports = <0x03>, <0x04>; /* 2 audio input ports */
audio-port-names = "i2s", "spdif";
#sound-dai-cells = <1>;
port { /* 1 video input port */
hdmi_0: endpoint@0 {
remote-endpoint = <&lcd0_0>;
};
};
};
};
Back to the DRM device pointer given to the tda998x driver, as the
tda998x is an encoder/connector, it has no bridge function, so, there
is no need to add a complex API for information exchange between both
drivers.
Anyway, there is a big lack in my proposal: the tda998x encoder is
hard-coded to the first CRTC. This could be solved by a scan of the DT
and of the encoder list by the DRM driver, but I think that the actual
definitions as proposed by media/video-interfaces.txt are not easy to
use.
Do you think that a description as done for ALSA could work?
A sound card creation is done by a global sound configuration and a
description of the links between the card elements. Here is the tda998x
part of the Cubox audio card ('audio1' is the audio device):
sound {
compatible = "simple-audio-card";
simple-audio-card,name = "Cubox Audio";
simple-audio-card,dai-link@0 { /* I2S - HDMI */
format = "i2s";
cpu {
sound-dai = <&audio1 0>; /* I2S output */
};
codec {
sound-dai = <&hdmi 0>; /* I2S input */
};
};
simple-audio-card,dai-link@1 { /* S/PDIF - HDMI */
cpu {
sound-dai = <&audio1 1>; /* S/PDIF output */
};
codec {
sound-dai = <&hdmi 1>; /* S/PDIF input */
};
};
};
Using the same elements, here is what could be the video card of the
Armada 510 with a panel, the tda998x and the display controller:
video {
compatible = "simple-video-card";
simple-video-card,dvi-link {
crtc {
dvi = <&lcd0>;
};
encoder {
dvi = <&panel>;
connector-type = 7; /* LVDS */
};
};
simple-video-card,dvi-link {
crtc {
dvi = <&lcd0>;
};
encoder {
dvi = <&hdmi>;
connector-type = 11; /* HDMI-A */
};
};
};
lcd0: lcd-controller@820000 {
compatible = "marvell,armada-510-lcd";
... hardware definitions ...
};
hdmi : hdmi-encoder {
.. same as above, but without the video input port ..
};
panel: panel {
.. panel parameters ..
};
Then, the generic 'simple-video-card' has all elements to create the
DRM device.
--
Ken ar c'hentañ | ** Breizh ha Linux atav! **
Jef | http://moinejf.free.fr/
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH RFC v2 2/6] drm/i2c: tda998x: Move tda998x to a couple encoder/connector
@ 2014-03-25 15:55 ` Jean-Francois Moine
0 siblings, 0 replies; 32+ messages in thread
From: Jean-Francois Moine @ 2014-03-25 15:55 UTC (permalink / raw)
To: linux-arm-kernel
On Mon, 24 Mar 2014 23:39:01 +0100
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:
> Hi Jean-Fran?ois,
Hi Laurent,
> Thank you for the patch.
>
> On Friday 21 March 2014 09:17:32 Jean-Francois Moine wrote:
> > The 'slave encoder' structure of the tda998x driver asks for glue
> > between the DRM driver and the encoder/connector structures.
> >
> > This patch changes the driver to a normal DRM encoder/connector
> > thanks to the infrastructure for componentised subsystems.
>
> I like the idea, but I'm not really happy with the implementation. Let me try
> to explain why below.
>
> > Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
> > ---
> > drivers/gpu/drm/i2c/tda998x_drv.c | 323 +++++++++++++++++++----------------
> > 1 file changed, 188 insertions(+), 135 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c
> > b/drivers/gpu/drm/i2c/tda998x_drv.c index fd6751c..1c25e40 100644
> > --- a/drivers/gpu/drm/i2c/tda998x_drv.c
> > +++ b/drivers/gpu/drm/i2c/tda998x_drv.c
>
> [snip]
>
> > @@ -44,10 +45,14 @@ struct tda998x_priv {
> >
> > wait_queue_head_t wq_edid;
> > volatile int wq_edid_wait;
> > - struct drm_encoder *encoder;
> > + struct drm_encoder encoder;
> > + struct drm_connector connector;
> > };
>
> [snip]
>
> > -static int
> > -tda998x_probe(struct i2c_client *client, const struct i2c_device_id *id)
> > +static int tda_bind(struct device *dev, struct device *master, void *data)
> > {
> > + struct drm_device *drm = data;
>
> This is the part that bothers me. You're making two assumptions here, that the
> DRM driver will pass a struct drm_device pointer to the bind operation, and
> that the I2C encoder driver can take control of DRM encoder and connector
> creation. Although it could become problematic later, the first assumption
> isn't too much of an issue for now. I'll thus focus on the second one.
>
> The component framework isolate the encoder and DRM master drivers as far as
> component creation and binding is concerned, but doesn't provide a way for the
> two drivers to communicate together (nor should it). You're solving this by
> passing a pointer to the DRM device to the encoder bind operation, making the
> encoder driver create a DRM encoder and connector, and relying on the DRM core
> to orchestrate CRTCs, encoders and connectors. You thus assume that the
> encoder hardware should be represented by a DRM encoder object, and that its
> output is connected to a connector that should be represented by a DRM
> connector object. While this can work in your use case, that won't always hold
> true. Hardware encoders can be chained together, while DRM encoders can't. The
> DRM core has recently received support for bridge objects to overcome that
> limitation. Depending on the hardware topology, a given hardware encoder
> should be modeled as a DRM encoder or as a DRM bridge. That decision shouldn't
> be taken by the encoder driver but by the DRM master driver. The I2C encoder
> driver thus shouldn't create the DRM encoder and DRM connector itself.
>
> I believe the encoder/master communication problem should be solved
> differently. Instead of passing a pointer to the DRM device to the encoder
> driver and making the encoder driver control DRM encoder and connector
> creation, the encoder driver should instead create an object not visible to
> userspace that can be retrieved by the DRM master driver (possibly through
> registration with the DRM core, or by going through drvdata in the encoder's
> struct device). The DRM master could use that object to communicate with the
> encoder, and would register the DRM encoder and DRM connector itself based on
> hardware topology.
>
> > + struct i2c_client *i2c_client = to_i2c_client(dev);
> > + struct tda998x_priv *priv = i2c_get_clientdata(i2c_client);
> > + struct drm_connector *connector = &priv->connector;
> > + struct drm_encoder *encoder = &priv->encoder;
> > + int ret;
> > +
> > + if (!try_module_get(THIS_MODULE)) {
> > + dev_err(dev, "cannot get module %s\n", THIS_MODULE->name);
> > + return -EINVAL;
> > + }
> > +
> > + ret = drm_connector_init(drm, connector,
> > + &connector_funcs,
> > + DRM_MODE_CONNECTOR_HDMIA);
>
> This is one example of the shortcomings I've explained above. An encoder
> driver can't always know what connector type it is connected to. If I'm not
> mistaken possible options here are DVII, DVID, HDMIA and HDMIB. It should be
> up to the master driver to select the connector type based on its overall view
> of the hardware, or even to a connector driver that would be bound to a
> connector DT node (as proposed in https://www.mail-archive.com/devicetree at vger.kernel.org/msg16585.html).
[snip]
The tda998x, as a HDMI transmitter, has to deal with both video and
audio.
Whereas the hardware connection schemes are the same in both worlds,
the way they are translated to computer objects are very different:
- video
DRM card -> CRTCs -> encoders -> (bridges) -> connectors
- audio
ALSA card -> CPUs -> (CODECs) -> CODECs
and it would be nice to have a common layout.
Actually, the tda998x is a slave encoder, that is, it plays the roles
of both encoder and connector. In the 2 DRM drivers (armada and tilcdc)
which use it, yes, the encoders and connectors are created by the main
DRM drivers, but, there is no notion of bridge, and, also, the encoder
is DRM_MODE_ENCODER_TMDS and the connector is DRM_MODE_CONNECTOR_HDMIA.
Then, nothing is changed in the global system working.
About the connector, yes, I let its type as hard-coded, but this could
be changed by configuration in the platform data or in the DT. Anyway,
there is nothing as such in the proposed patch
'Add DT binding documentation for HDMI Connector'
I also dislike this patch because it adds a device which is of no use.
I had a same remark from Mark Brown about a tda998x CODEC proposal of
mine:
hdmi_codec: hdmi-codec {
compatible = "nxp,tda998x-codec";
audio-ports = <0x03>, <0x04>;
};
So, the next tda998x CODEC will be directly included in the tda998x
driver, the audio output being the HDMI connector. Here is the DT
definition I have for the Cubox:
&i2c0 {
hdmi: hdmi-encoder {
compatible = "nxp,tda9989";
reg = <0x70>;
interrupt-parent = <&gpio0>;
interrupts = <27 IRQ_TYPE_EDGE_FALLING>;
pinctrl-0 = <&pmx_camera>;
pinctrl-names = "default";
audio-ports = <0x03>, <0x04>; /* 2 audio input ports */
audio-port-names = "i2s", "spdif";
#sound-dai-cells = <1>;
port { /* 1 video input port */
hdmi_0: endpoint at 0 {
remote-endpoint = <&lcd0_0>;
};
};
};
};
Back to the DRM device pointer given to the tda998x driver, as the
tda998x is an encoder/connector, it has no bridge function, so, there
is no need to add a complex API for information exchange between both
drivers.
Anyway, there is a big lack in my proposal: the tda998x encoder is
hard-coded to the first CRTC. This could be solved by a scan of the DT
and of the encoder list by the DRM driver, but I think that the actual
definitions as proposed by media/video-interfaces.txt are not easy to
use.
Do you think that a description as done for ALSA could work?
A sound card creation is done by a global sound configuration and a
description of the links between the card elements. Here is the tda998x
part of the Cubox audio card ('audio1' is the audio device):
sound {
compatible = "simple-audio-card";
simple-audio-card,name = "Cubox Audio";
simple-audio-card,dai-link at 0 { /* I2S - HDMI */
format = "i2s";
cpu {
sound-dai = <&audio1 0>; /* I2S output */
};
codec {
sound-dai = <&hdmi 0>; /* I2S input */
};
};
simple-audio-card,dai-link at 1 { /* S/PDIF - HDMI */
cpu {
sound-dai = <&audio1 1>; /* S/PDIF output */
};
codec {
sound-dai = <&hdmi 1>; /* S/PDIF input */
};
};
};
Using the same elements, here is what could be the video card of the
Armada 510 with a panel, the tda998x and the display controller:
video {
compatible = "simple-video-card";
simple-video-card,dvi-link {
crtc {
dvi = <&lcd0>;
};
encoder {
dvi = <&panel>;
connector-type = 7; /* LVDS */
};
};
simple-video-card,dvi-link {
crtc {
dvi = <&lcd0>;
};
encoder {
dvi = <&hdmi>;
connector-type = 11; /* HDMI-A */
};
};
};
lcd0: lcd-controller at 820000 {
compatible = "marvell,armada-510-lcd";
... hardware definitions ...
};
hdmi : hdmi-encoder {
.. same as above, but without the video input port ..
};
panel: panel {
.. panel parameters ..
};
Then, the generic 'simple-video-card' has all elements to create the
DRM device.
--
Ken ar c'henta? | ** Breizh ha Linux atav! **
Jef | http://moinejf.free.fr/
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH RFC v2 2/6] drm/i2c: tda998x: Move tda998x to a couple encoder/connector
@ 2014-03-25 15:55 ` Jean-Francois Moine
0 siblings, 0 replies; 32+ messages in thread
From: Jean-Francois Moine @ 2014-03-25 15:55 UTC (permalink / raw)
To: Laurent Pinchart; +Cc: Russell King, linux-arm-kernel, dri-devel, linux-media
On Mon, 24 Mar 2014 23:39:01 +0100
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:
> Hi Jean-François,
Hi Laurent,
> Thank you for the patch.
>
> On Friday 21 March 2014 09:17:32 Jean-Francois Moine wrote:
> > The 'slave encoder' structure of the tda998x driver asks for glue
> > between the DRM driver and the encoder/connector structures.
> >
> > This patch changes the driver to a normal DRM encoder/connector
> > thanks to the infrastructure for componentised subsystems.
>
> I like the idea, but I'm not really happy with the implementation. Let me try
> to explain why below.
>
> > Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
> > ---
> > drivers/gpu/drm/i2c/tda998x_drv.c | 323 +++++++++++++++++++----------------
> > 1 file changed, 188 insertions(+), 135 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c
> > b/drivers/gpu/drm/i2c/tda998x_drv.c index fd6751c..1c25e40 100644
> > --- a/drivers/gpu/drm/i2c/tda998x_drv.c
> > +++ b/drivers/gpu/drm/i2c/tda998x_drv.c
>
> [snip]
>
> > @@ -44,10 +45,14 @@ struct tda998x_priv {
> >
> > wait_queue_head_t wq_edid;
> > volatile int wq_edid_wait;
> > - struct drm_encoder *encoder;
> > + struct drm_encoder encoder;
> > + struct drm_connector connector;
> > };
>
> [snip]
>
> > -static int
> > -tda998x_probe(struct i2c_client *client, const struct i2c_device_id *id)
> > +static int tda_bind(struct device *dev, struct device *master, void *data)
> > {
> > + struct drm_device *drm = data;
>
> This is the part that bothers me. You're making two assumptions here, that the
> DRM driver will pass a struct drm_device pointer to the bind operation, and
> that the I2C encoder driver can take control of DRM encoder and connector
> creation. Although it could become problematic later, the first assumption
> isn't too much of an issue for now. I'll thus focus on the second one.
>
> The component framework isolate the encoder and DRM master drivers as far as
> component creation and binding is concerned, but doesn't provide a way for the
> two drivers to communicate together (nor should it). You're solving this by
> passing a pointer to the DRM device to the encoder bind operation, making the
> encoder driver create a DRM encoder and connector, and relying on the DRM core
> to orchestrate CRTCs, encoders and connectors. You thus assume that the
> encoder hardware should be represented by a DRM encoder object, and that its
> output is connected to a connector that should be represented by a DRM
> connector object. While this can work in your use case, that won't always hold
> true. Hardware encoders can be chained together, while DRM encoders can't. The
> DRM core has recently received support for bridge objects to overcome that
> limitation. Depending on the hardware topology, a given hardware encoder
> should be modeled as a DRM encoder or as a DRM bridge. That decision shouldn't
> be taken by the encoder driver but by the DRM master driver. The I2C encoder
> driver thus shouldn't create the DRM encoder and DRM connector itself.
>
> I believe the encoder/master communication problem should be solved
> differently. Instead of passing a pointer to the DRM device to the encoder
> driver and making the encoder driver control DRM encoder and connector
> creation, the encoder driver should instead create an object not visible to
> userspace that can be retrieved by the DRM master driver (possibly through
> registration with the DRM core, or by going through drvdata in the encoder's
> struct device). The DRM master could use that object to communicate with the
> encoder, and would register the DRM encoder and DRM connector itself based on
> hardware topology.
>
> > + struct i2c_client *i2c_client = to_i2c_client(dev);
> > + struct tda998x_priv *priv = i2c_get_clientdata(i2c_client);
> > + struct drm_connector *connector = &priv->connector;
> > + struct drm_encoder *encoder = &priv->encoder;
> > + int ret;
> > +
> > + if (!try_module_get(THIS_MODULE)) {
> > + dev_err(dev, "cannot get module %s\n", THIS_MODULE->name);
> > + return -EINVAL;
> > + }
> > +
> > + ret = drm_connector_init(drm, connector,
> > + &connector_funcs,
> > + DRM_MODE_CONNECTOR_HDMIA);
>
> This is one example of the shortcomings I've explained above. An encoder
> driver can't always know what connector type it is connected to. If I'm not
> mistaken possible options here are DVII, DVID, HDMIA and HDMIB. It should be
> up to the master driver to select the connector type based on its overall view
> of the hardware, or even to a connector driver that would be bound to a
> connector DT node (as proposed in https://www.mail-archive.com/devicetree@vger.kernel.org/msg16585.html).
[snip]
The tda998x, as a HDMI transmitter, has to deal with both video and
audio.
Whereas the hardware connection schemes are the same in both worlds,
the way they are translated to computer objects are very different:
- video
DRM card -> CRTCs -> encoders -> (bridges) -> connectors
- audio
ALSA card -> CPUs -> (CODECs) -> CODECs
and it would be nice to have a common layout.
Actually, the tda998x is a slave encoder, that is, it plays the roles
of both encoder and connector. In the 2 DRM drivers (armada and tilcdc)
which use it, yes, the encoders and connectors are created by the main
DRM drivers, but, there is no notion of bridge, and, also, the encoder
is DRM_MODE_ENCODER_TMDS and the connector is DRM_MODE_CONNECTOR_HDMIA.
Then, nothing is changed in the global system working.
About the connector, yes, I let its type as hard-coded, but this could
be changed by configuration in the platform data or in the DT. Anyway,
there is nothing as such in the proposed patch
'Add DT binding documentation for HDMI Connector'
I also dislike this patch because it adds a device which is of no use.
I had a same remark from Mark Brown about a tda998x CODEC proposal of
mine:
hdmi_codec: hdmi-codec {
compatible = "nxp,tda998x-codec";
audio-ports = <0x03>, <0x04>;
};
So, the next tda998x CODEC will be directly included in the tda998x
driver, the audio output being the HDMI connector. Here is the DT
definition I have for the Cubox:
&i2c0 {
hdmi: hdmi-encoder {
compatible = "nxp,tda9989";
reg = <0x70>;
interrupt-parent = <&gpio0>;
interrupts = <27 IRQ_TYPE_EDGE_FALLING>;
pinctrl-0 = <&pmx_camera>;
pinctrl-names = "default";
audio-ports = <0x03>, <0x04>; /* 2 audio input ports */
audio-port-names = "i2s", "spdif";
#sound-dai-cells = <1>;
port { /* 1 video input port */
hdmi_0: endpoint@0 {
remote-endpoint = <&lcd0_0>;
};
};
};
};
Back to the DRM device pointer given to the tda998x driver, as the
tda998x is an encoder/connector, it has no bridge function, so, there
is no need to add a complex API for information exchange between both
drivers.
Anyway, there is a big lack in my proposal: the tda998x encoder is
hard-coded to the first CRTC. This could be solved by a scan of the DT
and of the encoder list by the DRM driver, but I think that the actual
definitions as proposed by media/video-interfaces.txt are not easy to
use.
Do you think that a description as done for ALSA could work?
A sound card creation is done by a global sound configuration and a
description of the links between the card elements. Here is the tda998x
part of the Cubox audio card ('audio1' is the audio device):
sound {
compatible = "simple-audio-card";
simple-audio-card,name = "Cubox Audio";
simple-audio-card,dai-link@0 { /* I2S - HDMI */
format = "i2s";
cpu {
sound-dai = <&audio1 0>; /* I2S output */
};
codec {
sound-dai = <&hdmi 0>; /* I2S input */
};
};
simple-audio-card,dai-link@1 { /* S/PDIF - HDMI */
cpu {
sound-dai = <&audio1 1>; /* S/PDIF output */
};
codec {
sound-dai = <&hdmi 1>; /* S/PDIF input */
};
};
};
Using the same elements, here is what could be the video card of the
Armada 510 with a panel, the tda998x and the display controller:
video {
compatible = "simple-video-card";
simple-video-card,dvi-link {
crtc {
dvi = <&lcd0>;
};
encoder {
dvi = <&panel>;
connector-type = 7; /* LVDS */
};
};
simple-video-card,dvi-link {
crtc {
dvi = <&lcd0>;
};
encoder {
dvi = <&hdmi>;
connector-type = 11; /* HDMI-A */
};
};
};
lcd0: lcd-controller@820000 {
compatible = "marvell,armada-510-lcd";
... hardware definitions ...
};
hdmi : hdmi-encoder {
.. same as above, but without the video input port ..
};
panel: panel {
.. panel parameters ..
};
Then, the generic 'simple-video-card' has all elements to create the
DRM device.
--
Ken ar c'hentañ | ** Breizh ha Linux atav! **
Jef | http://moinejf.free.fr/
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH RFC v2 2/6] drm/i2c: tda998x: Move tda998x to a couple encoder/connector
2014-03-25 15:55 ` Jean-Francois Moine
(?)
@ 2014-03-26 17:33 ` Laurent Pinchart
-1 siblings, 0 replies; 32+ messages in thread
From: Laurent Pinchart @ 2014-03-26 17:33 UTC (permalink / raw)
To: Jean-Francois Moine
Cc: dri-devel, Russell King, Rob Clark, linux-arm-kernel, linux-media
Hi Jean-François,
On Tuesday 25 March 2014 16:55:48 Jean-Francois Moine wrote:
> On Mon, 24 Mar 2014 23:39:01 +0100 Laurent Pinchart wrote:
> > On Friday 21 March 2014 09:17:32 Jean-Francois Moine wrote:
> > > The 'slave encoder' structure of the tda998x driver asks for glue
> > > between the DRM driver and the encoder/connector structures.
> > >
> > > This patch changes the driver to a normal DRM encoder/connector
> > > thanks to the infrastructure for componentised subsystems.
> >
> > I like the idea, but I'm not really happy with the implementation. Let me
> > try to explain why below.
> >
> > > Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
> > > ---
> > >
> > > drivers/gpu/drm/i2c/tda998x_drv.c | 323 ++++++++++++++++---------------
> > > 1 file changed, 188 insertions(+), 135 deletions(-)
> > >
> > > diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c
> > > b/drivers/gpu/drm/i2c/tda998x_drv.c index fd6751c..1c25e40 100644
> > > --- a/drivers/gpu/drm/i2c/tda998x_drv.c
> > > +++ b/drivers/gpu/drm/i2c/tda998x_drv.c
> >
> > [snip]
> >
> > > @@ -44,10 +45,14 @@ struct tda998x_priv {
> > > wait_queue_head_t wq_edid;
> > > volatile int wq_edid_wait;
> > >
> > > - struct drm_encoder *encoder;
> > > + struct drm_encoder encoder;
> > > + struct drm_connector connector;
> > >
> > > };
> >
> > [snip]
> >
> > > -static int
> > > -tda998x_probe(struct i2c_client *client, const struct i2c_device_id
> > > *id)
> > > +static int tda_bind(struct device *dev, struct device *master, void
> > > *data)
> > > {
> > > + struct drm_device *drm = data;
> >
> > This is the part that bothers me. You're making two assumptions here, that
> > the DRM driver will pass a struct drm_device pointer to the bind
> > operation, and that the I2C encoder driver can take control of DRM
> > encoder and connector creation. Although it could become problematic
> > later, the first assumption isn't too much of an issue for now. I'll thus
> > focus on the second one.
> >
> > The component framework isolate the encoder and DRM master drivers as far
> > as component creation and binding is concerned, but doesn't provide a way
> > for the two drivers to communicate together (nor should it). You're
> > solving this by passing a pointer to the DRM device to the encoder bind
> > operation, making the encoder driver create a DRM encoder and connector,
> > and relying on the DRM core to orchestrate CRTCs, encoders and
> > connectors. You thus assume that the encoder hardware should be
> > represented by a DRM encoder object, and that its output is connected to
> > a connector that should be represented by a DRM connector object. While
> > this can work in your use case, that won't always hold true. Hardware
> > encoders can be chained together, while DRM encoders can't. The DRM core
> > has recently received support for bridge objects to overcome that
> > limitation. Depending on the hardware topology, a given hardware encoder
> > should be modeled as a DRM encoder or as a DRM bridge. That decision
> > shouldn't be taken by the encoder driver but by the DRM master driver.
> > The I2C encoder driver thus shouldn't create the DRM encoder and DRM
> > connector itself.
> >
> > I believe the encoder/master communication problem should be solved
> > differently. Instead of passing a pointer to the DRM device to the encoder
> > driver and making the encoder driver control DRM encoder and connector
> > creation, the encoder driver should instead create an object not visible
> > to userspace that can be retrieved by the DRM master driver (possibly
> > through registration with the DRM core, or by going through drvdata in the
> > encoder's struct device). The DRM master could use that object to
> > communicate with the encoder, and would register the DRM encoder and DRM
> > connector itself based on hardware topology.
> >
> > > + struct i2c_client *i2c_client = to_i2c_client(dev);
> > > + struct tda998x_priv *priv = i2c_get_clientdata(i2c_client);
> > > + struct drm_connector *connector = &priv->connector;
> > > + struct drm_encoder *encoder = &priv->encoder;
> > > + int ret;
> > > +
> > > + if (!try_module_get(THIS_MODULE)) {
> > > + dev_err(dev, "cannot get module %s\n", THIS_MODULE->name);
> > > + return -EINVAL;
> > > + }
> > > +
> > > + ret = drm_connector_init(drm, connector,
> > > + &connector_funcs,
> > > + DRM_MODE_CONNECTOR_HDMIA);
> >
> > This is one example of the shortcomings I've explained above. An encoder
> > driver can't always know what connector type it is connected to. If I'm
> > not mistaken possible options here are DVII, DVID, HDMIA and HDMIB. It
> > should be up to the master driver to select the connector type based on
> > its overall view of the hardware, or even to a connector driver that would
> > be bound to a connector DT node (as proposed in
> > https://www.mail-archive.com/devicetree@vger.kernel.org/msg16585.html).
> [snip]
>
> The tda998x, as a HDMI transmitter, has to deal with both video and
> audio.
>
> Whereas the hardware connection schemes are the same in both worlds,
> the way they are translated to computer objects are very different:
>
> - video
> DRM card -> CRTCs -> encoders -> (bridges) -> connectors
>
> - audio
> ALSA card -> CPUs -> (CODECs) -> CODECs
>
> and it would be nice to have a common layout.
>
> Actually, the tda998x is a slave encoder, that is, it plays the roles of
> both encoder and connector. In the 2 DRM drivers (armada and tilcdc) which
> use it, yes, the encoders and connectors are created by the main DRM
> drivers, but, there is no notion of bridge, and, also, the encoder is
> DRM_MODE_ENCODER_TMDS and the connector is DRM_MODE_CONNECTOR_HDMIA. Then,
> nothing is changed in the global system working.
>
> About the connector, yes, I let its type as hard-coded, but this could be
> changed by configuration in the platform data or in the DT. Anyway, there is
> nothing as such in the proposed patch
>
> 'Add DT binding documentation for HDMI Connector'
>
> I also dislike this patch because it adds a device which is of no use. I had
> a same remark from Mark Brown about a tda998x CODEC proposal of mine:
>
> hdmi_codec: hdmi-codec {
> compatible = "nxp,tda998x-codec";
> audio-ports = <0x03>, <0x04>;
> };
>
> So, the next tda998x CODEC will be directly included in the tda998x driver,
> the audio output being the HDMI connector. Here is the DT definition I have
> for the Cubox:
>
> &i2c0 {
> hdmi: hdmi-encoder {
> compatible = "nxp,tda9989";
> reg = <0x70>;
> interrupt-parent = <&gpio0>;
> interrupts = <27 IRQ_TYPE_EDGE_FALLING>;
> pinctrl-0 = <&pmx_camera>;
> pinctrl-names = "default";
>
> audio-ports = <0x03>, <0x04>; /* 2 audio input ports */
> audio-port-names = "i2s", "spdif";
> #sound-dai-cells = <1>;
>
> port { /* 1 video input port */
> hdmi_0: endpoint@0 {
> remote-endpoint = <&lcd0_0>;
> };
> };
> };
> };
>
> Back to the DRM device pointer given to the tda998x driver, as the tda998x
> is an encoder/connector, it has no bridge function, so, there is no need to
> add a complex API for information exchange between both drivers.
>
> Anyway, there is a big lack in my proposal: the tda998x encoder is hard-
>coded to the first CRTC. This could be solved by a scan of the DT and of the
> encoder list by the DRM driver, but I think that the actual definitions as
> proposed by media/video-interfaces.txt are not easy to use.
>
> Do you think that a description as done for ALSA could work?
>
> A sound card creation is done by a global sound configuration and a
> description of the links between the card elements. Here is the tda998x
> part of the Cubox audio card ('audio1' is the audio device):
>
> sound {
> compatible = "simple-audio-card";
> simple-audio-card,name = "Cubox Audio";
>
> simple-audio-card,dai-link@0 { /* I2S - HDMI */
> format = "i2s";
> cpu {
> sound-dai = <&audio1 0>; /* I2S output */
> };
> codec {
> sound-dai = <&hdmi 0>; /* I2S input */
> };
> };
>
> simple-audio-card,dai-link@1 { /* S/PDIF - HDMI */
> cpu {
> sound-dai = <&audio1 1>; /* S/PDIF output */
> };
> codec {
> sound-dai = <&hdmi 1>; /* S/PDIF input */
> };
> };
> };
>
> Using the same elements, here is what could be the video card of the
> Armada 510 with a panel, the tda998x and the display controller:
>
> video {
> compatible = "simple-video-card";
>
> simple-video-card,dvi-link {
> crtc {
> dvi = <&lcd0>;
> };
> encoder {
> dvi = <&panel>;
> connector-type = 7; /* LVDS */
> };
> };
> simple-video-card,dvi-link {
> crtc {
> dvi = <&lcd0>;
> };
> encoder {
> dvi = <&hdmi>;
> connector-type = 11; /* HDMI-A */
> };
> };
> };
>
> lcd0: lcd-controller@820000 {
> compatible = "marvell,armada-510-lcd";
> ... hardware definitions ...
> };
>
> hdmi : hdmi-encoder {
> .. same as above, but without the video input port ..
> };
>
> panel: panel {
> .. panel parameters ..
> };
>
> Then, the generic 'simple-video-card' has all elements to create the DRM
> device.
That could work in your case, but I don't really like that.
We need to describe the hardware topology, that might be the only point we all
agree on. There are various hardware topologies we need to support with
different levels of complexity, and several ways to describe them, also with a
wide range of complexities and features.
The more complex the hardware topology, the more complex its description needs
to be, and the more complex the code that will parse the description and
handle the hardware will be. I don't think there's any doubt here.
A given device can be integrated in a wide variety of hardware with different
complexities. A driver can't thus always assume that the whole composite
display device will match a given class of topologies. This is especially true
for encoders and connectors, they can be hooked up directly at the output of a
very simple display controller, or can be part of a very complex graph.
Encoder and connector drivers can't assume much about how they will be
integrated. I want to avoid a situation where using an HDMI encoder already
supported in mainline with a different SoC will result in having to rewrite
the HDMI encoder driver.
The story is a bit different for display controllers. While the hardware
topology outside (and sometimes inside as well) of the SoC can vary, a display
controller often approximately implies a given level of complexity. A cheap
SoC with a simple display controller will likely not be used to drive a 4k
display requiring 4 encoders running in parallel for instance. While I also
want to avoid having to make significant changes to a display controller
driver when using the SoC on a different board, I believe the requirement can
be slightly relaxed here, and the display controller driver(s) can assume more
about the hardware topology than the encoder drivers.
I've asked myself whether we needed a single, one-size-fits-them-all DT
representation of the hardware topology. The view of the world from an
external encoder point of view must not depend on the SoC it is hooked up to
(this would prevent using a single encoder driver with multiple SoCs), which
calls for at least some form of standardization. The topology representation
on the display controller side may vary from display controller to display
controller, but I believe this would just result in code duplication and
having to solve the same problem in multiple drivers. For those reasons I
believe that the OF graph proposal to represent the display hardware topology
would be a good choice. The bindings are flexible enough to represent both
simple and complex hardware.
Now, I don't want to force all display device drivers to implement complex
code when they only need to support simple hardware and simple hardware
topologies. Not only would that be rightfully rejected, I would be among the
ones nack'ing that approach. My opinion is that this calls for the creation of
helpers to handle common cases. Several (possibly many) display systems only
need (or want) to support linear pipelines at their output(s), made of a
single encoder and a single connector. There's no point in duplicating DT
parsing or encoder/connector instantiation code in several drivers in that
case where helpers could be reused. Several sets of helpers could support
different kinds of topologies, with the driver author selecting a set of
helpers depending on the expected hardware topology complexity.
We also need to decide on who (as in which driver) will be responsible for
binding all devices together. As DRM exposes a single device to userspace,
there needs to be a single driver that will perform front line handling of the
userspace API and delegate calls to the other drivers involved. I believe it
would be logical for that driver to also be in charge of bindings the
components together, as it will be the driver that delegate calls to the
components. For a similar reason I also believe that that driver should also
be the one in control of creating DRM encoders and DRM connectors. The
component drivers having only a narrow view of the device they handle, they
can't perform that task in a generic way and would thus get tied to specific
master drivers because of the assumptions they would make.
Whether the master driver is the CRTC driver or a separate driver that binds
standalone CRTCs, connectors and encoders doesn't in my opinion change my
above conclusions. Some SoCs could use a simple-video-card driver like the one
you've proposed, and others could implement a display controller driver that
would perform the same tasks for more complex pipelines in addition to
controlling the display controller's CRTC(s). The simple-video-card driver
would perform that same tasks as the helpers I've previously talked about, so
the two solutions are pretty similar, and I don't see much added value in the
general case in having a simple-video-card driver compared to using helpers in
the display controller driver.
The point that matters to me is that encoders DT bindings and drivers must be
identical regardless of whether the master driver is the display controller
driver or a driver for a logical composite device. For all those reasons we
should use the OF graph DT bindings for the simple-video-card driver should we
decide to implement it, and we should create DRM encoders and connectors in
the master driver, not in the encoder drivers.
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH RFC v2 2/6] drm/i2c: tda998x: Move tda998x to a couple encoder/connector
@ 2014-03-26 17:33 ` Laurent Pinchart
0 siblings, 0 replies; 32+ messages in thread
From: Laurent Pinchart @ 2014-03-26 17:33 UTC (permalink / raw)
To: linux-arm-kernel
Hi Jean-Fran?ois,
On Tuesday 25 March 2014 16:55:48 Jean-Francois Moine wrote:
> On Mon, 24 Mar 2014 23:39:01 +0100 Laurent Pinchart wrote:
> > On Friday 21 March 2014 09:17:32 Jean-Francois Moine wrote:
> > > The 'slave encoder' structure of the tda998x driver asks for glue
> > > between the DRM driver and the encoder/connector structures.
> > >
> > > This patch changes the driver to a normal DRM encoder/connector
> > > thanks to the infrastructure for componentised subsystems.
> >
> > I like the idea, but I'm not really happy with the implementation. Let me
> > try to explain why below.
> >
> > > Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
> > > ---
> > >
> > > drivers/gpu/drm/i2c/tda998x_drv.c | 323 ++++++++++++++++---------------
> > > 1 file changed, 188 insertions(+), 135 deletions(-)
> > >
> > > diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c
> > > b/drivers/gpu/drm/i2c/tda998x_drv.c index fd6751c..1c25e40 100644
> > > --- a/drivers/gpu/drm/i2c/tda998x_drv.c
> > > +++ b/drivers/gpu/drm/i2c/tda998x_drv.c
> >
> > [snip]
> >
> > > @@ -44,10 +45,14 @@ struct tda998x_priv {
> > > wait_queue_head_t wq_edid;
> > > volatile int wq_edid_wait;
> > >
> > > - struct drm_encoder *encoder;
> > > + struct drm_encoder encoder;
> > > + struct drm_connector connector;
> > >
> > > };
> >
> > [snip]
> >
> > > -static int
> > > -tda998x_probe(struct i2c_client *client, const struct i2c_device_id
> > > *id)
> > > +static int tda_bind(struct device *dev, struct device *master, void
> > > *data)
> > > {
> > > + struct drm_device *drm = data;
> >
> > This is the part that bothers me. You're making two assumptions here, that
> > the DRM driver will pass a struct drm_device pointer to the bind
> > operation, and that the I2C encoder driver can take control of DRM
> > encoder and connector creation. Although it could become problematic
> > later, the first assumption isn't too much of an issue for now. I'll thus
> > focus on the second one.
> >
> > The component framework isolate the encoder and DRM master drivers as far
> > as component creation and binding is concerned, but doesn't provide a way
> > for the two drivers to communicate together (nor should it). You're
> > solving this by passing a pointer to the DRM device to the encoder bind
> > operation, making the encoder driver create a DRM encoder and connector,
> > and relying on the DRM core to orchestrate CRTCs, encoders and
> > connectors. You thus assume that the encoder hardware should be
> > represented by a DRM encoder object, and that its output is connected to
> > a connector that should be represented by a DRM connector object. While
> > this can work in your use case, that won't always hold true. Hardware
> > encoders can be chained together, while DRM encoders can't. The DRM core
> > has recently received support for bridge objects to overcome that
> > limitation. Depending on the hardware topology, a given hardware encoder
> > should be modeled as a DRM encoder or as a DRM bridge. That decision
> > shouldn't be taken by the encoder driver but by the DRM master driver.
> > The I2C encoder driver thus shouldn't create the DRM encoder and DRM
> > connector itself.
> >
> > I believe the encoder/master communication problem should be solved
> > differently. Instead of passing a pointer to the DRM device to the encoder
> > driver and making the encoder driver control DRM encoder and connector
> > creation, the encoder driver should instead create an object not visible
> > to userspace that can be retrieved by the DRM master driver (possibly
> > through registration with the DRM core, or by going through drvdata in the
> > encoder's struct device). The DRM master could use that object to
> > communicate with the encoder, and would register the DRM encoder and DRM
> > connector itself based on hardware topology.
> >
> > > + struct i2c_client *i2c_client = to_i2c_client(dev);
> > > + struct tda998x_priv *priv = i2c_get_clientdata(i2c_client);
> > > + struct drm_connector *connector = &priv->connector;
> > > + struct drm_encoder *encoder = &priv->encoder;
> > > + int ret;
> > > +
> > > + if (!try_module_get(THIS_MODULE)) {
> > > + dev_err(dev, "cannot get module %s\n", THIS_MODULE->name);
> > > + return -EINVAL;
> > > + }
> > > +
> > > + ret = drm_connector_init(drm, connector,
> > > + &connector_funcs,
> > > + DRM_MODE_CONNECTOR_HDMIA);
> >
> > This is one example of the shortcomings I've explained above. An encoder
> > driver can't always know what connector type it is connected to. If I'm
> > not mistaken possible options here are DVII, DVID, HDMIA and HDMIB. It
> > should be up to the master driver to select the connector type based on
> > its overall view of the hardware, or even to a connector driver that would
> > be bound to a connector DT node (as proposed in
> > https://www.mail-archive.com/devicetree at vger.kernel.org/msg16585.html).
> [snip]
>
> The tda998x, as a HDMI transmitter, has to deal with both video and
> audio.
>
> Whereas the hardware connection schemes are the same in both worlds,
> the way they are translated to computer objects are very different:
>
> - video
> DRM card -> CRTCs -> encoders -> (bridges) -> connectors
>
> - audio
> ALSA card -> CPUs -> (CODECs) -> CODECs
>
> and it would be nice to have a common layout.
>
> Actually, the tda998x is a slave encoder, that is, it plays the roles of
> both encoder and connector. In the 2 DRM drivers (armada and tilcdc) which
> use it, yes, the encoders and connectors are created by the main DRM
> drivers, but, there is no notion of bridge, and, also, the encoder is
> DRM_MODE_ENCODER_TMDS and the connector is DRM_MODE_CONNECTOR_HDMIA. Then,
> nothing is changed in the global system working.
>
> About the connector, yes, I let its type as hard-coded, but this could be
> changed by configuration in the platform data or in the DT. Anyway, there is
> nothing as such in the proposed patch
>
> 'Add DT binding documentation for HDMI Connector'
>
> I also dislike this patch because it adds a device which is of no use. I had
> a same remark from Mark Brown about a tda998x CODEC proposal of mine:
>
> hdmi_codec: hdmi-codec {
> compatible = "nxp,tda998x-codec";
> audio-ports = <0x03>, <0x04>;
> };
>
> So, the next tda998x CODEC will be directly included in the tda998x driver,
> the audio output being the HDMI connector. Here is the DT definition I have
> for the Cubox:
>
> &i2c0 {
> hdmi: hdmi-encoder {
> compatible = "nxp,tda9989";
> reg = <0x70>;
> interrupt-parent = <&gpio0>;
> interrupts = <27 IRQ_TYPE_EDGE_FALLING>;
> pinctrl-0 = <&pmx_camera>;
> pinctrl-names = "default";
>
> audio-ports = <0x03>, <0x04>; /* 2 audio input ports */
> audio-port-names = "i2s", "spdif";
> #sound-dai-cells = <1>;
>
> port { /* 1 video input port */
> hdmi_0: endpoint at 0 {
> remote-endpoint = <&lcd0_0>;
> };
> };
> };
> };
>
> Back to the DRM device pointer given to the tda998x driver, as the tda998x
> is an encoder/connector, it has no bridge function, so, there is no need to
> add a complex API for information exchange between both drivers.
>
> Anyway, there is a big lack in my proposal: the tda998x encoder is hard-
>coded to the first CRTC. This could be solved by a scan of the DT and of the
> encoder list by the DRM driver, but I think that the actual definitions as
> proposed by media/video-interfaces.txt are not easy to use.
>
> Do you think that a description as done for ALSA could work?
>
> A sound card creation is done by a global sound configuration and a
> description of the links between the card elements. Here is the tda998x
> part of the Cubox audio card ('audio1' is the audio device):
>
> sound {
> compatible = "simple-audio-card";
> simple-audio-card,name = "Cubox Audio";
>
> simple-audio-card,dai-link at 0 { /* I2S - HDMI */
> format = "i2s";
> cpu {
> sound-dai = <&audio1 0>; /* I2S output */
> };
> codec {
> sound-dai = <&hdmi 0>; /* I2S input */
> };
> };
>
> simple-audio-card,dai-link at 1 { /* S/PDIF - HDMI */
> cpu {
> sound-dai = <&audio1 1>; /* S/PDIF output */
> };
> codec {
> sound-dai = <&hdmi 1>; /* S/PDIF input */
> };
> };
> };
>
> Using the same elements, here is what could be the video card of the
> Armada 510 with a panel, the tda998x and the display controller:
>
> video {
> compatible = "simple-video-card";
>
> simple-video-card,dvi-link {
> crtc {
> dvi = <&lcd0>;
> };
> encoder {
> dvi = <&panel>;
> connector-type = 7; /* LVDS */
> };
> };
> simple-video-card,dvi-link {
> crtc {
> dvi = <&lcd0>;
> };
> encoder {
> dvi = <&hdmi>;
> connector-type = 11; /* HDMI-A */
> };
> };
> };
>
> lcd0: lcd-controller at 820000 {
> compatible = "marvell,armada-510-lcd";
> ... hardware definitions ...
> };
>
> hdmi : hdmi-encoder {
> .. same as above, but without the video input port ..
> };
>
> panel: panel {
> .. panel parameters ..
> };
>
> Then, the generic 'simple-video-card' has all elements to create the DRM
> device.
That could work in your case, but I don't really like that.
We need to describe the hardware topology, that might be the only point we all
agree on. There are various hardware topologies we need to support with
different levels of complexity, and several ways to describe them, also with a
wide range of complexities and features.
The more complex the hardware topology, the more complex its description needs
to be, and the more complex the code that will parse the description and
handle the hardware will be. I don't think there's any doubt here.
A given device can be integrated in a wide variety of hardware with different
complexities. A driver can't thus always assume that the whole composite
display device will match a given class of topologies. This is especially true
for encoders and connectors, they can be hooked up directly at the output of a
very simple display controller, or can be part of a very complex graph.
Encoder and connector drivers can't assume much about how they will be
integrated. I want to avoid a situation where using an HDMI encoder already
supported in mainline with a different SoC will result in having to rewrite
the HDMI encoder driver.
The story is a bit different for display controllers. While the hardware
topology outside (and sometimes inside as well) of the SoC can vary, a display
controller often approximately implies a given level of complexity. A cheap
SoC with a simple display controller will likely not be used to drive a 4k
display requiring 4 encoders running in parallel for instance. While I also
want to avoid having to make significant changes to a display controller
driver when using the SoC on a different board, I believe the requirement can
be slightly relaxed here, and the display controller driver(s) can assume more
about the hardware topology than the encoder drivers.
I've asked myself whether we needed a single, one-size-fits-them-all DT
representation of the hardware topology. The view of the world from an
external encoder point of view must not depend on the SoC it is hooked up to
(this would prevent using a single encoder driver with multiple SoCs), which
calls for at least some form of standardization. The topology representation
on the display controller side may vary from display controller to display
controller, but I believe this would just result in code duplication and
having to solve the same problem in multiple drivers. For those reasons I
believe that the OF graph proposal to represent the display hardware topology
would be a good choice. The bindings are flexible enough to represent both
simple and complex hardware.
Now, I don't want to force all display device drivers to implement complex
code when they only need to support simple hardware and simple hardware
topologies. Not only would that be rightfully rejected, I would be among the
ones nack'ing that approach. My opinion is that this calls for the creation of
helpers to handle common cases. Several (possibly many) display systems only
need (or want) to support linear pipelines at their output(s), made of a
single encoder and a single connector. There's no point in duplicating DT
parsing or encoder/connector instantiation code in several drivers in that
case where helpers could be reused. Several sets of helpers could support
different kinds of topologies, with the driver author selecting a set of
helpers depending on the expected hardware topology complexity.
We also need to decide on who (as in which driver) will be responsible for
binding all devices together. As DRM exposes a single device to userspace,
there needs to be a single driver that will perform front line handling of the
userspace API and delegate calls to the other drivers involved. I believe it
would be logical for that driver to also be in charge of bindings the
components together, as it will be the driver that delegate calls to the
components. For a similar reason I also believe that that driver should also
be the one in control of creating DRM encoders and DRM connectors. The
component drivers having only a narrow view of the device they handle, they
can't perform that task in a generic way and would thus get tied to specific
master drivers because of the assumptions they would make.
Whether the master driver is the CRTC driver or a separate driver that binds
standalone CRTCs, connectors and encoders doesn't in my opinion change my
above conclusions. Some SoCs could use a simple-video-card driver like the one
you've proposed, and others could implement a display controller driver that
would perform the same tasks for more complex pipelines in addition to
controlling the display controller's CRTC(s). The simple-video-card driver
would perform that same tasks as the helpers I've previously talked about, so
the two solutions are pretty similar, and I don't see much added value in the
general case in having a simple-video-card driver compared to using helpers in
the display controller driver.
The point that matters to me is that encoders DT bindings and drivers must be
identical regardless of whether the master driver is the display controller
driver or a driver for a logical composite device. For all those reasons we
should use the OF graph DT bindings for the simple-video-card driver should we
decide to implement it, and we should create DRM encoders and connectors in
the master driver, not in the encoder drivers.
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH RFC v2 2/6] drm/i2c: tda998x: Move tda998x to a couple encoder/connector
@ 2014-03-26 17:33 ` Laurent Pinchart
0 siblings, 0 replies; 32+ messages in thread
From: Laurent Pinchart @ 2014-03-26 17:33 UTC (permalink / raw)
To: Jean-Francois Moine
Cc: Russell King, linux-arm-kernel, dri-devel, linux-media
Hi Jean-François,
On Tuesday 25 March 2014 16:55:48 Jean-Francois Moine wrote:
> On Mon, 24 Mar 2014 23:39:01 +0100 Laurent Pinchart wrote:
> > On Friday 21 March 2014 09:17:32 Jean-Francois Moine wrote:
> > > The 'slave encoder' structure of the tda998x driver asks for glue
> > > between the DRM driver and the encoder/connector structures.
> > >
> > > This patch changes the driver to a normal DRM encoder/connector
> > > thanks to the infrastructure for componentised subsystems.
> >
> > I like the idea, but I'm not really happy with the implementation. Let me
> > try to explain why below.
> >
> > > Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
> > > ---
> > >
> > > drivers/gpu/drm/i2c/tda998x_drv.c | 323 ++++++++++++++++---------------
> > > 1 file changed, 188 insertions(+), 135 deletions(-)
> > >
> > > diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c
> > > b/drivers/gpu/drm/i2c/tda998x_drv.c index fd6751c..1c25e40 100644
> > > --- a/drivers/gpu/drm/i2c/tda998x_drv.c
> > > +++ b/drivers/gpu/drm/i2c/tda998x_drv.c
> >
> > [snip]
> >
> > > @@ -44,10 +45,14 @@ struct tda998x_priv {
> > > wait_queue_head_t wq_edid;
> > > volatile int wq_edid_wait;
> > >
> > > - struct drm_encoder *encoder;
> > > + struct drm_encoder encoder;
> > > + struct drm_connector connector;
> > >
> > > };
> >
> > [snip]
> >
> > > -static int
> > > -tda998x_probe(struct i2c_client *client, const struct i2c_device_id
> > > *id)
> > > +static int tda_bind(struct device *dev, struct device *master, void
> > > *data)
> > > {
> > > + struct drm_device *drm = data;
> >
> > This is the part that bothers me. You're making two assumptions here, that
> > the DRM driver will pass a struct drm_device pointer to the bind
> > operation, and that the I2C encoder driver can take control of DRM
> > encoder and connector creation. Although it could become problematic
> > later, the first assumption isn't too much of an issue for now. I'll thus
> > focus on the second one.
> >
> > The component framework isolate the encoder and DRM master drivers as far
> > as component creation and binding is concerned, but doesn't provide a way
> > for the two drivers to communicate together (nor should it). You're
> > solving this by passing a pointer to the DRM device to the encoder bind
> > operation, making the encoder driver create a DRM encoder and connector,
> > and relying on the DRM core to orchestrate CRTCs, encoders and
> > connectors. You thus assume that the encoder hardware should be
> > represented by a DRM encoder object, and that its output is connected to
> > a connector that should be represented by a DRM connector object. While
> > this can work in your use case, that won't always hold true. Hardware
> > encoders can be chained together, while DRM encoders can't. The DRM core
> > has recently received support for bridge objects to overcome that
> > limitation. Depending on the hardware topology, a given hardware encoder
> > should be modeled as a DRM encoder or as a DRM bridge. That decision
> > shouldn't be taken by the encoder driver but by the DRM master driver.
> > The I2C encoder driver thus shouldn't create the DRM encoder and DRM
> > connector itself.
> >
> > I believe the encoder/master communication problem should be solved
> > differently. Instead of passing a pointer to the DRM device to the encoder
> > driver and making the encoder driver control DRM encoder and connector
> > creation, the encoder driver should instead create an object not visible
> > to userspace that can be retrieved by the DRM master driver (possibly
> > through registration with the DRM core, or by going through drvdata in the
> > encoder's struct device). The DRM master could use that object to
> > communicate with the encoder, and would register the DRM encoder and DRM
> > connector itself based on hardware topology.
> >
> > > + struct i2c_client *i2c_client = to_i2c_client(dev);
> > > + struct tda998x_priv *priv = i2c_get_clientdata(i2c_client);
> > > + struct drm_connector *connector = &priv->connector;
> > > + struct drm_encoder *encoder = &priv->encoder;
> > > + int ret;
> > > +
> > > + if (!try_module_get(THIS_MODULE)) {
> > > + dev_err(dev, "cannot get module %s\n", THIS_MODULE->name);
> > > + return -EINVAL;
> > > + }
> > > +
> > > + ret = drm_connector_init(drm, connector,
> > > + &connector_funcs,
> > > + DRM_MODE_CONNECTOR_HDMIA);
> >
> > This is one example of the shortcomings I've explained above. An encoder
> > driver can't always know what connector type it is connected to. If I'm
> > not mistaken possible options here are DVII, DVID, HDMIA and HDMIB. It
> > should be up to the master driver to select the connector type based on
> > its overall view of the hardware, or even to a connector driver that would
> > be bound to a connector DT node (as proposed in
> > https://www.mail-archive.com/devicetree@vger.kernel.org/msg16585.html).
> [snip]
>
> The tda998x, as a HDMI transmitter, has to deal with both video and
> audio.
>
> Whereas the hardware connection schemes are the same in both worlds,
> the way they are translated to computer objects are very different:
>
> - video
> DRM card -> CRTCs -> encoders -> (bridges) -> connectors
>
> - audio
> ALSA card -> CPUs -> (CODECs) -> CODECs
>
> and it would be nice to have a common layout.
>
> Actually, the tda998x is a slave encoder, that is, it plays the roles of
> both encoder and connector. In the 2 DRM drivers (armada and tilcdc) which
> use it, yes, the encoders and connectors are created by the main DRM
> drivers, but, there is no notion of bridge, and, also, the encoder is
> DRM_MODE_ENCODER_TMDS and the connector is DRM_MODE_CONNECTOR_HDMIA. Then,
> nothing is changed in the global system working.
>
> About the connector, yes, I let its type as hard-coded, but this could be
> changed by configuration in the platform data or in the DT. Anyway, there is
> nothing as such in the proposed patch
>
> 'Add DT binding documentation for HDMI Connector'
>
> I also dislike this patch because it adds a device which is of no use. I had
> a same remark from Mark Brown about a tda998x CODEC proposal of mine:
>
> hdmi_codec: hdmi-codec {
> compatible = "nxp,tda998x-codec";
> audio-ports = <0x03>, <0x04>;
> };
>
> So, the next tda998x CODEC will be directly included in the tda998x driver,
> the audio output being the HDMI connector. Here is the DT definition I have
> for the Cubox:
>
> &i2c0 {
> hdmi: hdmi-encoder {
> compatible = "nxp,tda9989";
> reg = <0x70>;
> interrupt-parent = <&gpio0>;
> interrupts = <27 IRQ_TYPE_EDGE_FALLING>;
> pinctrl-0 = <&pmx_camera>;
> pinctrl-names = "default";
>
> audio-ports = <0x03>, <0x04>; /* 2 audio input ports */
> audio-port-names = "i2s", "spdif";
> #sound-dai-cells = <1>;
>
> port { /* 1 video input port */
> hdmi_0: endpoint@0 {
> remote-endpoint = <&lcd0_0>;
> };
> };
> };
> };
>
> Back to the DRM device pointer given to the tda998x driver, as the tda998x
> is an encoder/connector, it has no bridge function, so, there is no need to
> add a complex API for information exchange between both drivers.
>
> Anyway, there is a big lack in my proposal: the tda998x encoder is hard-
>coded to the first CRTC. This could be solved by a scan of the DT and of the
> encoder list by the DRM driver, but I think that the actual definitions as
> proposed by media/video-interfaces.txt are not easy to use.
>
> Do you think that a description as done for ALSA could work?
>
> A sound card creation is done by a global sound configuration and a
> description of the links between the card elements. Here is the tda998x
> part of the Cubox audio card ('audio1' is the audio device):
>
> sound {
> compatible = "simple-audio-card";
> simple-audio-card,name = "Cubox Audio";
>
> simple-audio-card,dai-link@0 { /* I2S - HDMI */
> format = "i2s";
> cpu {
> sound-dai = <&audio1 0>; /* I2S output */
> };
> codec {
> sound-dai = <&hdmi 0>; /* I2S input */
> };
> };
>
> simple-audio-card,dai-link@1 { /* S/PDIF - HDMI */
> cpu {
> sound-dai = <&audio1 1>; /* S/PDIF output */
> };
> codec {
> sound-dai = <&hdmi 1>; /* S/PDIF input */
> };
> };
> };
>
> Using the same elements, here is what could be the video card of the
> Armada 510 with a panel, the tda998x and the display controller:
>
> video {
> compatible = "simple-video-card";
>
> simple-video-card,dvi-link {
> crtc {
> dvi = <&lcd0>;
> };
> encoder {
> dvi = <&panel>;
> connector-type = 7; /* LVDS */
> };
> };
> simple-video-card,dvi-link {
> crtc {
> dvi = <&lcd0>;
> };
> encoder {
> dvi = <&hdmi>;
> connector-type = 11; /* HDMI-A */
> };
> };
> };
>
> lcd0: lcd-controller@820000 {
> compatible = "marvell,armada-510-lcd";
> ... hardware definitions ...
> };
>
> hdmi : hdmi-encoder {
> .. same as above, but without the video input port ..
> };
>
> panel: panel {
> .. panel parameters ..
> };
>
> Then, the generic 'simple-video-card' has all elements to create the DRM
> device.
That could work in your case, but I don't really like that.
We need to describe the hardware topology, that might be the only point we all
agree on. There are various hardware topologies we need to support with
different levels of complexity, and several ways to describe them, also with a
wide range of complexities and features.
The more complex the hardware topology, the more complex its description needs
to be, and the more complex the code that will parse the description and
handle the hardware will be. I don't think there's any doubt here.
A given device can be integrated in a wide variety of hardware with different
complexities. A driver can't thus always assume that the whole composite
display device will match a given class of topologies. This is especially true
for encoders and connectors, they can be hooked up directly at the output of a
very simple display controller, or can be part of a very complex graph.
Encoder and connector drivers can't assume much about how they will be
integrated. I want to avoid a situation where using an HDMI encoder already
supported in mainline with a different SoC will result in having to rewrite
the HDMI encoder driver.
The story is a bit different for display controllers. While the hardware
topology outside (and sometimes inside as well) of the SoC can vary, a display
controller often approximately implies a given level of complexity. A cheap
SoC with a simple display controller will likely not be used to drive a 4k
display requiring 4 encoders running in parallel for instance. While I also
want to avoid having to make significant changes to a display controller
driver when using the SoC on a different board, I believe the requirement can
be slightly relaxed here, and the display controller driver(s) can assume more
about the hardware topology than the encoder drivers.
I've asked myself whether we needed a single, one-size-fits-them-all DT
representation of the hardware topology. The view of the world from an
external encoder point of view must not depend on the SoC it is hooked up to
(this would prevent using a single encoder driver with multiple SoCs), which
calls for at least some form of standardization. The topology representation
on the display controller side may vary from display controller to display
controller, but I believe this would just result in code duplication and
having to solve the same problem in multiple drivers. For those reasons I
believe that the OF graph proposal to represent the display hardware topology
would be a good choice. The bindings are flexible enough to represent both
simple and complex hardware.
Now, I don't want to force all display device drivers to implement complex
code when they only need to support simple hardware and simple hardware
topologies. Not only would that be rightfully rejected, I would be among the
ones nack'ing that approach. My opinion is that this calls for the creation of
helpers to handle common cases. Several (possibly many) display systems only
need (or want) to support linear pipelines at their output(s), made of a
single encoder and a single connector. There's no point in duplicating DT
parsing or encoder/connector instantiation code in several drivers in that
case where helpers could be reused. Several sets of helpers could support
different kinds of topologies, with the driver author selecting a set of
helpers depending on the expected hardware topology complexity.
We also need to decide on who (as in which driver) will be responsible for
binding all devices together. As DRM exposes a single device to userspace,
there needs to be a single driver that will perform front line handling of the
userspace API and delegate calls to the other drivers involved. I believe it
would be logical for that driver to also be in charge of bindings the
components together, as it will be the driver that delegate calls to the
components. For a similar reason I also believe that that driver should also
be the one in control of creating DRM encoders and DRM connectors. The
component drivers having only a narrow view of the device they handle, they
can't perform that task in a generic way and would thus get tied to specific
master drivers because of the assumptions they would make.
Whether the master driver is the CRTC driver or a separate driver that binds
standalone CRTCs, connectors and encoders doesn't in my opinion change my
above conclusions. Some SoCs could use a simple-video-card driver like the one
you've proposed, and others could implement a display controller driver that
would perform the same tasks for more complex pipelines in addition to
controlling the display controller's CRTC(s). The simple-video-card driver
would perform that same tasks as the helpers I've previously talked about, so
the two solutions are pretty similar, and I don't see much added value in the
general case in having a simple-video-card driver compared to using helpers in
the display controller driver.
The point that matters to me is that encoders DT bindings and drivers must be
identical regardless of whether the master driver is the display controller
driver or a driver for a logical composite device. For all those reasons we
should use the OF graph DT bindings for the simple-video-card driver should we
decide to implement it, and we should create DRM encoders and connectors in
the master driver, not in the encoder drivers.
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH RFC v2 2/6] drm/i2c: tda998x: Move tda998x to a couple encoder/connector
2014-03-26 17:33 ` Laurent Pinchart
(?)
@ 2014-03-27 11:34 ` Jean-Francois Moine
-1 siblings, 0 replies; 32+ messages in thread
From: Jean-Francois Moine @ 2014-03-27 11:34 UTC (permalink / raw)
To: Laurent Pinchart
Cc: dri-devel, Russell King, Rob Clark, linux-arm-kernel, linux-media
Hi Laurent,
On Wed, 26 Mar 2014 18:33:09 +0100
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:
> That could work in your case, but I don't really like that.
>
> We need to describe the hardware topology, that might be the only point we all
> agree on. There are various hardware topologies we need to support with
> different levels of complexity, and several ways to describe them, also with a
> wide range of complexities and features.
>
> The more complex the hardware topology, the more complex its description needs
> to be, and the more complex the code that will parse the description and
> handle the hardware will be. I don't think there's any doubt here.
But also, the simpler is the topology and the simpler should be its
description.
In your approach, the connections between endpoints are described in
the components themselves, which makes hard for the DT maintainers to
have a global understanding of the video subsystem.
Having the topology described in the master device is clearer, even if
it is complex.
> A given device can be integrated in a wide variety of hardware with different
> complexities. A driver can't thus always assume that the whole composite
> display device will match a given class of topologies. This is especially true
> for encoders and connectors, they can be hooked up directly at the output of a
> very simple display controller, or can be part of a very complex graph.
> Encoder and connector drivers can't assume much about how they will be
> integrated. I want to avoid a situation where using an HDMI encoder already
> supported in mainline with a different SoC will result in having to rewrite
> the HDMI encoder driver.
The tda998x chips are simple enough for being used in many boards: one
video input and one serial digital output. I don't see in which
circumstance the driver should be rewritten.
> The story is a bit different for display controllers. While the hardware
> topology outside (and sometimes inside as well) of the SoC can vary, a display
> controller often approximately implies a given level of complexity. A cheap
> SoC with a simple display controller will likely not be used to drive a 4k
> display requiring 4 encoders running in parallel for instance. While I also
> want to avoid having to make significant changes to a display controller
> driver when using the SoC on a different board, I believe the requirement can
> be slightly relaxed here, and the display controller driver(s) can assume more
> about the hardware topology than the encoder drivers.
I don't think that the display controllers or the encoders have to know
about the topology. They have well-knwon specific functions and the way
they are interconnected should not impact these functions. If that
would be the case, they should offer a particular configuration
interface to the master driver.
> I've asked myself whether we needed a single, one-size-fits-them-all DT
> representation of the hardware topology. The view of the world from an
> external encoder point of view must not depend on the SoC it is hooked up to
> (this would prevent using a single encoder driver with multiple SoCs), which
> calls for at least some form of standardization. The topology representation
> on the display controller side may vary from display controller to display
> controller, but I believe this would just result in code duplication and
> having to solve the same problem in multiple drivers. For those reasons I
> believe that the OF graph proposal to represent the display hardware topology
> would be a good choice. The bindings are flexible enough to represent both
> simple and complex hardware.
Your OF graph proposal is rather complex, and it does not even indicate
which way the data flows.
> Now, I don't want to force all display device drivers to implement complex
> code when they only need to support simple hardware and simple hardware
> topologies. Not only would that be rightfully rejected, I would be among the
> ones nack'ing that approach. My opinion is that this calls for the creation of
> helpers to handle common cases. Several (possibly many) display systems only
> need (or want) to support linear pipelines at their output(s), made of a
> single encoder and a single connector. There's no point in duplicating DT
> parsing or encoder/connector instantiation code in several drivers in that
> case where helpers could be reused. Several sets of helpers could support
> different kinds of topologies, with the driver author selecting a set of
> helpers depending on the expected hardware topology complexity.
I don't like this 'helper' notion. See below.
> We also need to decide on who (as in which driver) will be responsible for
> binding all devices together. As DRM exposes a single device to userspace,
> there needs to be a single driver that will perform front line handling of the
> userspace API and delegate calls to the other drivers involved. I believe it
> would be logical for that driver to also be in charge of bindings the
> components together, as it will be the driver that delegate calls to the
> components. For a similar reason I also believe that that driver should also
> be the one in control of creating DRM encoders and DRM connectors. The
> component drivers having only a narrow view of the device they handle, they
> can't perform that task in a generic way and would thus get tied to specific
> master drivers because of the assumptions they would make.
I don't see why the encoders and connectors should be created by some
external entity. They are declared in the DT or created by the board
init, so, they exist at system startup time.
> Whether the master driver is the CRTC driver or a separate driver that binds
> standalone CRTCs, connectors and encoders doesn't in my opinion change my
> above conclusions. Some SoCs could use a simple-video-card driver like the one
> you've proposed, and others could implement a display controller driver that
> would perform the same tasks for more complex pipelines in addition to
> controlling the display controller's CRTC(s). The simple-video-card driver
> would perform that same tasks as the helpers I've previously talked about, so
> the two solutions are pretty similar, and I don't see much added value in the
> general case in having a simple-video-card driver compared to using helpers in
> the display controller driver.
In fact, I wonder why there is not only one DRM driver. The global
logic is always the same, and, actually, it is duplicated in each
specific driver. I had this approach in the gspca driver: one main
driver and many specific subdrivers. Once, I even had the idea to
convert your UVC driver into a gspca subdriver, but, at this time, I
had too much work with the other webcams. Nevertheless, it would have
been easy: all the required interfaces were (are still) present in the
main gspca driver.
In the same order of idea, there is only one ALSA SoC driver, and you
cannot say that the audio topology is less complex than the video one.
When thinking about the unique DRM driver, there would no helper anymore:
the driver would offer to each component the functions they need for
working correctly.
> The point that matters to me is that encoders DT bindings and drivers must be
> identical regardless of whether the master driver is the display controller
> driver or a driver for a logical composite device. For all those reasons we
> should use the OF graph DT bindings for the simple-video-card driver should we
> decide to implement it, and we should create DRM encoders and connectors in
> the master driver, not in the encoder drivers.
Sorry, but I think that a DRM encoder is the hardware encoder. What
else could it be? At initialization time, the master driver has only to
manage the relation between the video components. It has not to create
entities which already exist. So my preferred scheme is:
- at starting time, each video component initializes its software and
hardware, and then, plugs itself into the DRM driver.
- from the central video topology, the DRM device waits for all
components to be plugged and then it links the components together,
creating the user's view of the system.
If you create the encoders and connectors in the DRM master, the
hardware encoder and connector devices are just like zombies. They must
put something in the system to say they are here and they must also
have a callback function for them to know to which video subsystem they
belong.
On the contrary, if the DRM encoders and connectors are created by the
hardware devices, they are immediately alive and operational.
--
Ken ar c'hentañ | ** Breizh ha Linux atav! **
Jef | http://moinejf.free.fr/
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH RFC v2 2/6] drm/i2c: tda998x: Move tda998x to a couple encoder/connector
@ 2014-03-27 11:34 ` Jean-Francois Moine
0 siblings, 0 replies; 32+ messages in thread
From: Jean-Francois Moine @ 2014-03-27 11:34 UTC (permalink / raw)
To: linux-arm-kernel
Hi Laurent,
On Wed, 26 Mar 2014 18:33:09 +0100
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:
> That could work in your case, but I don't really like that.
>
> We need to describe the hardware topology, that might be the only point we all
> agree on. There are various hardware topologies we need to support with
> different levels of complexity, and several ways to describe them, also with a
> wide range of complexities and features.
>
> The more complex the hardware topology, the more complex its description needs
> to be, and the more complex the code that will parse the description and
> handle the hardware will be. I don't think there's any doubt here.
But also, the simpler is the topology and the simpler should be its
description.
In your approach, the connections between endpoints are described in
the components themselves, which makes hard for the DT maintainers to
have a global understanding of the video subsystem.
Having the topology described in the master device is clearer, even if
it is complex.
> A given device can be integrated in a wide variety of hardware with different
> complexities. A driver can't thus always assume that the whole composite
> display device will match a given class of topologies. This is especially true
> for encoders and connectors, they can be hooked up directly at the output of a
> very simple display controller, or can be part of a very complex graph.
> Encoder and connector drivers can't assume much about how they will be
> integrated. I want to avoid a situation where using an HDMI encoder already
> supported in mainline with a different SoC will result in having to rewrite
> the HDMI encoder driver.
The tda998x chips are simple enough for being used in many boards: one
video input and one serial digital output. I don't see in which
circumstance the driver should be rewritten.
> The story is a bit different for display controllers. While the hardware
> topology outside (and sometimes inside as well) of the SoC can vary, a display
> controller often approximately implies a given level of complexity. A cheap
> SoC with a simple display controller will likely not be used to drive a 4k
> display requiring 4 encoders running in parallel for instance. While I also
> want to avoid having to make significant changes to a display controller
> driver when using the SoC on a different board, I believe the requirement can
> be slightly relaxed here, and the display controller driver(s) can assume more
> about the hardware topology than the encoder drivers.
I don't think that the display controllers or the encoders have to know
about the topology. They have well-knwon specific functions and the way
they are interconnected should not impact these functions. If that
would be the case, they should offer a particular configuration
interface to the master driver.
> I've asked myself whether we needed a single, one-size-fits-them-all DT
> representation of the hardware topology. The view of the world from an
> external encoder point of view must not depend on the SoC it is hooked up to
> (this would prevent using a single encoder driver with multiple SoCs), which
> calls for at least some form of standardization. The topology representation
> on the display controller side may vary from display controller to display
> controller, but I believe this would just result in code duplication and
> having to solve the same problem in multiple drivers. For those reasons I
> believe that the OF graph proposal to represent the display hardware topology
> would be a good choice. The bindings are flexible enough to represent both
> simple and complex hardware.
Your OF graph proposal is rather complex, and it does not even indicate
which way the data flows.
> Now, I don't want to force all display device drivers to implement complex
> code when they only need to support simple hardware and simple hardware
> topologies. Not only would that be rightfully rejected, I would be among the
> ones nack'ing that approach. My opinion is that this calls for the creation of
> helpers to handle common cases. Several (possibly many) display systems only
> need (or want) to support linear pipelines at their output(s), made of a
> single encoder and a single connector. There's no point in duplicating DT
> parsing or encoder/connector instantiation code in several drivers in that
> case where helpers could be reused. Several sets of helpers could support
> different kinds of topologies, with the driver author selecting a set of
> helpers depending on the expected hardware topology complexity.
I don't like this 'helper' notion. See below.
> We also need to decide on who (as in which driver) will be responsible for
> binding all devices together. As DRM exposes a single device to userspace,
> there needs to be a single driver that will perform front line handling of the
> userspace API and delegate calls to the other drivers involved. I believe it
> would be logical for that driver to also be in charge of bindings the
> components together, as it will be the driver that delegate calls to the
> components. For a similar reason I also believe that that driver should also
> be the one in control of creating DRM encoders and DRM connectors. The
> component drivers having only a narrow view of the device they handle, they
> can't perform that task in a generic way and would thus get tied to specific
> master drivers because of the assumptions they would make.
I don't see why the encoders and connectors should be created by some
external entity. They are declared in the DT or created by the board
init, so, they exist at system startup time.
> Whether the master driver is the CRTC driver or a separate driver that binds
> standalone CRTCs, connectors and encoders doesn't in my opinion change my
> above conclusions. Some SoCs could use a simple-video-card driver like the one
> you've proposed, and others could implement a display controller driver that
> would perform the same tasks for more complex pipelines in addition to
> controlling the display controller's CRTC(s). The simple-video-card driver
> would perform that same tasks as the helpers I've previously talked about, so
> the two solutions are pretty similar, and I don't see much added value in the
> general case in having a simple-video-card driver compared to using helpers in
> the display controller driver.
In fact, I wonder why there is not only one DRM driver. The global
logic is always the same, and, actually, it is duplicated in each
specific driver. I had this approach in the gspca driver: one main
driver and many specific subdrivers. Once, I even had the idea to
convert your UVC driver into a gspca subdriver, but, at this time, I
had too much work with the other webcams. Nevertheless, it would have
been easy: all the required interfaces were (are still) present in the
main gspca driver.
In the same order of idea, there is only one ALSA SoC driver, and you
cannot say that the audio topology is less complex than the video one.
When thinking about the unique DRM driver, there would no helper anymore:
the driver would offer to each component the functions they need for
working correctly.
> The point that matters to me is that encoders DT bindings and drivers must be
> identical regardless of whether the master driver is the display controller
> driver or a driver for a logical composite device. For all those reasons we
> should use the OF graph DT bindings for the simple-video-card driver should we
> decide to implement it, and we should create DRM encoders and connectors in
> the master driver, not in the encoder drivers.
Sorry, but I think that a DRM encoder is the hardware encoder. What
else could it be? At initialization time, the master driver has only to
manage the relation between the video components. It has not to create
entities which already exist. So my preferred scheme is:
- at starting time, each video component initializes its software and
hardware, and then, plugs itself into the DRM driver.
- from the central video topology, the DRM device waits for all
components to be plugged and then it links the components together,
creating the user's view of the system.
If you create the encoders and connectors in the DRM master, the
hardware encoder and connector devices are just like zombies. They must
put something in the system to say they are here and they must also
have a callback function for them to know to which video subsystem they
belong.
On the contrary, if the DRM encoders and connectors are created by the
hardware devices, they are immediately alive and operational.
--
Ken ar c'henta? | ** Breizh ha Linux atav! **
Jef | http://moinejf.free.fr/
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH RFC v2 2/6] drm/i2c: tda998x: Move tda998x to a couple encoder/connector
@ 2014-03-27 11:34 ` Jean-Francois Moine
0 siblings, 0 replies; 32+ messages in thread
From: Jean-Francois Moine @ 2014-03-27 11:34 UTC (permalink / raw)
To: Laurent Pinchart; +Cc: Russell King, linux-arm-kernel, dri-devel, linux-media
Hi Laurent,
On Wed, 26 Mar 2014 18:33:09 +0100
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:
> That could work in your case, but I don't really like that.
>
> We need to describe the hardware topology, that might be the only point we all
> agree on. There are various hardware topologies we need to support with
> different levels of complexity, and several ways to describe them, also with a
> wide range of complexities and features.
>
> The more complex the hardware topology, the more complex its description needs
> to be, and the more complex the code that will parse the description and
> handle the hardware will be. I don't think there's any doubt here.
But also, the simpler is the topology and the simpler should be its
description.
In your approach, the connections between endpoints are described in
the components themselves, which makes hard for the DT maintainers to
have a global understanding of the video subsystem.
Having the topology described in the master device is clearer, even if
it is complex.
> A given device can be integrated in a wide variety of hardware with different
> complexities. A driver can't thus always assume that the whole composite
> display device will match a given class of topologies. This is especially true
> for encoders and connectors, they can be hooked up directly at the output of a
> very simple display controller, or can be part of a very complex graph.
> Encoder and connector drivers can't assume much about how they will be
> integrated. I want to avoid a situation where using an HDMI encoder already
> supported in mainline with a different SoC will result in having to rewrite
> the HDMI encoder driver.
The tda998x chips are simple enough for being used in many boards: one
video input and one serial digital output. I don't see in which
circumstance the driver should be rewritten.
> The story is a bit different for display controllers. While the hardware
> topology outside (and sometimes inside as well) of the SoC can vary, a display
> controller often approximately implies a given level of complexity. A cheap
> SoC with a simple display controller will likely not be used to drive a 4k
> display requiring 4 encoders running in parallel for instance. While I also
> want to avoid having to make significant changes to a display controller
> driver when using the SoC on a different board, I believe the requirement can
> be slightly relaxed here, and the display controller driver(s) can assume more
> about the hardware topology than the encoder drivers.
I don't think that the display controllers or the encoders have to know
about the topology. They have well-knwon specific functions and the way
they are interconnected should not impact these functions. If that
would be the case, they should offer a particular configuration
interface to the master driver.
> I've asked myself whether we needed a single, one-size-fits-them-all DT
> representation of the hardware topology. The view of the world from an
> external encoder point of view must not depend on the SoC it is hooked up to
> (this would prevent using a single encoder driver with multiple SoCs), which
> calls for at least some form of standardization. The topology representation
> on the display controller side may vary from display controller to display
> controller, but I believe this would just result in code duplication and
> having to solve the same problem in multiple drivers. For those reasons I
> believe that the OF graph proposal to represent the display hardware topology
> would be a good choice. The bindings are flexible enough to represent both
> simple and complex hardware.
Your OF graph proposal is rather complex, and it does not even indicate
which way the data flows.
> Now, I don't want to force all display device drivers to implement complex
> code when they only need to support simple hardware and simple hardware
> topologies. Not only would that be rightfully rejected, I would be among the
> ones nack'ing that approach. My opinion is that this calls for the creation of
> helpers to handle common cases. Several (possibly many) display systems only
> need (or want) to support linear pipelines at their output(s), made of a
> single encoder and a single connector. There's no point in duplicating DT
> parsing or encoder/connector instantiation code in several drivers in that
> case where helpers could be reused. Several sets of helpers could support
> different kinds of topologies, with the driver author selecting a set of
> helpers depending on the expected hardware topology complexity.
I don't like this 'helper' notion. See below.
> We also need to decide on who (as in which driver) will be responsible for
> binding all devices together. As DRM exposes a single device to userspace,
> there needs to be a single driver that will perform front line handling of the
> userspace API and delegate calls to the other drivers involved. I believe it
> would be logical for that driver to also be in charge of bindings the
> components together, as it will be the driver that delegate calls to the
> components. For a similar reason I also believe that that driver should also
> be the one in control of creating DRM encoders and DRM connectors. The
> component drivers having only a narrow view of the device they handle, they
> can't perform that task in a generic way and would thus get tied to specific
> master drivers because of the assumptions they would make.
I don't see why the encoders and connectors should be created by some
external entity. They are declared in the DT or created by the board
init, so, they exist at system startup time.
> Whether the master driver is the CRTC driver or a separate driver that binds
> standalone CRTCs, connectors and encoders doesn't in my opinion change my
> above conclusions. Some SoCs could use a simple-video-card driver like the one
> you've proposed, and others could implement a display controller driver that
> would perform the same tasks for more complex pipelines in addition to
> controlling the display controller's CRTC(s). The simple-video-card driver
> would perform that same tasks as the helpers I've previously talked about, so
> the two solutions are pretty similar, and I don't see much added value in the
> general case in having a simple-video-card driver compared to using helpers in
> the display controller driver.
In fact, I wonder why there is not only one DRM driver. The global
logic is always the same, and, actually, it is duplicated in each
specific driver. I had this approach in the gspca driver: one main
driver and many specific subdrivers. Once, I even had the idea to
convert your UVC driver into a gspca subdriver, but, at this time, I
had too much work with the other webcams. Nevertheless, it would have
been easy: all the required interfaces were (are still) present in the
main gspca driver.
In the same order of idea, there is only one ALSA SoC driver, and you
cannot say that the audio topology is less complex than the video one.
When thinking about the unique DRM driver, there would no helper anymore:
the driver would offer to each component the functions they need for
working correctly.
> The point that matters to me is that encoders DT bindings and drivers must be
> identical regardless of whether the master driver is the display controller
> driver or a driver for a logical composite device. For all those reasons we
> should use the OF graph DT bindings for the simple-video-card driver should we
> decide to implement it, and we should create DRM encoders and connectors in
> the master driver, not in the encoder drivers.
Sorry, but I think that a DRM encoder is the hardware encoder. What
else could it be? At initialization time, the master driver has only to
manage the relation between the video components. It has not to create
entities which already exist. So my preferred scheme is:
- at starting time, each video component initializes its software and
hardware, and then, plugs itself into the DRM driver.
- from the central video topology, the DRM device waits for all
components to be plugged and then it links the components together,
creating the user's view of the system.
If you create the encoders and connectors in the DRM master, the
hardware encoder and connector devices are just like zombies. They must
put something in the system to say they are here and they must also
have a callback function for them to know to which video subsystem they
belong.
On the contrary, if the DRM encoders and connectors are created by the
hardware devices, they are immediately alive and operational.
--
Ken ar c'hentañ | ** Breizh ha Linux atav! **
Jef | http://moinejf.free.fr/
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH RFC v2 2/6] drm/i2c: tda998x: Move tda998x to a couple encoder/connector
2014-03-27 11:34 ` Jean-Francois Moine
(?)
@ 2014-04-01 17:55 ` Laurent Pinchart
-1 siblings, 0 replies; 32+ messages in thread
From: Laurent Pinchart @ 2014-04-01 17:55 UTC (permalink / raw)
To: Jean-Francois Moine
Cc: dri-devel, Russell King, Rob Clark, linux-arm-kernel, linux-media
Hi Jean-François,
Sorry for the late reply.
On Thursday 27 March 2014 12:34:49 Jean-Francois Moine wrote:
> On Wed, 26 Mar 2014 18:33:09 +0100 Laurent Pinchart wrote:
> > That could work in your case, but I don't really like that.
> >
> > We need to describe the hardware topology, that might be the only point we
> > all agree on. There are various hardware topologies we need to support
> > with different levels of complexity, and several ways to describe them,
> > also with a wide range of complexities and features.
> >
> > The more complex the hardware topology, the more complex its description
> > needs to be, and the more complex the code that will parse the
> > description and handle the hardware will be. I don't think there's any
> > doubt here.
>
> But also, the simpler is the topology and the simpler should be its
> description.
I wouldn't put it so strongly. I believe we can slightly relax our
requirements regarding DT bindings complexity as long as drivers remain
simple. There's of course no reason to use more complex bindings just for the
sake of it.
> In your approach, the connections between endpoints are described in the
> components themselves, which makes hard for the DT maintainers to have a
> global understanding of the video subsystem.
>
> Having the topology described in the master device is clearer, even if it is
> complex.
First of all, let's clarify what a "master device" is. In the "simple-video-
card" example you've proposed the master device is a logical device (with a DT
node that has no corresponding hardware device). The second approach I can
think of is to make the IP core that implements the CRTC (which I usually call
display controller) be the master device. Let's note that the second case
makes both the link and "global description" DT binding styles possible.
My concern with the "global description" bindings style is that the approach
only applies to simple hardware and can't be generalized. Now, I'm not too
concerned about using that approach for simple hardware and a link-based
approach for more complex hardware. The "slave" components, however, need to
use a single DT bindings style, regardless of the DT bindings used by the
master device. That's why I believe we should try to standardize one DT
bindings style for master devices, even if it results in a slightly more
complex DT description than strictly needed in some cases.
> > A given device can be integrated in a wide variety of hardware with
> > different complexities. A driver can't thus always assume that the whole
> > composite display device will match a given class of topologies. This is
> > especially true for encoders and connectors, they can be hooked up
> > directly at the output of a very simple display controller, or can be
> > part of a very complex graph. Encoder and connector drivers can't assume
> > much about how they will be integrated. I want to avoid a situation where
> > using an HDMI encoder already supported in mainline with a different SoC
> > will result in having to rewrite the HDMI encoder driver.
>
> The tda998x chips are simple enough for being used in many boards: one video
> input and one serial digital output. I don't see in which circumstance the
> driver should be rewritten.
It shouldn't, that's the whole point ;-) I wasn't talking about the tda998x
only, but about encoder drivers in general. I have a display controller in a
Renesas SoC that has two LVDS encoders that output LVDS signals out of the
SoC. One LVDS port is connected to an LVDS panel (a connector from a DRM point
of view), the second one to an LVDS receiver that outputs parallel RGB data to
an HDMI encoder. The LVDS encoder can't thus assume any particular downstream
topology and its driver thus can't create DRM encoder and connector instances.
The same could be true for an HDMI encoder in theory, although an HDMI encoder
connected on the same board directly to an HDMI decoder that outputs RGB data
to a panel is probably a case that will be rarely seen in practice.
In the general case an encoder driver can't assume any specific upstream or
downstream topology. As long as DRM can't expose the real hardware topology to
userspace, someone needs to decide on how to map the hardware topology to the
DRM topology exposed to userspace. Encoder drivers can't take that role and
thus shouldn't create DRM encoder and connector instances themselves.
> > The story is a bit different for display controllers. While the hardware
> > topology outside (and sometimes inside as well) of the SoC can vary, a
> > display controller often approximately implies a given level of
> > complexity. A cheap SoC with a simple display controller will likely not
> > be used to drive a 4k display requiring 4 encoders running in parallel
> > for instance. While I also want to avoid having to make significant
> > changes to a display controller driver when using the SoC on a different
> > board, I believe the requirement can be slightly relaxed here, and the
> > display controller driver(s) can assume more about the hardware topology
> > than the encoder drivers.
>
> I don't think that the display controllers or the encoders have to know
> about the topology. They have well-knwon specific functions and the way they
> are interconnected should not impact these functions. If that would be the
> case, they should offer a particular configuration interface to the master
> driver.
As explained above, part of the problem comes from the fact that we need to
expose a logical topology to userspace that doesn't map 1:1 to the hardware
topology. We can discuss whether or not this should be changed, but I believe
that's out of scope. As we'll need to map logical encoders and connectors to
real hardware in the foreseeable future we need a solution to that problem,
and I believe the display controller should be in control of the mapping
(possibly using helpers).
> > I've asked myself whether we needed a single, one-size-fits-them-all DT
> > representation of the hardware topology. The view of the world from an
> > external encoder point of view must not depend on the SoC it is hooked up
> > to (this would prevent using a single encoder driver with multiple SoCs),
> > which calls for at least some form of standardization. The topology
> > representation on the display controller side may vary from display
> > controller to display controller, but I believe this would just result in
> > code duplication and having to solve the same problem in multiple
> > drivers. For those reasons I believe that the OF graph proposal to
> > represent the display hardware topology would be a good choice. The
> > bindings are flexible enough to represent both simple and complex
> > hardware.
>
> Your OF graph proposal is rather complex,
I agree it's slightly more complex than your simple-video-card proposal for
simple hardware, but I don't see it as overly complex, as long as we can
provide helper functions that moves the parsing complexity out of drivers (at
least drivers for simple hardware).
> and it does not even indicate which way the data flows.
Please note that the OF graph DT bindings are not set in stone. If we need to
indicate the data flow direction (and I assume we will need to at some point)
that should just be discussed and implemented.
> > Now, I don't want to force all display device drivers to implement complex
> > code when they only need to support simple hardware and simple hardware
> > topologies. Not only would that be rightfully rejected, I would be among
> > the ones nack'ing that approach. My opinion is that this calls for the
> > creation of helpers to handle common cases. Several (possibly many)
> > display systems only need (or want) to support linear pipelines at their
> > output(s), made of a single encoder and a single connector. There's no
> > point in duplicating DT parsing or encoder/connector instantiation code
> > in several drivers in that case where helpers could be reused. Several
> > sets of helpers could support different kinds of topologies, with the
> > driver author selecting a set of helpers depending on the expected
> > hardware topology complexity.
>
> I don't like this 'helper' notion. See below.
>
> > We also need to decide on who (as in which driver) will be responsible for
> > binding all devices together. As DRM exposes a single device to userspace,
> > there needs to be a single driver that will perform front line handling of
> > the userspace API and delegate calls to the other drivers involved. I
> > believe it would be logical for that driver to also be in charge of
> > bindings the components together, as it will be the driver that delegate
> > calls to the components. For a similar reason I also believe that that
> > driver should also be the one in control of creating DRM encoders and DRM
> > connectors. The component drivers having only a narrow view of the device
> > they handle, they can't perform that task in a generic way and would thus
> > get tied to specific master drivers because of the assumptions they would
> > make.
>
> I don't see why the encoders and connectors should be created by some
> external entity. They are declared in the DT or created by the board init,
> so, they exist at system startup time.
As explained above, the hardware encoders and connectors are declared in DT,
but they don't map 1:1 to the logical encoders and connectors exposed by DRM
to userspace.
Obviously, in some cases (and probably a majority of cases today), the mapping
will be 1:1, and the DRM encoders and connectors could be created by the
encoder drivers, but that won't scale to more complex hardware. The transition
to DT is a disruption that I'd like to take as an opportunity to model things
correctly. We *could* transition to DT by creating encoders and connectors in
encoder drivers, but a second disruption will then be needed in the near
future to transition to a model that can handle most hardware. The drm_bridge
API was a first step in such a direction, and I think we should take it into
account.
> > Whether the master driver is the CRTC driver or a separate driver that
> > binds standalone CRTCs, connectors and encoders doesn't in my opinion
> > change my above conclusions. Some SoCs could use a simple-video-card
> > driver like the one you've proposed, and others could implement a display
> > controller driver that would perform the same tasks for more complex
> > pipelines in addition to controlling the display controller's CRTC(s).
> > The simple-video-card driver would perform that same tasks as the helpers
> > I've previously talked about, so the two solutions are pretty similar,
> > and I don't see much added value in the general case in having a
> > simple-video-card driver compared to using helpers in the display
> > controller driver.
>
> In fact, I wonder why there is not only one DRM driver. The global logic is
> always the same, and, actually, it is duplicated in each specific driver.
That's quite a good question, and I agree that several DRM drivers, especially
drivers for embedded systems, are pretty similar in architecture.
The CRTC helpers are a pretty good example of an attempt to share common code
between drivers. The Intel DRM driver used them, developers then realized that
it made their life more difficult, and decided to implement the DRM API
directly instead of using the helpers.
The devil is in the details. Architectures can seem similar, but subtle
difference can make a common implementation not only complex and difficult to
maintain (as changing it will impact all drivers), but also useless when it
tries to cater for the needs of everybody. An abstraction for something that
can't be abstracted is just pointless.
I'm all for sharing code. This should, in my opinion, be done by providing
optional helpers that drivers can use. That's how the CRTC helpers work,
that's how the V4L2 control framework and videobuf2 library work, and that
allows drivers to provide their own implementation in the rare cases when the
generic helpers are not good enough. I think simple helpers that work in 90%
of the cases are much better than really complex helpers that would achieve a
95% coverage only anyway.
Given that several embedded DRM drivers duplicate code it might be that a
different set of CRTC helpers could be better for that class of hardware. If
you want to experiment with you I'd be glad to discuss requirements and
architectures.
> I had this approach in the gspca driver: one main driver and many specific
> subdrivers. Once, I even had the idea to convert your UVC driver into a
> gspca subdriver, but, at this time, I had too much work with the other
> webcams. Nevertheless, it would have been easy: all the required interfaces
> were (are still) present in the main gspca driver.
If you look into the details of the uvcvideo driver you'll find that it
wouldn't be that easy. UVC is a special case in that its architecture is
pretty different from the other webcam drivers. For instance the driver
doesn't use the V4L2 control framework, as its needs regarding controls are
quite special. I've discussed this with Hans Verkuil and we've concluded (at
least for the time being) that implementing the features uvcvideo requires in
the control framework would make the framework way too complex, just because
of a single driver.
This is just a counterexample of course. It doesn't mean that code shouldn't
be shared, quite the contrary.
> In the same order of idea, there is only one ALSA SoC driver, and you cannot
> say that the audio topology is less complex than the video one.
I can't really comment on that, I don't have enough knowledge of ALSA.
> When thinking about the unique DRM driver, there would no helper anymore:
> the driver would offer to each component the functions they need for working
> correctly.
That's where things get hairy. There will be one driver that won't fit in the
model. There will then be two options: filling the framework with exceptions
and corner cases for just one user, create a spaghetti mess in common code, or
allowing drivers to opt-out. I'm a strong believer in the second option. For
the sake of completeness I want to mention the third option, which is to make
the framework so thin that in becomes a wrapper that mostly translates
function calls 1:1, which is pretty useless.
We're getting out of scope here though. I'd be happy to continue discussing
the single DRM driver with you, but let's do so in a separate mail thread
then.
> > The point that matters to me is that encoders DT bindings and drivers must
> > be identical regardless of whether the master driver is the display
> > controller driver or a driver for a logical composite device. For all
> > those reasons we should use the OF graph DT bindings for the
> > simple-video-card driver should we decide to implement it, and we should
> > create DRM encoders and connectors in the master driver, not in the
> > encoder drivers.
>
> Sorry, but I think that a DRM encoder is the hardware encoder. What else
> could it be?
As explained above, a DRM encoder is a logical entity that usually corresponds
to a hardware encoder, but that can also correspond to several encoders
chained (sometimes using the drm_bridge abstraction, but not always).
> At initialization time, the master driver has only to manage the relation
> between the video components. It has not to create entities which already
> exist. So my preferred scheme is:
>
> - at starting time, each video component initializes its software and
> hardware, and then, plugs itself into the DRM driver.
>
> - from the central video topology, the DRM device waits for all
> components to be plugged and then it links the components together,
> creating the user's view of the system.
So far I agree with you, but I believe this calls for an in-kernel view of the
components possibly different than the drm_encoder and drm_connector objects.
> If you create the encoders and connectors in the DRM master, the hardware
> encoder and connector devices are just like zombies. They must put something
> in the system to say they are here and they must also have a callback
> function for them to know to which video subsystem they belong.
>
> On the contrary, if the DRM encoders and connectors are created by the
> hardware devices, they are immediately alive and operational.
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH RFC v2 2/6] drm/i2c: tda998x: Move tda998x to a couple encoder/connector
@ 2014-04-01 17:55 ` Laurent Pinchart
0 siblings, 0 replies; 32+ messages in thread
From: Laurent Pinchart @ 2014-04-01 17:55 UTC (permalink / raw)
To: linux-arm-kernel
Hi Jean-Fran?ois,
Sorry for the late reply.
On Thursday 27 March 2014 12:34:49 Jean-Francois Moine wrote:
> On Wed, 26 Mar 2014 18:33:09 +0100 Laurent Pinchart wrote:
> > That could work in your case, but I don't really like that.
> >
> > We need to describe the hardware topology, that might be the only point we
> > all agree on. There are various hardware topologies we need to support
> > with different levels of complexity, and several ways to describe them,
> > also with a wide range of complexities and features.
> >
> > The more complex the hardware topology, the more complex its description
> > needs to be, and the more complex the code that will parse the
> > description and handle the hardware will be. I don't think there's any
> > doubt here.
>
> But also, the simpler is the topology and the simpler should be its
> description.
I wouldn't put it so strongly. I believe we can slightly relax our
requirements regarding DT bindings complexity as long as drivers remain
simple. There's of course no reason to use more complex bindings just for the
sake of it.
> In your approach, the connections between endpoints are described in the
> components themselves, which makes hard for the DT maintainers to have a
> global understanding of the video subsystem.
>
> Having the topology described in the master device is clearer, even if it is
> complex.
First of all, let's clarify what a "master device" is. In the "simple-video-
card" example you've proposed the master device is a logical device (with a DT
node that has no corresponding hardware device). The second approach I can
think of is to make the IP core that implements the CRTC (which I usually call
display controller) be the master device. Let's note that the second case
makes both the link and "global description" DT binding styles possible.
My concern with the "global description" bindings style is that the approach
only applies to simple hardware and can't be generalized. Now, I'm not too
concerned about using that approach for simple hardware and a link-based
approach for more complex hardware. The "slave" components, however, need to
use a single DT bindings style, regardless of the DT bindings used by the
master device. That's why I believe we should try to standardize one DT
bindings style for master devices, even if it results in a slightly more
complex DT description than strictly needed in some cases.
> > A given device can be integrated in a wide variety of hardware with
> > different complexities. A driver can't thus always assume that the whole
> > composite display device will match a given class of topologies. This is
> > especially true for encoders and connectors, they can be hooked up
> > directly at the output of a very simple display controller, or can be
> > part of a very complex graph. Encoder and connector drivers can't assume
> > much about how they will be integrated. I want to avoid a situation where
> > using an HDMI encoder already supported in mainline with a different SoC
> > will result in having to rewrite the HDMI encoder driver.
>
> The tda998x chips are simple enough for being used in many boards: one video
> input and one serial digital output. I don't see in which circumstance the
> driver should be rewritten.
It shouldn't, that's the whole point ;-) I wasn't talking about the tda998x
only, but about encoder drivers in general. I have a display controller in a
Renesas SoC that has two LVDS encoders that output LVDS signals out of the
SoC. One LVDS port is connected to an LVDS panel (a connector from a DRM point
of view), the second one to an LVDS receiver that outputs parallel RGB data to
an HDMI encoder. The LVDS encoder can't thus assume any particular downstream
topology and its driver thus can't create DRM encoder and connector instances.
The same could be true for an HDMI encoder in theory, although an HDMI encoder
connected on the same board directly to an HDMI decoder that outputs RGB data
to a panel is probably a case that will be rarely seen in practice.
In the general case an encoder driver can't assume any specific upstream or
downstream topology. As long as DRM can't expose the real hardware topology to
userspace, someone needs to decide on how to map the hardware topology to the
DRM topology exposed to userspace. Encoder drivers can't take that role and
thus shouldn't create DRM encoder and connector instances themselves.
> > The story is a bit different for display controllers. While the hardware
> > topology outside (and sometimes inside as well) of the SoC can vary, a
> > display controller often approximately implies a given level of
> > complexity. A cheap SoC with a simple display controller will likely not
> > be used to drive a 4k display requiring 4 encoders running in parallel
> > for instance. While I also want to avoid having to make significant
> > changes to a display controller driver when using the SoC on a different
> > board, I believe the requirement can be slightly relaxed here, and the
> > display controller driver(s) can assume more about the hardware topology
> > than the encoder drivers.
>
> I don't think that the display controllers or the encoders have to know
> about the topology. They have well-knwon specific functions and the way they
> are interconnected should not impact these functions. If that would be the
> case, they should offer a particular configuration interface to the master
> driver.
As explained above, part of the problem comes from the fact that we need to
expose a logical topology to userspace that doesn't map 1:1 to the hardware
topology. We can discuss whether or not this should be changed, but I believe
that's out of scope. As we'll need to map logical encoders and connectors to
real hardware in the foreseeable future we need a solution to that problem,
and I believe the display controller should be in control of the mapping
(possibly using helpers).
> > I've asked myself whether we needed a single, one-size-fits-them-all DT
> > representation of the hardware topology. The view of the world from an
> > external encoder point of view must not depend on the SoC it is hooked up
> > to (this would prevent using a single encoder driver with multiple SoCs),
> > which calls for at least some form of standardization. The topology
> > representation on the display controller side may vary from display
> > controller to display controller, but I believe this would just result in
> > code duplication and having to solve the same problem in multiple
> > drivers. For those reasons I believe that the OF graph proposal to
> > represent the display hardware topology would be a good choice. The
> > bindings are flexible enough to represent both simple and complex
> > hardware.
>
> Your OF graph proposal is rather complex,
I agree it's slightly more complex than your simple-video-card proposal for
simple hardware, but I don't see it as overly complex, as long as we can
provide helper functions that moves the parsing complexity out of drivers (at
least drivers for simple hardware).
> and it does not even indicate which way the data flows.
Please note that the OF graph DT bindings are not set in stone. If we need to
indicate the data flow direction (and I assume we will need to at some point)
that should just be discussed and implemented.
> > Now, I don't want to force all display device drivers to implement complex
> > code when they only need to support simple hardware and simple hardware
> > topologies. Not only would that be rightfully rejected, I would be among
> > the ones nack'ing that approach. My opinion is that this calls for the
> > creation of helpers to handle common cases. Several (possibly many)
> > display systems only need (or want) to support linear pipelines at their
> > output(s), made of a single encoder and a single connector. There's no
> > point in duplicating DT parsing or encoder/connector instantiation code
> > in several drivers in that case where helpers could be reused. Several
> > sets of helpers could support different kinds of topologies, with the
> > driver author selecting a set of helpers depending on the expected
> > hardware topology complexity.
>
> I don't like this 'helper' notion. See below.
>
> > We also need to decide on who (as in which driver) will be responsible for
> > binding all devices together. As DRM exposes a single device to userspace,
> > there needs to be a single driver that will perform front line handling of
> > the userspace API and delegate calls to the other drivers involved. I
> > believe it would be logical for that driver to also be in charge of
> > bindings the components together, as it will be the driver that delegate
> > calls to the components. For a similar reason I also believe that that
> > driver should also be the one in control of creating DRM encoders and DRM
> > connectors. The component drivers having only a narrow view of the device
> > they handle, they can't perform that task in a generic way and would thus
> > get tied to specific master drivers because of the assumptions they would
> > make.
>
> I don't see why the encoders and connectors should be created by some
> external entity. They are declared in the DT or created by the board init,
> so, they exist at system startup time.
As explained above, the hardware encoders and connectors are declared in DT,
but they don't map 1:1 to the logical encoders and connectors exposed by DRM
to userspace.
Obviously, in some cases (and probably a majority of cases today), the mapping
will be 1:1, and the DRM encoders and connectors could be created by the
encoder drivers, but that won't scale to more complex hardware. The transition
to DT is a disruption that I'd like to take as an opportunity to model things
correctly. We *could* transition to DT by creating encoders and connectors in
encoder drivers, but a second disruption will then be needed in the near
future to transition to a model that can handle most hardware. The drm_bridge
API was a first step in such a direction, and I think we should take it into
account.
> > Whether the master driver is the CRTC driver or a separate driver that
> > binds standalone CRTCs, connectors and encoders doesn't in my opinion
> > change my above conclusions. Some SoCs could use a simple-video-card
> > driver like the one you've proposed, and others could implement a display
> > controller driver that would perform the same tasks for more complex
> > pipelines in addition to controlling the display controller's CRTC(s).
> > The simple-video-card driver would perform that same tasks as the helpers
> > I've previously talked about, so the two solutions are pretty similar,
> > and I don't see much added value in the general case in having a
> > simple-video-card driver compared to using helpers in the display
> > controller driver.
>
> In fact, I wonder why there is not only one DRM driver. The global logic is
> always the same, and, actually, it is duplicated in each specific driver.
That's quite a good question, and I agree that several DRM drivers, especially
drivers for embedded systems, are pretty similar in architecture.
The CRTC helpers are a pretty good example of an attempt to share common code
between drivers. The Intel DRM driver used them, developers then realized that
it made their life more difficult, and decided to implement the DRM API
directly instead of using the helpers.
The devil is in the details. Architectures can seem similar, but subtle
difference can make a common implementation not only complex and difficult to
maintain (as changing it will impact all drivers), but also useless when it
tries to cater for the needs of everybody. An abstraction for something that
can't be abstracted is just pointless.
I'm all for sharing code. This should, in my opinion, be done by providing
optional helpers that drivers can use. That's how the CRTC helpers work,
that's how the V4L2 control framework and videobuf2 library work, and that
allows drivers to provide their own implementation in the rare cases when the
generic helpers are not good enough. I think simple helpers that work in 90%
of the cases are much better than really complex helpers that would achieve a
95% coverage only anyway.
Given that several embedded DRM drivers duplicate code it might be that a
different set of CRTC helpers could be better for that class of hardware. If
you want to experiment with you I'd be glad to discuss requirements and
architectures.
> I had this approach in the gspca driver: one main driver and many specific
> subdrivers. Once, I even had the idea to convert your UVC driver into a
> gspca subdriver, but, at this time, I had too much work with the other
> webcams. Nevertheless, it would have been easy: all the required interfaces
> were (are still) present in the main gspca driver.
If you look into the details of the uvcvideo driver you'll find that it
wouldn't be that easy. UVC is a special case in that its architecture is
pretty different from the other webcam drivers. For instance the driver
doesn't use the V4L2 control framework, as its needs regarding controls are
quite special. I've discussed this with Hans Verkuil and we've concluded (at
least for the time being) that implementing the features uvcvideo requires in
the control framework would make the framework way too complex, just because
of a single driver.
This is just a counterexample of course. It doesn't mean that code shouldn't
be shared, quite the contrary.
> In the same order of idea, there is only one ALSA SoC driver, and you cannot
> say that the audio topology is less complex than the video one.
I can't really comment on that, I don't have enough knowledge of ALSA.
> When thinking about the unique DRM driver, there would no helper anymore:
> the driver would offer to each component the functions they need for working
> correctly.
That's where things get hairy. There will be one driver that won't fit in the
model. There will then be two options: filling the framework with exceptions
and corner cases for just one user, create a spaghetti mess in common code, or
allowing drivers to opt-out. I'm a strong believer in the second option. For
the sake of completeness I want to mention the third option, which is to make
the framework so thin that in becomes a wrapper that mostly translates
function calls 1:1, which is pretty useless.
We're getting out of scope here though. I'd be happy to continue discussing
the single DRM driver with you, but let's do so in a separate mail thread
then.
> > The point that matters to me is that encoders DT bindings and drivers must
> > be identical regardless of whether the master driver is the display
> > controller driver or a driver for a logical composite device. For all
> > those reasons we should use the OF graph DT bindings for the
> > simple-video-card driver should we decide to implement it, and we should
> > create DRM encoders and connectors in the master driver, not in the
> > encoder drivers.
>
> Sorry, but I think that a DRM encoder is the hardware encoder. What else
> could it be?
As explained above, a DRM encoder is a logical entity that usually corresponds
to a hardware encoder, but that can also correspond to several encoders
chained (sometimes using the drm_bridge abstraction, but not always).
> At initialization time, the master driver has only to manage the relation
> between the video components. It has not to create entities which already
> exist. So my preferred scheme is:
>
> - at starting time, each video component initializes its software and
> hardware, and then, plugs itself into the DRM driver.
>
> - from the central video topology, the DRM device waits for all
> components to be plugged and then it links the components together,
> creating the user's view of the system.
So far I agree with you, but I believe this calls for an in-kernel view of the
components possibly different than the drm_encoder and drm_connector objects.
> If you create the encoders and connectors in the DRM master, the hardware
> encoder and connector devices are just like zombies. They must put something
> in the system to say they are here and they must also have a callback
> function for them to know to which video subsystem they belong.
>
> On the contrary, if the DRM encoders and connectors are created by the
> hardware devices, they are immediately alive and operational.
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH RFC v2 2/6] drm/i2c: tda998x: Move tda998x to a couple encoder/connector
@ 2014-04-01 17:55 ` Laurent Pinchart
0 siblings, 0 replies; 32+ messages in thread
From: Laurent Pinchart @ 2014-04-01 17:55 UTC (permalink / raw)
To: Jean-Francois Moine
Cc: Russell King, linux-arm-kernel, dri-devel, linux-media
Hi Jean-François,
Sorry for the late reply.
On Thursday 27 March 2014 12:34:49 Jean-Francois Moine wrote:
> On Wed, 26 Mar 2014 18:33:09 +0100 Laurent Pinchart wrote:
> > That could work in your case, but I don't really like that.
> >
> > We need to describe the hardware topology, that might be the only point we
> > all agree on. There are various hardware topologies we need to support
> > with different levels of complexity, and several ways to describe them,
> > also with a wide range of complexities and features.
> >
> > The more complex the hardware topology, the more complex its description
> > needs to be, and the more complex the code that will parse the
> > description and handle the hardware will be. I don't think there's any
> > doubt here.
>
> But also, the simpler is the topology and the simpler should be its
> description.
I wouldn't put it so strongly. I believe we can slightly relax our
requirements regarding DT bindings complexity as long as drivers remain
simple. There's of course no reason to use more complex bindings just for the
sake of it.
> In your approach, the connections between endpoints are described in the
> components themselves, which makes hard for the DT maintainers to have a
> global understanding of the video subsystem.
>
> Having the topology described in the master device is clearer, even if it is
> complex.
First of all, let's clarify what a "master device" is. In the "simple-video-
card" example you've proposed the master device is a logical device (with a DT
node that has no corresponding hardware device). The second approach I can
think of is to make the IP core that implements the CRTC (which I usually call
display controller) be the master device. Let's note that the second case
makes both the link and "global description" DT binding styles possible.
My concern with the "global description" bindings style is that the approach
only applies to simple hardware and can't be generalized. Now, I'm not too
concerned about using that approach for simple hardware and a link-based
approach for more complex hardware. The "slave" components, however, need to
use a single DT bindings style, regardless of the DT bindings used by the
master device. That's why I believe we should try to standardize one DT
bindings style for master devices, even if it results in a slightly more
complex DT description than strictly needed in some cases.
> > A given device can be integrated in a wide variety of hardware with
> > different complexities. A driver can't thus always assume that the whole
> > composite display device will match a given class of topologies. This is
> > especially true for encoders and connectors, they can be hooked up
> > directly at the output of a very simple display controller, or can be
> > part of a very complex graph. Encoder and connector drivers can't assume
> > much about how they will be integrated. I want to avoid a situation where
> > using an HDMI encoder already supported in mainline with a different SoC
> > will result in having to rewrite the HDMI encoder driver.
>
> The tda998x chips are simple enough for being used in many boards: one video
> input and one serial digital output. I don't see in which circumstance the
> driver should be rewritten.
It shouldn't, that's the whole point ;-) I wasn't talking about the tda998x
only, but about encoder drivers in general. I have a display controller in a
Renesas SoC that has two LVDS encoders that output LVDS signals out of the
SoC. One LVDS port is connected to an LVDS panel (a connector from a DRM point
of view), the second one to an LVDS receiver that outputs parallel RGB data to
an HDMI encoder. The LVDS encoder can't thus assume any particular downstream
topology and its driver thus can't create DRM encoder and connector instances.
The same could be true for an HDMI encoder in theory, although an HDMI encoder
connected on the same board directly to an HDMI decoder that outputs RGB data
to a panel is probably a case that will be rarely seen in practice.
In the general case an encoder driver can't assume any specific upstream or
downstream topology. As long as DRM can't expose the real hardware topology to
userspace, someone needs to decide on how to map the hardware topology to the
DRM topology exposed to userspace. Encoder drivers can't take that role and
thus shouldn't create DRM encoder and connector instances themselves.
> > The story is a bit different for display controllers. While the hardware
> > topology outside (and sometimes inside as well) of the SoC can vary, a
> > display controller often approximately implies a given level of
> > complexity. A cheap SoC with a simple display controller will likely not
> > be used to drive a 4k display requiring 4 encoders running in parallel
> > for instance. While I also want to avoid having to make significant
> > changes to a display controller driver when using the SoC on a different
> > board, I believe the requirement can be slightly relaxed here, and the
> > display controller driver(s) can assume more about the hardware topology
> > than the encoder drivers.
>
> I don't think that the display controllers or the encoders have to know
> about the topology. They have well-knwon specific functions and the way they
> are interconnected should not impact these functions. If that would be the
> case, they should offer a particular configuration interface to the master
> driver.
As explained above, part of the problem comes from the fact that we need to
expose a logical topology to userspace that doesn't map 1:1 to the hardware
topology. We can discuss whether or not this should be changed, but I believe
that's out of scope. As we'll need to map logical encoders and connectors to
real hardware in the foreseeable future we need a solution to that problem,
and I believe the display controller should be in control of the mapping
(possibly using helpers).
> > I've asked myself whether we needed a single, one-size-fits-them-all DT
> > representation of the hardware topology. The view of the world from an
> > external encoder point of view must not depend on the SoC it is hooked up
> > to (this would prevent using a single encoder driver with multiple SoCs),
> > which calls for at least some form of standardization. The topology
> > representation on the display controller side may vary from display
> > controller to display controller, but I believe this would just result in
> > code duplication and having to solve the same problem in multiple
> > drivers. For those reasons I believe that the OF graph proposal to
> > represent the display hardware topology would be a good choice. The
> > bindings are flexible enough to represent both simple and complex
> > hardware.
>
> Your OF graph proposal is rather complex,
I agree it's slightly more complex than your simple-video-card proposal for
simple hardware, but I don't see it as overly complex, as long as we can
provide helper functions that moves the parsing complexity out of drivers (at
least drivers for simple hardware).
> and it does not even indicate which way the data flows.
Please note that the OF graph DT bindings are not set in stone. If we need to
indicate the data flow direction (and I assume we will need to at some point)
that should just be discussed and implemented.
> > Now, I don't want to force all display device drivers to implement complex
> > code when they only need to support simple hardware and simple hardware
> > topologies. Not only would that be rightfully rejected, I would be among
> > the ones nack'ing that approach. My opinion is that this calls for the
> > creation of helpers to handle common cases. Several (possibly many)
> > display systems only need (or want) to support linear pipelines at their
> > output(s), made of a single encoder and a single connector. There's no
> > point in duplicating DT parsing or encoder/connector instantiation code
> > in several drivers in that case where helpers could be reused. Several
> > sets of helpers could support different kinds of topologies, with the
> > driver author selecting a set of helpers depending on the expected
> > hardware topology complexity.
>
> I don't like this 'helper' notion. See below.
>
> > We also need to decide on who (as in which driver) will be responsible for
> > binding all devices together. As DRM exposes a single device to userspace,
> > there needs to be a single driver that will perform front line handling of
> > the userspace API and delegate calls to the other drivers involved. I
> > believe it would be logical for that driver to also be in charge of
> > bindings the components together, as it will be the driver that delegate
> > calls to the components. For a similar reason I also believe that that
> > driver should also be the one in control of creating DRM encoders and DRM
> > connectors. The component drivers having only a narrow view of the device
> > they handle, they can't perform that task in a generic way and would thus
> > get tied to specific master drivers because of the assumptions they would
> > make.
>
> I don't see why the encoders and connectors should be created by some
> external entity. They are declared in the DT or created by the board init,
> so, they exist at system startup time.
As explained above, the hardware encoders and connectors are declared in DT,
but they don't map 1:1 to the logical encoders and connectors exposed by DRM
to userspace.
Obviously, in some cases (and probably a majority of cases today), the mapping
will be 1:1, and the DRM encoders and connectors could be created by the
encoder drivers, but that won't scale to more complex hardware. The transition
to DT is a disruption that I'd like to take as an opportunity to model things
correctly. We *could* transition to DT by creating encoders and connectors in
encoder drivers, but a second disruption will then be needed in the near
future to transition to a model that can handle most hardware. The drm_bridge
API was a first step in such a direction, and I think we should take it into
account.
> > Whether the master driver is the CRTC driver or a separate driver that
> > binds standalone CRTCs, connectors and encoders doesn't in my opinion
> > change my above conclusions. Some SoCs could use a simple-video-card
> > driver like the one you've proposed, and others could implement a display
> > controller driver that would perform the same tasks for more complex
> > pipelines in addition to controlling the display controller's CRTC(s).
> > The simple-video-card driver would perform that same tasks as the helpers
> > I've previously talked about, so the two solutions are pretty similar,
> > and I don't see much added value in the general case in having a
> > simple-video-card driver compared to using helpers in the display
> > controller driver.
>
> In fact, I wonder why there is not only one DRM driver. The global logic is
> always the same, and, actually, it is duplicated in each specific driver.
That's quite a good question, and I agree that several DRM drivers, especially
drivers for embedded systems, are pretty similar in architecture.
The CRTC helpers are a pretty good example of an attempt to share common code
between drivers. The Intel DRM driver used them, developers then realized that
it made their life more difficult, and decided to implement the DRM API
directly instead of using the helpers.
The devil is in the details. Architectures can seem similar, but subtle
difference can make a common implementation not only complex and difficult to
maintain (as changing it will impact all drivers), but also useless when it
tries to cater for the needs of everybody. An abstraction for something that
can't be abstracted is just pointless.
I'm all for sharing code. This should, in my opinion, be done by providing
optional helpers that drivers can use. That's how the CRTC helpers work,
that's how the V4L2 control framework and videobuf2 library work, and that
allows drivers to provide their own implementation in the rare cases when the
generic helpers are not good enough. I think simple helpers that work in 90%
of the cases are much better than really complex helpers that would achieve a
95% coverage only anyway.
Given that several embedded DRM drivers duplicate code it might be that a
different set of CRTC helpers could be better for that class of hardware. If
you want to experiment with you I'd be glad to discuss requirements and
architectures.
> I had this approach in the gspca driver: one main driver and many specific
> subdrivers. Once, I even had the idea to convert your UVC driver into a
> gspca subdriver, but, at this time, I had too much work with the other
> webcams. Nevertheless, it would have been easy: all the required interfaces
> were (are still) present in the main gspca driver.
If you look into the details of the uvcvideo driver you'll find that it
wouldn't be that easy. UVC is a special case in that its architecture is
pretty different from the other webcam drivers. For instance the driver
doesn't use the V4L2 control framework, as its needs regarding controls are
quite special. I've discussed this with Hans Verkuil and we've concluded (at
least for the time being) that implementing the features uvcvideo requires in
the control framework would make the framework way too complex, just because
of a single driver.
This is just a counterexample of course. It doesn't mean that code shouldn't
be shared, quite the contrary.
> In the same order of idea, there is only one ALSA SoC driver, and you cannot
> say that the audio topology is less complex than the video one.
I can't really comment on that, I don't have enough knowledge of ALSA.
> When thinking about the unique DRM driver, there would no helper anymore:
> the driver would offer to each component the functions they need for working
> correctly.
That's where things get hairy. There will be one driver that won't fit in the
model. There will then be two options: filling the framework with exceptions
and corner cases for just one user, create a spaghetti mess in common code, or
allowing drivers to opt-out. I'm a strong believer in the second option. For
the sake of completeness I want to mention the third option, which is to make
the framework so thin that in becomes a wrapper that mostly translates
function calls 1:1, which is pretty useless.
We're getting out of scope here though. I'd be happy to continue discussing
the single DRM driver with you, but let's do so in a separate mail thread
then.
> > The point that matters to me is that encoders DT bindings and drivers must
> > be identical regardless of whether the master driver is the display
> > controller driver or a driver for a logical composite device. For all
> > those reasons we should use the OF graph DT bindings for the
> > simple-video-card driver should we decide to implement it, and we should
> > create DRM encoders and connectors in the master driver, not in the
> > encoder drivers.
>
> Sorry, but I think that a DRM encoder is the hardware encoder. What else
> could it be?
As explained above, a DRM encoder is a logical entity that usually corresponds
to a hardware encoder, but that can also correspond to several encoders
chained (sometimes using the drm_bridge abstraction, but not always).
> At initialization time, the master driver has only to manage the relation
> between the video components. It has not to create entities which already
> exist. So my preferred scheme is:
>
> - at starting time, each video component initializes its software and
> hardware, and then, plugs itself into the DRM driver.
>
> - from the central video topology, the DRM device waits for all
> components to be plugged and then it links the components together,
> creating the user's view of the system.
So far I agree with you, but I believe this calls for an in-kernel view of the
components possibly different than the drm_encoder and drm_connector objects.
> If you create the encoders and connectors in the DRM master, the hardware
> encoder and connector devices are just like zombies. They must put something
> in the system to say they are here and they must also have a callback
> function for them to know to which video subsystem they belong.
>
> On the contrary, if the DRM encoders and connectors are created by the
> hardware devices, they are immediately alive and operational.
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 32+ messages in thread
end of thread, other threads:[~2014-04-01 17:55 UTC | newest]
Thread overview: 32+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-03-21 10:27 [PATCH RFC v2 0/6] drm/i2c: Move tda998x to a couple encoder/connector Jean-Francois Moine
2014-03-21 10:27 ` Jean-Francois Moine
2014-03-21 8:17 ` [PATCH RFC v2 2/6] drm/i2c: tda998x: " Jean-Francois Moine
2014-03-21 8:17 ` Jean-Francois Moine
2014-03-24 22:39 ` Laurent Pinchart
2014-03-24 22:39 ` Laurent Pinchart
2014-03-24 22:39 ` Laurent Pinchart
2014-03-25 15:55 ` Jean-Francois Moine
2014-03-25 15:55 ` Jean-Francois Moine
2014-03-25 15:55 ` Jean-Francois Moine
2014-03-26 17:33 ` Laurent Pinchart
2014-03-26 17:33 ` Laurent Pinchart
2014-03-26 17:33 ` Laurent Pinchart
2014-03-27 11:34 ` Jean-Francois Moine
2014-03-27 11:34 ` Jean-Francois Moine
2014-03-27 11:34 ` Jean-Francois Moine
2014-04-01 17:55 ` Laurent Pinchart
2014-04-01 17:55 ` Laurent Pinchart
2014-04-01 17:55 ` Laurent Pinchart
2014-03-21 8:36 ` [PATCH RFC v2 4/6] drm/tilcdc: Change the interface with the tda998x driver Jean-Francois Moine
2014-03-21 8:36 ` Jean-Francois Moine
2014-03-21 8:52 ` [PATCH RFC v2 6/6] ARM: AM33XX: dts: Change the tda998x description Jean-Francois Moine
2014-03-21 8:52 ` Jean-Francois Moine
2014-03-21 9:31 ` [PATCH RFC v2 3/6] drm/tilcd: dts: Add the video output port Jean-Francois Moine
2014-03-21 9:31 ` Jean-Francois Moine
2014-03-21 9:31 ` [PATCH RFC v2 5/6] drm/tilcd: dts: Remove unused slave description Jean-Francois Moine
2014-03-21 9:31 ` Jean-Francois Moine
2014-03-21 9:48 ` [PATCH RFC v2 1/6] drm/i2c: tda998x: Add the required port property Jean-Francois Moine
2014-03-21 9:48 ` Jean-Francois Moine
2014-03-21 11:21 ` [PATCH RFC v2 0/6] drm/i2c: Move tda998x to a couple encoder/connector Russell King - ARM Linux
2014-03-21 11:21 ` Russell King - ARM Linux
2014-03-21 11:21 ` Russell King - ARM Linux
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.