Hi Meng, I did not come around to have a proper look at this, but some stuff to start with below: On 2016-02-15 17:52, Meng Yi wrote: > DCU is the shortcut of 'display controller unit', some HDMI transmitter > attached to DCU, such as sii9022a, and this driver add the relavent > functions to DRM framewrok. > > Signed-off-by: Meng Yi <meng.yi@xxxxxxx> > Signed-off-by: Alison Wang <alison.wang@xxxxxxx> > Signed-off-by: Xiubo Li <lixiubo@xxxxxxxxxxxxxxxxxxxx> > Signed-off-by: Jianwei Wang <jianwei.wang.chn@xxxxxxxxx> > --- > Change in v2 > -Fixed conflict with Stefan's branch. > http://git.agner.ch/gitweb/?p=linux-drm-fsl-dcu.git;a=summary > --- > .../bindings/display/bridge/sil,sii9022a.txt | 55 +++++ > drivers/gpu/drm/fsl-dcu/Makefile | 1 + > drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h | 1 + > drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_hdmi.c | 271 +++++++++++++++++++++ > drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_kms.c | 12 + > drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_output.h | 11 + > 6 files changed, 351 insertions(+) > create mode 100644 > Documentation/devicetree/bindings/display/bridge/sil,sii9022a.txt > create mode 100644 drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_hdmi.c > > diff --git > a/Documentation/devicetree/bindings/display/bridge/sil,sii9022a.txt > b/Documentation/devicetree/bindings/display/bridge/sil,sii9022a.txt > new file mode 100644 > index 0000000..7debbfb > --- /dev/null > +++ b/Documentation/devicetree/bindings/display/bridge/sil,sii9022a.txt > @@ -0,0 +1,55 @@ > +Device-Tree bindings for the SiI902x hdmi transmitter. > +----------------------------------------- > + > +The SiI9022A is an ultra low-power HDMI transmitter. It supports > resolutions from > +standard definition 480i/p and 576i/p all the way to high-definition > 720p, 1080i, > +and 1080p, the highest resolution supported by HDTVs today. It also > supports all > +PC resolutions up to UXGA for netbooks > + > +Required properties: > +- compatible: Should be "sil,sii9022a". > +- reg: The I2C address of the device. > +- interrupts: Interrupt number to the cpu. > + > +Required nodes: > + > +The sii9022 has one video ports. Its connection is modelled using the OF > +graph bindings specified in Documentation/devicetree/bindings/graph.txt. > + > +- Video port 0 for the HDMI output > + > +Example: > +------- > + > +/ { > + hdmi-out { > + compatible = "hdmi-connector"; > + type = "a"; > + > + port { > + hdmi_con: endpoint { > + remote-endpoint = <&sii9022a_out>; > + }; > + }; > + }; > +}; > + > +&i2c1 { > + sii9022: hdmi@39 { > + compatible = "sil,sii9022a"; > + reg = <0x39>; > + interrupts = <GIC_SPI 167 IRQ_TYPE_EDGE_RISING>; > + > + ports { > + #address-cells = <1>; > + #size-cells = <0>; > + > + port@0 { > + reg = <1>; > + sii9022_out: endpoint { > + remote-endpoint = <&hdmi_con>; > + }; > + }; This does not look like a proper description of this HDMI transmitter. It should have two ports, one towards the display controller, the other towards the HDMI port. Also, we always use the reg value after @ sign of the node name, (should be port@1 in this case). The ADV7511 seems to be a good example how to do this, along with a device tree which makes use of it (e.g. arch/arm/boot/dts/r8a7791-koelsch.dts). > + }; > + }; > +}; > diff --git a/drivers/gpu/drm/fsl-dcu/Makefile b/drivers/gpu/drm/fsl-dcu/Makefile > index 6ea1523..98cacc2 100644 > --- a/drivers/gpu/drm/fsl-dcu/Makefile > +++ b/drivers/gpu/drm/fsl-dcu/Makefile > @@ -1,6 +1,7 @@ > fsl-dcu-drm-y := fsl_dcu_drm_drv.o \ > fsl_dcu_drm_kms.o \ > fsl_dcu_drm_rgb.o \ > + fsl_dcu_drm_hdmi.o \ > fsl_dcu_drm_plane.o \ > fsl_dcu_drm_crtc.o \ > fsl_dcu_drm_fbdev.o > diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h > b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h > index 6413ac9..7d1b0fd 100644 > --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h > +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h > @@ -189,6 +189,7 @@ struct fsl_dcu_drm_device { > struct drm_fbdev_cma *fbdev; > struct drm_crtc crtc; > struct drm_encoder encoder; > + struct drm_encoder_slave *slave; > struct fsl_dcu_drm_connector connector; > const struct fsl_dcu_soc_data *soc; > }; > diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_hdmi.c > b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_hdmi.c > new file mode 100644 > index 0000000..0b06060 > --- /dev/null > +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_hdmi.c > @@ -0,0 +1,271 @@ > +/* > + * Copyright 2015 Freescale Semiconductor, Inc. > + * > + * Freescale DCU drm device driver > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + */ > + > +#include <linux/console.h> > +#include <linux/delay.h> > +#include <linux/errno.h> > +#include <linux/fb.h> > +#include <linux/fsl_devices.h> > +#include <linux/i2c.h> > +#include <linux/init.h> > +#include <linux/interrupt.h> > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/of_device.h> > +#include <linux/backlight.h> > +#include <video/videomode.h> > +#include <video/of_display_timing.h> > + > +#include <drm/drmP.h> > +#include <drm/drm_atomic_helper.h> > +#include <drm/drm_encoder_slave.h> > +#include <drm/drm_crtc_helper.h> > +#include <drm/drm_edid.h> > + > +#include "fsl_dcu_drm_drv.h" > +#include "fsl_dcu_drm_output.h" > + > +#define to_drm_encoder_slave(e) \ > + container_of(e, struct drm_encoder_slave, base) > +#define to_slave_funcs(e) (to_drm_encoder_slave(e)->slave_funcs) > +static void fsl_dcu_drm_hdmienc_mode_set(struct drm_encoder *encoder, > + struct drm_display_mode *mode, > + struct drm_display_mode *adjusted_mode) > +{ > + const struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder); > + > + if (sfuncs->mode_set) > + sfuncs->mode_set(encoder, mode, adjusted_mode); > + > +} > + > +static int > +fsl_dcu_drm_hdmienc_atomic_check(struct drm_encoder *encoder, > + struct drm_crtc_state *crtc_state, > + struct drm_connector_state *conn_state) > +{ > + return 0; > +} > + > +static void fsl_dcu_drm_hdmienc_disable(struct drm_encoder *encoder) > +{ > + const struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder); > + > + if (sfuncs->dpms) > + sfuncs->dpms(encoder, DRM_MODE_DPMS_OFF); > +} > + > +static void fsl_dcu_drm_hdmienc_enable(struct drm_encoder *encoder) > +{ > + const struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder); > + > + if (sfuncs->dpms) > + sfuncs->dpms(encoder, DRM_MODE_DPMS_ON); > +} > + > +static const struct drm_encoder_helper_funcs encoder_helper_funcs = { > + .atomic_check = fsl_dcu_drm_hdmienc_atomic_check, > + .disable = fsl_dcu_drm_hdmienc_disable, > + .enable = fsl_dcu_drm_hdmienc_enable, > + .mode_set = fsl_dcu_drm_hdmienc_mode_set, > +}; > + > +static void fsl_dcu_drm_hdmienc_destroy(struct drm_encoder *encoder) > +{ > + drm_encoder_cleanup(encoder); > +} > + > +static const struct drm_encoder_funcs encoder_funcs = { > + .destroy = fsl_dcu_drm_hdmienc_destroy, > +}; > +int fsl_dcu_drm_hdmienc_create(struct fsl_dcu_drm_device *fsl_dev, > + struct drm_crtc *crtc) > +{ > + struct drm_i2c_encoder_driver *driver; > + struct drm_encoder_slave *enc_slave; > + struct drm_encoder *encoder; > + struct i2c_client *i2c_slave; > + int ret; > + > + > + struct device_node *np = of_find_compatible_node(NULL, > + NULL, "sil,sii9022a"); > + if (np == NULL) > + return -1; With a proper description above, searching the node with a static compatible string shouldn't be necessary anymore... Also, return proper error codes such as -ENODEV etc... -- Stefan > + > + /* Locate the slave I2C device and driver. */ > + i2c_slave = of_find_i2c_device_by_node(np); > + if (!i2c_slave || !i2c_get_clientdata(i2c_slave)) > + return -EPROBE_DEFER; > + > + enc_slave = devm_kzalloc(fsl_dev->dev, sizeof(*enc_slave), GFP_KERNEL); > + if (!enc_slave) > + return -1; > + > + /* Initialize the slave encoder. */ > + driver = to_drm_i2c_encoder_driver( > + to_i2c_driver(i2c_slave->dev.driver)); > + ret = driver->encoder_init(i2c_slave, fsl_dev->drm, enc_slave); > + if (ret < 0) > + return -1; > + > + fsl_dev->slave = enc_slave; > + encoder = &enc_slave->base; > + > + > + encoder->possible_crtcs = 1; > + ret = drm_encoder_init(fsl_dev->drm, encoder, &encoder_funcs, > + DRM_MODE_ENCODER_TMDS, NULL); > + if (ret < 0) > + return ret; > + > + drm_encoder_helper_add(encoder, &encoder_helper_funcs); > + encoder->crtc = crtc; > + > + return 0; > +} > + > +#define to_fsl_dcu_drm_hdmicon(connector) \ > + container_of(connector, struct fsl_dcu_drm_hdmicon, connector) > + > +static struct drm_encoder *fsl_dcu_drm_hdmi_find_encoder(struct > drm_device *dev) > +{ > + struct drm_encoder *encoder; > + > + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { > + if (encoder->encoder_type == DRM_MODE_ENCODER_TMDS) > + return encoder; > + } > + > + return NULL; > +} > + > +static void fsl_dcu_drm_hdmicon_destroy(struct drm_connector *connector) > +{ > + drm_connector_unregister(connector); > + drm_connector_cleanup(connector); > +} > + > +static enum drm_connector_status > +fsl_dcu_drm_hdmicon_detect(struct drm_connector *connector, bool force) > +{ > + struct fsl_dcu_drm_hdmicon *hdmicon = to_fsl_dcu_drm_hdmicon(connector); > + > + if (hdmicon->status == connector_status_disconnected) > + return connector_status_disconnected; > + else > + return connector_status_connected; > +} > + > +static const struct drm_connector_funcs fsl_dcu_drm_connector_funcs = { > + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, > + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, > + .destroy = fsl_dcu_drm_hdmicon_destroy, > + .detect = fsl_dcu_drm_hdmicon_detect, > + .dpms = drm_atomic_helper_connector_dpms, > + .fill_modes = drm_helper_probe_single_connector_modes, > + .reset = drm_atomic_helper_connector_reset, > +}; > + > +static struct drm_encoder * > +fsl_dcu_drm_hdmicon_best_encoder(struct drm_connector *connector) > +{ > + struct drm_device *dev = connector->dev; > + > + return fsl_dcu_drm_hdmi_find_encoder(dev); > +} > + > + > +static int fsl_dcu_drm_hdmicon_get_modes(struct drm_connector *connector) > +{ > + struct fsl_dcu_drm_hdmicon *con = to_fsl_dcu_drm_hdmicon(connector); > + const struct drm_encoder_slave_funcs *sfuncs = con->slave->slave_funcs; > + > + if (sfuncs->get_modes == NULL) > + return 0; > + > + return sfuncs->get_modes(NULL, connector); > +} > + > +static int fsl_dcu_drm_hdmicon_mode_valid(struct drm_connector *connector, > + struct drm_display_mode *mode) > +{ > + if (mode->hdisplay > 1024) > + return MODE_VIRTUAL_X; > + else if (mode->vdisplay > 768) > + return MODE_VIRTUAL_Y; > + > + return MODE_OK; > +} > + > +static const struct drm_connector_helper_funcs connector_helper_funcs = { > + .best_encoder = fsl_dcu_drm_hdmicon_best_encoder, > + .get_modes = fsl_dcu_drm_hdmicon_get_modes, > + .mode_valid = fsl_dcu_drm_hdmicon_mode_valid, > +}; > + > +int fsl_dcu_drm_hdmicon_create(struct fsl_dcu_drm_device *fsl_dev) > +{ > + struct drm_device *dev = fsl_dev->drm; > + struct fsl_dcu_drm_hdmicon *hdmicon; > + struct drm_connector *connector; > + struct drm_encoder *encoder; > + int ret; > + > + hdmicon = devm_kzalloc(fsl_dev->dev, > + sizeof(struct fsl_dcu_drm_hdmicon), GFP_KERNEL); > + if (!hdmicon) > + return -ENOMEM; > + > + hdmicon->slave = fsl_dev->slave; > + connector = &hdmicon->connector; > + > + connector->display_info.width_mm = 0; > + connector->display_info.height_mm = 0; > + connector->polled = DRM_CONNECTOR_POLL_HPD; > + > + ret = drm_connector_init(fsl_dev->drm, connector, > + &fsl_dcu_drm_connector_funcs, > + DRM_MODE_CONNECTOR_HDMIA); > + if (ret < 0) > + return ret; > + > + connector->dpms = DRM_MODE_DPMS_OFF; > + drm_connector_helper_add(connector, &connector_helper_funcs); > + ret = drm_connector_register(connector); > + if (ret < 0) > + goto err_cleanup; > + > + encoder = fsl_dcu_drm_hdmi_find_encoder(dev); > + if (!encoder) > + goto err_cleanup; > + ret = drm_mode_connector_attach_encoder(connector, encoder); > + if (ret < 0) > + goto err_sysfs; > + > + connector->encoder = encoder; > + > + drm_object_property_set_value > + (&connector->base, fsl_dev->drm->mode_config.dpms_property, > + DRM_MODE_DPMS_OFF); > + > + return 0; > + > +err_sysfs: > + drm_connector_unregister(connector); > +err_cleanup: > + drm_connector_cleanup(connector); > + return ret; > +} > + > +MODULE_AUTHOR("Freescale Semiconductor, Inc."); > +MODULE_DESCRIPTION("SII902x DVI/HDMI driver"); > +MODULE_LICENSE("GPL"); > diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_kms.c > b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_kms.c > index c564ec6..94b1111 100644 > --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_kms.c > +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_kms.c > @@ -17,10 +17,18 @@ > #include "fsl_dcu_drm_crtc.h" > #include "fsl_dcu_drm_drv.h" > > +static void fsl_dcu_drm_output_poll_changed(struct drm_device *dev) > +{ > + struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; > + > + drm_fbdev_cma_hotplug_event(fsl_dev->fbdev); > +} > + > static const struct drm_mode_config_funcs fsl_dcu_drm_mode_config_funcs = { > .atomic_check = drm_atomic_helper_check, > .atomic_commit = drm_atomic_helper_commit, > .fb_create = drm_fb_cma_create, > + .output_poll_changed = fsl_dcu_drm_output_poll_changed, > }; > > int fsl_dcu_drm_modeset_init(struct fsl_dcu_drm_device *fsl_dev) > @@ -43,10 +51,14 @@ int fsl_dcu_drm_modeset_init(struct > fsl_dcu_drm_device *fsl_dev) > if (ret) > goto fail_encoder; > > + fsl_dcu_drm_hdmienc_create(fsl_dev, &fsl_dev->crtc); > + > ret = fsl_dcu_drm_connector_create(fsl_dev, &fsl_dev->encoder); > if (ret) > goto fail_connector; > > + fsl_dcu_drm_hdmicon_create(fsl_dev); > + > drm_mode_config_reset(fsl_dev->drm); > drm_kms_helper_poll_init(fsl_dev->drm); > > diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_output.h > b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_output.h > index 7093109..fa0d7ed 100644 > --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_output.h > +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_output.h > @@ -18,6 +18,13 @@ struct fsl_dcu_drm_connector { > struct drm_panel *panel; > }; > > +struct fsl_dcu_drm_hdmicon { > + struct drm_connector connector; > + struct drm_encoder_slave *slave; > + struct i2c_client *client; > + enum drm_connector_status status; > +}; > + > static inline struct fsl_dcu_drm_connector * > to_fsl_dcu_connector(struct drm_connector *con) > { > @@ -29,5 +36,9 @@ int fsl_dcu_drm_connector_create(struct > fsl_dcu_drm_device *fsl_dev, > struct drm_encoder *encoder); > int fsl_dcu_drm_encoder_create(struct fsl_dcu_drm_device *fsl_dev, > struct drm_crtc *crtc); > +int fsl_dcu_drm_hdmicon_create(struct fsl_dcu_drm_device *fsl_dev); > +int fsl_dcu_drm_hdmienc_create(struct fsl_dcu_drm_device *fsl_dev, > + struct drm_crtc *crtc); > + > > #endif /* __FSL_DCU_DRM_CONNECTOR_H__ */ _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/dri-devel