[go: nahoru, domu]

1/*
2 * Copyright 2014 Red Hat Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
21 *
22 * Authors: Ben Skeggs
23 */
24
25#include <core/os.h>
26#include <nvif/event.h>
27
28#include <subdev/i2c.h>
29
30#include "outpdp.h"
31#include "conn.h"
32#include "dport.h"
33
34int
35nvkm_output_dp_train(struct nvkm_output *base, u32 datarate, bool wait)
36{
37	struct nvkm_output_dp *outp = (void *)base;
38	bool retrain = true;
39	u8 link[2], stat[3];
40	u32 linkrate;
41	int ret, i;
42
43	/* check that the link is trained at a high enough rate */
44	ret = nv_rdaux(outp->base.edid, DPCD_LC00_LINK_BW_SET, link, 2);
45	if (ret) {
46		DBG("failed to read link config, assuming no sink\n");
47		goto done;
48	}
49
50	linkrate = link[0] * 27000 * (link[1] & DPCD_LC01_LANE_COUNT_SET);
51	linkrate = (linkrate * 8) / 10; /* 8B/10B coding overhead */
52	datarate = (datarate + 9) / 10; /* -> decakilobits */
53	if (linkrate < datarate) {
54		DBG("link not trained at sufficient rate\n");
55		goto done;
56	}
57
58	/* check that link is still trained */
59	ret = nv_rdaux(outp->base.edid, DPCD_LS02, stat, 3);
60	if (ret) {
61		DBG("failed to read link status, assuming no sink\n");
62		goto done;
63	}
64
65	if (stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE) {
66		for (i = 0; i < (link[1] & DPCD_LC01_LANE_COUNT_SET); i++) {
67			u8 lane = (stat[i >> 1] >> ((i & 1) * 4)) & 0x0f;
68			if (!(lane & DPCD_LS02_LANE0_CR_DONE) ||
69			    !(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) ||
70			    !(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED)) {
71				DBG("lane %d not equalised\n", lane);
72				goto done;
73			}
74		}
75		retrain = false;
76	} else {
77		DBG("no inter-lane alignment\n");
78	}
79
80done:
81	if (retrain || !atomic_read(&outp->lt.done)) {
82		/* no sink, but still need to configure source */
83		if (outp->dpcd[DPCD_RC00_DPCD_REV] == 0x00) {
84			outp->dpcd[DPCD_RC01_MAX_LINK_RATE] =
85				outp->base.info.dpconf.link_bw;
86			outp->dpcd[DPCD_RC02] =
87				outp->base.info.dpconf.link_nr;
88		}
89		atomic_set(&outp->lt.done, 0);
90		schedule_work(&outp->lt.work);
91	} else {
92		nvkm_notify_get(&outp->irq);
93	}
94
95	if (wait) {
96		if (!wait_event_timeout(outp->lt.wait,
97					atomic_read(&outp->lt.done),
98					msecs_to_jiffies(2000)))
99			ret = -ETIMEDOUT;
100	}
101
102	return ret;
103}
104
105static void
106nvkm_output_dp_enable(struct nvkm_output_dp *outp, bool present)
107{
108	struct nouveau_i2c_port *port = outp->base.edid;
109	if (present) {
110		if (!outp->present) {
111			nouveau_i2c(port)->acquire_pad(port, 0);
112			DBG("aux power -> always\n");
113			outp->present = true;
114		}
115		nvkm_output_dp_train(&outp->base, 0, true);
116	} else {
117		if (outp->present) {
118			nouveau_i2c(port)->release_pad(port);
119			DBG("aux power -> demand\n");
120			outp->present = false;
121		}
122		atomic_set(&outp->lt.done, 0);
123	}
124}
125
126static void
127nvkm_output_dp_detect(struct nvkm_output_dp *outp)
128{
129	struct nouveau_i2c_port *port = outp->base.edid;
130	int ret = nouveau_i2c(port)->acquire_pad(port, 0);
131	if (ret == 0) {
132		ret = nv_rdaux(outp->base.edid, DPCD_RC00_DPCD_REV,
133			       outp->dpcd, sizeof(outp->dpcd));
134		nvkm_output_dp_enable(outp, ret == 0);
135		nouveau_i2c(port)->release_pad(port);
136	}
137}
138
139static int
140nvkm_output_dp_hpd(struct nvkm_notify *notify)
141{
142	struct nvkm_connector *conn = container_of(notify, typeof(*conn), hpd);
143	struct nvkm_output_dp *outp;
144	struct nouveau_disp *disp = nouveau_disp(conn);
145	const struct nvkm_i2c_ntfy_rep *line = notify->data;
146	struct nvif_notify_conn_rep_v0 rep = {};
147
148	list_for_each_entry(outp, &disp->outp, base.head) {
149		if (outp->base.conn == conn &&
150		    outp->info.type == DCB_OUTPUT_DP) {
151			DBG("HPD: %d\n", line->mask);
152			nvkm_output_dp_detect(outp);
153
154			if (line->mask & NVKM_I2C_UNPLUG)
155				rep.mask |= NVIF_NOTIFY_CONN_V0_UNPLUG;
156			if (line->mask & NVKM_I2C_PLUG)
157				rep.mask |= NVIF_NOTIFY_CONN_V0_PLUG;
158
159			nvkm_event_send(&disp->hpd, rep.mask, conn->index,
160					&rep, sizeof(rep));
161			return NVKM_NOTIFY_KEEP;
162		}
163	}
164
165	WARN_ON(1);
166	return NVKM_NOTIFY_DROP;
167}
168
169static int
170nvkm_output_dp_irq(struct nvkm_notify *notify)
171{
172	struct nvkm_output_dp *outp = container_of(notify, typeof(*outp), irq);
173	struct nouveau_disp *disp = nouveau_disp(outp);
174	const struct nvkm_i2c_ntfy_rep *line = notify->data;
175	struct nvif_notify_conn_rep_v0 rep = {
176		.mask = NVIF_NOTIFY_CONN_V0_IRQ,
177	};
178	int index = outp->base.info.connector;
179
180	DBG("IRQ: %d\n", line->mask);
181	nvkm_output_dp_train(&outp->base, 0, true);
182
183	nvkm_event_send(&disp->hpd, rep.mask, index, &rep, sizeof(rep));
184	return NVKM_NOTIFY_DROP;
185}
186
187int
188_nvkm_output_dp_fini(struct nouveau_object *object, bool suspend)
189{
190	struct nvkm_output_dp *outp = (void *)object;
191	nvkm_notify_put(&outp->irq);
192	nvkm_output_dp_enable(outp, false);
193	return nvkm_output_fini(&outp->base, suspend);
194}
195
196int
197_nvkm_output_dp_init(struct nouveau_object *object)
198{
199	struct nvkm_output_dp *outp = (void *)object;
200	nvkm_output_dp_detect(outp);
201	return nvkm_output_init(&outp->base);
202}
203
204void
205_nvkm_output_dp_dtor(struct nouveau_object *object)
206{
207	struct nvkm_output_dp *outp = (void *)object;
208	nvkm_notify_fini(&outp->irq);
209	nvkm_output_destroy(&outp->base);
210}
211
212int
213nvkm_output_dp_create_(struct nouveau_object *parent,
214		       struct nouveau_object *engine,
215		       struct nouveau_oclass *oclass,
216		       struct dcb_output *info, int index,
217		       int length, void **pobject)
218{
219	struct nouveau_bios *bios = nouveau_bios(parent);
220	struct nouveau_i2c *i2c = nouveau_i2c(parent);
221	struct nvkm_output_dp *outp;
222	u8  hdr, cnt, len;
223	u32 data;
224	int ret;
225
226	ret = nvkm_output_create_(parent, engine, oclass, info, index,
227				  length, pobject);
228	outp = *pobject;
229	if (ret)
230		return ret;
231
232	nvkm_notify_fini(&outp->base.conn->hpd);
233
234	/* access to the aux channel is not optional... */
235	if (!outp->base.edid) {
236		ERR("aux channel not found\n");
237		return -ENODEV;
238	}
239
240	/* nor is the bios data for this output... */
241	data = nvbios_dpout_match(bios, outp->base.info.hasht,
242				  outp->base.info.hashm, &outp->version,
243				  &hdr, &cnt, &len, &outp->info);
244	if (!data) {
245		ERR("no bios dp data\n");
246		return -ENODEV;
247	}
248
249	DBG("bios dp %02x %02x %02x %02x\n", outp->version, hdr, cnt, len);
250
251	/* link training */
252	INIT_WORK(&outp->lt.work, nouveau_dp_train);
253	init_waitqueue_head(&outp->lt.wait);
254	atomic_set(&outp->lt.done, 0);
255
256	/* link maintenance */
257	ret = nvkm_notify_init(NULL, &i2c->event, nvkm_output_dp_irq, true,
258			       &(struct nvkm_i2c_ntfy_req) {
259				.mask = NVKM_I2C_IRQ,
260				.port = outp->base.edid->index,
261			       },
262			       sizeof(struct nvkm_i2c_ntfy_req),
263			       sizeof(struct nvkm_i2c_ntfy_rep),
264			       &outp->irq);
265	if (ret) {
266		ERR("error monitoring aux irq event: %d\n", ret);
267		return ret;
268	}
269
270	/* hotplug detect, replaces gpio-based mechanism with aux events */
271	ret = nvkm_notify_init(NULL, &i2c->event, nvkm_output_dp_hpd, true,
272			       &(struct nvkm_i2c_ntfy_req) {
273				.mask = NVKM_I2C_PLUG | NVKM_I2C_UNPLUG,
274				.port = outp->base.edid->index,
275			       },
276			       sizeof(struct nvkm_i2c_ntfy_req),
277			       sizeof(struct nvkm_i2c_ntfy_rep),
278			       &outp->base.conn->hpd);
279	if (ret) {
280		ERR("error monitoring aux hpd events: %d\n", ret);
281		return ret;
282	}
283
284	return 0;
285}
286
287int
288_nvkm_output_dp_ctor(struct nouveau_object *parent,
289		     struct nouveau_object *engine,
290		     struct nouveau_oclass *oclass, void *info, u32 index,
291		     struct nouveau_object **pobject)
292{
293	struct nvkm_output_dp *outp;
294	int ret;
295
296	ret = nvkm_output_dp_create(parent, engine, oclass, info, index, &outp);
297	*pobject = nv_object(outp);
298	if (ret)
299		return ret;
300
301	return 0;
302}
303