aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>2014-03-30 15:55:38 -0400
committerLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>2014-11-26 13:09:38 -0500
commit637e6194e0daf76e2c06cd78528e8d0a24eca3cd (patch)
treef778c0194563c9e12899bcaa492abc91e75b429c
parent69746b4112e3c83442c1df59b7011ab3c5ed2d5a (diff)
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>
-rw-r--r--drivers/gpu/drm/rcar-du/Kconfig11
-rw-r--r--drivers/gpu/drm/rcar-du/Makefile2
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_encoder.c30
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_encoder.h3
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c118
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_hdmicon.h31
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c151
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_hdmienc.h35
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_kms.c1
9 files changed, 375 insertions, 7 deletions
diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig
index c96f6089f8bf..2324a526de65 100644
--- a/drivers/gpu/drm/rcar-du/Kconfig
+++ b/drivers/gpu/drm/rcar-du/Kconfig
@@ -11,10 +11,17 @@ config DRM_RCAR_DU
11 Choose this option if you have an R-Car chipset. 11 Choose this option if you have an R-Car chipset.
12 If M is selected the module will be called rcar-du-drm. 12 If M is selected the module will be called rcar-du-drm.
13 13
14config 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
14config DRM_RCAR_LVDS 21config DRM_RCAR_LVDS
15 bool "R-Car DU LVDS Encoder Support" 22 bool "R-Car DU LVDS Encoder Support"
16 depends on DRM_RCAR_DU 23 depends on DRM_RCAR_DU
17 depends on ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST 24 depends on ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST
18 help 25 help
19 Enable support the R-Car Display Unit embedded LVDS encoders 26 Enable support for the R-Car Display Unit embedded LVDS encoders
20 (currently only on R8A7790). 27 (currently only on R8A7790 and R8A7791).
diff --git a/drivers/gpu/drm/rcar-du/Makefile b/drivers/gpu/drm/rcar-du/Makefile
index 12b8d4477835..05de1c4097af 100644
--- a/drivers/gpu/drm/rcar-du/Makefile
+++ b/drivers/gpu/drm/rcar-du/Makefile
@@ -7,6 +7,8 @@ rcar-du-drm-y := rcar_du_crtc.o \
7 rcar_du_plane.o \ 7 rcar_du_plane.o \
8 rcar_du_vgacon.o 8 rcar_du_vgacon.o
9 9
10rcar-du-drm-$(CONFIG_DRM_RCAR_HDMI) += rcar_du_hdmicon.o \
11 rcar_du_hdmienc.o
10rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS) += rcar_du_lvdsenc.o 12rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS) += rcar_du_lvdsenc.o
11 13
12obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o 14obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
index e88e63b06b09..34a122a39664 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
@@ -19,6 +19,8 @@
19 19
20#include "rcar_du_drv.h" 20#include "rcar_du_drv.h"
21#include "rcar_du_encoder.h" 21#include "rcar_du_encoder.h"
22#include "rcar_du_hdmicon.h"
23#include "rcar_du_hdmienc.h"
22#include "rcar_du_kms.h" 24#include "rcar_du_kms.h"
23#include "rcar_du_lvdscon.h" 25#include "rcar_du_lvdscon.h"
24#include "rcar_du_lvdsenc.h" 26#include "rcar_du_lvdsenc.h"
@@ -177,6 +179,9 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
177 case RCAR_DU_ENCODER_LVDS: 179 case RCAR_DU_ENCODER_LVDS:
178 encoder_type = DRM_MODE_ENCODER_LVDS; 180 encoder_type = DRM_MODE_ENCODER_LVDS;
179 break; 181 break;
182 case RCAR_DU_ENCODER_HDMI:
183 encoder_type = DRM_MODE_ENCODER_TMDS;
184 break;
180 case RCAR_DU_ENCODER_NONE: 185 case RCAR_DU_ENCODER_NONE:
181 default: 186 default:
182 /* No external encoder, use the internal encoder type. */ 187 /* No external encoder, use the internal encoder type. */
@@ -184,12 +189,24 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
184 break; 189 break;
185 } 190 }
186 191
187 ret = drm_encoder_init(rcdu->ddev, encoder, &encoder_funcs, 192 if (type == RCAR_DU_ENCODER_HDMI) {
188 encoder_type); 193 if (renc->lvds) {
189 if (ret < 0) 194 dev_err(rcdu->dev,
190 return ret; 195 "Chaining LVDS and HDMI encoders not supported\n");
196 return -EINVAL;
197 }
191 198
192 drm_encoder_helper_add(encoder, &encoder_helper_funcs); 199 ret = rcar_du_hdmienc_init(rcdu, renc, enc_node);
200 if (ret < 0)
201 return ret;
202 } else {
203 ret = drm_encoder_init(rcdu->ddev, encoder, &encoder_funcs,
204 encoder_type);
205 if (ret < 0)
206 return ret;
207
208 drm_encoder_helper_add(encoder, &encoder_helper_funcs);
209 }
193 210
194 switch (encoder_type) { 211 switch (encoder_type) {
195 case DRM_MODE_ENCODER_LVDS: 212 case DRM_MODE_ENCODER_LVDS:
@@ -198,6 +215,9 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
198 case DRM_MODE_ENCODER_DAC: 215 case DRM_MODE_ENCODER_DAC:
199 return rcar_du_vga_connector_init(rcdu, renc); 216 return rcar_du_vga_connector_init(rcdu, renc);
200 217
218 case DRM_MODE_ENCODER_TMDS:
219 return rcar_du_hdmi_connector_init(rcdu, renc);
220
201 default: 221 default:
202 return -EINVAL; 222 return -EINVAL;
203 } 223 }
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
index c4dccdbcff33..719b6f2a031c 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
@@ -18,6 +18,7 @@
18#include <drm/drm_encoder_slave.h> 18#include <drm/drm_encoder_slave.h>
19 19
20struct rcar_du_device; 20struct rcar_du_device;
21struct rcar_du_hdmienc;
21struct rcar_du_lvdsenc; 22struct rcar_du_lvdsenc;
22 23
23enum rcar_du_encoder_type { 24enum rcar_du_encoder_type {
@@ -25,11 +26,13 @@ enum rcar_du_encoder_type {
25 RCAR_DU_ENCODER_NONE, 26 RCAR_DU_ENCODER_NONE,
26 RCAR_DU_ENCODER_VGA, 27 RCAR_DU_ENCODER_VGA,
27 RCAR_DU_ENCODER_LVDS, 28 RCAR_DU_ENCODER_LVDS,
29 RCAR_DU_ENCODER_HDMI,
28}; 30};
29 31
30struct rcar_du_encoder { 32struct rcar_du_encoder {
31 struct drm_encoder_slave slave; 33 struct drm_encoder_slave slave;
32 enum rcar_du_output output; 34 enum rcar_du_output output;
35 struct rcar_du_hdmienc *hdmi;
33 struct rcar_du_lvdsenc *lvds; 36 struct rcar_du_lvdsenc *lvds;
34}; 37};
35 38
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c b/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c
new file mode 100644
index 000000000000..8abaaf258f45
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c
@@ -0,0 +1,118 @@
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
26static 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
37static 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
49static 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
55static void rcar_du_hdmi_connector_destroy(struct drm_connector *connector)
56{
57 drm_connector_unregister(connector);
58 drm_connector_cleanup(connector);
59}
60
61static enum drm_connector_status
62rcar_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
73static 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
80int 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}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.h b/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.h
new file mode 100644
index 000000000000..87daa949227f
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.h
@@ -0,0 +1,31 @@
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
17struct rcar_du_device;
18struct rcar_du_encoder;
19
20#if IS_ENABLED(CONFIG_DRM_RCAR_HDMI)
21int rcar_du_hdmi_connector_init(struct rcar_du_device *rcdu,
22 struct rcar_du_encoder *renc);
23#else
24static 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__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c b/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c
new file mode 100644
index 000000000000..359bc999a9c8
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c
@@ -0,0 +1,151 @@
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
25struct 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
34static 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
48static 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
60static void rcar_du_hdmienc_mode_prepare(struct drm_encoder *encoder)
61{
62 rcar_du_hdmienc_dpms(encoder, DRM_MODE_DPMS_OFF);
63}
64
65static void rcar_du_hdmienc_mode_commit(struct drm_encoder *encoder)
66{
67 rcar_du_hdmienc_dpms(encoder, DRM_MODE_DPMS_ON);
68}
69
70static 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
83static 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
91static 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
101static const struct drm_encoder_funcs encoder_funcs = {
102 .destroy = rcar_du_hdmienc_cleanup,
103};
104
105int 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
148error:
149 put_device(hdmienc->dev);
150 return ret;
151}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.h b/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.h
new file mode 100644
index 000000000000..2ff0128ac8e1
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.h
@@ -0,0 +1,35 @@
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
19struct device_node;
20struct rcar_du_device;
21struct rcar_du_encoder;
22
23#if IS_ENABLED(CONFIG_DRM_RCAR_HDMI)
24int rcar_du_hdmienc_init(struct rcar_du_device *rcdu,
25 struct rcar_du_encoder *renc, struct device_node *np);
26#else
27static 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__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
index 543fa8bde616..0c5ee616b5a3 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
@@ -199,6 +199,7 @@ static int rcar_du_encoders_init_one(struct rcar_du_device *rcdu,
199 enum rcar_du_encoder_type type; 199 enum rcar_du_encoder_type type;
200 } encoders[] = { 200 } encoders[] = {
201 { "adi,adv7123", RCAR_DU_ENCODER_VGA }, 201 { "adi,adv7123", RCAR_DU_ENCODER_VGA },
202 { "adi,adv7511w", RCAR_DU_ENCODER_HDMI },
202 { "thine,thc63lvdm83d", RCAR_DU_ENCODER_LVDS }, 203 { "thine,thc63lvdm83d", RCAR_DU_ENCODER_LVDS },
203 }; 204 };
204 205