Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm: rcar-du: Add HDMI encoder and connector support

SoCs that integrate the DU have no internal HDMI encoder, support
external encoders only.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>

+375 -7
+9 -2
drivers/gpu/drm/rcar-du/Kconfig
··· 11 11 Choose this option if you have an R-Car chipset. 12 12 If M is selected the module will be called rcar-du-drm. 13 13 14 + config DRM_RCAR_HDMI 15 + bool "R-Car DU HDMI Encoder Support" 16 + depends on DRM_RCAR_DU 17 + depends on OF 18 + help 19 + Enable support for external HDMI encoders. 20 + 14 21 config DRM_RCAR_LVDS 15 22 bool "R-Car DU LVDS Encoder Support" 16 23 depends on DRM_RCAR_DU 17 24 depends on ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST 18 25 help 19 - Enable support the R-Car Display Unit embedded LVDS encoders 20 - (currently only on R8A7790). 26 + Enable support for the R-Car Display Unit embedded LVDS encoders 27 + (currently only on R8A7790 and R8A7791).
+2
drivers/gpu/drm/rcar-du/Makefile
··· 7 7 rcar_du_plane.o \ 8 8 rcar_du_vgacon.o 9 9 10 + rcar-du-drm-$(CONFIG_DRM_RCAR_HDMI) += rcar_du_hdmicon.o \ 11 + rcar_du_hdmienc.o 10 12 rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS) += rcar_du_lvdsenc.o 11 13 12 14 obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o
+25 -5
drivers/gpu/drm/rcar-du/rcar_du_encoder.c
··· 19 19 20 20 #include "rcar_du_drv.h" 21 21 #include "rcar_du_encoder.h" 22 + #include "rcar_du_hdmicon.h" 23 + #include "rcar_du_hdmienc.h" 22 24 #include "rcar_du_kms.h" 23 25 #include "rcar_du_lvdscon.h" 24 26 #include "rcar_du_lvdsenc.h" ··· 179 177 case RCAR_DU_ENCODER_LVDS: 180 178 encoder_type = DRM_MODE_ENCODER_LVDS; 181 179 break; 180 + case RCAR_DU_ENCODER_HDMI: 181 + encoder_type = DRM_MODE_ENCODER_TMDS; 182 + break; 182 183 case RCAR_DU_ENCODER_NONE: 183 184 default: 184 185 /* No external encoder, use the internal encoder type. */ ··· 189 184 break; 190 185 } 191 186 192 - ret = drm_encoder_init(rcdu->ddev, encoder, &encoder_funcs, 193 - encoder_type); 194 - if (ret < 0) 195 - return ret; 187 + if (type == RCAR_DU_ENCODER_HDMI) { 188 + if (renc->lvds) { 189 + dev_err(rcdu->dev, 190 + "Chaining LVDS and HDMI encoders not supported\n"); 191 + return -EINVAL; 192 + } 196 193 197 - drm_encoder_helper_add(encoder, &encoder_helper_funcs); 194 + ret = rcar_du_hdmienc_init(rcdu, renc, enc_node); 195 + if (ret < 0) 196 + return ret; 197 + } else { 198 + ret = drm_encoder_init(rcdu->ddev, encoder, &encoder_funcs, 199 + encoder_type); 200 + if (ret < 0) 201 + return ret; 202 + 203 + drm_encoder_helper_add(encoder, &encoder_helper_funcs); 204 + } 198 205 199 206 switch (encoder_type) { 200 207 case DRM_MODE_ENCODER_LVDS: ··· 214 197 215 198 case DRM_MODE_ENCODER_DAC: 216 199 return rcar_du_vga_connector_init(rcdu, renc); 200 + 201 + case DRM_MODE_ENCODER_TMDS: 202 + return rcar_du_hdmi_connector_init(rcdu, renc); 217 203 218 204 default: 219 205 return -EINVAL;
+3
drivers/gpu/drm/rcar-du/rcar_du_encoder.h
··· 18 18 #include <drm/drm_encoder_slave.h> 19 19 20 20 struct rcar_du_device; 21 + struct rcar_du_hdmienc; 21 22 struct rcar_du_lvdsenc; 22 23 23 24 enum rcar_du_encoder_type { ··· 26 25 RCAR_DU_ENCODER_NONE, 27 26 RCAR_DU_ENCODER_VGA, 28 27 RCAR_DU_ENCODER_LVDS, 28 + RCAR_DU_ENCODER_HDMI, 29 29 }; 30 30 31 31 struct rcar_du_encoder { 32 32 struct drm_encoder_slave slave; 33 33 enum rcar_du_output output; 34 + struct rcar_du_hdmienc *hdmi; 34 35 struct rcar_du_lvdsenc *lvds; 35 36 }; 36 37
+118
drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c
··· 1 + /* 2 + * R-Car Display Unit HDMI Connector 3 + * 4 + * Copyright (C) 2014 Renesas Electronics Corporation 5 + * 6 + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) 7 + * 8 + * This program is free software; you can redistribute it and/or modify 9 + * it under the terms of the GNU General Public License as published by 10 + * the Free Software Foundation; either version 2 of the License, or 11 + * (at your option) any later version. 12 + */ 13 + 14 + #include <drm/drmP.h> 15 + #include <drm/drm_crtc.h> 16 + #include <drm/drm_crtc_helper.h> 17 + #include <drm/drm_encoder_slave.h> 18 + 19 + #include "rcar_du_drv.h" 20 + #include "rcar_du_encoder.h" 21 + #include "rcar_du_hdmicon.h" 22 + #include "rcar_du_kms.h" 23 + 24 + #define to_slave_funcs(e) (to_rcar_encoder(e)->slave.slave_funcs) 25 + 26 + static int rcar_du_hdmi_connector_get_modes(struct drm_connector *connector) 27 + { 28 + struct drm_encoder *encoder = connector->encoder; 29 + struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder); 30 + 31 + if (sfuncs->get_modes == NULL) 32 + return 0; 33 + 34 + return sfuncs->get_modes(encoder, connector); 35 + } 36 + 37 + static int rcar_du_hdmi_connector_mode_valid(struct drm_connector *connector, 38 + struct drm_display_mode *mode) 39 + { 40 + struct drm_encoder *encoder = connector->encoder; 41 + struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder); 42 + 43 + if (sfuncs->mode_valid == NULL) 44 + return MODE_OK; 45 + 46 + return sfuncs->mode_valid(encoder, mode); 47 + } 48 + 49 + static const struct drm_connector_helper_funcs connector_helper_funcs = { 50 + .get_modes = rcar_du_hdmi_connector_get_modes, 51 + .mode_valid = rcar_du_hdmi_connector_mode_valid, 52 + .best_encoder = rcar_du_connector_best_encoder, 53 + }; 54 + 55 + static void rcar_du_hdmi_connector_destroy(struct drm_connector *connector) 56 + { 57 + drm_connector_unregister(connector); 58 + drm_connector_cleanup(connector); 59 + } 60 + 61 + static enum drm_connector_status 62 + rcar_du_hdmi_connector_detect(struct drm_connector *connector, bool force) 63 + { 64 + struct drm_encoder *encoder = connector->encoder; 65 + struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder); 66 + 67 + if (sfuncs->detect == NULL) 68 + return connector_status_unknown; 69 + 70 + return sfuncs->detect(encoder, connector); 71 + } 72 + 73 + static const struct drm_connector_funcs connector_funcs = { 74 + .dpms = drm_helper_connector_dpms, 75 + .detect = rcar_du_hdmi_connector_detect, 76 + .fill_modes = drm_helper_probe_single_connector_modes, 77 + .destroy = rcar_du_hdmi_connector_destroy, 78 + }; 79 + 80 + int rcar_du_hdmi_connector_init(struct rcar_du_device *rcdu, 81 + struct rcar_du_encoder *renc) 82 + { 83 + struct drm_encoder *encoder = rcar_encoder_to_drm_encoder(renc); 84 + struct rcar_du_connector *rcon; 85 + struct drm_connector *connector; 86 + int ret; 87 + 88 + rcon = devm_kzalloc(rcdu->dev, sizeof(*rcon), GFP_KERNEL); 89 + if (rcon == NULL) 90 + return -ENOMEM; 91 + 92 + connector = &rcon->connector; 93 + connector->display_info.width_mm = 0; 94 + connector->display_info.height_mm = 0; 95 + 96 + ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs, 97 + DRM_MODE_CONNECTOR_HDMIA); 98 + if (ret < 0) 99 + return ret; 100 + 101 + drm_connector_helper_add(connector, &connector_helper_funcs); 102 + ret = drm_connector_register(connector); 103 + if (ret < 0) 104 + return ret; 105 + 106 + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); 107 + drm_object_property_set_value(&connector->base, 108 + rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF); 109 + 110 + ret = drm_mode_connector_attach_encoder(connector, encoder); 111 + if (ret < 0) 112 + return ret; 113 + 114 + connector->encoder = encoder; 115 + rcon->encoder = renc; 116 + 117 + return 0; 118 + }
+31
drivers/gpu/drm/rcar-du/rcar_du_hdmicon.h
··· 1 + /* 2 + * R-Car Display Unit HDMI Connector 3 + * 4 + * Copyright (C) 2014 Renesas Electronics Corporation 5 + * 6 + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) 7 + * 8 + * This program is free software; you can redistribute it and/or modify 9 + * it under the terms of the GNU General Public License as published by 10 + * the Free Software Foundation; either version 2 of the License, or 11 + * (at your option) any later version. 12 + */ 13 + 14 + #ifndef __RCAR_DU_HDMICON_H__ 15 + #define __RCAR_DU_HDMICON_H__ 16 + 17 + struct rcar_du_device; 18 + struct rcar_du_encoder; 19 + 20 + #if IS_ENABLED(CONFIG_DRM_RCAR_HDMI) 21 + int rcar_du_hdmi_connector_init(struct rcar_du_device *rcdu, 22 + struct rcar_du_encoder *renc); 23 + #else 24 + static inline int rcar_du_hdmi_connector_init(struct rcar_du_device *rcdu, 25 + struct rcar_du_encoder *renc) 26 + { 27 + return -ENOSYS; 28 + } 29 + #endif 30 + 31 + #endif /* __RCAR_DU_HDMICON_H__ */
+151
drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c
··· 1 + /* 2 + * R-Car Display Unit HDMI Encoder 3 + * 4 + * Copyright (C) 2014 Renesas Electronics Corporation 5 + * 6 + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) 7 + * 8 + * This program is free software; you can redistribute it and/or modify 9 + * it under the terms of the GNU General Public License as published by 10 + * the Free Software Foundation; either version 2 of the License, or 11 + * (at your option) any later version. 12 + */ 13 + 14 + #include <linux/slab.h> 15 + 16 + #include <drm/drmP.h> 17 + #include <drm/drm_crtc.h> 18 + #include <drm/drm_crtc_helper.h> 19 + #include <drm/drm_encoder_slave.h> 20 + 21 + #include "rcar_du_drv.h" 22 + #include "rcar_du_encoder.h" 23 + #include "rcar_du_hdmienc.h" 24 + 25 + struct rcar_du_hdmienc { 26 + struct rcar_du_encoder *renc; 27 + struct device *dev; 28 + int dpms; 29 + }; 30 + 31 + #define to_rcar_hdmienc(e) (to_rcar_encoder(e)->hdmi) 32 + #define to_slave_funcs(e) (to_rcar_encoder(e)->slave.slave_funcs) 33 + 34 + static void rcar_du_hdmienc_dpms(struct drm_encoder *encoder, int mode) 35 + { 36 + struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder); 37 + struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder); 38 + 39 + if (hdmienc->dpms == mode) 40 + return; 41 + 42 + if (sfuncs->dpms) 43 + sfuncs->dpms(encoder, mode); 44 + 45 + hdmienc->dpms = mode; 46 + } 47 + 48 + static bool rcar_du_hdmienc_mode_fixup(struct drm_encoder *encoder, 49 + const struct drm_display_mode *mode, 50 + struct drm_display_mode *adjusted_mode) 51 + { 52 + struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder); 53 + 54 + if (sfuncs->mode_fixup == NULL) 55 + return true; 56 + 57 + return sfuncs->mode_fixup(encoder, mode, adjusted_mode); 58 + } 59 + 60 + static void rcar_du_hdmienc_mode_prepare(struct drm_encoder *encoder) 61 + { 62 + rcar_du_hdmienc_dpms(encoder, DRM_MODE_DPMS_OFF); 63 + } 64 + 65 + static void rcar_du_hdmienc_mode_commit(struct drm_encoder *encoder) 66 + { 67 + rcar_du_hdmienc_dpms(encoder, DRM_MODE_DPMS_ON); 68 + } 69 + 70 + static void rcar_du_hdmienc_mode_set(struct drm_encoder *encoder, 71 + struct drm_display_mode *mode, 72 + struct drm_display_mode *adjusted_mode) 73 + { 74 + struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder); 75 + struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder); 76 + 77 + if (sfuncs->mode_set) 78 + sfuncs->mode_set(encoder, mode, adjusted_mode); 79 + 80 + rcar_du_crtc_route_output(encoder->crtc, hdmienc->renc->output); 81 + } 82 + 83 + static const struct drm_encoder_helper_funcs encoder_helper_funcs = { 84 + .dpms = rcar_du_hdmienc_dpms, 85 + .mode_fixup = rcar_du_hdmienc_mode_fixup, 86 + .prepare = rcar_du_hdmienc_mode_prepare, 87 + .commit = rcar_du_hdmienc_mode_commit, 88 + .mode_set = rcar_du_hdmienc_mode_set, 89 + }; 90 + 91 + static void rcar_du_hdmienc_cleanup(struct drm_encoder *encoder) 92 + { 93 + struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder); 94 + 95 + rcar_du_hdmienc_dpms(encoder, DRM_MODE_DPMS_OFF); 96 + 97 + drm_encoder_cleanup(encoder); 98 + put_device(hdmienc->dev); 99 + } 100 + 101 + static const struct drm_encoder_funcs encoder_funcs = { 102 + .destroy = rcar_du_hdmienc_cleanup, 103 + }; 104 + 105 + int rcar_du_hdmienc_init(struct rcar_du_device *rcdu, 106 + struct rcar_du_encoder *renc, struct device_node *np) 107 + { 108 + struct drm_encoder *encoder = rcar_encoder_to_drm_encoder(renc); 109 + struct drm_i2c_encoder_driver *driver; 110 + struct i2c_client *i2c_slave; 111 + struct rcar_du_hdmienc *hdmienc; 112 + int ret; 113 + 114 + hdmienc = devm_kzalloc(rcdu->dev, sizeof(*hdmienc), GFP_KERNEL); 115 + if (hdmienc == NULL) 116 + return -ENOMEM; 117 + 118 + /* Locate the slave I2C device and driver. */ 119 + i2c_slave = of_find_i2c_device_by_node(np); 120 + if (!i2c_slave || !i2c_get_clientdata(i2c_slave)) 121 + return -EPROBE_DEFER; 122 + 123 + hdmienc->dev = &i2c_slave->dev; 124 + 125 + if (hdmienc->dev->driver == NULL) { 126 + ret = -EPROBE_DEFER; 127 + goto error; 128 + } 129 + 130 + /* Initialize the slave encoder. */ 131 + driver = to_drm_i2c_encoder_driver(to_i2c_driver(hdmienc->dev->driver)); 132 + ret = driver->encoder_init(i2c_slave, rcdu->ddev, &renc->slave); 133 + if (ret < 0) 134 + goto error; 135 + 136 + ret = drm_encoder_init(rcdu->ddev, encoder, &encoder_funcs, 137 + DRM_MODE_ENCODER_TMDS); 138 + if (ret < 0) 139 + goto error; 140 + 141 + drm_encoder_helper_add(encoder, &encoder_helper_funcs); 142 + 143 + renc->hdmi = hdmienc; 144 + hdmienc->renc = renc; 145 + 146 + return 0; 147 + 148 + error: 149 + put_device(hdmienc->dev); 150 + return ret; 151 + }
+35
drivers/gpu/drm/rcar-du/rcar_du_hdmienc.h
··· 1 + /* 2 + * R-Car Display Unit HDMI Encoder 3 + * 4 + * Copyright (C) 2014 Renesas Electronics Corporation 5 + * 6 + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) 7 + * 8 + * This program is free software; you can redistribute it and/or modify 9 + * it under the terms of the GNU General Public License as published by 10 + * the Free Software Foundation; either version 2 of the License, or 11 + * (at your option) any later version. 12 + */ 13 + 14 + #ifndef __RCAR_DU_HDMIENC_H__ 15 + #define __RCAR_DU_HDMIENC_H__ 16 + 17 + #include <linux/module.h> 18 + 19 + struct device_node; 20 + struct rcar_du_device; 21 + struct rcar_du_encoder; 22 + 23 + #if IS_ENABLED(CONFIG_DRM_RCAR_HDMI) 24 + int rcar_du_hdmienc_init(struct rcar_du_device *rcdu, 25 + struct rcar_du_encoder *renc, struct device_node *np); 26 + #else 27 + static inline int rcar_du_hdmienc_init(struct rcar_du_device *rcdu, 28 + struct rcar_du_encoder *renc, 29 + struct device_node *np) 30 + { 31 + return -ENOSYS; 32 + } 33 + #endif 34 + 35 + #endif /* __RCAR_DU_HDMIENC_H__ */
+1
drivers/gpu/drm/rcar-du/rcar_du_kms.c
··· 199 199 enum rcar_du_encoder_type type; 200 200 } encoders[] = { 201 201 { "adi,adv7123", RCAR_DU_ENCODER_VGA }, 202 + { "adi,adv7511w", RCAR_DU_ENCODER_HDMI }, 202 203 { "thine,thc63lvdm83d", RCAR_DU_ENCODER_LVDS }, 203 204 }; 204 205