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@xxxxxxx> --- 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@xxxxxxxxx"); MODULE_DESCRIPTION("NXP Semiconductors TDA998X HDMI Encoder"); MODULE_LICENSE("GPL"); - -module_init(tda998x_init); -module_exit(tda998x_exit); -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html