Merge branch 'drm-fixes-4.7' of git://people.freedesktop.org/~agd5f/linux into drm...
[linux-2.6-block.git] / drivers / gpu / drm / arc / arcpgu_hdmi.c
CommitLineData
51dacf20
CP
1/*
2 * ARC PGU DRM driver.
3 *
4 * Copyright (C) 2016 Synopsys, Inc. (www.synopsys.com)
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 */
16
17#include <drm/drm_crtc_helper.h>
18#include <drm/drm_encoder_slave.h>
19#include <drm/drm_atomic_helper.h>
20
21#include "arcpgu.h"
22
23struct arcpgu_drm_connector {
24 struct drm_connector connector;
25 struct drm_encoder_slave *encoder_slave;
26};
27
28static int arcpgu_drm_connector_get_modes(struct drm_connector *connector)
29{
30 const struct drm_encoder_slave_funcs *sfuncs;
31 struct drm_encoder_slave *slave;
32 struct arcpgu_drm_connector *con =
33 container_of(connector, struct arcpgu_drm_connector, connector);
34
35 slave = con->encoder_slave;
36 if (slave == NULL) {
37 dev_err(connector->dev->dev,
38 "connector_get_modes: cannot find slave encoder for connector\n");
39 return 0;
40 }
41
42 sfuncs = slave->slave_funcs;
43 if (sfuncs->get_modes == NULL)
44 return 0;
45
46 return sfuncs->get_modes(&slave->base, connector);
47}
48
49struct drm_encoder *
50arcpgu_drm_connector_best_encoder(struct drm_connector *connector)
51{
52 struct drm_encoder_slave *slave;
53 struct arcpgu_drm_connector *con =
54 container_of(connector, struct arcpgu_drm_connector, connector);
55
56 slave = con->encoder_slave;
57 if (slave == NULL) {
58 dev_err(connector->dev->dev,
59 "connector_best_encoder: cannot find slave encoder for connector\n");
60 return NULL;
61 }
62
63 return &slave->base;
64}
65
66static enum drm_connector_status
67arcpgu_drm_connector_detect(struct drm_connector *connector, bool force)
68{
69 enum drm_connector_status status = connector_status_unknown;
70 const struct drm_encoder_slave_funcs *sfuncs;
71 struct drm_encoder_slave *slave;
72
73 struct arcpgu_drm_connector *con =
74 container_of(connector, struct arcpgu_drm_connector, connector);
75
76 slave = con->encoder_slave;
77 if (slave == NULL) {
78 dev_err(connector->dev->dev,
79 "connector_detect: cannot find slave encoder for connector\n");
80 return status;
81 }
82
83 sfuncs = slave->slave_funcs;
84 if (sfuncs && sfuncs->detect)
85 return sfuncs->detect(&slave->base, connector);
86
87 dev_err(connector->dev->dev, "connector_detect: could not detect slave funcs\n");
88 return status;
89}
90
91static void arcpgu_drm_connector_destroy(struct drm_connector *connector)
92{
93 drm_connector_unregister(connector);
94 drm_connector_cleanup(connector);
95}
96
97static const struct drm_connector_helper_funcs
98arcpgu_drm_connector_helper_funcs = {
99 .get_modes = arcpgu_drm_connector_get_modes,
100 .best_encoder = arcpgu_drm_connector_best_encoder,
101};
102
103static const struct drm_connector_funcs arcpgu_drm_connector_funcs = {
104 .dpms = drm_helper_connector_dpms,
105 .reset = drm_atomic_helper_connector_reset,
106 .detect = arcpgu_drm_connector_detect,
107 .fill_modes = drm_helper_probe_single_connector_modes,
108 .destroy = arcpgu_drm_connector_destroy,
109 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
110 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
111};
112
113static struct drm_encoder_helper_funcs arcpgu_drm_encoder_helper_funcs = {
114 .dpms = drm_i2c_encoder_dpms,
115 .mode_fixup = drm_i2c_encoder_mode_fixup,
116 .mode_set = drm_i2c_encoder_mode_set,
117 .prepare = drm_i2c_encoder_prepare,
118 .commit = drm_i2c_encoder_commit,
119 .detect = drm_i2c_encoder_detect,
120};
121
122static struct drm_encoder_funcs arcpgu_drm_encoder_funcs = {
123 .destroy = drm_encoder_cleanup,
124};
125
126int arcpgu_drm_hdmi_init(struct drm_device *drm, struct device_node *np)
127{
128 struct arcpgu_drm_connector *arcpgu_connector;
129 struct drm_i2c_encoder_driver *driver;
130 struct drm_encoder_slave *encoder;
131 struct drm_connector *connector;
132 struct i2c_client *i2c_slave;
133 int ret;
134
135 encoder = devm_kzalloc(drm->dev, sizeof(*encoder), GFP_KERNEL);
136 if (encoder == NULL)
137 return -ENOMEM;
138
139 i2c_slave = of_find_i2c_device_by_node(np);
140 if (!i2c_slave || !i2c_get_clientdata(i2c_slave)) {
141 dev_err(drm->dev, "failed to find i2c slave encoder\n");
142 return -EPROBE_DEFER;
143 }
144
145 if (i2c_slave->dev.driver == NULL) {
146 dev_err(drm->dev, "failed to find i2c slave driver\n");
147 return -EPROBE_DEFER;
148 }
149
150 driver =
151 to_drm_i2c_encoder_driver(to_i2c_driver(i2c_slave->dev.driver));
152 ret = driver->encoder_init(i2c_slave, drm, encoder);
153 if (ret) {
154 dev_err(drm->dev, "failed to initialize i2c encoder slave\n");
155 return ret;
156 }
157
158 encoder->base.possible_crtcs = 1;
159 encoder->base.possible_clones = 0;
160 ret = drm_encoder_init(drm, &encoder->base, &arcpgu_drm_encoder_funcs,
161 DRM_MODE_ENCODER_TMDS, NULL);
162 if (ret)
163 return ret;
164
165 drm_encoder_helper_add(&encoder->base,
166 &arcpgu_drm_encoder_helper_funcs);
167
168 arcpgu_connector = devm_kzalloc(drm->dev, sizeof(*arcpgu_connector),
169 GFP_KERNEL);
170 if (!arcpgu_connector) {
171 ret = -ENOMEM;
172 goto error_encoder_cleanup;
173 }
174
175 connector = &arcpgu_connector->connector;
176 drm_connector_helper_add(connector, &arcpgu_drm_connector_helper_funcs);
177 ret = drm_connector_init(drm, connector, &arcpgu_drm_connector_funcs,
178 DRM_MODE_CONNECTOR_HDMIA);
179 if (ret < 0) {
180 dev_err(drm->dev, "failed to initialize drm connector\n");
181 goto error_encoder_cleanup;
182 }
183
184 ret = drm_mode_connector_attach_encoder(connector, &encoder->base);
185 if (ret < 0) {
186 dev_err(drm->dev, "could not attach connector to encoder\n");
187 drm_connector_unregister(connector);
188 goto error_connector_cleanup;
189 }
190
191 arcpgu_connector->encoder_slave = encoder;
192
193 return 0;
194
195error_connector_cleanup:
196 drm_connector_cleanup(connector);
197
198error_encoder_cleanup:
199 drm_encoder_cleanup(&encoder->base);
200 return ret;
201}